codeaudit 1.4.0__py3-none-any.whl → 1.4.2__py3-none-any.whl

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/__about__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2025-present Maikel Mardjan <mike@bm-support.org>
2
2
  #
3
3
  # SPDX-License-Identifier: GPL-3.0-or-later
4
- __version__ = "1.4.0"
4
+ __version__ = "1.4.2"
@@ -18,7 +18,7 @@ from codeaudit.filehelpfunctions import get_filename_from_path , collect_python_
18
18
  from codeaudit.security_checks import perform_validations , ast_security_checks
19
19
  from codeaudit.totals import overview_per_file , get_statistics , overview_count , total_modules
20
20
  from codeaudit.checkmodules import get_all_modules , get_imported_modules_by_file , get_standard_library_modules , check_module_vulnerability
21
-
21
+ from codeaudit.pypi_package_scan import get_pypi_download_info , get_package_source
22
22
 
23
23
  from pathlib import Path
24
24
  import json
@@ -35,10 +35,63 @@ def version():
35
35
  return {"name" : "Python_Code_Audit",
36
36
  "version" : ca_version}
37
37
 
38
-
39
38
  def filescan(input_path):
40
- """Scans a Python file or directory and returns result as JSON"""
41
- output ={}
39
+ """
40
+ Scan a Python source file, a local directory, or a **PyPI package** from PyPI.org for
41
+ security weaknesses and return the results as a JSON-serializable
42
+ dictionary.
43
+
44
+ This API function works on:
45
+
46
+ - **Local directory**: Recursively scans all supported Python files in the
47
+ directory.
48
+ - **Single Python file**: Scans the file if it exists and can be parsed
49
+ into an AST.
50
+ - **PyPI package** on PyPI.org: Downloads the
51
+ source distribution from PyPI, scans it, and cleans up temporary files.
52
+
53
+ The returned output always includes Python Code Audit version information and a
54
+ generation timestamp. For consistency, single-file scans are normalized
55
+ to match the structure of directory/package scans.
56
+
57
+ **Note:**
58
+ The filescan command does NOT include all directories. This is done on purpose!
59
+ The following directories are skipped by default:
60
+
61
+ - `/docs`
62
+ - `/docker`
63
+ - `/dist`
64
+ - `/tests`
65
+ - all directories that start with . (dot) or _ (underscore)
66
+
67
+ But you can easily change this if needed!
68
+
69
+ Args:
70
+ input_path (str): One of the following:
71
+ - Path to a local directory containing Python code.
72
+ - Path to a single ``.py`` file.
73
+ - Name of a package available on PyPI.
74
+
75
+ Returns:
76
+ dict: A JSON-serializable dictionary containing scan results and
77
+ metadata. The structure varies slightly depending on the scan type,
78
+ but always includes:
79
+ - Version information from ``version()``.
80
+ - ``generated_on`` timestamp (``YYYY-MM-DD HH:MM``).
81
+ - Package or file-level security findings.
82
+
83
+ If the input cannot be interpreted as a valid directory, Python file,
84
+ or PyPI package, a dictionary with an ``"Error"`` key is returned.
85
+
86
+ Raises:
87
+ None explicitly. Any unexpected exceptions are allowed to propagate
88
+ unless handled by downstream callers.
89
+
90
+ Example:
91
+ >>> result = filescan("example_package")
92
+ >>> result["package_name"]
93
+
94
+ """
42
95
  file_output = {}
43
96
  file_path = Path(input_path)
44
97
  ca_version_info = version()
@@ -46,35 +99,37 @@ def filescan(input_path):
46
99
  timestamp_str = now.strftime("%Y-%m-%d %H:%M")
47
100
  output = ca_version_info | {"generated_on" : timestamp_str}
48
101
  # Check if the input is a valid directory or a single valid Python file
49
- if file_path.is_dir():
50
- files_to_check = collect_python_source_files(input_path)
51
- if len(files_to_check) > 1:
52
- modules_discovered = get_all_modules(input_path) #all modules for the package aka directory
53
- name_of_package = get_filename_from_path(input_path)
54
- package_overview = get_overview(input_path)
55
- output |= {"package_name" : name_of_package ,
56
- "statistics_overview" : package_overview ,
57
- "module_overview" : modules_discovered }
58
- for i,file in enumerate(files_to_check):
59
- file_information = overview_per_file(file)
60
- module_information = get_modules(file) # modules per file
61
- scan_output = _codeaudit_scan(file)
62
- file_output[i] = file_information | module_information | scan_output
63
- output |= { "file_security_info" : file_output}
64
- return output
65
- else:
66
- output_msg = f'Directory path {input_path} contains no Python files.'
67
- return {"Error" : output_msg}
102
+ if file_path.is_dir(): #local directory scan
103
+ package_name = get_filename_from_path(input_path)
104
+ output |= {"package_name": package_name}
105
+ scan_output = _codeaudit_directory_scan(input_path)
106
+ output |= scan_output
107
+ return output
68
108
  elif file_path.suffix.lower() == ".py" and file_path.is_file() and is_ast_parsable(input_path): #check on parseable single Python file
69
- #do a file check
109
+ # do a file check
70
110
  file_information = overview_per_file(input_path)
71
111
  module_information = get_modules(input_path) # modules per file
72
112
  scan_output = _codeaudit_scan(input_path)
73
- file_output[0] = file_information | module_information | scan_output #there is only 1 file , so index 0 equals as for package to make functionality that use the output that works on the dict or json can equal for a package or a single file!
113
+ file_output["0"] = file_information | module_information | scan_output #there is only 1 file , so index 0 equals as for package to make functionality that use the output that works on the dict or json can equal for a package or a single file!
74
114
  output |= { "file_security_info" : file_output}
75
115
  return output
116
+ elif (pypi_data := get_pypi_download_info(input_path)):
117
+ package_name = input_path #The variable input_path is now equal to the package name
118
+ url = pypi_data['download_url']
119
+ release = pypi_data['release']
120
+ if url is not None:
121
+ src_dir, tmp_handle = get_package_source(url)
122
+ output |= {"package_name": package_name,
123
+ "package_release": release}
124
+ try:
125
+ scan_output = _codeaudit_directory_scan(src_dir)
126
+ output |= scan_output
127
+ finally:
128
+ # Cleaning up temp directory
129
+ tmp_handle.cleanup() # deletes everything from temp directory
130
+ return output
76
131
  else:
77
- #Its not a directory nor a valid Python file:
132
+ # Its not a directory nor a valid Python file:
78
133
  return {"Error" : "File is not a *.py file, does not exist or is not a valid directory path towards a Python package."}
79
134
 
80
135
  def _codeaudit_scan(filename):
@@ -90,6 +145,30 @@ def _codeaudit_scan(filename):
90
145
  "sast_result": sast_result}
91
146
  return output
92
147
 
148
+ def _codeaudit_directory_scan(input_path):
149
+ """Performs a scan on a local directory
150
+ Function is also used with scanning directory PyPI.org packages, since in that case a tmp directory is used
151
+ """
152
+ output ={}
153
+ file_output = {}
154
+ files_to_check = collect_python_source_files(input_path)
155
+ if len(files_to_check) > 1:
156
+ modules_discovered = get_all_modules(input_path) #all modules for the package aka directory
157
+ package_overview = get_overview(input_path)
158
+ output |= {"statistics_overview" : package_overview ,
159
+ "module_overview" : modules_discovered }
160
+ for i,file in enumerate(files_to_check):
161
+ file_information = overview_per_file(file)
162
+ module_information = get_modules(file) # modules per file
163
+ scan_output = _codeaudit_scan(file)
164
+ file_output[i] = file_information | module_information | scan_output
165
+ output |= { "file_security_info" : file_output}
166
+ return output
167
+ else:
168
+ output_msg = f'Directory path {input_path} contains no Python files.'
169
+ return {"Error" : output_msg}
170
+
171
+
93
172
  def save_to_json(sast_result, filename="codeaudit_output.json"):
94
173
  """
95
174
  Save a SAST result (dict or serializable object) to a JSON file.
@@ -208,13 +287,39 @@ def get_overview(input_path):
208
287
  return {"Error" : "File is not a *.py file, does not exist or is not a valid directory path to a Python package."}
209
288
 
210
289
  def get_default_validations():
211
- """Retrieves the implemented default security validations
212
- Args:
213
- none
290
+ """Retrieve the default implemented security validations.
291
+
292
+ This function collects the built-in Static Application Security Testing (SAST)
293
+ validations applied to standard Python modules. It retrieves the validation
294
+ definitions, converts them into a serializable format, and enriches the result
295
+ with generation metadata.
296
+
297
+ The returned structure is intended to be consumed by reporting, API, or
298
+ documentation layers.
214
299
 
215
300
  Returns:
216
- dict: Overview of implemented security SAST validation on Standard Python modules. Including vital help text.
217
- """
301
+ dict: A dictionary containing generation metadata and a list of security
302
+ validations. The dictionary has the following structure:
303
+
304
+ {
305
+ "<metadata_key>": <metadata_value>,
306
+ ...,
307
+ "validations": [
308
+ {
309
+ "<field>": <value>,
310
+ ...
311
+ },
312
+ ...
313
+ ]
314
+ }
315
+
316
+
317
+ **Notes**:
318
+
319
+ - Requires Python 3.9 or later due to use of the dictionary union operator (`|`).
320
+ - The `validations` list is derived from a pandas DataFrame using
321
+ `to_dict(orient="records")`.
322
+ """
218
323
  df = ast_security_checks()
219
324
  result = df.to_dict(orient="records")
220
325
  output = _generation_info() | {"validations" : result}
@@ -255,15 +360,16 @@ def get_psl_modules():
255
360
  return output
256
361
 
257
362
  def get_module_vulnerability_info(module):
258
- """Retrieves vulnerability information for external modules using the OSV Database
363
+ """
364
+ Retrieves vulnerability information for an external module using the OSV Database.
365
+
259
366
  Args:
260
- input: module name
261
-
367
+ module (str): Name of the module to query.
368
+
262
369
  Returns:
263
- dict: Result of OSV query
264
- """
370
+ dict: Generation metadata combined with OSV vulnerability results.
371
+ """
265
372
  vuln_info = check_module_vulnerability(module)
266
373
  key_string = f'{module}_vulnerability_info'
267
374
  output = _generation_info() | { key_string : vuln_info}
268
375
  return output
269
-
codeaudit/codeaudit.py CHANGED
@@ -36,8 +36,8 @@ def display_help():
36
36
  print(codeaudit_ascii_art)
37
37
  print("Python Code Audit - A modern Python security source code analyzer based on distrust.\n")
38
38
  print("Commands to evaluate Python source code:")
39
- print('Usage: codeaudit COMMAND [PATH or FILE] [OUTPUTFILE] \n')
40
- print('Depending on the command, a directory or file name must be specified. The output is a static HTML file to be examined in a browser. Specifying a name for the output file is optional.\n')
39
+ print('Usage: codeaudit COMMAND <directory|package> [report.html] \n')
40
+ print('Depending on the command, you must specify a local directory, a Python file, or a package name hosted on PyPI.org.Reporting: The results are generated as a static HTML report for viewing in a web browser.\n')
41
41
  print('Commands:')
42
42
  commands = ["overview", "filescan", "modulescan", "checks","version"] # commands on CLI
43
43
  functions = [overview_report, scan_report, report_module_information, report_implemented_tests,display_version] # Related functions relevant for help
@@ -45,29 +45,32 @@ def display_help():
45
45
  docstring = function.__doc__.strip().split('\n')[0] or ""
46
46
  summary = docstring.split("\n", 1)[0]
47
47
  print(f" {command:<20} {summary}")
48
- print("\nUse the Codeaudit documentation to check the security of Python programs and make your Python programs more secure!\nCheck https://simplifysecurity.nocomplexity.com/ \n")
49
-
48
+ print("\nUse the Python Code Audit documentation (https://codeaudit.nocomplexity.com) to audit and secure your Python programmes. Explore further essential open-source security tools at https://simplifysecurity.nocomplexity.com/\n")
50
49
 
51
50
  def main():
51
+ if "-?" in sys.argv: # Normalize help flags BEFORE Fire sees them: fire module treats anything starting with - as a flag/value, not as a help alias.
52
+ sys.argv[sys.argv.index("-?")] = "--help"
53
+ if "-help" in sys.argv: # Normalize help flags BEFORE Fire sees them
54
+ sys.argv[sys.argv.index("-help")] = "--help"
52
55
  if len(sys.argv) > 1 and sys.argv[1] in ("-v", "--v", "--version", "-version"):
53
56
  display_version()
54
- elif len(sys.argv) > 1 and sys.argv[1] in ("-help", "-?", "--help", "-h"):
57
+ elif len(sys.argv) > 1 and sys.argv[1] in ("-help", "--help", "-h"):
55
58
  display_help()
56
59
  elif len(sys.argv) == 1:
57
60
  display_help()
58
61
  else:
59
62
  fire.Fire(
60
63
  {
61
- "overview": overview_report,
64
+ "overview": overview_report,
62
65
  "modulescan": report_module_information,
63
- "filescan" : scan_report,
64
- "checks" : report_implemented_tests,
65
- "version" : display_version,
66
- "-help": display_help,
66
+ "filescan": scan_report,
67
+ "checks": report_implemented_tests,
68
+ "version": display_version,
67
69
  }
68
70
  )
69
71
 
70
72
 
73
+
71
74
  if __name__ == "__main__":
72
75
  main()
73
76
 
@@ -51,39 +51,38 @@ def get_pypi_download_info(package_name):
51
51
  """Retrieves the sdist download URL
52
52
  Using the PyPI JSON API to get the sdist download URL (https://docs.pypi.org/api/json/)
53
53
  Note JSON API result is a nested dict with all release info published, so finding the correct sdist download URL needs logic.
54
- """
55
- if get_pypi_package_info(package_name) :
56
- data = get_pypi_package_info(package_name)
57
- releases_dict = data['releases']
58
- # Convert the key-value pairs (items) into a list and get the last one
59
- last_item = list(releases_dict.items())[-1] #last_item is a Python tuple
60
- sdist_download_url = find_download_url(last_item,'source') # We want the download URL of the source, so *.tar.gz file
61
- release_info = last_item[0]
62
- pypi_package_info= { "download_url" : sdist_download_url ,
63
- "release" : release_info}
64
- return pypi_package_info
65
- else:
66
- #package does not exist
67
- return False
68
-
69
- def find_download_url(data, source):
70
- """
71
- Given the PyPI release tuple and a python_version string,
72
- return the URL of the first matching item.
73
54
  """
74
- items = data[1] # Access the list of items directly via index 1 , data is a tuple
75
-
76
- for item in items:
77
- if item.get("python_version") == source:
78
- return item.get("url")
55
+ data = get_pypi_package_info(package_name)
56
+ if not data:
57
+ return False
58
+ # Get the official "latest" version string from the API metadata
59
+ latest_version = data.get('info', {}).get('version')
60
+ if not latest_version:
61
+ return False
79
62
 
80
- return None # if no match found`
63
+ # Access the files associated with that specific version
64
+ releases_list = data.get('releases', {}).get(latest_version, [])
65
+
66
+ sdist_download_url = None
67
+
68
+ # Explicitly look for the source distribution (sdist)
69
+ for file_info in releases_list:
70
+ if file_info.get('packagetype') == 'sdist':
71
+ url = file_info.get('url')
72
+ if url and url.endswith(".tar.gz"): #PEP527 I only extract .tar.gz files, older source formats not supported.
73
+ sdist_download_url = url
74
+ break # Found it, stop looking
75
+
76
+ return {
77
+ "download_url": sdist_download_url,
78
+ "release": latest_version
79
+ }
81
80
 
82
81
 
83
82
  def get_package_source(url, nocxheaders=NOCX_HEADERS, nocxtimeout=10):
84
83
  """Retrieves a package source and extract so SAST scanning can be applied
85
84
  Make sure to cleanup the temporary dir!! Using e.g. `tmp_handle.cleanup()` # deletes everything
86
- """
85
+ """
87
86
  try:
88
87
  request = Request(url, headers=nocxheaders or {})
89
88
  with urlopen(request, timeout=nocxtimeout) as response:
codeaudit/reporting.py CHANGED
@@ -41,24 +41,76 @@ SIMPLE_CSS_FILE = files('codeaudit') / 'simple.css'
41
41
  DEFAULT_OUTPUT_FILE = 'codeaudit-report.html'
42
42
 
43
43
  def overview_report(directory, filename=DEFAULT_OUTPUT_FILE):
44
- """Reports complexity and statistics for Python files in a project directory.
44
+ """Generates an overview report of code complexity and security indicators.
45
+
46
+ This function analyzes a Python project to produce a high-level overview of
47
+ complexity and security-related metrics. The input may be either:
48
+
49
+ - A local directory containing Python source files
50
+ - The name of a package hosted on PyPI.org
51
+
52
+ For PyPI packages, the source distribution (sdist) is downloaded,
53
+ extracted to a temporary directory, scanned, and removed after the report
54
+ is generated.
55
+
56
+ The report includes summary statistics, security risk indicators based on
57
+ complexity and total lines of code, a list of discovered modules, per-file
58
+ metrics, and a visual overview. Results are written to a static HTML file.
45
59
 
46
- Parameters:
47
- directory (str): Path to the directory to scan.
48
- filename (str): Output filename for the HTML report.
60
+ Examples:
61
+ Generate an overview report for a local project directory::
62
+
63
+ codeaudit overview /projects/mycolleaguesproject
64
+
65
+ Generate an overview report for a PyPI package::
66
+
67
+ codeaudit overview linkaudit #A nice project on PyPI.org
68
+
69
+ codeaudit overview pydantic #A complex project on PyPI.org from a security perspective?
70
+
71
+ Args:
72
+ directory (str): Path to a local directory containing Python source files
73
+ or the name of a package available on PyPI.org.
74
+ filename (str, optional): Name (and optional path) of the HTML file to
75
+ write the overview report to. The filename should use the ``.html``
76
+ extension. Defaults to ``DEFAULT_OUTPUT_FILE``.
77
+
78
+ Returns:
79
+ None. The function writes a static HTML overview report to disk.
80
+
81
+ Raises:
82
+ SystemExit: If the provided path is not a directory, contains no Python
83
+ files, or is neither a valid local directory nor a valid PyPI
84
+ package name.
49
85
  """
50
- if not os.path.exists(directory):
51
- print(f"ERROR: Directory '{directory}' does not exist.")
52
- print(f"This function only works for directories which contains one or more Python source code files (*.py). ")
53
- exit(1)
54
- if not os.path.isdir(directory):
55
- print(f"ERROR: '{directory}' is not a directory (maybe you try to run it for a single file)")
56
- print(f"This function only works for directories which contains one or more Python source code files (*.py). ")
57
- exit(1)
58
- #Check if the directory has Python files
59
- if not has_python_files(directory):
60
- print(f'Error: Directory path {directory} contains no Python files.')
61
- exit(1)
86
+ clean_up = False
87
+ if os.path.exists(directory):
88
+ # Check if the path is actually a directory
89
+ if not os.path.isdir(directory):
90
+ print(f"ERROR: '{directory}' is not a directory.")
91
+ print("This function only works for directories containing Python files (*.py).")
92
+ exit(1)
93
+ # Check if the directory contains any .py files
94
+ if not has_python_files(directory):
95
+ print(f"ERROR: Directory '{directory}' contains no Python files.")
96
+ exit(1)
97
+ elif get_pypi_download_info(directory):
98
+ # If local path doesn't exist, try to treat it as a PyPI package
99
+ print(f"No local directory with name:{directory} found locally. Checking if package exist on PyPI...")
100
+ package_name = directory #The variable input_path is now equal to the package name
101
+ print(f"Package: {package_name} exist on PyPI.org!")
102
+ pypi_data = get_pypi_download_info(package_name)
103
+ url = pypi_data['download_url']
104
+ release = pypi_data['release']
105
+ if url is not None:
106
+ print(f'Creating Python Code Audit overview for package:\n{url}')
107
+ src_dir, tmp_handle = get_package_source(url)
108
+ directory = src_dir
109
+ clean_up = True
110
+ else:
111
+ # Neither a local directory nor a valid PyPI package
112
+ print(f"ERROR: '{directory}' is not a local directory or a valid PyPI package.")
113
+ exit(1)
62
114
  result = get_statistics(directory)
63
115
  modules = total_modules(directory)
64
116
  df = pd.DataFrame(result)
@@ -66,7 +118,11 @@ def overview_report(directory, filename=DEFAULT_OUTPUT_FILE):
66
118
  df['External-Modules'] = modules['External-Modules']
67
119
  overview_df = overview_count(df)
68
120
  html = '<h1>' + f'Python Code Audit overview report' + '</h1><br>'
69
- html += f'<p>Codeaudit overview scan of the directory:<b> {directory}</b></p>'
121
+ if clean_up:
122
+ html += f'<p>Codeaudit overview scan of package:<b> {package_name}</b></p>'
123
+ html += f'<p>Version:<b>{release}</b></p>'
124
+ else:
125
+ html += f'<p>Codeaudit overview scan of the directory:<b> {directory}</b></p>'
70
126
  html += f'<h2>Summary</h2>'
71
127
  html += overview_df.to_html(escape=True,index=False)
72
128
  html += '<br><br>'
@@ -83,6 +139,8 @@ def overview_report(directory, filename=DEFAULT_OUTPUT_FILE):
83
139
  html += '<br>'
84
140
  ## Module overview
85
141
  modules_discovered = get_all_modules(directory)
142
+ if clean_up:
143
+ tmp_handle.cleanup() #Clean up tmp directory if overview is created directly from PyPI package
86
144
  html += '<details>'
87
145
  html += '<summary>Click to see all discovered modules.</summary>'
88
146
  html+=dict_to_html(modules_discovered)
@@ -106,21 +164,51 @@ def overview_report(directory, filename=DEFAULT_OUTPUT_FILE):
106
164
 
107
165
 
108
166
 
109
- def scan_report(input_path , filename=DEFAULT_OUTPUT_FILE):
110
- """Scans Python code or packages on PyPI.org on security weaknesses.
111
-
112
- This function performs security validations on the specified file or directory,
113
- formats the results into an HTML report, and writes the output to an HTML file.
114
-
115
- You can specify the name of the outputfile and directory for the generated HTML report. Make sure you chose the extension `.html` since the output file is a static html file.
167
+ def scan_report(input_path, filename=DEFAULT_OUTPUT_FILE):
168
+ """Scans Python source code or PyPI packages for security weaknesses.
116
169
 
117
- Parameters:
118
- file_to_scan (str) : The full path to the Python source file to be scanned.
119
- filename (str, optional): The name of the HTML file to save the report to.
120
- Defaults to `DEFAULT_OUTPUT_FILE`.
170
+ This function performs static application security testing (SAST) on a
171
+ given input, which can be:
172
+
173
+ - A local directory containing Python source code
174
+ - A single local Python file
175
+ - A package name hosted on PyPI.org
176
+
177
+ Depending on the input type, the function analyzes the source code for
178
+ potential security issues, generates an HTML report summarizing the
179
+ findings, and writes the report to a static HTML file.
180
+
181
+ If a PyPI package name is provided, the function downloads the source
182
+ distribution (sdist), scans the extracted source code, and removes all
183
+ temporary files after the scan completes.
184
+
185
+ Example:
186
+ Scan a local directory and write the report to ``report.html``::
187
+
188
+ codeaudit filescan_/shitwork/custompythonmodule/
189
+
190
+ Scan a single Python file::
191
+
192
+ codeaudit filescan myexample.py
193
+
194
+ Scan a package hosted on PyPI::
195
+
196
+ codeaudit filescan linkaudit #A nice project to check broken links in markdown files
197
+
198
+ codeaudit filescan requests
199
+
200
+ Args:
201
+ input_path (str): Path to a local Python file or directory, or the name
202
+ of a package available on PyPI.org.
203
+ filename (str, optional): Name (and optional path) of the HTML file to
204
+ write the scan report to. The filename should use the ``.html``
205
+ extension. Defaults to ``DEFAULT_OUTPUT_FILE``.
121
206
 
122
207
  Returns:
123
- None - A HTML report is written as output
208
+ None. The function writes a static HTML security report to disk.
209
+
210
+ Raises:
211
+ None explicitly. Errors and invalid inputs are reported to stdout.
124
212
  """
125
213
  # Check if the input is a valid directory or a single valid Python file
126
214
  # In case no local file or directory is found, check if the givin input is pypi package name
@@ -146,12 +234,16 @@ def scan_report(input_path , filename=DEFAULT_OUTPUT_FILE):
146
234
  pypi_data = get_pypi_download_info(package_name)
147
235
  url = pypi_data['download_url']
148
236
  release = pypi_data['release']
149
- print(url)
150
- print(release)
151
- src_dir, tmp_handle = get_package_source(url)
152
- directory_scan_report(src_dir , filename , package_name, release ) #create scan report for a package or directory
153
- # Cleaning up temp directory
154
- tmp_handle.cleanup() # deletes everything from temp directory
237
+ if url is not None:
238
+ print(url)
239
+ print(release)
240
+ src_dir, tmp_handle = get_package_source(url)
241
+ directory_scan_report(src_dir , filename , package_name, release ) #create scan report for a package or directory
242
+ # Cleaning up temp directory
243
+ tmp_handle.cleanup() # deletes everything from temp directory
244
+ else:
245
+ print(f'Error:A source distribution (sdist in .tar.gz format) for package: {package_name} can not be found or does not exist on PyPi.org.\n')
246
+ print(f"Make a local git clone of the {package_name} using `git clone` and run `codeaudit filescan <directory-with-src-cloned-of-{package_name}>` to check for weaknesses.")
155
247
  else:
156
248
  #File is NOT a valid Python file, can not be parsed or directory is invalid.
157
249
  print(f"Error: '{input_path}' isn't a valid Python file, directory path to a package or a package on PyPI.org.")
@@ -235,7 +327,8 @@ def directory_scan_report(directory_to_scan , filename=DEFAULT_OUTPUT_FILE , pac
235
327
  name_of_package = get_filename_from_path(directory_to_scan)
236
328
  if package_name is not None:
237
329
  #Use real package name and retrieved release info
238
- html += f'<p>Below the result of the Codeaudit scan of the package - Release :<b> {package_name} - {release} </b></p>'
330
+ html += f'<p>Below the result of the Codeaudit scan of (Package name - Release):</p>'
331
+ html += f'<p><b> {package_name} - {release} </b></p>'
239
332
  else:
240
333
  html += f'<p>Below the result of the Codeaudit scan of the directory:<b> {name_of_package}</b></p>'
241
334
  html += f'<p>Total Python files found: <b>{len(files_to_check)}</b></p>'
@@ -262,13 +355,44 @@ def directory_scan_report(directory_to_scan , filename=DEFAULT_OUTPUT_FILE , pac
262
355
  html += '<p>The Python files with no security issues <b>detected</b> by codeaudit are:<p>'
263
356
  html += dict_list_to_html_table(collection_ok_files)
264
357
  html += '<br>'
358
+ if package_name is not None:
359
+ html += f'<p><b>Note:</b><i>Since this check is done on a package on PyPI.org, the temporary local directories are deleted. To examine the package in detail, you should download the sources locally and run the command:<code>codeaudit filescan</code> again.</i></p>'
265
360
  html += '<p><b>Disclaimer:</b><i>Only Python source files are taken into account for this scan. Sometimes security issues are present in configuration files, like ini,yaml or json files!</i></p>'
266
361
  html += DISCLAIMER_TEXT
267
362
  create_htmlfile(html,filename)
268
363
 
364
+ def report_module_information(inputfile, reportname=DEFAULT_OUTPUT_FILE):
365
+ """Generates a vulnerability report for imported Python modules.
366
+
367
+ This function analyzes a single Python source file to identify imported
368
+ modules and checks externally imported modules against the OSV vulnerability
369
+ database. The results are compiled into a static HTML report.
370
+
371
+ For each detected external module, the report indicates whether known
372
+ vulnerability information exists and, if available, includes detailed
373
+ vulnerability data.
374
+
375
+ Progress information is printed to stdout while processing modules.
376
+
377
+ Example:
378
+ Generate a module vulnerability report for a Python file::
379
+
380
+ codeaudit modulescan mypythonfile.py
381
+
382
+ Args:
383
+ inputfile (str): Path to the Python source file to analyze.
384
+ reportname (str, optional): Name (and optional path) of the HTML file
385
+ to write the module vulnerability report to. The filename should
386
+ use the ``.html`` extension. Defaults to ``DEFAULT_OUTPUT_FILE``.
387
+
388
+ Returns:
389
+ None. The function writes a static HTML report to disk.
269
390
 
270
- def report_module_information(inputfile,reportname=DEFAULT_OUTPUT_FILE):
271
- """Reports module vulnerability information."""
391
+ Raises:
392
+ None explicitly. File reading errors or invalid input are reported
393
+ via standard output.
394
+
395
+ """
272
396
  source = read_in_source_file(inputfile)
273
397
  used_modules = get_imported_modules(source)
274
398
  # Initial call to print 0% progress
@@ -415,18 +539,23 @@ def report_implemented_tests(filename=DEFAULT_OUTPUT_FILE):
415
539
  Creates an HTML report of all implemented security checks.
416
540
 
417
541
  This report provides a user-friendly overview of the static security checks
418
- currently supported by codeaudit. It is intended to make it easier to review
542
+ currently supported by Python Code Audit. It is intended to make it easier to review
419
543
  the available validations without digging through the codebase.
420
544
 
421
545
  The generated HTML includes:
422
546
  - A table of all implemented checks
423
547
  - The number of validations
424
- - The version of codeaudit used
548
+ - The version of Python Code Audit (codeaudit) used
425
549
  - A disclaimer about version-specific reporting
426
550
 
427
551
  The report is saved to the specified filename and is formatted to be
428
552
  embeddable in larger multi-report documents.
429
553
 
554
+ Help me continue developing Python Code Audit as free and open-source software.
555
+ Join the community to contribute to the most complete, local first , Python Security Static scanner.
556
+ Help!! Join the journey, check: https://github.com/nocomplexity/codeaudit#contributing
557
+
558
+
430
559
  Parameters:
431
560
  filename (str): The output HTML filename. Defaults to 'codeaudit_checks.html'.
432
561
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeaudit
3
- Version: 1.4.0
3
+ Version: 1.4.2
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
@@ -74,17 +74,12 @@ Python Code Audit has the following features:
74
74
 
75
75
  ## Installation
76
76
 
77
- ```console
78
- pip install codeaudit
79
- ```
80
-
81
- or use:
82
-
83
77
  ```console
84
78
  pip install -U codeaudit
85
79
  ```
86
80
 
87
- If you have installed Python `codeaudit` in the past and want to make sure you use the latest new validations and features.
81
+ If you have installed **Python Code Audit** previously and want to ensure you are using the latest validations and features, simply run this command again. Python Code Audit is frequently updated with new checks.
82
+
88
83
 
89
84
  ## Usage
90
85
 
@@ -106,34 +101,32 @@ This will show all commands:
106
101
  Python Code Audit - A modern Python security source code analyzer based on distrust.
107
102
 
108
103
  Commands to evaluate Python source code:
109
- Usage: codeaudit COMMAND [PATH or FILE] [OUTPUTFILE]
104
+ Usage: codeaudit COMMAND <directory|package> [report.html]
110
105
 
111
- Depending on the command, a directory or file name must be specified. The output is a static HTML file to be examined in a browser. Specifying a name for the output file is optional.
106
+ Depending on the command, you must specify a local directory, a Python file, or a package name hosted on PyPI.org.Reporting: The results are generated as a static HTML report for viewing in a web browser.
112
107
 
113
108
  Commands:
114
- overview Reports Complexity and statistics per Python file from a directory.
115
- filescan Scans Python files or directories(packages) for vulnerabilities and reports potential issues.
116
- modulescan Reports module vulnerability information.
109
+ overview Generates an overview report of code complexity and security indicators.
110
+ filescan Scans Python source code or PyPI packages for security weaknesses.
111
+ modulescan Generates a vulnerability report for imported Python modules.
117
112
  checks Creates an HTML report of all implemented security checks.
118
113
  version Prints the module version. Or use codeaudit [-v] [--v] [-version] or [--version].
119
114
 
120
- Use the Codeaudit documentation to check the security of Python programs and make your Python programs more secure!
121
- Check https://simplifysecurity.nocomplexity.com/
122
-
115
+ Use the Python Code Audit documentation (https://codeaudit.nocomplexity.com) to audit and secure your Python programmes. Explore further essential open-source security tools at https://simplifysecurity.nocomplexity.com/
123
116
  ```
124
117
 
125
118
  ## Example
126
119
 
127
- By running the `codeaudit filescan` command, detailed security information is determined for a Python file based on more than **70 validations** implemented.
120
+ By running the `codeaudit filescan` command, detailed security information is determined for a Python file based on more than **80 validations** implemented.
128
121
 
129
122
  The `codeaudit filescan` command shows all **potential** security issues that are detected in the source file in a HTML-report.
130
123
 
131
124
  Per line a the in construct that can cause a security risks is shown, along with the relevant code lines where the issue is detected.
132
125
 
133
- To scan a Python file on possible security issues, do:
126
+ To scan a Python package on PyPI.org on possible security issues, do:
134
127
 
135
128
  ```bash
136
- codeaudit filescan ../codeaudit/tests/validationfiles/allshit.py
129
+ codeaudit filescan <package-name> [reportname.html]
137
130
 
138
131
  =====================================================================
139
132
  Codeaudit report file created!
@@ -1,22 +1,22 @@
1
- codeaudit/__about__.py,sha256=-bxUcBaBrbGG65qQWVQjAxsqncjywKoDesQoM0JjIYc,144
1
+ codeaudit/__about__.py,sha256=ZFZWLshIXTzzWzLpG6F82-bf1qgOivq-oT9i9-lECak,144
2
2
  codeaudit/__init__.py,sha256=YGs6qU0BVHPGtXCS-vfBDLO4TOfJDLTWMgaFDTmi_Iw,157
3
3
  codeaudit/altairplots.py,sha256=gBXN1_wxUmjzTNizvzbOeCKvUxpClGPdZmK7ICK1x68,4531
4
- codeaudit/api_interfaces.py,sha256=nnPhVPmodb-2MYCSiToT87uYZypMoX5oKg6WmLCjdiw,11336
4
+ codeaudit/api_interfaces.py,sha256=zWJrLDM8b3b2-rN0gCoPdflEFMzKUz3M7PfXtXvDpd4,15358
5
5
  codeaudit/api_reporting.py,sha256=W8eutTJ0d-TENbv5cCmAOfu4GEp_RwiQ4XU5FCmfkoI,1736
6
6
  codeaudit/checkmodules.py,sha256=aiF34KO-9HZDRgVBtSwVFdeUxT5_Ka5VtmlfgoLgNVs,5582
7
- codeaudit/codeaudit.py,sha256=yQ7SHx8b3Q9rMu8nCVyyuu3wJr3DlO-BuSIz2ZwJFGM,3426
7
+ codeaudit/codeaudit.py,sha256=g2HzRX6a3fckKUhyRrk6n3-5qNdVYtZRI1gqQ-QNl10,3775
8
8
  codeaudit/complexitycheck.py,sha256=A3_a5v-U0YQr80pWQwSVvOsY_eQtqwNkQf9Txr9mNtQ,3722
9
9
  codeaudit/filehelpfunctions.py,sha256=tx7HDCyTkZuw8YieXipQXM8iRfrDfIVZyKb7vjmkEFY,4358
10
10
  codeaudit/htmlhelpfunctions.py,sha256=-SMsyfF7TRIfJkrUqoJuh7AoG1RVrYFsZfFljoxVHXc,3246
11
11
  codeaudit/issuevalidations.py,sha256=-WdaXT_R-P9w0JbQpJ5ngVoVhG9Yee2ri0aH5SoC1Ao,6404
12
- codeaudit/pypi_package_scan.py,sha256=JxgcCtJaVH9pAQ902gjmzgGGECt71r4gnFhqcZUy3OE,4872
13
- codeaudit/reporting.py,sha256=eXlslslMgG_4ATFpTEi5wtgwTMOgngAi-38Fg-xaYN4,24299
12
+ codeaudit/pypi_package_scan.py,sha256=yxCXrRvjc4r0YsJYHvHJuJTyHC5QZl3sRQp73akCXx8,4723
13
+ codeaudit/reporting.py,sha256=GXIiq2fzN5vvSDjSTDKsEuR0hfEWybbvid7DYzAjsZg,30029
14
14
  codeaudit/security_checks.py,sha256=wEO_A054zXmLccWGREi6cNADa4IgoOPxHsq-Je5iMIY,2167
15
15
  codeaudit/simple.css,sha256=7auhDAUwjdluFIyoCskl-Vfh503prXKqftQrmo0-e_g,3565
16
16
  codeaudit/totals.py,sha256=b6OkzcMdqGKPwuGBKrwAeCxBOJxHa5FHauGWnEb-6zM,6387
17
17
  codeaudit/data/sastchecks.csv,sha256=fIcyZgymCtAluPta9fTEk6a9DJ2AGJczZYRPUIQuSag,9738
18
- codeaudit-1.4.0.dist-info/METADATA,sha256=I-dT_-7IdlcdNEinahwY4CN-j6Ty8Q9wlrTbb84dqUk,7505
19
- codeaudit-1.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
20
- codeaudit-1.4.0.dist-info/entry_points.txt,sha256=7w6I8zii62nJHIIF30CRP5g1z8enMqF1pZEDdlw4HcQ,55
21
- codeaudit-1.4.0.dist-info/licenses/LICENSE.txt,sha256=-5gWaMGKJ54oX8TYP7oeg2zITdTapzyWl9PP0tispuA,34674
22
- codeaudit-1.4.0.dist-info/RECORD,,
18
+ codeaudit-1.4.2.dist-info/METADATA,sha256=RtT5hL75GoLxYNav079UwxBdpMkLMfbfID-HX6Ijx_E,7628
19
+ codeaudit-1.4.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
20
+ codeaudit-1.4.2.dist-info/entry_points.txt,sha256=7w6I8zii62nJHIIF30CRP5g1z8enMqF1pZEDdlw4HcQ,55
21
+ codeaudit-1.4.2.dist-info/licenses/LICENSE.txt,sha256=-5gWaMGKJ54oX8TYP7oeg2zITdTapzyWl9PP0tispuA,34674
22
+ codeaudit-1.4.2.dist-info/RECORD,,