codeaudit 1.4.2__tar.gz → 1.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {codeaudit-1.4.2 → codeaudit-1.5.0}/CHANGELOG.md +23 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/PKG-INFO +7 -2
- {codeaudit-1.4.2 → codeaudit-1.5.0}/README.md +6 -1
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/_toc.yml +15 -12
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checksinformation.md +1 -1
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/codeauditcommands.md +29 -19
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/examples/demoscan.json +2 -2
- codeaudit-1.5.0/docs/features.md +88 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/filescan.md +3 -3
- codeaudit-1.5.0/docs/howtoscan.md +173 -0
- codeaudit-1.5.0/docs/images/filescan_screenshot_16012026.png +0 -0
- codeaudit-1.5.0/docs/images/modulescan_screenshot_16012026.png +0 -0
- codeaudit-1.5.0/docs/images/overview_screenshot_16012026.png +0 -0
- codeaudit-1.5.0/docs/installation.md +24 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/intro.md +7 -2
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/makeitbetter.md +2 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/modulescan.md +32 -9
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/userguide.md +21 -17
- {codeaudit-1.4.2 → codeaudit-1.5.0}/src/codeaudit/__about__.py +1 -1
- codeaudit-1.5.0/src/codeaudit/data/secretslist.txt +135 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/src/codeaudit/filehelpfunctions.py +1 -1
- codeaudit-1.5.0/src/codeaudit/privacy_lint.py +292 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/src/codeaudit/reporting.py +322 -143
- {codeaudit-1.4.2 → codeaudit-1.5.0}/src/codeaudit/simple.css +31 -5
- codeaudit-1.5.0/tests/test_secretfinding.py +20 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/allshit.py +7 -0
- codeaudit-1.5.0/tests/validationfiles/apivalidations.py +54 -0
- codeaudit-1.4.2/docs/features.md +0 -53
- codeaudit-1.4.2/docs/howtoscan.md +0 -120
- {codeaudit-1.4.2 → codeaudit-1.5.0}/.gitignore +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/CONTRIBUTE.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/LICENSE.txt +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/SECURITY.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/CLIcommands.ipynb +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/CONTRIBUTE.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/_config.yml +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/_static/nocxstyle.css +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/about.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/apidocs/api_intro.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/apidocs/codeaudit.rst +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/apidocs/modules.rst +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/astlines.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/astlines2.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/changelog.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/assert_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/base64_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/binding_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/builtinfunctions_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/chmod_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/directorycreation_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/dynamicimport_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/exception_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/hash_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/httpserver_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/input_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/loggingconf_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/marshal_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/mktemp_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/multiprocessing_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/pickle_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/random_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/shelve_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/shutil_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/subprocess_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/syscalls_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/systemcalls_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/tarfile_extract_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/xml_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/checks/zipfile_check.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/codeauditchecks.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/codeauditoverview.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/complexitycheck.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/examples/ca_api_example_basic.ipynb +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/examples/ca_api_example_checks.ipynb +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/examples/ca_api_example_json.ipynb +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/examples/ca_api_example_overview.ipynb +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/examples/ca_api_example_scanning.ipynb +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/examples/checks.html +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/examples/demofile.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/examples/directoryscan.html +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/examples/filescan.html +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/examples/modulescan.html +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/examples/overview.html +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/filescan.png +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/handling_errors.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/help.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/images/OO.png +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/images/ROI_logo.png +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/images/YourLogoHere.png +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/images/codeauditlogo.png +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/images/nocxbanner.png +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/images/overview_linkaudit.png +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/implementedvalidations.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/issues.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/license.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/overviewplot.png +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/pca_overview.png +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/project_philosophy.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/securecoding.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/sponsors.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/warnings.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/whatissast.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/docs/whysast.md +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/filescan.png +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/pyproject.toml +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/src/codeaudit/__init__.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/src/codeaudit/altairplots.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/src/codeaudit/api_interfaces.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/src/codeaudit/api_reporting.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/src/codeaudit/checkmodules.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/src/codeaudit/codeaudit.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/src/codeaudit/complexitycheck.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/src/codeaudit/data/sastchecks.csv +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/src/codeaudit/htmlhelpfunctions.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/src/codeaudit/issuevalidations.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/src/codeaudit/pypi_package_scan.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/src/codeaudit/security_checks.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/src/codeaudit/totals.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/__init__.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/count_lines_file1.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/test_apicalls.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/test_basicpatterns.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/test_chmod.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/test_constructspart2.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/test_correctexceptionuse.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/test_count_commentlines.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/test_directorycreation.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/test_directorycreation2.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/test_hashstrenght.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/test_modulecheck.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/test_obfuscatingbuiltins.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/test_oschecks.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/test_pypiscan.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/test_random.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/test_standardlibconstructs.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/test_totalscheck.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/test_zstd.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/assert.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/base64.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/chmod_things.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/complexitycheck.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/correctcounts.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/directorycreation.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/directorycreation2.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/dunderexec_with_parsing_error.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/exception.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/file3.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/file_with_warnings.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/gzip.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/hashcheck.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/httpserver.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/inputstatement.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/marshal.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/modulecheck.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/multiprocessing.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/obfuscating.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/oschecks.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/pickle.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/python2_file_willnotwork.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/random.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/shelve.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/shutil.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/subprocess.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/syslibrary.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/tarfilevalidation.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/tempcheck.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/validation1.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/validation2.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/xml.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/zipfile.py +0 -0
- {codeaudit-1.4.2 → codeaudit-1.5.0}/tests/validationfiles/zstd.py +0 -0
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## Version 1.5.0:
|
|
4
|
+
Added:
|
|
5
|
+
|
|
6
|
+
* External Egress Risk Detection: New functionality to identify potential API keys or logic used for connecting to remote services. Reports now include the specific line and "keyword" associated with the identified risk.
|
|
7
|
+
*Note: External Egress Risk Detection is still experimental and in beta status! [Help improve it!](CONTRIBUTE).*
|
|
8
|
+
|
|
9
|
+
Changed:
|
|
10
|
+
|
|
11
|
+
* CLI HTML Reporting: Refined the reporting output for single file scans. Users will now see a clear, dedicated line when no security weaknesses are found.
|
|
12
|
+
|
|
13
|
+
* UI/UX Enhancements: Applied "Look & Feel" improvements across all HTML report templates for better readability and aesthetics.
|
|
14
|
+
|
|
15
|
+
* General CLI Polish: Improved various text strings throughout the Command Line Interface for better clarity.
|
|
16
|
+
|
|
17
|
+
Fixed:
|
|
18
|
+
|
|
19
|
+
* Error Messaging: Improved the descriptiveness and clarity of CLI error messages to assist in troubleshooting.
|
|
20
|
+
|
|
21
|
+
Documentation:
|
|
22
|
+
|
|
23
|
+
* Report Naming Conventions: Improved the titles of HTML reports. This ensures that when a user saves a report as a PDF via a browser, the default filename is more descriptive and professional.
|
|
24
|
+
|
|
25
|
+
|
|
3
26
|
## Version 1.4.2: API updates and fixes
|
|
4
27
|
|
|
5
28
|
Added:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeaudit
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
4
4
|
Summary: Simplified static security checks for Python
|
|
5
5
|
Project-URL: Documentation, https://github.com/nocomplexity/codeaudit#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/nocomplexity/codeaudit/issues
|
|
@@ -64,6 +64,10 @@ Python Code Audit has the following features:
|
|
|
64
64
|
|
|
65
65
|
* **Inline Issue Reporting**: Shows potential security issues with line numbers and code snippets.
|
|
66
66
|
|
|
67
|
+
|
|
68
|
+
* **External Egress Detection**: Identifies embedded API keys and logic that enables communication with remote services, helping uncover hidden data exfiltration paths.
|
|
69
|
+
|
|
70
|
+
|
|
67
71
|
* **HTML Reports**: All output is saved in simple, static HTML reports viewable in any browser.
|
|
68
72
|
|
|
69
73
|
|
|
@@ -100,6 +104,7 @@ This will show all commands:
|
|
|
100
104
|
|
|
101
105
|
Python Code Audit - A modern Python security source code analyzer based on distrust.
|
|
102
106
|
|
|
107
|
+
|
|
103
108
|
Commands to evaluate Python source code:
|
|
104
109
|
Usage: codeaudit COMMAND <directory|package> [report.html]
|
|
105
110
|
|
|
@@ -108,7 +113,7 @@ Depending on the command, you must specify a local directory, a Python file, or
|
|
|
108
113
|
Commands:
|
|
109
114
|
overview Generates an overview report of code complexity and security indicators.
|
|
110
115
|
filescan Scans Python source code or PyPI packages for security weaknesses.
|
|
111
|
-
modulescan
|
|
116
|
+
modulescan Generate a report on known vulnerabilities in Python modules and packages.
|
|
112
117
|
checks Creates an HTML report of all implemented security checks.
|
|
113
118
|
version Prints the module version. Or use codeaudit [-v] [--v] [-version] or [--version].
|
|
114
119
|
|
|
@@ -36,6 +36,10 @@ Python Code Audit has the following features:
|
|
|
36
36
|
|
|
37
37
|
* **Inline Issue Reporting**: Shows potential security issues with line numbers and code snippets.
|
|
38
38
|
|
|
39
|
+
|
|
40
|
+
* **External Egress Detection**: Identifies embedded API keys and logic that enables communication with remote services, helping uncover hidden data exfiltration paths.
|
|
41
|
+
|
|
42
|
+
|
|
39
43
|
* **HTML Reports**: All output is saved in simple, static HTML reports viewable in any browser.
|
|
40
44
|
|
|
41
45
|
|
|
@@ -72,6 +76,7 @@ This will show all commands:
|
|
|
72
76
|
|
|
73
77
|
Python Code Audit - A modern Python security source code analyzer based on distrust.
|
|
74
78
|
|
|
79
|
+
|
|
75
80
|
Commands to evaluate Python source code:
|
|
76
81
|
Usage: codeaudit COMMAND <directory|package> [report.html]
|
|
77
82
|
|
|
@@ -80,7 +85,7 @@ Depending on the command, you must specify a local directory, a Python file, or
|
|
|
80
85
|
Commands:
|
|
81
86
|
overview Generates an overview report of code complexity and security indicators.
|
|
82
87
|
filescan Scans Python source code or PyPI packages for security weaknesses.
|
|
83
|
-
modulescan
|
|
88
|
+
modulescan Generate a report on known vulnerabilities in Python modules and packages.
|
|
84
89
|
checks Creates an HTML report of all implemented security checks.
|
|
85
90
|
version Prints the module version. Or use codeaudit [-v] [--v] [-version] or [--version].
|
|
86
91
|
|
|
@@ -2,23 +2,29 @@ format: jb-book
|
|
|
2
2
|
root: intro
|
|
3
3
|
|
|
4
4
|
parts:
|
|
5
|
-
- caption:
|
|
5
|
+
- caption: Getting Started
|
|
6
6
|
chapters:
|
|
7
7
|
- file: features
|
|
8
|
+
- file: installation
|
|
8
9
|
- file: howtoscan
|
|
10
|
+
- file: whatissast
|
|
11
|
+
- file: whysast
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
- caption: User Guide
|
|
16
|
+
chapters:
|
|
9
17
|
- file: userguide
|
|
10
18
|
sections:
|
|
11
19
|
- file: codeauditoverview
|
|
12
20
|
- file: filescan
|
|
13
21
|
- file: modulescan
|
|
14
22
|
- file: codeauditchecks
|
|
15
|
-
- file: whatissast
|
|
16
|
-
- file: whysast
|
|
17
23
|
- file: issues
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
-
|
|
21
|
-
|
|
24
|
+
- file: securecoding
|
|
25
|
+
- file: complexitycheck
|
|
26
|
+
- file: warnings
|
|
27
|
+
- file: handling_errors
|
|
22
28
|
- file: implementedvalidations
|
|
23
29
|
- file: checksinformation
|
|
24
30
|
sections:
|
|
@@ -47,17 +53,14 @@ parts:
|
|
|
47
53
|
- file: checks/xml_check
|
|
48
54
|
- file: checks/zipfile_check
|
|
49
55
|
- file: checks/shutil_check
|
|
50
|
-
|
|
56
|
+
|
|
51
57
|
|
|
52
58
|
- caption: Architecture
|
|
53
59
|
chapters:
|
|
54
60
|
#- file: astlines
|
|
55
61
|
# - file: astlines2
|
|
56
62
|
- file: makeitbetter
|
|
57
|
-
- file: project_philosophy
|
|
58
|
-
- file: complexitycheck
|
|
59
|
-
- file: warnings
|
|
60
|
-
- file: handling_errors
|
|
63
|
+
- file: project_philosophy
|
|
61
64
|
- file: codeauditcommands
|
|
62
65
|
- file: changelog
|
|
63
66
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
% THIS FILE IS GENERATED! - Use CLIcommands.ipynb to make it better!
|
|
2
2
|
# Commands Overview
|
|
3
|
-
Python Code Audit commands for: version: 1.
|
|
3
|
+
Python Code Audit commands for: version: 1.5.0
|
|
4
4
|
```
|
|
5
5
|
----------------------------------------------------
|
|
6
6
|
_ __ _
|
|
@@ -18,7 +18,7 @@ Depending on the command, you must specify a local directory, a Python file, or
|
|
|
18
18
|
Commands:
|
|
19
19
|
overview Generates an overview report of code complexity and security indicators.
|
|
20
20
|
filescan Scans Python source code or PyPI packages for security weaknesses.
|
|
21
|
-
modulescan
|
|
21
|
+
modulescan Generate a report on known vulnerabilities in Python modules and packages.
|
|
22
22
|
checks Creates an HTML report of all implemented security checks.
|
|
23
23
|
version Prints the module version. Or use codeaudit [-v] [--v] [-version] or [--version].
|
|
24
24
|
|
|
@@ -35,6 +35,9 @@ complexity and security-related metrics. The input may be either:
|
|
|
35
35
|
- A local directory containing Python source files
|
|
36
36
|
- The name of a package hosted on PyPI.org
|
|
37
37
|
|
|
38
|
+
So:
|
|
39
|
+
codeaudit overview <package-name|directory> [reportname.html]
|
|
40
|
+
|
|
38
41
|
For PyPI packages, the source distribution (sdist) is downloaded,
|
|
39
42
|
extracted to a temporary directory, scanned, and removed after the report
|
|
40
43
|
is generated.
|
|
@@ -81,36 +84,41 @@ errors defaults to 'strict'.
|
|
|
81
84
|
```
|
|
82
85
|
## codeaudit modulescan
|
|
83
86
|
```text
|
|
84
|
-
Generates a vulnerability report for imported Python modules.
|
|
85
87
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
Generate a report on known vulnerabilities in Python modules and packages.
|
|
89
|
+
|
|
90
|
+
This function analyzes a single Python file to identify imported
|
|
91
|
+
external modules and checks those modules against the OSV vulnerability
|
|
92
|
+
database. The collected results are written to a static HTML report.
|
|
89
93
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
If the input refers to a valid PyPI package name instead of a local Python
|
|
95
|
+
file, the function generates a vulnerability report directly for that
|
|
96
|
+
package.
|
|
93
97
|
|
|
94
|
-
|
|
98
|
+
While processing modules, progress information is printed to standard
|
|
99
|
+
output.
|
|
95
100
|
|
|
96
101
|
Example:
|
|
97
102
|
Generate a module vulnerability report for a Python file::
|
|
98
103
|
|
|
99
|
-
codeaudit modulescan
|
|
104
|
+
codeaudit modulescan <pythonfile>|<package> [yourreportname.html]
|
|
105
|
+
|
|
106
|
+
codeaudit modulescan mypythonfile.py
|
|
100
107
|
|
|
101
108
|
Args:
|
|
102
|
-
inputfile (str): Path to
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
109
|
+
inputfile (str): Path to a Python source file (*.py) to analyze, or the
|
|
110
|
+
name of a package available on PyPI.
|
|
111
|
+
reportname (str, optional): Name (and optional path) of the HTML file to
|
|
112
|
+
write the vulnerability report to. The filename should use the
|
|
113
|
+
``.html`` extension. Defaults to ``DEFAULT_OUTPUT_FILE``.
|
|
106
114
|
|
|
107
115
|
Returns:
|
|
108
|
-
None
|
|
116
|
+
None: The function writes a static HTML report to disk.
|
|
109
117
|
|
|
110
118
|
Raises:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
119
|
+
SystemExit: If the input is not a valid Python file or a valid PyPI
|
|
120
|
+
package. File parsing and I/O errors are reported via standard
|
|
121
|
+
output before exiting.
|
|
114
122
|
str(object='') -> str
|
|
115
123
|
str(bytes_or_buffer[, encoding[, errors]]) -> str
|
|
116
124
|
|
|
@@ -133,6 +141,8 @@ given input, which can be:
|
|
|
133
141
|
- A single local Python file
|
|
134
142
|
- A package name hosted on PyPI.org
|
|
135
143
|
|
|
144
|
+
codeaudit filescan <pythonfile|package-name|directory> [reportname.html]
|
|
145
|
+
|
|
136
146
|
Depending on the input type, the function analyzes the source code for
|
|
137
147
|
potential security issues, generates an HTML report summarizing the
|
|
138
148
|
findings, and writes the report to a static HTML file.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Features
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
**Python Code Audit** is a modern Python **security** source code analysis tool built on a *zero-trust* mindset. It focuses on identifying security risks, hidden behaviors, and trust boundaries in Python code—without executing it.
|
|
5
|
+
|
|
6
|
+
:::{admonition} Key Features of Python Code Audit
|
|
7
|
+
:class: tip
|
|
8
|
+
|
|
9
|
+
* **Vulnerability Detection**
|
|
10
|
+
Detects potential security issues in Python source files. This is essential for validating trust in third-party modules and supporting security research.
|
|
11
|
+
|
|
12
|
+
+++
|
|
13
|
+
|
|
14
|
+
* **External Egress Detection**
|
|
15
|
+
Identifies embedded API keys and logic that enables communication with remote services, helping uncover hidden data exfiltration paths.
|
|
16
|
+
|
|
17
|
+
+++
|
|
18
|
+
|
|
19
|
+
* **Complexity & Security-Relevant Statistics**
|
|
20
|
+
Reports code metrics relevant to security analysis, including a fast and lightweight
|
|
21
|
+
[cyclomatic complexity](https://en.wikipedia.org/wiki/Cyclomatic_complexity) calculation using the Python AST.
|
|
22
|
+
|
|
23
|
+
+++
|
|
24
|
+
|
|
25
|
+
* **Module Usage & Known Vulnerabilities**
|
|
26
|
+
Detects imported modules and correlates them with known security vulnerabilities.
|
|
27
|
+
|
|
28
|
+
+++
|
|
29
|
+
|
|
30
|
+
* **Inline Issue Reporting**
|
|
31
|
+
Highlights potential security issues directly in context, including line numbers and relevant code snippets.
|
|
32
|
+
|
|
33
|
+
+++
|
|
34
|
+
|
|
35
|
+
* **Static HTML Reports**
|
|
36
|
+
Generates clean, self-contained HTML reports that can be viewed in any modern web browser.
|
|
37
|
+
|
|
38
|
+
:::
|
|
39
|
+
|
|
40
|
+
## In-Depth Capability Overview
|
|
41
|
+
|
|
42
|
+
Python Code Audit provides a comprehensive set of features designed to enhance Python code security analysis:
|
|
43
|
+
|
|
44
|
+
### Code Complexity & Statistics
|
|
45
|
+
|
|
46
|
+
Analyzes individual Python files or entire packages *prior to execution* and collects security relevant metrics, including:
|
|
47
|
+
|
|
48
|
+
- Number of files
|
|
49
|
+
- Total lines of code
|
|
50
|
+
- AST node count
|
|
51
|
+
- Imported modules
|
|
52
|
+
- Defined functions
|
|
53
|
+
- Defined classes
|
|
54
|
+
- Comment lines
|
|
55
|
+
|
|
56
|
+
Statistics are reported per file, along with an aggregated summary for the entire package or directory.
|
|
57
|
+
|
|
58
|
+
### Module Usage Reporting
|
|
59
|
+
|
|
60
|
+
Identifies and lists all modules imported by each Python file, providing visibility into dependencies and potential attack surfaces.
|
|
61
|
+
|
|
62
|
+
### Module Security Intelligence
|
|
63
|
+
|
|
64
|
+
Surfaces known security information and vulnerabilities associated with the detected modules.
|
|
65
|
+
|
|
66
|
+
### Per-File Vulnerability Detection
|
|
67
|
+
|
|
68
|
+
Detects potential security weaknesses within individual Python files and reports:
|
|
69
|
+
- Affected line numbers
|
|
70
|
+
- Relevant code snippets
|
|
71
|
+
- Contextual details to aid investigation
|
|
72
|
+
|
|
73
|
+
### External Egress Detection
|
|
74
|
+
|
|
75
|
+
Scans for:
|
|
76
|
+
- Over 135 known API key formats
|
|
77
|
+
- Common networking and remote-connection patterns
|
|
78
|
+
|
|
79
|
+
This capability helps determine whether a Python file or library can transmit data to external services.
|
|
80
|
+
|
|
81
|
+
### Directory-Wide (Package-Level) Scanning
|
|
82
|
+
|
|
83
|
+
Performs vulnerability detection across all Python files in a directory or package, making it ideal for assessing the security posture of Python libraries and distributions.
|
|
84
|
+
|
|
85
|
+
### APIs for Integration
|
|
86
|
+
|
|
87
|
+
The Python Code Audit APIs empower you to build your own Python security tools or create seamless integrations you need!
|
|
88
|
+
So create your own security dashboards, CICD integrations or custom integrations needed for your security management system. Powerfull but simple APIs are provided.
|
|
@@ -11,13 +11,13 @@ See section [validations](checksinformation) for all security checks implemented
|
|
|
11
11
|
To use the `codescan filescan` feature type in the console:
|
|
12
12
|
|
|
13
13
|
```
|
|
14
|
-
codeaudit filescan <
|
|
14
|
+
codeaudit filescan <pythonfile|package-name|directory> [OUTPUTFILE]
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
**Python Code Audit** will create a detailed security scan report based on a single Python file, a local directory or a package on PyPI.org
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
So the input for `codeaudit filescan` can be:
|
|
21
21
|
* A single Python file;
|
|
22
22
|
* A package on PyPI.org: Python Code Audit checks this package on security weakness, so cloning the sources local is not needed!
|
|
23
23
|
* A local directory with Python files, e.g. a local package development environment or a cloned package.
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# How to do a SAST test?
|
|
2
|
+
|
|
3
|
+
Running a Static Application Security Test (SAST) on Python code is essential for ensuring security. It’s also a straightforward [shift-left practice](https://nocomplexity.com/documents/simplifysecurity/intro.html#) that takes only a fraction of your time yet can help you avoid serious security incidents.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Follow these steps to perform a **static application security test (SAST)** on Python projects using **Python Code Audit**.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## Install Python Code Audit
|
|
12
|
+
|
|
13
|
+
[Python Code Audit](https://pypi.org/project/codeaudit/) is an open-source, zero-configuration tool that validates whether your Python code introduces potential security vulnerabilities.
|
|
14
|
+
|
|
15
|
+
Install (or update) it with:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install -U codeaudit
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
:::{tip}
|
|
22
|
+
Even if you already have it installed, it’s recommended to run the command again to ensure you’re using the latest checks and features.
|
|
23
|
+
:::
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## Chose a Python package that is available on PyPI.org
|
|
28
|
+
|
|
29
|
+
:::{admonition} Install Python programs only from Trusted Sources.
|
|
30
|
+
:class: note
|
|
31
|
+
This is crucial from a security perspective!
|
|
32
|
+
|
|
33
|
+
Use only official, managed repositories for installation such as:
|
|
34
|
+
- PyPI.org
|
|
35
|
+
- conda
|
|
36
|
+
- conda-forge
|
|
37
|
+
:::
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
### Do the security sccan
|
|
41
|
+
|
|
42
|
+
On the command line do:
|
|
43
|
+
```bash
|
|
44
|
+
codeaudit filescanscan <package-name|directory|file> [reportname.html]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
You can chose a custom report name. But make sure it ends with `.html` since a the report is a static html file.
|
|
48
|
+
|
|
49
|
+
Example for a security check on a PyPI library for detecting broken links in markdown files:
|
|
50
|
+
```bash
|
|
51
|
+
codeaudit filescan linkaudit
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
This gives the output:
|
|
55
|
+
```
|
|
56
|
+
Package: linkaudit exist on PyPI.org!
|
|
57
|
+
Now SAST scanning package from the remote location: https://pypi.org/pypi/linkaudit
|
|
58
|
+
https://files.pythonhosted.org/packages/8a/5a/e9d4ae4b006ac7fb2faf0d3047ee759d8366b85290ddc0e595bbe290d6c5/linkaudit-0.9.7.tar.gz
|
|
59
|
+
0.9.7
|
|
60
|
+
Number of files that are checked for security issues:6
|
|
61
|
+
Progress: |██████████████████████████████████████████████████| 100.0% Complete
|
|
62
|
+
|
|
63
|
+
=====================================================================
|
|
64
|
+
Code Audit report file created!
|
|
65
|
+
Paste the line below directly into your browser bar:
|
|
66
|
+
file:///home/securityproject/example/codeaudit-report.html
|
|
67
|
+
|
|
68
|
+
=====================================================================
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The report will look like:
|
|
73
|
+

|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
## Generate an Overview Report
|
|
77
|
+
|
|
78
|
+
Navigate into the cloned repository, then run:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
codeaudit overview <package-name|directory> [reportname.html]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
This command provides:
|
|
85
|
+
- Total number of files
|
|
86
|
+
- Total lines of code
|
|
87
|
+
- Imported modules
|
|
88
|
+
- Complexity per file
|
|
89
|
+
- Overall complexity score
|
|
90
|
+
|
|
91
|
+
Example for a security check on a PyPI library for RSS parsing:
|
|
92
|
+
```bash
|
|
93
|
+
codeaudit overview ultrafastrss
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
This gives the output:
|
|
97
|
+
```
|
|
98
|
+
No local directory with name:ultrafastrss found locally. Checking if package exist on PyPI...
|
|
99
|
+
Package: ultrafastrss exist on PyPI.org!
|
|
100
|
+
Creating Python Code Audit overview for package:
|
|
101
|
+
https://files.pythonhosted.org/packages/c7/45/4f10aaf692e0bc67e8f089a3514804491f3edbacbed2658b191adc3a109b/ultrafastrss-0.9.2.tar.gz
|
|
102
|
+
|
|
103
|
+
=====================================================================
|
|
104
|
+
Code Audit report file created!
|
|
105
|
+
Paste the line below directly into your browser bar:
|
|
106
|
+
file:///home/securityproject/example/codeaudit-report.html
|
|
107
|
+
|
|
108
|
+
=====================================================================
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
The report will look like:
|
|
112
|
+

|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
:::{tip}
|
|
116
|
+
📖 More detailed explanations of these metrics can be found in the [Python Code Audit documentation](https://nocomplexity.com/documents/codeaudit/intro.html).
|
|
117
|
+
:::
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Check for known vulnerabilities in imported libraries
|
|
124
|
+
|
|
125
|
+
To check for known vulnerabilities in Python modules and packages you can do:
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
codeaudit modulescan <pythonfile>|<package> [yourreportname.html]
|
|
129
|
+
```
|
|
130
|
+
To check for known vulnerabilities in a local Python file or a package on PyPI.org
|
|
131
|
+
|
|
132
|
+
Example, to check for known vulnerabilities in imported libraries in the Flair NLP package:
|
|
133
|
+
```bash
|
|
134
|
+
codeaudit modulescan flair
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
The report will look like:
|
|
139
|
+

|
|
140
|
+
|
|
141
|
+
:::{tip}
|
|
142
|
+
To analyze this package for potential security weaknesses, run:
|
|
143
|
+
|
|
144
|
+
`codeaudit filescan <file|package-name>`
|
|
145
|
+
|
|
146
|
+
**Most real-world vulnerabilities and insecure coding practices are never formally disclosed or assigned a CVE**. As a result, source code analysis provides far greater security insight than relying solely on known vulnerability databases. Scanning the code itself helps uncover hidden risks, unsafe patterns, and trust violations that would otherwise remain undetected.
|
|
147
|
+
|
|
148
|
+
:::
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
## Review the Security Report
|
|
152
|
+
|
|
153
|
+
The **Python Code Audit** security scan generates a static **HTML report** in the directory where you ran the command.
|
|
154
|
+
|
|
155
|
+
Example output path:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
file:///home/usainbolt/testdir/codeaudit-report.html
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
- On **Linux**, you can usually click the link directly in the terminal.
|
|
162
|
+
- On **Windows**, you may need to manually copy and paste the file path into your browser.
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
✅ You now have a detailed static application security test (SAST) report highlighting potential security issues in your Python code.
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
:::{hint}
|
|
170
|
+
If you need assistance with solving or want short and clear advice on possible security risks for your context:
|
|
171
|
+
|
|
172
|
+
Get expert security advice from one of our [sponsors](sponsors)!
|
|
173
|
+
:::
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Installation
|
|
2
|
+
|
|
3
|
+
Python Code Audit is compatible with both Unix-based systems (Linux/macOS) and Windows.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
To install or upgrade to the latest version, run the following command in your terminal or command prompt:
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pip install -U codeaudit
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Once the installation is complete, you can begin scanning Python Packages immediately. Open a new shell or Command Prompt window and execute any of the Python Code Audit commands to verify the setup.
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
codeaudit filescan ultrafastrss
|
|
18
|
+
```
|
|
19
|
+
This will scan the `ultrafastrss` package directly from PyPI.org and create a html report.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
:::{hint}
|
|
23
|
+
It is recommended to use `pip` for installation.
|
|
24
|
+
:::
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
^^^
|
|
21
21
|
In the Getting Started section you can find installation instructions and a high-level overview of the main concepts.
|
|
22
22
|
+++
|
|
23
|
-
```{button-ref}
|
|
23
|
+
```{button-ref} installation
|
|
24
24
|
:link-type: ref
|
|
25
25
|
:color: danger
|
|
26
26
|
Quick Start Guide
|
|
@@ -32,7 +32,7 @@ Quick Start Guide
|
|
|
32
32
|
^^^
|
|
33
33
|
Check out the User Guides for in-depth information on the key concepts of Python Code Audit.
|
|
34
34
|
+++
|
|
35
|
-
```{button-ref}
|
|
35
|
+
```{button-ref} userguide
|
|
36
36
|
:link-type: ref
|
|
37
37
|
:color: danger
|
|
38
38
|
User Guide
|
|
@@ -113,6 +113,11 @@ Are you ready to discover what's lurking in your code?
|
|
|
113
113
|
|
|
114
114
|
+++
|
|
115
115
|
|
|
116
|
+
|
|
117
|
+
* **External Egress Detection**: Identifies embedded API keys and logic that enables communication with remote services, helping uncover hidden data exfiltration paths.
|
|
118
|
+
|
|
119
|
+
+++
|
|
120
|
+
|
|
116
121
|
* **Module Usage & External Vulnerabilities**: Detects used modules and reports known vulnerabilities in used modules.
|
|
117
122
|
|
|
118
123
|
|
|
@@ -78,6 +78,8 @@ Get in contact with the developers! The most simple way is to report the feature
|
|
|
78
78
|
:::
|
|
79
79
|
|
|
80
80
|
|
|
81
|
+
`Hatch` is used for packaging. By default [`Hatch`](https://hatch.pypa.io/latest/config/build/#reproducible-builds) supports [reproducible builds](https://nocomplexity.com/documents/securityarchitecture/prevention/reproduciblebuilds.html#reproducible-builds).
|
|
82
|
+
|
|
81
83
|
|
|
82
84
|
The **Python Code Audit** tool is designed using the [Zero Complexity By Design principles](https://nocomplexity.com/documents/0complexity/abstract.html). So the goal is to keep the tool simple to use and the **code** simple to adjust or to extend.
|
|
83
85
|
|