From cadaf6b5a8e74de0fb1a5e0322cf0b1af521ccfd Mon Sep 17 00:00:00 2001 From: jurgenwigg Date: Wed, 8 Apr 2026 14:23:32 +0200 Subject: [PATCH 1/8] initial commit --- src/codeaudit/__about__.py | 4 +- src/codeaudit/__init__.py | 2 +- src/codeaudit/altairplots.py | 6 +- src/codeaudit/api_helpers.py | 12 +- src/codeaudit/api_interfaces.py | 43 +- src/codeaudit/api_reporting.py | 3 +- src/codeaudit/checkmodules.py | 2 +- src/codeaudit/codeaudit.py | 6 +- src/codeaudit/codeaudit_dashboard.py | 116 ++-- src/codeaudit/dashboard_reports.py | 12 +- src/codeaudit/filehelpfunctions.py | 4 +- src/codeaudit/issuevalidations.py | 1 + src/codeaudit/privacy_lint.py | 20 +- src/codeaudit/pypi_package_scan.py | 9 +- src/codeaudit/reporting.py | 105 ++- src/codeaudit/security_checks.py | 7 +- src/codeaudit/suppression.py | 4 +- src/codeaudit/totals.py | 13 +- tests/count_lines_file1.py | 11 +- tests/spytestdir/clean.py | 2 +- tests/spytestdir/elastic.py | 14 +- tests/spytestdir/example1.py | 3 +- tests/spytestdir/klyne.py | 7 +- tests/spytestdir/mixed.py | 4 +- tests/spytestdir/telemetry.py | 41 +- tests/spytestdir/telemetryfile2.py | 41 +- tests/suppression/sastsuppression_0.py | 63 +- tests/suppression/sastsuppression_1.py | 36 +- tests/suppression/sastsuppression_2.py | 37 +- tests/test_apicalls.py | 40 +- tests/test_basicpatterns.py | 30 +- tests/test_chmod.py | 19 +- tests/test_constructspart2.py | 67 +- tests/test_correctexceptionuse.py | 13 +- tests/test_count_commentlines.py | 20 +- tests/test_directorycreation.py | 55 +- tests/test_directorycreation2.py | 91 ++- tests/test_edgecases.py | 29 +- tests/test_hashstrenght.py | 13 +- tests/test_modulecheck.py | 17 +- tests/test_obfuscatingbuiltins.py | 15 +- tests/test_oschecks.py | 37 +- tests/test_pylintreport.py | 42 +- tests/test_pypiscan.py | 45 +- tests/test_random.py | 15 +- tests/test_secretfinding.py | 17 +- tests/test_standardlibconstructs.py | 65 +- tests/test_subprocess.py | 15 +- tests/test_suppression.py | 27 +- tests/test_suppressionlogic.py | 23 +- tests/test_totalscheck.py | 19 +- tests/test_zstd.py | 6 +- tests/validationfiles/allshit.py | 305 +++++---- tests/validationfiles/apivalidations.py | 85 +-- tests/validationfiles/assert.py | 2 +- tests/validationfiles/base64.py | 5 +- tests/validationfiles/chmod_things.py | 18 +- tests/validationfiles/complexitycheck.py | 2 + tests/validationfiles/correctcounts.py | 7 +- tests/validationfiles/directorycreation.py | 2 - tests/validationfiles/directorycreation2.py | 4 +- tests/validationfiles/eval.py | 22 +- tests/validationfiles/eval2.py | 8 +- tests/validationfiles/exception.py | 10 +- tests/validationfiles/file3.py | 8 +- tests/validationfiles/file_with_warnings.py | 608 ++++++++---------- tests/validationfiles/gzip.py | 15 +- tests/validationfiles/hashcheck.py | 73 +-- tests/validationfiles/httpserver.py | 19 +- tests/validationfiles/inputstatement.py | 1 + tests/validationfiles/marshal.py | 19 +- tests/validationfiles/modulecheck.py | 20 +- tests/validationfiles/multiprocessing.py | 25 +- tests/validationfiles/obfuscating.py | 48 +- tests/validationfiles/oschecks.py | 16 +- tests/validationfiles/pickle.py | 9 +- .../python2_file_willnotwork.py | 3 +- tests/validationfiles/random.py | 25 +- tests/validationfiles/shelve.py | 8 +- tests/validationfiles/shutil.py | 19 +- tests/validationfiles/subprocess.py | 50 +- tests/validationfiles/syslibrary.py | 7 +- tests/validationfiles/tarfilevalidation.py | 15 +- tests/validationfiles/validation1.py | 10 +- tests/validationfiles/validation2.py | 8 +- tests/validationfiles/xml.py | 16 +- tests/validationfiles/zipfile.py | 24 +- tests/validationfiles/zstd.py | 2 +- 88 files changed, 1487 insertions(+), 1379 deletions(-) diff --git a/src/codeaudit/__about__.py b/src/codeaudit/__about__.py index afbebcb..b626244 100644 --- a/src/codeaudit/__about__.py +++ b/src/codeaudit/__about__.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2025-present Maikel Mardjan +# SPDX-FileCopyrightText: 2025-present Maikel Mardjan # # SPDX-License-Identifier: GPL-3.0-or-later -__version__ = "1.6.2" +__version__ = "1.6.1" diff --git a/src/codeaudit/__init__.py b/src/codeaudit/__init__.py index 8439191..c27c1ea 100644 --- a/src/codeaudit/__init__.py +++ b/src/codeaudit/__init__.py @@ -1,4 +1,4 @@ # SPDX-FileCopyrightText: 2025-present Maikel Mardjan - https://nocomplexity.com/ # # SPDX-License-Identifier: GPL-3.0-or-later -from . __about__ import __version__ \ No newline at end of file +from .__about__ import __version__ diff --git a/src/codeaudit/altairplots.py b/src/codeaudit/altairplots.py index e254bdf..e0d8939 100644 --- a/src/codeaudit/altairplots.py +++ b/src/codeaudit/altairplots.py @@ -12,12 +12,12 @@ Altair Plotting functions for Python Code Audit (aka codeaudit) """ -import altair as alt -import pandas as pd - from collections import Counter from pathlib import Path +import altair as alt +import pandas as pd + def module_count_barchart(scanresult): """Create a bar chart showing module counts by category. diff --git a/src/codeaudit/api_helpers.py b/src/codeaudit/api_helpers.py index a9a8271..f29763f 100644 --- a/src/codeaudit/api_helpers.py +++ b/src/codeaudit/api_helpers.py @@ -12,18 +12,18 @@ Function to create nice APIs. So API helper functions. """ -import pandas as pd import html -from codeaudit.security_checks import ast_security_checks +import pandas as pd + +from codeaudit.api_interfaces import get_modules, get_overview +from codeaudit.checkmodules import get_all_modules from codeaudit.filehelpfunctions import ( - get_filename_from_path, collect_python_source_files, + get_filename_from_path, ) -from codeaudit.security_checks import perform_validations +from codeaudit.security_checks import ast_security_checks, perform_validations from codeaudit.suppression import filter_sast_results -from codeaudit.checkmodules import get_all_modules -from codeaudit.api_interfaces import get_modules, get_overview from codeaudit.totals import overview_per_file diff --git a/src/codeaudit/api_interfaces.py b/src/codeaudit/api_interfaces.py index 13f1e5c..1eda2b2 100644 --- a/src/codeaudit/api_interfaces.py +++ b/src/codeaudit/api_interfaces.py @@ -13,38 +13,37 @@ Public API functions for Python Code Audit aka codeaudit on pypi.org """ +import datetime +import json +import platform +from collections import Counter +from pathlib import Path + +import altair as alt +import pandas as pd + from codeaudit import __version__ +from codeaudit.checkmodules import ( + check_module_vulnerability, + get_all_modules, + get_imported_modules_by_file, + get_standard_library_modules, +) from codeaudit.filehelpfunctions import ( - get_filename_from_path, collect_python_source_files, + get_filename_from_path, is_ast_parsable, ) -from codeaudit.security_checks import perform_validations, ast_security_checks +from codeaudit.privacy_lint import data_egress_scan +from codeaudit.pypi_package_scan import get_package_source, get_pypi_download_info +from codeaudit.security_checks import ast_security_checks, perform_validations +from codeaudit.suppression import filter_sast_results from codeaudit.totals import ( - overview_per_file, get_statistics, overview_count, + overview_per_file, total_modules, ) -from codeaudit.checkmodules import ( - get_all_modules, - get_imported_modules_by_file, - get_standard_library_modules, - check_module_vulnerability, -) -from codeaudit.pypi_package_scan import get_pypi_download_info, get_package_source -from codeaudit.suppression import filter_sast_results -from codeaudit.privacy_lint import data_egress_scan - -from pathlib import Path -import json -import datetime -import pandas as pd -import platform -from collections import Counter - - -import altair as alt def version(): diff --git a/src/codeaudit/api_reporting.py b/src/codeaudit/api_reporting.py index 11e44a6..21386ca 100644 --- a/src/codeaudit/api_reporting.py +++ b/src/codeaudit/api_reporting.py @@ -19,9 +19,10 @@ """ -import pandas as pd from collections import Counter +import pandas as pd + def total_weaknesses(input_file): """Returns the total weaknesses found""" diff --git a/src/codeaudit/checkmodules.py b/src/codeaudit/checkmodules.py index 12d0dc3..a2ac4a0 100644 --- a/src/codeaudit/checkmodules.py +++ b/src/codeaudit/checkmodules.py @@ -14,8 +14,8 @@ """ import ast -import sys import json +import sys import urllib.request from codeaudit.filehelpfunctions import collect_python_source_files, read_in_source_file diff --git a/src/codeaudit/codeaudit.py b/src/codeaudit/codeaudit.py index 54922d9..86a7c9b 100644 --- a/src/codeaudit/codeaudit.py +++ b/src/codeaudit/codeaudit.py @@ -13,14 +13,16 @@ CLI functions for codeaudit """ -import fire # for working CLI with this PoC-thing (The Google way) import sys + +import fire # for working CLI with this PoC-thing (The Google way) + from codeaudit import __version__ from codeaudit.reporting import ( overview_report, + report_implemented_tests, report_module_information, scan_report, - report_implemented_tests, ) codeaudit_ascii_art = r""" diff --git a/src/codeaudit/codeaudit_dashboard.py b/src/codeaudit/codeaudit_dashboard.py index cb27bab..f8dfcfb 100644 --- a/src/codeaudit/codeaudit_dashboard.py +++ b/src/codeaudit/codeaudit_dashboard.py @@ -10,30 +10,19 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . -WASM Dashboard version of codeaudit - limited functionality - +WASM Dashboard version of codeaudit - limited functionality - """ -import inspect -import sys import asyncio - import datetime +import inspect import json +import sys import panel as pn -pn.extension('vega') +pn.extension("vega") -from codeaudit.api_interfaces import version, get_package_source -from codeaudit.api_helpers import _codeaudit_directory_scan_wasm - -from codeaudit.dashboard_reports import ( - report_sast_results, - report_used_modules, - get_info_text, - get_disclaimer_text, - create_statistics_overview, -) from codeaudit.altairplots import ( ast_nodes_overview, @@ -45,24 +34,35 @@ weaknesses_overview, weaknesses_radial_overview, ) +from codeaudit.api_helpers import _codeaudit_directory_scan_wasm +from codeaudit.api_interfaces import get_package_source, version +from codeaudit.dashboard_reports import ( + create_statistics_overview, + get_disclaimer_text, + get_info_text, + report_sast_results, + report_used_modules, +) # --- Environment Detection --- IS_PYODIDE = "pyodide" in sys.modules async def get_pypi_package_info_wasm(package_name): - url = f"https://pypi.org/pypi/{package_name}/json" + url = f"https://pypi.org/pypi/{package_name}/json" if IS_PYODIDE: from pyodide.http import pyfetch + try: - response = await pyfetch(url) + response = await pyfetch(url) if not response.ok: return False - return await response.json() + return await response.json() except: return False else: import urllib.request + try: with urllib.request.urlopen(url) as response: return json.loads(response.read().decode("utf-8")) @@ -72,21 +72,27 @@ async def get_pypi_package_info_wasm(package_name): async def get_pypi_download_info_wasm(package_name): data = await get_pypi_package_info_wasm(package_name) - if not data or 'info' not in data: + if not data or "info" not in data: return False - version_str = data.get('info', {}).get('version') - releases = data.get('releases', {}).get(version_str, []) + version_str = data.get("info", {}).get("version") + releases = data.get("releases", {}).get(version_str, []) for file_info in releases: - if file_info.get('packagetype') == 'sdist' and file_info.get('url').endswith(".tar.gz"): - return {"download_url": file_info.get('url'), "release": version_str} + if file_info.get("packagetype") == "sdist" and file_info.get("url").endswith( + ".tar.gz" + ): + return {"download_url": file_info.get("url"), "release": version_str} return False async def get_package_source_wasm(url): + import gzip + import tarfile + import tempfile + import zlib + from pyodide.http import pyfetch - import gzip, zlib, tarfile, tempfile try: response = await pyfetch(url) @@ -109,7 +115,7 @@ async def get_package_source_wasm(url): f.write(content) with tarfile.open(tar_path, "r:gz") as tar: - tar.extractall(path=temp_dir, filter='data') + tar.extractall(path=temp_dir, filter="data") return temp_dir, tmpdir_obj @@ -130,7 +136,7 @@ async def filescan_wasm(input_path, nosec=False): ca_version_info = version() now = datetime.datetime.now() timestamp_str = now.strftime("%Y-%m-%d %H:%M") - output = ca_version_info | {"generated_on": timestamp_str} + output = ca_version_info | {"generated_on": timestamp_str} pypi_data = await get_pypi_download_info_wasm(input_path) if pypi_data: @@ -153,7 +159,7 @@ async def filescan_wasm(input_path, nosec=False): if decoded_res is None: return { "Error": f"Could not download or extract package from {url}. " - f"This may be due to browser restrictions." + f"This may be due to browser restrictions." } src_dir, tmp_handle = decoded_res @@ -165,9 +171,7 @@ async def filescan_wasm(input_path, nosec=False): } try: - scan_output = _codeaudit_directory_scan_wasm( - src_dir, nosec_flag=nosec - ) + scan_output = _codeaudit_directory_scan_wasm(src_dir, nosec_flag=nosec) output |= scan_output finally: if tmp_handle: @@ -175,9 +179,7 @@ async def filescan_wasm(input_path, nosec=False): return output # --------------------------------------------------------- - return { - "Error": "Package not found on PyPI.org." - } + return {"Error": "Package not found on PyPI.org."} # - END of specific HELPERS to do CA things -# @@ -185,31 +187,33 @@ async def filescan_wasm(input_path, nosec=False): # --- UI Component Definitions --- text_input = pn.widgets.TextInput( - name='Python Package Name', - placeholder='Enter PyPI package (e.g., requests)...' + name="Python Package Name", placeholder="Enter PyPI package (e.g., requests)..." ) -run_button = pn.widgets.Button(name='Run Scan', button_type='primary') +run_button = pn.widgets.Button(name="Run Scan", button_type="primary") status = pn.pane.Markdown("### Ready to scan.") -result_pane = pn.pane.JSON({}, name='JSON', sizing_mode="stretch_both", depth=-1) -loading = pn.indicators.LoadingSpinner(value=False, size=60, color='primary', bgcolor='light' , name='Scanning...') +result_pane = pn.pane.JSON({}, name="JSON", sizing_mode="stretch_both", depth=-1) +loading = pn.indicators.LoadingSpinner( + value=False, size=60, color="primary", bgcolor="light", name="Scanning..." +) overview_visuals = create_statistics_overview(result_pane.object) -tabs =pn.Tabs( - ('Package Overview', overview_visuals), - ('Used Modules', overview_visuals), - ('Complexity Insights', overview_visuals), - ('Weaknesses Overview', overview_visuals), - ('Weaknesses per file', overview_visuals), - ('Weaknesses Details', overview_visuals), +tabs = pn.Tabs( + ("Package Overview", overview_visuals), + ("Used Modules", overview_visuals), + ("Complexity Insights", overview_visuals), + ("Weaknesses Overview", overview_visuals), + ("Weaknesses per file", overview_visuals), + ("Weaknesses Details", overview_visuals), dynamic=True, - sizing_mode="stretch_both" + sizing_mode="stretch_both", ) # --- UI Callback --- + async def run_scan(event): package_name = text_input.value.strip() @@ -252,7 +256,9 @@ async def run_scan(event): pn.Column( pn.Row( pn.pane.Vega(module_count_barchart(result), show_actions=True), - pn.pane.Vega(module_distribution_view(result), show_actions=True), + pn.pane.Vega( + module_distribution_view(result), show_actions=True + ), ), report_used_modules(result), pn.Spacer(height=60), @@ -271,9 +277,7 @@ async def run_scan(event): tabs[3] = ( "Weaknesses Overview", - pn.Column( - pn.pane.Vega(weaknesses_overview(result), show_actions=True) - ), + pn.Column(pn.pane.Vega(weaknesses_overview(result), show_actions=True)), ) tabs[4] = ( @@ -315,23 +319,21 @@ async def run_scan(event): text_input, run_button, loading, - status, + status, infotext, - disclaimer_text, + disclaimer_text, sizing_mode="stretch_width", ) -main_pane = pn.Column( - tabs, sizing_mode="stretch_both" -) +main_pane = pn.Column(tabs, sizing_mode="stretch_both") app = pn.template.MaterialTemplate( - header_background="#262626", + header_background="#262626", title="Python Security Code Audit", sidebar=[ca_sidebar], - main=[main_pane] + main=[main_pane], ) app.servable() diff --git a/src/codeaudit/dashboard_reports.py b/src/codeaudit/dashboard_reports.py index aa778a3..e3777d4 100644 --- a/src/codeaudit/dashboard_reports.py +++ b/src/codeaudit/dashboard_reports.py @@ -12,6 +12,7 @@ API functions: Used for dashboard reporting (Panel / WASM) and notebooks, or to build custom reports. """ +# import panel as pn SAST_REPORT_CSS = """