codeaudit 1.0.0__py3-none-any.whl → 1.1.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.1.0"
@@ -0,0 +1,209 @@
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
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
+
29
+ def version():
30
+ """Returns the version of Python Code Audit"""
31
+ ca_version = __version__
32
+ return {"name" : "Python_Code_Audit",
33
+ "version" : ca_version}
34
+
35
+
36
+ def filescan(input_path):
37
+ """Scans a Python file or directory and returns result as JSON"""
38
+ output ={}
39
+ file_output = {}
40
+ file_path = Path(input_path)
41
+ ca_version_info = version()
42
+ now = datetime.datetime.now()
43
+ timestamp_str = now.strftime("%Y-%m-%d %H:%M")
44
+ output = ca_version_info | {"generated_on" : timestamp_str}
45
+ # Check if the input is a valid directory or a single valid Python file
46
+ if file_path.is_dir():
47
+ files_to_check = collect_python_source_files(input_path)
48
+ if len(files_to_check) > 1:
49
+ modules_discovered = get_all_modules(input_path) #all modules for the package aka directory
50
+ name_of_package = get_filename_from_path(input_path)
51
+ package_overview = get_overview(input_path)
52
+ output |= {"package_name" : name_of_package ,
53
+ "statistics_overview" : package_overview ,
54
+ "module_overview" : modules_discovered }
55
+ for i,file in enumerate(files_to_check):
56
+ file_information = overview_per_file(file)
57
+ module_information = get_modules(file) # modules per file
58
+ scan_output = codeaudit_scan(file)
59
+ file_output[i] = file_information | module_information | scan_output
60
+ output |= { "file_security_info" : file_output}
61
+ return output
62
+ else:
63
+ output_msg = f'Directory path {input_path} contains no Python files.'
64
+ return {"Error" : output_msg}
65
+ elif file_path.suffix.lower() == ".py" and file_path.is_file():
66
+ #do a file check
67
+ file_information = overview_per_file(input_path)
68
+ module_information = get_modules(input_path) # modules per file
69
+ scan_output = codeaudit_scan(input_path)
70
+ 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!
71
+ output |= { "file_security_info" : file_output}
72
+ return output
73
+ else:
74
+ #Its not a directory nor a valid Python file:
75
+ return {"Error" : "File is not a *.py file, does not exist or is not a valid directory path towards a Python package."}
76
+
77
+ def codeaudit_scan(filename):
78
+ """Function to do a SAST scan on a single file"""
79
+ #get the file name
80
+ name_of_file = get_filename_from_path(filename)
81
+ sast_data = perform_validations(filename)
82
+ sast_data_results = sast_data["result"]
83
+ sast_result = dict(sorted(sast_data_results.items()))
84
+ output = { "file_name" : name_of_file ,
85
+ "sast_result": sast_result}
86
+ return output
87
+
88
+ def save_to_json(sast_result, filename="codeaudit_output.json"):
89
+ """
90
+ Save a SAST result (dict or serializable object) to a JSON file.
91
+
92
+ Args:
93
+ sast_result (dict or list): The data to be saved as JSON.
94
+ filename (str, optional): The file path to save the JSON data.
95
+ Defaults to "codeaudit_output.json".
96
+
97
+ Returns:
98
+ Path: The absolute path of the saved file, or None if saving failed.
99
+ """
100
+ filepath = Path(filename).expanduser().resolve()
101
+
102
+ try:
103
+ filepath.parent.mkdir(parents=True, exist_ok=True) # ensure directory exists
104
+ with filepath.open("w", encoding="utf-8") as f:
105
+ json.dump(sast_result, f, indent=2, ensure_ascii=False)
106
+ return
107
+ except (TypeError, ValueError) as e:
108
+ print(f"[Error] Failed to serialize data to JSON: {e}")
109
+ except OSError as e:
110
+ print(f"[Error] Failed to write file '{filepath}': {e}")
111
+
112
+ def get_modules(filename):
113
+ """Gets modules of a Python file """
114
+ modules_found = get_imported_modules_by_file(filename)
115
+ return modules_found
116
+
117
+ def get_overview(input_path):
118
+ """Retrieves the security relevant statistics of a Python package(directory) or of a single Python
119
+
120
+ Based on the input path, call the overview function and return the result in a dict
121
+
122
+ Args:
123
+ input_path: Directory path of the package to use
124
+
125
+
126
+ Returns:
127
+ dict: Returns the overview statistics in DICT format
128
+ """
129
+ file_path = Path(input_path)
130
+ if file_path.is_dir():
131
+ files_to_check = collect_python_source_files(input_path)
132
+ if len(files_to_check) > 1:
133
+ statistics = get_statistics(input_path)
134
+ modules = total_modules(input_path)
135
+ df = pd.DataFrame(statistics)
136
+ df['Std-Modules'] = modules['Std-Modules'] #Needed for the correct overall count
137
+ df['External-Modules'] = modules['External-Modules'] #Needed for the correct overall count
138
+ overview_df = overview_count(df) #create the overview Dataframe
139
+ dict_overview = overview_df.to_dict(orient="records")[0] #The overview Dataframe has only one row
140
+ return dict_overview
141
+ else:
142
+ output_msg = f'Directory path {input_path} contains no Python files.'
143
+ return {"Error" : output_msg}
144
+ elif file_path.suffix.lower() == ".py" and file_path.is_file():
145
+ security_statistics = overview_per_file(input_path)
146
+ return security_statistics
147
+ else:
148
+ #Its not a directory nor a valid Python file:
149
+ return {"Error" : "File is not a *.py file, does not exist or is not a valid directory path to a Python package."}
150
+
151
+ def get_default_validations():
152
+ """Retrieves the implemented default security validations
153
+ Args:
154
+ none
155
+
156
+ Returns:
157
+ dict: Overview of implemented security SAST validation on Standard Python modules
158
+ """
159
+ df = ast_security_checks()
160
+ result = df.to_dict(orient="records")
161
+ output = generation_info() | {"validations" : result}
162
+ return output
163
+
164
+ def generation_info():
165
+ """Internal function to retrieve generation info for APIs output"""
166
+ ca_version_info = version()
167
+ now = datetime.datetime.now()
168
+ timestamp_str = now.strftime("%Y-%m-%d %H:%M")
169
+ output = ca_version_info | {"generated_on" : timestamp_str}
170
+ return output
171
+
172
+ def platform_info():
173
+ """Get platform info
174
+ Args:
175
+ none
176
+
177
+ Returns:
178
+ dict: Overview of implemented security SAST validation on Standard Python modules
179
+ """
180
+ python_version = platform.python_version()
181
+ platform_implementation = platform.python_implementation()
182
+ output = { "python_version" : python_version ,
183
+ "python_implementation" : platform_implementation}
184
+ return output
185
+
186
+
187
+ def get_psl_modules():
188
+ """Retrieves a list of collection of Python modules that are part of a Python distribution aka standard installation
189
+
190
+ Returns:
191
+ dict: Overview of PSL modules in the Python version used.
192
+
193
+ """
194
+ psl_modules = get_standard_library_modules()
195
+ output = generation_info() | platform_info() | { "psl_modules" : psl_modules}
196
+ return output
197
+
198
+ def get_module_vulnerability_info(module):
199
+ """Retrieves vulnerability information for external modules using the OSV Database
200
+ Args:
201
+ input: module name
202
+
203
+ Returns:
204
+ dict: Result of OSV query
205
+ """
206
+ vuln_info = check_module_vulnerability(module)
207
+ key_string = f'{module}_vulnerability_info'
208
+ output = generation_info() | { key_string : vuln_info}
209
+ return output
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,Reading from unvalidated file descriptors can lead to information disclosure.
39
+ OS File Operations,os.writev,Low,Reading from unvalidated file descriptors can lead to information disclosure.
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
 
@@ -89,9 +89,12 @@ 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
 
96
+
97
+
95
98
  def is_ast_parseable(file_path):
96
99
  """
97
100
  Checks whether a Python file can be parsed using the AST module.
@@ -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
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,26 @@ 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():
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
+ #Its not a directory nor a valid Python file:
140
+ print(f"Error: {input_path} File is not a *.py file, does not exist or {input_path} is not a valid directory path.\n")
141
+
126
142
 
127
143
  def single_file_report(filename , scan_output):
128
144
  """Function to DRY for a codescan when used for single for CLI or within a directory scan"""
@@ -197,8 +213,9 @@ def directory_scan_report(directory_to_scan , filename=DEFAULT_OUTPUT_FILE):
197
213
  collection_ok_files = [] # create a collection of files with no issues found
198
214
  html = '<h1>Python Code Audit Report</h1>'
199
215
  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>'
216
+ html += '<h2>Directory scan report</h2>'
217
+ name_of_package = get_filename_from_path(directory_to_scan)
218
+ html += f'<p>Below the result of the Codeaudit scan of the package or directory:<b> {name_of_package}</b></p>'
202
219
  html += f'<p>Total Python files found: {len(files_to_check)}</p>'
203
220
  number_of_files = len(files_to_check)
204
221
  print(f'Number of files that are checked for security issues:{number_of_files}')
@@ -218,7 +235,7 @@ def directory_scan_report(directory_to_scan , filename=DEFAULT_OUTPUT_FILE):
218
235
  collection_ok_files.append({'filename' : file_name_with_no_issue ,
219
236
  'directory': file_to_scan})
220
237
  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>'
238
+ html += f'<p>Total Python files <b>without</b> detected security issues: {len(collection_ok_files)}</p>'
222
239
  html += '<p>The Python files with no security issues <b>detected</b> by codeaudit are:<p>'
223
240
  html += dict_list_to_html_table(collection_ok_files)
224
241
  html += '<br>'
@@ -228,7 +245,7 @@ def directory_scan_report(directory_to_scan , filename=DEFAULT_OUTPUT_FILE):
228
245
 
229
246
 
230
247
  def report_module_information(inputfile,reportname=DEFAULT_OUTPUT_FILE):
231
- """Reports module information per file."""
248
+ """Reports module vulnerability information."""
232
249
  source = read_in_source_file(inputfile)
233
250
  used_modules = get_imported_modules(source)
234
251
  # Initial call to print 0% progress
@@ -243,7 +260,7 @@ def report_module_information(inputfile,reportname=DEFAULT_OUTPUT_FILE):
243
260
  html += '<h2>Vulnerability information for detected modules</h2>'
244
261
  for i,module in enumerate(external_modules): #sorted for nicer report
245
262
  printProgressBar(i + 1, l, prefix='Progress:', suffix='Complete', length=50)
246
- vuln_info = check_module_on_vuln(module)
263
+ vuln_info = check_module_vulnerability(module)
247
264
  if not vuln_info:
248
265
  html += f'<h3>Vulnerability information for module <b>{module}</b></h3> '
249
266
  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.1.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
@@ -37,11 +37,14 @@ Python Code Audit - A modern Python source code analyzer based on distrust.
37
37
 
38
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.
39
39
 
40
+ This tool is designed for anyone who uses or creates Python programs and wants to understand and mitigate potential security risks.
41
+
40
42
  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.
43
+ * Python Users who want to assess the security risks in the Python code they use.
44
+ * Python Developers: Anyone, from professionals to hobbyists, who wants to deliver secure Python code.
45
+ * Security-Conscious Users: People seeking a simple, fast way to gain insight into potential security vulnerabilities within Python packages or files.
44
46
 
47
+ 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
48
 
46
49
  ## Features
47
50
 
@@ -71,7 +74,7 @@ pip install codeaudit
71
74
 
72
75
  or use:
73
76
 
74
- ```bash
77
+ ```console
75
78
  pip install -U codeaudit
76
79
  ```
77
80
 
@@ -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=7IM6Rr1UeszM-VWh3mhwMMoHZMIUsUsaOUYnFtTaZgU,144
2
+ codeaudit/__init__.py,sha256=YGs6qU0BVHPGtXCS-vfBDLO4TOfJDLTWMgaFDTmi_Iw,157
3
+ codeaudit/altairplots.py,sha256=YFXrJxBjN44Mr2JEGad8h_KjSOYuyzt4YE8JyQr9Kj8,2183
4
+ codeaudit/api_interfaces.py,sha256=tMyL9rBJxJhldW_Hp2fPvUlKhdDO4JBkJ3OqrKuyV6E,8898
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=WiVzaCkWSb6n9Y-1WOjGYcjcN9jyiLgHHAqnvQopTvk,4360
9
+ codeaudit/htmlhelpfunctions.py,sha256=-SMsyfF7TRIfJkrUqoJuh7AoG1RVrYFsZfFljoxVHXc,3246
10
+ codeaudit/issuevalidations.py,sha256=-WdaXT_R-P9w0JbQpJ5ngVoVhG9Yee2ri0aH5SoC1Ao,6404
11
+ codeaudit/reporting.py,sha256=hwIZcUryqVlnzfbRKjkIVrT-HJ_7Dx3xd1nkBfn54Ts,22876
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=-f0WpX9vGCcyy_i-GrEUwIMb-KhzAfoDpJwH6hDtQwo,8286
16
+ codeaudit-1.1.0.dist-info/METADATA,sha256=_hmbLKWxbs0Rj_Mz2oTcYia8m1E_hdOJoOLXU1jYejk,7020
17
+ codeaudit-1.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
18
+ codeaudit-1.1.0.dist-info/entry_points.txt,sha256=7w6I8zii62nJHIIF30CRP5g1z8enMqF1pZEDdlw4HcQ,55
19
+ codeaudit-1.1.0.dist-info/licenses/LICENSE.txt,sha256=-5gWaMGKJ54oX8TYP7oeg2zITdTapzyWl9PP0tispuA,34674
20
+ codeaudit-1.1.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,,