codeaudit 1.0.0__py3-none-any.whl → 1.2.0__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.0.0"
4
+ __version__ = "1.2.0"
codeaudit/altairplots.py CHANGED
@@ -64,3 +64,56 @@ def multi_bar_chart(df):
64
64
  # Stack the rows vertically
65
65
  multi_chart = alt.vconcat(*rows)
66
66
  return multi_chart
67
+
68
+
69
+
70
+ def issue_plot(input_dict):
71
+ """
72
+ Create a radial (polar area) chart using Altair.
73
+
74
+ Parameters
75
+ ----------
76
+ input_dict : dict
77
+ Dictionary where keys are 'construct' and values are 'count'.
78
+
79
+ Returns
80
+ -------
81
+ alt.Chart
82
+ Altair chart object.
83
+ """
84
+ # Convert input dict to DataFrame
85
+ df = pd.DataFrame(list(input_dict.items()), columns=['construct', 'count'])
86
+
87
+ # Validation
88
+ if not {'construct', 'count'}.issubset(df.columns):
89
+ raise ValueError("DataFrame must have 'construct' and 'count' columns.")
90
+
91
+ # Add a combined label for legend
92
+ df["legend_label"] = df["construct"] + " (" + df["count"].astype(str) + ")"
93
+
94
+ # Compute fraction of total for angular width
95
+ total = df['count'].sum()
96
+ df['fraction'] = df['count'] / total
97
+
98
+ # Compute cumulative angle for start and end of each slice
99
+ df['theta0'] = df['fraction'].cumsum() - df['fraction']
100
+ df['theta1'] = df['fraction'].cumsum()
101
+
102
+ # Radial chart using mark_arc
103
+ chart = alt.Chart(df).mark_arc(innerRadius=20).encode(
104
+ theta=alt.Theta('theta1:Q', stack=None, title=None),
105
+ theta2='theta0:Q', # define start angle
106
+ radius=alt.Radius('count:Q', scale=alt.Scale(type='sqrt')), # radial extent
107
+ color=alt.Color(
108
+ 'legend_label:N',
109
+ scale=alt.Scale(scheme='category20'),
110
+ legend=alt.Legend(title='Weaknesses (Count)')
111
+ ),
112
+ tooltip=['construct', 'count']
113
+ ).properties(
114
+ title='Overview of Security Weaknesses',
115
+ width=600,
116
+ height=600
117
+ )
118
+
119
+ return chart
@@ -0,0 +1,269 @@
1
+ """
2
+ License GPLv3 or higher.
3
+
4
+ (C) 2025 Created by Maikel Mardjan - https://nocomplexity.com/
5
+
6
+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
7
+
8
+ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
9
+
10
+ You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
11
+
12
+
13
+ Public API functions for Python Code Audit aka codeaudit on pypi.org
14
+ """
15
+
16
+ from codeaudit import __version__
17
+ from codeaudit.filehelpfunctions import get_filename_from_path , collect_python_source_files , is_ast_parsable
18
+ from codeaudit.security_checks import perform_validations , ast_security_checks
19
+ from codeaudit.totals import overview_per_file , get_statistics , overview_count , total_modules
20
+ from codeaudit.checkmodules import get_all_modules , get_imported_modules_by_file , get_standard_library_modules , check_module_vulnerability
21
+
22
+
23
+ from pathlib import Path
24
+ import json
25
+ import datetime
26
+ import pandas as pd
27
+ import platform
28
+ from collections import Counter
29
+
30
+ import altair as alt
31
+
32
+ def version():
33
+ """Returns the version of Python Code Audit"""
34
+ ca_version = __version__
35
+ return {"name" : "Python_Code_Audit",
36
+ "version" : ca_version}
37
+
38
+
39
+ def filescan(input_path):
40
+ """Scans a Python file or directory and returns result as JSON"""
41
+ output ={}
42
+ file_output = {}
43
+ file_path = Path(input_path)
44
+ ca_version_info = version()
45
+ now = datetime.datetime.now()
46
+ timestamp_str = now.strftime("%Y-%m-%d %H:%M")
47
+ output = ca_version_info | {"generated_on" : timestamp_str}
48
+ # 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}
68
+ 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
70
+ file_information = overview_per_file(input_path)
71
+ module_information = get_modules(input_path) # modules per file
72
+ 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!
74
+ output |= { "file_security_info" : file_output}
75
+ return output
76
+ else:
77
+ #Its not a directory nor a valid Python file:
78
+ return {"Error" : "File is not a *.py file, does not exist or is not a valid directory path towards a Python package."}
79
+
80
+ def _codeaudit_scan(filename):
81
+ """Internal helper function to do a SAST scan on a single file
82
+ To scan a file, or Python package using the API interface, use the `filescan` API call!
83
+ """
84
+ #get the file name
85
+ name_of_file = get_filename_from_path(filename)
86
+ sast_data = perform_validations(filename)
87
+ sast_data_results = sast_data["result"]
88
+ sast_result = dict(sorted(sast_data_results.items()))
89
+ output = { "file_name" : name_of_file ,
90
+ "sast_result": sast_result}
91
+ return output
92
+
93
+ def save_to_json(sast_result, filename="codeaudit_output.json"):
94
+ """
95
+ Save a SAST result (dict or serializable object) to a JSON file.
96
+
97
+ Args:
98
+ sast_result (dict or list): The data to be saved as JSON.
99
+ filename (str, optional): The file path to save the JSON data.
100
+ Defaults to "codeaudit_output.json".
101
+
102
+ Returns:
103
+ Path: The absolute path of the saved file, or None if saving failed.
104
+ """
105
+ filepath = Path(filename).expanduser().resolve()
106
+
107
+ try:
108
+ filepath.parent.mkdir(parents=True, exist_ok=True) # ensure directory exists
109
+ with filepath.open("w", encoding="utf-8") as f:
110
+ json.dump(sast_result, f, indent=2, ensure_ascii=False)
111
+ return
112
+ except (TypeError, ValueError) as e:
113
+ print(f"[Error] Failed to serialize data to JSON: {e}")
114
+ except OSError as e:
115
+ print(f"[Error] Failed to write file '{filepath}': {e}")
116
+
117
+ def read_input_file(filename):
118
+ """
119
+ Read a Python CodeAudit JSON file and return its contents as a Python dictionary.
120
+
121
+ Args:
122
+ filename: Path to the JSON file.
123
+
124
+ Returns:
125
+ dict: The contents of the JSON file.
126
+
127
+ Raises:
128
+ FileNotFoundError: If the file does not exist.
129
+ json.JSONDecodeError: If the file is not valid JSON.
130
+ """
131
+ try:
132
+ with open(filename, 'r', encoding='utf-8') as f:
133
+ return json.load(f)
134
+ except FileNotFoundError as e:
135
+ raise FileNotFoundError(f"File not found: {filename}") from e
136
+ except json.JSONDecodeError as e:
137
+ raise json.JSONDecodeError(f"Invalid JSON in file: {filename}", e.doc, e.pos)
138
+
139
+
140
+ def get_construct_counts(input_file):
141
+ """
142
+ Analyze a scan result and count occurrences of code constructs (aka weaknesses).
143
+
144
+ This function uses `filescan` API call to retrieve security-related information
145
+ about the input file. This returns a dict. Then it counts how many times each code construct
146
+ appears across all scanned files.
147
+
148
+ Args:
149
+ input_file (str): Path to the file or directory(package) to scan.
150
+
151
+ Returns:
152
+ dict: A dictionary mapping each construct name (str) to the total
153
+ number of occurrences (int) across all scanned files.
154
+
155
+ Notes:
156
+ - The `filescan` function is expected to return a dictionary with
157
+ a 'file_security_info' key, containing per-file information.
158
+ - Each file's 'sast_result' should be a dictionary mapping
159
+ construct names to lists of occurrences.
160
+ """
161
+ scan_result = filescan(input_file)
162
+ counter = Counter()
163
+
164
+ for file_info in scan_result.get('file_security_info', {}).values():
165
+ sast_result = file_info.get('sast_result', {})
166
+ for construct, occurence in sast_result.items(): #occurence is times the construct appears in a single file
167
+ counter[construct] += len(occurence)
168
+
169
+ return dict(counter)
170
+
171
+ def get_modules(filename):
172
+ """Gets modules of a Python file """
173
+ modules_found = get_imported_modules_by_file(filename)
174
+ return modules_found
175
+
176
+ def get_overview(input_path):
177
+ """Retrieves the security relevant statistics of a Python package(directory) or of a single Python
178
+
179
+ Based on the input path, call the overview function and return the result in a dict
180
+
181
+ Args:
182
+ input_path: Directory path of the package to use
183
+
184
+
185
+ Returns:
186
+ dict: Returns the overview statistics in DICT format
187
+ """
188
+ file_path = Path(input_path)
189
+ if file_path.is_dir(): #only for valid parsable Python files
190
+ files_to_check = collect_python_source_files(input_path)
191
+ if len(files_to_check) > 1:
192
+ statistics = get_statistics(input_path)
193
+ modules = total_modules(input_path)
194
+ df = pd.DataFrame(statistics)
195
+ df['Std-Modules'] = modules['Std-Modules'] #Needed for the correct overall count
196
+ df['External-Modules'] = modules['External-Modules'] #Needed for the correct overall count
197
+ overview_df = overview_count(df) #create the overview Dataframe
198
+ dict_overview = overview_df.to_dict(orient="records")[0] #The overview Dataframe has only one row
199
+ return dict_overview
200
+ else:
201
+ output_msg = f'Directory path {input_path} contains no Python files.'
202
+ return {"Error" : output_msg}
203
+ elif file_path.suffix.lower() == ".py" and file_path.is_file() and is_ast_parsable(input_path):
204
+ security_statistics = overview_per_file(input_path)
205
+ return security_statistics
206
+ else:
207
+ #Its not a directory nor a valid Python file:
208
+ return {"Error" : "File is not a *.py file, does not exist or is not a valid directory path to a Python package."}
209
+
210
+ def get_default_validations():
211
+ """Retrieves the implemented default security validations
212
+ Args:
213
+ none
214
+
215
+ Returns:
216
+ dict: Overview of implemented security SAST validation on Standard Python modules. Including vital help text.
217
+ """
218
+ df = ast_security_checks()
219
+ result = df.to_dict(orient="records")
220
+ output = _generation_info() | {"validations" : result}
221
+ return output
222
+
223
+ def _generation_info():
224
+ """Internal function to retrieve generation info for APIs output"""
225
+ ca_version_info = version()
226
+ now = datetime.datetime.now()
227
+ timestamp_str = now.strftime("%Y-%m-%d %H:%M")
228
+ output = ca_version_info | {"generated_on" : timestamp_str}
229
+ return output
230
+
231
+ def platform_info():
232
+ """Get Python platform information - Python version and Python runtime interpreter used.
233
+ Args:
234
+ none
235
+
236
+ Returns:
237
+ dict: Overview of implemented security SAST validation on Standard Python modules
238
+ """
239
+ python_version = platform.python_version()
240
+ platform_implementation = platform.python_implementation()
241
+ output = { "python_version" : python_version ,
242
+ "python_implementation" : platform_implementation}
243
+ return output
244
+
245
+
246
+ def get_psl_modules():
247
+ """Retrieves a list of collection of Python modules that are part of a Python distribution aka standard installation
248
+
249
+ Returns:
250
+ dict: Overview of PSL modules in the Python version used.
251
+
252
+ """
253
+ psl_modules = get_standard_library_modules()
254
+ output = _generation_info() | platform_info() | { "psl_modules" : psl_modules}
255
+ return output
256
+
257
+ def get_module_vulnerability_info(module):
258
+ """Retrieves vulnerability information for external modules using the OSV Database
259
+ Args:
260
+ input: module name
261
+
262
+ Returns:
263
+ dict: Result of OSV query
264
+ """
265
+ vuln_info = check_module_vulnerability(module)
266
+ key_string = f'{module}_vulnerability_info'
267
+ output = _generation_info() | { key_string : vuln_info}
268
+ return output
269
+
codeaudit/checkmodules.py CHANGED
@@ -66,7 +66,14 @@ def get_standard_library_modules():
66
66
 
67
67
 
68
68
  def query_osv(package_name, ecosystem="PyPI"):
69
- """MVP version to check imported module on vulnerabilities on osv db"""
69
+ """Query the OSV DB (Open Source Vulnerabilities) API for a given package.
70
+ Args:
71
+ package_name (str): The name of the package to check.
72
+ ecosystem (str, optional): The package ecosystem (default: "PyPI").
73
+
74
+ Returns:
75
+ dict: The parsed JSON response from the OSV API, or an error response.
76
+ """
70
77
  url = "https://api.osv.dev/v1/query"
71
78
  headers = {"Content-Type": "application/json"}
72
79
  data = {
@@ -81,23 +88,34 @@ def query_osv(package_name, ecosystem="PyPI"):
81
88
  with urllib.request.urlopen(request) as response:
82
89
  return json.loads(response.read().decode("utf-8"))
83
90
 
91
+ def extract_vulnerability_info(data):
92
+ """
93
+ Extract vulnerability details from OSV response data.
84
94
 
85
- def extract_vuln_info(data):
95
+ Args:
96
+ data (dict): The JSON response from the OSV API.
97
+
98
+ Returns:
99
+ list: A list of vulnerability summaries containing ID, details, and aliases.
100
+ """
86
101
  results = []
87
102
  for vuln in data.get("vulns", []):
88
- info = {
89
- "id": vuln.get("id"),
90
- "details": vuln.get("details"),
91
- "aliases": vuln.get("aliases", []),
92
- }
93
- results.append(info)
103
+ results.append(
104
+ {
105
+ "id": vuln.get("id"),
106
+ "summary": vuln.get("summary", ""),
107
+ "details": vuln.get("details", ""),
108
+ "aliases": vuln.get("aliases", []),
109
+ "severity": vuln.get("severity", []), # CVSS scores if available
110
+ }
111
+ )
94
112
  return results
95
113
 
96
114
 
97
- def check_module_on_vuln(module):
115
+ def check_module_vulnerability(module):
98
116
  """Retrieves vuln info for external modules using osv-db"""
99
117
  result = query_osv(module)
100
- vulnerability_info = extract_vuln_info(result)
118
+ vulnerability_info = extract_vulnerability_info(result)
101
119
  return vulnerability_info
102
120
 
103
121
 
codeaudit/codeaudit.py CHANGED
@@ -15,7 +15,7 @@ CLI functions for codeaudit
15
15
  import fire # for working CLI with this PoC-thing (The Google way)
16
16
  import sys
17
17
  from codeaudit import __version__
18
- from codeaudit.reporting import overview_report ,report_module_information ,file_scan_report , directory_scan_report , report_implemented_tests
18
+ from codeaudit.reporting import overview_report ,report_module_information , scan_report , report_implemented_tests
19
19
 
20
20
  codeaudit_ascii_art=r"""
21
21
  ----------------------------------------------------
@@ -39,8 +39,8 @@ def display_help():
39
39
  print('Usage: codeaudit COMMAND [PATH or FILE] [OUTPUTFILE] \n')
40
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')
41
41
  print('Commands:')
42
- commands = ["overview", "directoryscan", "filescan", "modulescan", "checks","version"] # commands on CLI
43
- functions = [overview_report, directory_scan_report, file_scan_report, report_module_information, report_implemented_tests,display_version] # Related functions relevant for help
42
+ commands = ["overview", "filescan", "modulescan", "checks","version"] # commands on CLI
43
+ functions = [overview_report, scan_report, report_module_information, report_implemented_tests,display_version] # Related functions relevant for help
44
44
  for command, function in zip(commands, functions):
45
45
  docstring = function.__doc__.strip().split('\n')[0] or ""
46
46
  summary = docstring.split("\n", 1)[0]
@@ -60,8 +60,7 @@ def main():
60
60
  {
61
61
  "overview": overview_report,
62
62
  "modulescan": report_module_information,
63
- "filescan" : file_scan_report,
64
- "directoryscan" : directory_scan_report,
63
+ "filescan" : scan_report,
65
64
  "checks" : report_implemented_tests,
66
65
  "version" : display_version,
67
66
  "-help": display_help,
@@ -1,71 +1,71 @@
1
1
  name,construct,severity,info
2
- Check on assert,assert,Low,Assertions are for debugging and development. Misuse can lead to security vulnerabilities.
3
- Binding All Interfaces,s.bind,Medium,Network sockets require additional measurements.
4
- Check for chmod,os.chmod,High,Operating System calls can have a security impact and should be inspected in detail.
5
- Directory Creation,os.makedirs,Low,Operating System calls can have a security impact and should be inspected in detail.
6
- Directory Creation,os.mkdir,Low,Operating System calls can have a security impact and should be inspected in detail.
7
- Directory Creation,os.mkfifo,Low,Operating System calls can have a security impact and should be inspected in detail.
8
- Directory Creation,os.mknod,Low,Operating System calls can have a security impact and should be inspected in detail.
9
- Directory Creation,os.makedev,Low,Operating System calls can have a security impact and should be inspected in detail.
10
- OS System call - Fork a child process,os.fork,Low,"On macOS use of this function is unsafe when mixed with using higher-level system APIs, and that includes using urllib.request."
11
- Check on eval usage,eval,High,This function can executes arbitrary code.
12
- Check on input statement,input,Low,Use of input requires strict sanitizing and validation.
13
- Exception Handling,pass,Low,Too broad exception handling risk when not used correctly.
14
- Exception Handling- Continue statement,continue,Low,Too broad exception handling risk when not used correctly.
15
- Built-in Functions: Check for exec usage.,exec,High,This built-in function can execute code you do not want. Check and refuse using dynamic contructs within exec you can not validate upfront.
16
- Built-in Functions: Check on compile usage.,compile,High,It is possible to crash the Python interpreter when using this function.
17
- Use of dynamic Imports,__import__,Medium,"Validate the what is imported and only allow of known, safe modules."
18
- Use of dynamic Imports,importlib.import_module,Medium,"Validate the what is imported and only allow of known, safe modules."
19
- Hash Check - md5,hashlib.md5,High,Use of insecure hashing algorithms detected.
20
- Hash Check -sha1,hashlib.sha1,High,Use of insecure hashing algorithms detected.
21
- Logging - configuration ,logging.config,Low,Potential security issues can arise with parsing objects and incorrect sanitizing.
22
- Pickle use,pickle.loads,High,unpickling will import any class or function that it finds in the pickle data
23
- Pickle use,pickle.load,High,unpickling will import any class or function that it finds in the pickle data
24
- OS - direct calls,os.system,High,Operating System calls can have a security impact and should be inspected in detail.
25
- OS - execl,os.execl,High,Operating System calls can have a security impact and should be inspected in detail.
26
- OS - execle,os.execle,High,Operating System calls can have a security impact and should be inspected in detail.
27
- OS - execlp,os.execlp,High,Operating System calls can have a security impact and should be inspected in detail.
28
- OS - execlpe,os.execlpe,High,Operating System calls can have a security impact and should be inspected in detail.
29
- OS - execv,os.execv,High,Operating System calls can have a security impact and should be inspected in detail.
30
- OS - execve,os.execve,High,Operating System calls can have a security impact and should be inspected in detail.
31
- OS - execvp,os.execvp,High,Operating System calls can have a security impact and should be inspected in detail.
32
- OS - execvpe,os.execvpe,High,Operating System calls can have a security impact and should be inspected in detail.
33
- OS - popen,os.popen,High,Operating System calls can have a security impact and should be inspected in detail.
34
- Sys calls,sys.call_tracing,Medium,Sys functions that can give ow-level access to the interpreter's execution flow.
35
- Sys calls,sys.setprofile,Medium,Sys functions that can give ow-level access to the interpreter's execution flow.
36
- Sys calls,sys.settrace,Medium,Sys functions that can give ow-level access to the interpreter's execution flow.
37
- OS Access,os.access,High,Operating System calls can have a security impact and should be inspected in detail.
38
- OS Interfaces,os.write,Low,os.write can cause availability issues if not done correct.
39
- OS Interfaces,os.writev,Low,os.writev can cause availability issues if not done correct.
2
+ Assertions,assert,Low,Assertions are for debugging and development. Assertions can be disabled during runtime. Use in production can introduce vulnerabilities.
3
+ Insecure Network Binding,s.bind,Medium,Binding to all interfaces can expose the service to a wider network attack surface.
4
+ OS File Permissions,os.chmod,High,Changing permissions carelessly can expose sensitive files.
5
+ Directory Creation,os.makedirs,Low,Direct file system calls require careful input validation to prevent vulnerabilities.
6
+ Directory Creation,os.mkdir,Low,Direct file system calls require careful input validation to prevent vulnerabilities.
7
+ Directory Creation,os.mkfifo,Low,Direct file system calls require careful input validation to prevent vulnerabilities.
8
+ Directory Creation,os.mknod,Low,Direct file system calls require careful input validation to prevent vulnerabilities.
9
+ Directory Creation,os.makedev,Low,Direct file system calls require careful input validation to prevent vulnerabilities.
10
+ OS Forking,os.fork,Low,"On macOS use of this function is unsafe when mixed with using higher-level system APIs, and that includes using urllib.request."
11
+ Dangerous Built-in: eval,eval,High,This function can execute arbitrary code. Never safe with untrusted input.
12
+ Input Function,input,Low,User input must be strictly sanitized and validated to prevent injection vulnerabilities.
13
+ Overly Broad Exception Handling,pass,Low,Using `pass` in an `except` block can silently ignore critical security exceptions.
14
+ Overly Broad Exception Handling,continue,Low,Skipping over exceptions can mask critical errors and security risks.
15
+ Dangerous Built-in: exec,exec,High,This function can execute arbitrary code and should be used only with validated constructs.
16
+ Dangerous Built-in: compile,compile,High,This function can be used to execute arbitrary code or crash the Python interpreter.
17
+ Dynamic Imports,__import__,Medium,"Importing modules dynamically can load untrusted code."
18
+ Dynamic Imports,importlib.import_module,Medium,"Importing modules dynamically can load untrusted code."
19
+ Insecure Hashing Algorithm,hashlib.md5,High,MD5 is cryptographically broken and should not be used for security purposes.
20
+ Insecure Hashing Algorithm,hashlib.sha1,High,SHA-1 is cryptographically broken and should not be used for security purposes.
21
+ Logging Configuration,logging.config,Medium,Parsing untrusted logging configurations can lead to vulnerabilities if not handled correctly.
22
+ Pickle Usage,pickle.loads,High,Deserializing untrusted data with `pickle` can lead to arbitrary code execution.
23
+ Pickle Usage,pickle.load,High,Deserializing untrusted data with `pickle` can lead to arbitrary code execution.
24
+ OS Execution,os.system,High,Direct OS function calls can have significant security implications and require careful review.
25
+ OS Execution,os.execl,High,Direct OS function calls can have significant security implications and require careful review.
26
+ OS Execution,os.execle,High,Direct OS function calls can have significant security implications and require careful review.
27
+ OS Execution,os.execlp,High,Direct OS function calls can have significant security implications and require careful review.
28
+ OS Execution,os.execlpe,High,Direct OS function calls can have significant security implications and require careful review.
29
+ OS Execution,os.execv,High,Direct OS function calls can have significant security implications and require careful review.
30
+ OS Execution,os.execve,High,Direct OS function calls can have significant security implications and require careful review.
31
+ OS Execution,os.execvp,High,Direct OS function calls can have significant security implications and require careful review.
32
+ OS Execution,os.execvpe,High,Direct OS function calls can have significant security implications and require careful review.
33
+ OS Execution,os.popen,High,Direct OS function calls can have significant security implications and require careful review.
34
+ Sys Calls,sys.call_tracing,Medium,Provides low-level access to interpreter execution; dangerous if exposed.
35
+ Sys Calls,sys.setprofile,Medium,Provides low-level access to interpreter execution; dangerous if exposed.
36
+ Sys Calls,sys.settrace,Medium,Provides low-level access to interpreter execution; dangerous if exposed.
37
+ OS Access,os.access,High,Direct OS function calls can have significant security implications and require careful review.
38
+ OS File Operations,os.write,Low,"Writing to unvalidated or unintended file descriptors can lead to data corruption, privilege escalation, or denial of service."
39
+ OS File Operations,os.writev,Low,"Writing to unvalidated or unintended file descriptors can lead to data corruption, privilege escalation, or denial of service."
40
40
  OS Interfaces,os.forkpty,Low,Use of forkpty can be unsafe when used on MacOS.
41
- OS Interface,os.read,Low,"When files can be read from , they can be transfered to some place you do not want."
42
- tempfile ,tempfile.mktemp,Low,This function may introduce race conditions which could negatively impact security.
43
- Marshal,marshal.loads,High,The marshal module is not intended to be secure against erroneous or maliciously constructed data.
44
- Marshal,marshal.load,High,The marshal module is not intended to be secure against erroneous or maliciously constructed data.
45
- Subprocesses - call,subprocess.call,High,Use of the subprocess module calls should be analyzed in-depth.
46
- Subprocesses - check_call,subprocess.check_call,High,Use of the subprocess module calls should be analyzed in-depth.
47
- Subprocesses - Popen,subprocess.Popen,Medium,Use of the subprocess module calls should be analyzed in-depth.
48
- Subprocesses - run,subprocess.run,Medium,Use of the subprocess module calls should be analyzed in-depth.
49
- Tarfile,tarfile.TarFile,High,Extracting files within a program should never be trusted by default. This issue is detected when the zipfile and/or tarfile module with an extraction method is used.
50
- Encodings,base64,Low,"Base encoding visually hides otherwise easily recognized information such as passwords, but does not provide any computational confidentiality."
51
- XML - client,xmlrpc.client,High,xmlrpc is vulnerable to the “decompression bomb” attack.
52
- XML - server,xmlrpc.server.SimpleXMLRPCServer,High,xmlrpc.server is vulnerable to the “decompression bomb” attack.
53
- Random numbers generation module,random.random,Low,The pseudo-random generators of this module should not be used for security purposes.
54
- Random numbers generation module,random.seed,Low,The pseudo-random generators of this module should not be used for security purposes.
55
- Shelve module,shelve.open,High,Only loading a shelve from a trusted source is secure. So check if this is the case.
56
- Multiprocessing ,connection.recv,High,Connection.recv() uses pickle
57
- Multiprocessing ,multiprocessing.connection.Connection,High,Connection.recv() uses pickle
58
- Zipfile,zipfile.ZipFile,High,Extracting files within a program should never be trusted by default. This issue is detected when the zipfile and/or tarfile module with an extraction method is used.
59
- Gzip,gzip.open,Medium,Potential resource consumption if the file is untrusted.
60
- bz2 use,bz2.open,Medium,Potential resource consumption if bz2 compressed file is untrusted.
61
- bz2 class use,bz2.BZ2File,Medium,Potential risk with bz2 class when decompressing data from untrusted or unknown source.
62
- lzma use,lzma.open,Medium,Potential risk with lzma when decompressing data from untrusted or unknown source.
63
- lzma class use,lzma.LZMAFile,Medium,Potential risk with lzma class when decompressing data from untrusted or unknown source.
64
- shutil,shutil.unpack_archive,Medium,Extracting files within a program should not be trusted by default.
65
- shutil,shutil.copy,Medium,Information can be transfered without permission.
66
- shutil,shutil.copy2,Medium,Information can be transfered without permission.
67
- shutil,shutil.copytree,Medium,Information can be transfered without permission.
68
- shutil,shutil.chown,Medium,Programs should not change access rights on files they do not own.
69
- shutil,shutil.rmtree,Medium,Risk on path traversal attack.
70
- HTTP servers: Check on usage.,http.server.BaseHTTPRequestHandler,High,Insecure for production use.
71
- HTTP servers: Check on usage.,http.server.HTTPServer,High,Insecure for production use.
41
+ OS File Operations,os.read,Low,"Reading from unvalidated file descriptors can lead to information disclosure."
42
+ Tempfile,tempfile.mktemp,Low,This function is deprecated because of race conditions that can lead to security vulnerabilities.
43
+ Marshal Usage,marshal.loads,High,This module is not secure and should not be used to deserialize data from untrusted sources.
44
+ Marshal Usage,marshal.load,High,This module is not secure and should not be used to deserialize data from untrusted sources.
45
+ Subprocess Usage,subprocess.call,High,Requires careful input validation to prevent command injection vulnerabilities.
46
+ Subprocess Usage,subprocess.check_call,High,Requires careful input validation to prevent command injection vulnerabilities.
47
+ Subprocess Usage,subprocess.Popen,Medium,Requires careful input validation to prevent command injection vulnerabilities.
48
+ Subprocess Usage,subprocess.run,Medium,Requires careful input validation to prevent command injection vulnerabilities.
49
+ Tarfile Extraction,tarfile.TarFile,High,Vulnerable to path traversal attacks if used with untrusted archives.
50
+ Base64 Encoding ,base64,Low,"Base64 encoding is not for security. It only visually hides data and provides no confidentiality. Often used to obfuscate malware in code."
51
+ XML-RPC Client,xmlrpc.client,High,Vulnerable to denial-of-service via decompression bombs.
52
+ XML-RPC Server,xmlrpc.server.SimpleXMLRPCServer,High,Vulnerable to denial-of-service via decompression bombs.
53
+ Cryptographically Unsafe Randomness,random.random,Low,The pseudo-random generators in this module are not suitable for security purposes.
54
+ Cryptographically Unsafe Randomness,random.seed,Low,The pseudo-random generators in this module are not suitable for security purposes.
55
+ Shelve Usage,shelve.open,High,"The `shelve` module uses `pickle` internally, making it unsafe for untrusted data."
56
+ Unsafe Deserialization: multiprocessing,connection.recv,High,"Uses pickle, which can execute arbitrary code when receiving data. "
57
+ Unsafe Deserialization: multiprocessing,multiprocessing.connection.Connection,High,Relies on pickle; dangerous with untrusted data.
58
+ Zipfile Extraction,zipfile.ZipFile,High,Vulnerable to path traversal attacks if used with untrusted archives.
59
+ Gzip File Handling,gzip.open,Medium,Risk of decompression bombs or resource exhaustion with untrusted data.
60
+ BZ2 File Handling,bz2.open,Medium,Decompressing untrusted data can lead to resource exhaustion attacks.
61
+ BZ2 File Handling,bz2.BZ2File,Medium,Decompressing untrusted data can lead to resource exhaustion attacks.
62
+ LZMA File Handling,lzma.open,Medium,Risk of decompression bombs or resource exhaustion with untrusted data.
63
+ LZMA File Handling,lzma.LZMAFile,Medium,Risk of decompression bombs or resource exhaustion with untrusted data.
64
+ Shutil Extraction,shutil.unpack_archive,Medium,Untrusted archives can contain malicious paths or payloads.
65
+ Shutil Copying,shutil.copy,Medium,Files may be copied without authorization if paths are not validated.
66
+ Shutil Copying,shutil.copy2,Medium,Files may be copied without authorization if paths are not validated.
67
+ Shutil Copying,shutil.copytree,Medium,Files may be copied without authorization if paths are not validated.
68
+ Shutil Operations,shutil.chown,Medium,Changing file ownership can introduce vulnerabilities.
69
+ Shutil Removal,shutil.rmtree,Medium,Vulnerable to path traversal attacks if not used carefully.
70
+ HTTP Server (Base Handler),http.server.BaseHTTPRequestHandler,High,These modules are for development only and are not secure for production use.
71
+ HTTP Server,http.server.HTTPServer,High,These modules are for development only and are not secure for production use.
@@ -28,7 +28,7 @@ def read_in_source_file(file_path):
28
28
  )
29
29
  sys.exit(1)
30
30
 
31
- if file_path.suffix != ".py":
31
+ if file_path.suffix.lower() != ".py":
32
32
  print("Error: The given file is not a Python (.py) file.")
33
33
  sys.exit(1)
34
34
 
@@ -72,7 +72,7 @@ def collect_python_source_files(directory):
72
72
  #check if the file can be parsed using the AST
73
73
  final_file_list = []
74
74
  for python_file in python_files:
75
- if is_ast_parseable(python_file):
75
+ if is_ast_parsable(python_file):
76
76
  final_file_list.append(python_file)
77
77
  else:
78
78
  print(f'Error: {python_file} will be skipped due to syntax error while parsing into AST.')
@@ -89,10 +89,13 @@ def get_filename_from_path(file_path):
89
89
  Returns:
90
90
  str: The file name.
91
91
  """
92
- return os.path.basename(file_path)
92
+ #return os.path.basename(file_path)
93
+ return Path(file_path).name
93
94
 
94
95
 
95
- def is_ast_parseable(file_path):
96
+
97
+
98
+ def is_ast_parsable(file_path):
96
99
  """
97
100
  Checks whether a Python file can be parsed using the AST module.
98
101
 
@@ -111,4 +114,23 @@ def is_ast_parseable(file_path):
111
114
  ast.parse(source, filename=file_path)
112
115
  return True
113
116
  except (SyntaxError, UnicodeDecodeError, ValueError) as e:
114
- return False
117
+ return False
118
+
119
+
120
+ def has_python_files(input_path):
121
+ """
122
+ Check whether a directory contains at least one Python file.
123
+
124
+ Args:
125
+ input_path (str | Path): Path to a directory.
126
+
127
+ Returns:
128
+ bool: True if the directory contains at least one Python file, False otherwise.
129
+ """
130
+ file_path = Path(input_path)
131
+
132
+ if not file_path.is_dir():
133
+ return False
134
+
135
+ files_to_check = collect_python_source_files(file_path)
136
+ return len(files_to_check) > 0
codeaudit/reporting.py CHANGED
@@ -15,15 +15,16 @@ Reporting functions for codeaudit
15
15
 
16
16
  import re
17
17
  import os
18
+ from pathlib import Path
18
19
 
19
20
  import pandas as pd
20
21
  import datetime
21
22
 
22
23
  from codeaudit.security_checks import perform_validations , ast_security_checks
23
- from codeaudit.filehelpfunctions import get_filename_from_path , collect_python_source_files , read_in_source_file
24
+ from codeaudit.filehelpfunctions import get_filename_from_path , collect_python_source_files , read_in_source_file , has_python_files , is_ast_parsable
24
25
  from codeaudit.altairplots import multi_bar_chart
25
26
  from codeaudit.totals import get_statistics , overview_count , overview_per_file , total_modules
26
- from codeaudit.checkmodules import get_imported_modules , check_module_on_vuln , get_all_modules , get_imported_modules_by_file
27
+ from codeaudit.checkmodules import get_imported_modules , check_module_vulnerability , get_all_modules , get_imported_modules_by_file
27
28
  from codeaudit.htmlhelpfunctions import dict_to_html , json_to_html , dict_list_to_html_table
28
29
  from codeaudit import __version__
29
30
 
@@ -51,6 +52,10 @@ def overview_report(directory, filename=DEFAULT_OUTPUT_FILE):
51
52
  print(f"ERROR: '{directory}' is not a directory (maybe you try to run it for a single file)")
52
53
  print(f"This function only works for directories which contains one or more Python source code files (*.py). ")
53
54
  exit(1)
55
+ #Check if the directory has Python files
56
+ if not has_python_files(directory):
57
+ print(f'Error: Directory path {directory} contains no Python files.')
58
+ exit(1)
54
59
  result = get_statistics(directory)
55
60
  modules = total_modules(directory)
56
61
  df = pd.DataFrame(result)
@@ -95,15 +100,16 @@ def overview_report(directory, filename=DEFAULT_OUTPUT_FILE):
95
100
  html += '<h2>Visual Overview</h2>'
96
101
  html += extract_altair_html(plot_html)
97
102
  create_htmlfile(html,filename)
98
-
103
+
104
+
99
105
 
100
- def file_scan_report(file_to_scan , filename=DEFAULT_OUTPUT_FILE):
101
- """Reports potential security issues for a single Python file.
102
-
103
- This function performs security validations on the specified file,
106
+ def scan_report(input_path , filename=DEFAULT_OUTPUT_FILE):
107
+ """Scans Python files or directories(packages) for vulnerabilities and reports potential issues.
108
+
109
+ This function performs security validations on the specified file or directory,
104
110
  formats the results into an HTML report, and writes the output to an HTML file.
105
111
 
106
- You can specify the name and directory for the generated HTML report.
112
+ 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.
107
113
 
108
114
  Parameters:
109
115
  file_to_scan (str) : The full path to the Python source file to be scanned.
@@ -113,16 +119,27 @@ def file_scan_report(file_to_scan , filename=DEFAULT_OUTPUT_FILE):
113
119
  Returns:
114
120
  None - A HTML report is written as output
115
121
  """
116
- scan_output = perform_validations(file_to_scan)
117
- file_report_html = single_file_report(file_to_scan , scan_output)
118
- name_of_file = get_filename_from_path(file_to_scan)
119
- html = '<h1>Python Code Audit Report</h1>' #prepared to be embedded to display multiple reports, so <h2> used
120
- html += f'<h2>Result of scan of file {name_of_file}</h2>'
121
- html += '<p>' + f'Location of the file: {file_to_scan} </p>'
122
- html += file_report_html
123
- html += '<br>'
124
- html += DISCLAIMER_TEXT
125
- create_htmlfile(html,filename)
122
+ # Check if the input is a valid directory or a single valid Python file
123
+ file_path = Path(input_path)
124
+ if file_path.is_dir():
125
+ directory_scan_report(input_path , filename ) #create a package aka directory scan report
126
+ elif file_path.suffix == ".py" and file_path.is_file() and is_ast_parsable(input_path):
127
+ #create a sast file check report
128
+ scan_output = perform_validations(input_path)
129
+ file_report_html = single_file_report(input_path , scan_output)
130
+ name_of_file = get_filename_from_path(input_path)
131
+ html = '<h1>Python Code Audit Report</h1>' #prepared to be embedded to display multiple reports, so <h2> used
132
+ html += f'<h2>Result of scan of file {name_of_file}</h2>'
133
+ html += '<p>' + f'Location of the file: {input_path} </p>'
134
+ html += file_report_html
135
+ html += '<br>'
136
+ html += DISCLAIMER_TEXT
137
+ create_htmlfile(html,filename)
138
+ else:
139
+ #File is NOT a valid Python file, can not be parsed or directory is invalid.
140
+ print(f"Error: '{input_path}' isn't a valid Python file or directory path.")
141
+
142
+
126
143
 
127
144
  def single_file_report(filename , scan_output):
128
145
  """Function to DRY for a codescan when used for single for CLI or within a directory scan"""
@@ -197,8 +214,9 @@ def directory_scan_report(directory_to_scan , filename=DEFAULT_OUTPUT_FILE):
197
214
  collection_ok_files = [] # create a collection of files with no issues found
198
215
  html = '<h1>Python Code Audit Report</h1>'
199
216
  files_to_check = collect_python_source_files(directory_to_scan)
200
- html += '<h2>Directory scan report</h2>'
201
- html += f'<p>Below the result of the Codeaudit scan of the directory:<b> {directory_to_scan}</b></p>'
217
+ html += '<h2>Directory scan report</h2>'
218
+ name_of_package = get_filename_from_path(directory_to_scan)
219
+ html += f'<p>Below the result of the Codeaudit scan of the package or directory:<b> {name_of_package}</b></p>'
202
220
  html += f'<p>Total Python files found: {len(files_to_check)}</p>'
203
221
  number_of_files = len(files_to_check)
204
222
  print(f'Number of files that are checked for security issues:{number_of_files}')
@@ -218,7 +236,7 @@ def directory_scan_report(directory_to_scan , filename=DEFAULT_OUTPUT_FILE):
218
236
  collection_ok_files.append({'filename' : file_name_with_no_issue ,
219
237
  'directory': file_to_scan})
220
238
  html += '<h2>Files in directory with no security issues</h2>'
221
- html += f'<p>Total Python files with no detected security issue: {len(collection_ok_files)}</p>'
239
+ html += f'<p>Total Python files <b>without</b> detected security issues: {len(collection_ok_files)}</p>'
222
240
  html += '<p>The Python files with no security issues <b>detected</b> by codeaudit are:<p>'
223
241
  html += dict_list_to_html_table(collection_ok_files)
224
242
  html += '<br>'
@@ -228,7 +246,7 @@ def directory_scan_report(directory_to_scan , filename=DEFAULT_OUTPUT_FILE):
228
246
 
229
247
 
230
248
  def report_module_information(inputfile,reportname=DEFAULT_OUTPUT_FILE):
231
- """Reports module information per file."""
249
+ """Reports module vulnerability information."""
232
250
  source = read_in_source_file(inputfile)
233
251
  used_modules = get_imported_modules(source)
234
252
  # Initial call to print 0% progress
@@ -243,7 +261,7 @@ def report_module_information(inputfile,reportname=DEFAULT_OUTPUT_FILE):
243
261
  html += '<h2>Vulnerability information for detected modules</h2>'
244
262
  for i,module in enumerate(external_modules): #sorted for nicer report
245
263
  printProgressBar(i + 1, l, prefix='Progress:', suffix='Complete', length=50)
246
- vuln_info = check_module_on_vuln(module)
264
+ vuln_info = check_module_vulnerability(module)
247
265
  if not vuln_info:
248
266
  html += f'<h3>Vulnerability information for module <b>{module}</b></h3> '
249
267
  html += f'<li>No information found in OSV Database for module: <b>{module}</b>.</li> '
codeaudit/totals.py CHANGED
@@ -131,7 +131,7 @@ def total_modules(directory):
131
131
 
132
132
 
133
133
  def overview_per_file(python_file):
134
- """gets the overview per file."""
134
+ """gets the relevant security statistics overview per file."""
135
135
  result = {}
136
136
  source = read_in_source_file(python_file)
137
137
  name_of_file = get_filename_from_path(python_file)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeaudit
3
- Version: 1.0.0
3
+ Version: 1.2.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
@@ -35,13 +35,17 @@ Description-Content-Type: text/markdown
35
35
 
36
36
  Python Code Audit - A modern Python source code analyzer based on distrust.
37
37
 
38
- Python Code Audit is a tool to find **security issues** in Python code. This static application security testing (SAST) tool has **great** features to simplify the necessary security tasks and make it fun and easy.
38
+ Python Code Audit is a tool to find **security weaknesses** in Python code. This static application security testing (SAST) tool has **great** features to simplify the necessary security tasks and make it fun and easy.
39
+
40
+
41
+ This tool is designed for anyone who uses or creates Python programs and wants to understand and mitigate potential security risks.
39
42
 
40
43
  This tool is created for:
41
- * Users of Python programs who want to known the security risks of the used Python code.
42
- * Anyone who loves to create Python programs and want to deliver Python code without vulnerabilities. So this tool is not only professional programs, but also occasional Python programmers. Creating secure software is very difficult. This program with the extensive documentation is your friendly security colleague!
43
- * Anyone who wants a simple way to get fast insight in possible security risks with Python packages or Python files.
44
+ * Python Users who want to assess the security risks in the Python code they use.
45
+ * Python Developers: Anyone, from professionals to hobbyists, who wants to deliver secure Python code.
46
+ * Security-Conscious Users: People seeking a simple, fast way to gain insight into potential security vulnerabilities within Python packages or files.
44
47
 
48
+ Creating secure software can be challenging. This tool, with its comprehensive [documentation](https://nocomplexity.com/documents/codeaudit/intro.html), acts as your helpful security colleague, making it easier to identify and address vulnerabilities.
45
49
 
46
50
  ## Features
47
51
 
@@ -59,7 +63,7 @@ Python Code Audit has the following features:
59
63
 
60
64
 
61
65
 
62
- > [!IMPORTANT]
66
+ > [!NOTE]
63
67
  > Python Code Audit uses the Python's Abstract Syntax Tree (AST) to get robust and reliable result. Using the Python AST makes contextual Vulnerability Detection possible and false positive are minimized.
64
68
 
65
69
 
@@ -71,7 +75,7 @@ pip install codeaudit
71
75
 
72
76
  or use:
73
77
 
74
- ```bash
78
+ ```console
75
79
  pip install -U codeaudit
76
80
  ```
77
81
 
@@ -103,9 +107,8 @@ Depending on the command, a directory or file name must be specified. The output
103
107
 
104
108
  Commands:
105
109
  overview Reports Complexity and statistics per Python file from a directory.
106
- directoryscan Reports potential security issues for all Python files found in a directory.
107
- filescan Reports potential security issues for a single Python file.
108
- modulescan Reports module information per file.
110
+ filescan Scans Python files or directories(packages) for vulnerabilities and reports potential issues.
111
+ modulescan Reports module vulnerability information.
109
112
  checks Creates an HTML report of all implemented security checks.
110
113
  version Prints the module version. Or use codeaudit [-v] [--v] [-version] or [--version].
111
114
 
@@ -116,7 +119,7 @@ Check https://simplifysecurity.nocomplexity.com/
116
119
 
117
120
  ## Example
118
121
 
119
- By running the `codeaudit filescan` command, detailed security information is determined for a Python file based on more than **60 validations** implemented.
122
+ By running the `codeaudit filescan` command, detailed security information is determined for a Python file based on more than **70 validations** implemented.
120
123
 
121
124
  The `codeaudit filescan` command shows all **potential** security issues that are detected in the source file in a HTML-report.
122
125
 
@@ -0,0 +1,20 @@
1
+ codeaudit/__about__.py,sha256=MfxviMO2_cKfQT82PlGFnwIXLZFjVPs4xEE5dnxb474,144
2
+ codeaudit/__init__.py,sha256=YGs6qU0BVHPGtXCS-vfBDLO4TOfJDLTWMgaFDTmi_Iw,157
3
+ codeaudit/altairplots.py,sha256=pWW5ZQ8HGRjcwXEnME9d7OvM83P7a9-BtvMxj0RyEkE,3794
4
+ codeaudit/api_interfaces.py,sha256=WhxFvGIxc6Jy-MVVJUPSC4f8ZZMHdcMhlTlnS-QARQM,11314
5
+ codeaudit/checkmodules.py,sha256=aiF34KO-9HZDRgVBtSwVFdeUxT5_Ka5VtmlfgoLgNVs,5582
6
+ codeaudit/codeaudit.py,sha256=yQ7SHx8b3Q9rMu8nCVyyuu3wJr3DlO-BuSIz2ZwJFGM,3426
7
+ codeaudit/complexitycheck.py,sha256=A3_a5v-U0YQr80pWQwSVvOsY_eQtqwNkQf9Txr9mNtQ,3722
8
+ codeaudit/filehelpfunctions.py,sha256=tx7HDCyTkZuw8YieXipQXM8iRfrDfIVZyKb7vjmkEFY,4358
9
+ codeaudit/htmlhelpfunctions.py,sha256=-SMsyfF7TRIfJkrUqoJuh7AoG1RVrYFsZfFljoxVHXc,3246
10
+ codeaudit/issuevalidations.py,sha256=-WdaXT_R-P9w0JbQpJ5ngVoVhG9Yee2ri0aH5SoC1Ao,6404
11
+ codeaudit/reporting.py,sha256=S3bSMpWJ2EAs1vO5qke7nNOOL7lvgib3_MjQ16lic6M,22924
12
+ codeaudit/security_checks.py,sha256=wEO_A054zXmLccWGREi6cNADa4IgoOPxHsq-Je5iMIY,2167
13
+ codeaudit/simple.css,sha256=7auhDAUwjdluFIyoCskl-Vfh503prXKqftQrmo0-e_g,3565
14
+ codeaudit/totals.py,sha256=b6OkzcMdqGKPwuGBKrwAeCxBOJxHa5FHauGWnEb-6zM,6387
15
+ codeaudit/data/sastchecks.csv,sha256=jQhQPtCfdsfMlAoCiOsI1fEaN1w9BOvrfSHdm2bmO5E,8388
16
+ codeaudit-1.2.0.dist-info/METADATA,sha256=BJOFHqp6O507qKhYpqq7jxTYuZQ9YJaN_EmpRbU3qdI,6959
17
+ codeaudit-1.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
18
+ codeaudit-1.2.0.dist-info/entry_points.txt,sha256=7w6I8zii62nJHIIF30CRP5g1z8enMqF1pZEDdlw4HcQ,55
19
+ codeaudit-1.2.0.dist-info/licenses/LICENSE.txt,sha256=-5gWaMGKJ54oX8TYP7oeg2zITdTapzyWl9PP0tispuA,34674
20
+ codeaudit-1.2.0.dist-info/RECORD,,
@@ -1,19 +0,0 @@
1
- codeaudit/__about__.py,sha256=AYtzx9ZrNDKvd0GU8vABzU9bqhe7Wo-GYBY5FzypjPs,144
2
- codeaudit/__init__.py,sha256=YGs6qU0BVHPGtXCS-vfBDLO4TOfJDLTWMgaFDTmi_Iw,157
3
- codeaudit/altairplots.py,sha256=YFXrJxBjN44Mr2JEGad8h_KjSOYuyzt4YE8JyQr9Kj8,2183
4
- codeaudit/checkmodules.py,sha256=_oMbidp0iKUYF8yOieFIIiCMQ3nl6qC-OhNDnYclf0Q,4895
5
- codeaudit/codeaudit.py,sha256=TF9hn3B8GVUJ04aI4mGKArD62akmhhPzPLzN8A1UYhg,3545
6
- codeaudit/complexitycheck.py,sha256=A3_a5v-U0YQr80pWQwSVvOsY_eQtqwNkQf9Txr9mNtQ,3722
7
- codeaudit/filehelpfunctions.py,sha256=eM-B9JeF3Krx2vaefaqLrCAl-lrtec_fy0NbTkj7a3s,3846
8
- codeaudit/htmlhelpfunctions.py,sha256=-SMsyfF7TRIfJkrUqoJuh7AoG1RVrYFsZfFljoxVHXc,3246
9
- codeaudit/issuevalidations.py,sha256=-WdaXT_R-P9w0JbQpJ5ngVoVhG9Yee2ri0aH5SoC1Ao,6404
10
- codeaudit/reporting.py,sha256=-xWgpuccGYCaXVTNJSqfv2xwCdseuQD6SgDk55BrUMs,21824
11
- codeaudit/security_checks.py,sha256=wEO_A054zXmLccWGREi6cNADa4IgoOPxHsq-Je5iMIY,2167
12
- codeaudit/simple.css,sha256=7auhDAUwjdluFIyoCskl-Vfh503prXKqftQrmo0-e_g,3565
13
- codeaudit/totals.py,sha256=V809eImKZepsKqKMNr0lNfJ0ILf7qFjS_NrU-veVpm0,6358
14
- codeaudit/data/sastchecks.csv,sha256=RlyJvDdEDeuACNV-gpQFg8R0_PhikNyV6jzxbbHGu04,7828
15
- codeaudit-1.0.0.dist-info/METADATA,sha256=AsqTlXkkec74FsAsTClcy7Gmz45zUguzAcDxCmn4HGs,6820
16
- codeaudit-1.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
17
- codeaudit-1.0.0.dist-info/entry_points.txt,sha256=7w6I8zii62nJHIIF30CRP5g1z8enMqF1pZEDdlw4HcQ,55
18
- codeaudit-1.0.0.dist-info/licenses/LICENSE.txt,sha256=-5gWaMGKJ54oX8TYP7oeg2zITdTapzyWl9PP0tispuA,34674
19
- codeaudit-1.0.0.dist-info/RECORD,,