codeaudit 1.4.2__py3-none-any.whl → 1.6.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 +1 -1
- codeaudit/api_interfaces.py +88 -28
- codeaudit/data/sastchecks.csv +3 -0
- codeaudit/data/secretslist.txt +136 -0
- codeaudit/filehelpfunctions.py +1 -1
- codeaudit/issuevalidations.py +1 -1
- codeaudit/privacy_lint.py +292 -0
- codeaudit/pypi_package_scan.py +1 -1
- codeaudit/reporting.py +491 -190
- codeaudit/security_checks.py +2 -2
- codeaudit/simple.css +31 -5
- codeaudit/suppression.py +233 -0
- {codeaudit-1.4.2.dist-info → codeaudit-1.6.0.dist-info}/METADATA +7 -2
- codeaudit-1.6.0.dist-info/RECORD +25 -0
- codeaudit-1.4.2.dist-info/RECORD +0 -22
- {codeaudit-1.4.2.dist-info → codeaudit-1.6.0.dist-info}/WHEEL +0 -0
- {codeaudit-1.4.2.dist-info → codeaudit-1.6.0.dist-info}/entry_points.txt +0 -0
- {codeaudit-1.4.2.dist-info → codeaudit-1.6.0.dist-info}/licenses/LICENSE.txt +0 -0
codeaudit/security_checks.py
CHANGED
|
@@ -49,9 +49,9 @@ def perform_validations(sourcefile):
|
|
|
49
49
|
|
|
50
50
|
name_of_file = get_filename_from_path (sourcefile)
|
|
51
51
|
|
|
52
|
-
result = {'
|
|
52
|
+
result = {'file_name' : name_of_file ,
|
|
53
53
|
'file_location': sourcefile ,
|
|
54
|
-
'
|
|
54
|
+
'checks_done:' : constructs ,
|
|
55
55
|
'result': scan_result}
|
|
56
56
|
|
|
57
57
|
return result
|
codeaudit/simple.css
CHANGED
|
@@ -24,7 +24,7 @@ p {
|
|
|
24
24
|
|
|
25
25
|
/* Body base styles */
|
|
26
26
|
body {
|
|
27
|
-
font-family: Arial, Helvetica, sans-serif;
|
|
27
|
+
font-family: Inter, Roboto, Arial, Helvetica, sans-serif;
|
|
28
28
|
background-color: #FFFFFF;
|
|
29
29
|
color: #333;
|
|
30
30
|
line-height: 1.6;
|
|
@@ -175,11 +175,37 @@ pre {
|
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
footer {
|
|
178
|
-
background-color: #
|
|
179
|
-
color:
|
|
178
|
+
background-color: #E6E6E6; /* nocx grey background */
|
|
179
|
+
color: #555; /* Softer text color for better readability */
|
|
180
180
|
text-align: center;
|
|
181
|
-
padding: 10px;
|
|
182
|
-
|
|
181
|
+
padding: 30px 10px;
|
|
182
|
+
margin-top: 10px;
|
|
183
|
+
border-top: 1px solid #eee;
|
|
184
|
+
font-size: 14px;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
.footer-links {
|
|
189
|
+
margin-top: 10px;
|
|
190
|
+
line-height: 2;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.footer-links a {
|
|
194
|
+
color: #ff0000;
|
|
195
|
+
font-weight: 500;
|
|
196
|
+
transition: color 0.2s;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.footer-links a:hover {
|
|
200
|
+
color: #cc0000;
|
|
201
|
+
text-decoration: underline;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.heart {
|
|
205
|
+
color: #ff0000;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
|
|
183
209
|
|
|
184
210
|
.json-display {
|
|
185
211
|
background-color: #2d2d2d; /* dark gray background */
|
codeaudit/suppression.py
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import tokenize
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
import re
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
def get_all_comments_by_line(filename):
|
|
8
|
+
"""
|
|
9
|
+
Tokenize the file once and collect all real # comments
|
|
10
|
+
grouped by their starting line number.
|
|
11
|
+
"""
|
|
12
|
+
comments_by_line = defaultdict(list)
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
with tokenize.open(filename) as f:
|
|
16
|
+
for token in tokenize.generate_tokens(f.readline):
|
|
17
|
+
if token.type == tokenize.COMMENT:
|
|
18
|
+
text = token.string.lstrip("# \t").rstrip()
|
|
19
|
+
if text:
|
|
20
|
+
comments_by_line[token.start[0]].append(text)
|
|
21
|
+
|
|
22
|
+
except (OSError, UnicodeDecodeError, tokenize.TokenError) as exc:
|
|
23
|
+
# Fail loudly with context instead of silently ignoring
|
|
24
|
+
raise RuntimeError(
|
|
25
|
+
f"Failed to extract comments from {filename}"
|
|
26
|
+
) from exc
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
line: "\n".join(texts)
|
|
30
|
+
for line, texts in comments_by_line.items()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_start_to_end_lines(filename):
|
|
39
|
+
"""
|
|
40
|
+
Parse the file once using AST and build a mapping:
|
|
41
|
+
start_line → highest end_lineno found for any node starting on that line.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
dict[int, int] — line numbers are 1-based
|
|
45
|
+
Returns empty dict if the file cannot be read or parsed.
|
|
46
|
+
"""
|
|
47
|
+
end_lines = {}
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
with open(filename, 'r', encoding='utf-8') as f:
|
|
51
|
+
source = f.read()
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
tree = ast.parse(source, filename=filename)
|
|
55
|
+
|
|
56
|
+
for node in ast.walk(tree):
|
|
57
|
+
# Most nodes have lineno, but some (like comprehension ifs) might not
|
|
58
|
+
if not hasattr(node, 'lineno'):
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
start = node.lineno
|
|
62
|
+
# end_lineno may be missing in very old Python versions → fallback to start
|
|
63
|
+
end = getattr(node, 'end_lineno', start)
|
|
64
|
+
|
|
65
|
+
# Keep the maximum span for nodes starting on the same line
|
|
66
|
+
if start not in end_lines or end > end_lines[start]:
|
|
67
|
+
end_lines[start] = end
|
|
68
|
+
|
|
69
|
+
except SyntaxError as e:
|
|
70
|
+
print(
|
|
71
|
+
f"Syntax error in {filename} (line {e.lineno}): {e.msg}",
|
|
72
|
+
file=sys.stderr
|
|
73
|
+
)
|
|
74
|
+
return {}
|
|
75
|
+
except (ValueError, UnicodeDecodeError) as e:
|
|
76
|
+
print(
|
|
77
|
+
f"Cannot read {filename} properly: {type(e).__name__}: {e}",
|
|
78
|
+
file=sys.stderr
|
|
79
|
+
)
|
|
80
|
+
return {}
|
|
81
|
+
except MemoryError:
|
|
82
|
+
print(f"Out of memory while parsing {filename}", file=sys.stderr)
|
|
83
|
+
return {}
|
|
84
|
+
except Exception as e:
|
|
85
|
+
print(
|
|
86
|
+
f"Unexpected error parsing AST of {filename}: "
|
|
87
|
+
f"{type(e).__name__}: {e}",
|
|
88
|
+
file=sys.stderr
|
|
89
|
+
)
|
|
90
|
+
return {}
|
|
91
|
+
|
|
92
|
+
except FileNotFoundError:
|
|
93
|
+
print(f"File not found: {filename}", file=sys.stderr)
|
|
94
|
+
return {}
|
|
95
|
+
except PermissionError:
|
|
96
|
+
print(f"Permission denied: {filename}", file=sys.stderr)
|
|
97
|
+
return {}
|
|
98
|
+
except IsADirectoryError:
|
|
99
|
+
print(f"Is a directory, not a file: {filename}", file=sys.stderr)
|
|
100
|
+
return {}
|
|
101
|
+
except OSError as e:
|
|
102
|
+
print(f"OS error opening {filename}: {e}", file=sys.stderr)
|
|
103
|
+
return {}
|
|
104
|
+
except Exception as e:
|
|
105
|
+
print(
|
|
106
|
+
f"Critical error while accessing {filename}: "
|
|
107
|
+
f"{type(e).__name__}: {e}",
|
|
108
|
+
file=sys.stderr
|
|
109
|
+
)
|
|
110
|
+
return {}
|
|
111
|
+
|
|
112
|
+
return end_lines
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# def get_start_to_end_lines(filename):
|
|
116
|
+
# """
|
|
117
|
+
# Parse AST once and build mapping: start_line → highest end_line found for nodes
|
|
118
|
+
# starting on that line.
|
|
119
|
+
# """
|
|
120
|
+
# end_lines = {}
|
|
121
|
+
|
|
122
|
+
# try:
|
|
123
|
+
# with open(filename, 'r', encoding='utf-8') as f:
|
|
124
|
+
# source = f.read()
|
|
125
|
+
# tree = ast.parse(source)
|
|
126
|
+
|
|
127
|
+
# for node in ast.walk(tree):
|
|
128
|
+
# if not hasattr(node, 'lineno'):
|
|
129
|
+
# continue
|
|
130
|
+
# start = node.lineno
|
|
131
|
+
# end = getattr(node, 'end_lineno', start)
|
|
132
|
+
# # Take the maximum end line if multiple nodes start on same line
|
|
133
|
+
# if start not in end_lines or end > end_lines[start]:
|
|
134
|
+
# end_lines[start] = end
|
|
135
|
+
# except Exception:
|
|
136
|
+
# pass
|
|
137
|
+
|
|
138
|
+
# return end_lines
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def is_suppressed(line, comments_by_line, start_to_end, match_func):
|
|
142
|
+
"""
|
|
143
|
+
Check if the statement starting at `line` is suppressed by looking at comments
|
|
144
|
+
from start_line to end_line inclusive.
|
|
145
|
+
"""
|
|
146
|
+
end = start_to_end.get(line, line)
|
|
147
|
+
for comment_line in range(line, end + 1):
|
|
148
|
+
comment = comments_by_line.get(comment_line, "")
|
|
149
|
+
if match_func(comment):
|
|
150
|
+
return True
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def filter_sast_results(sast_dict):
|
|
155
|
+
"""
|
|
156
|
+
Returns a new filtered dictionary with suppressed findings removed.
|
|
157
|
+
Parses & tokenizes the file only once.
|
|
158
|
+
Respects multi-line statements via AST end_lineno.
|
|
159
|
+
Empty lists and their keys are removed from the result.
|
|
160
|
+
"""
|
|
161
|
+
file_location = sast_dict["file_location"]
|
|
162
|
+
original_result = sast_dict.get("result", {})
|
|
163
|
+
|
|
164
|
+
if not original_result:
|
|
165
|
+
return sast_dict.copy()
|
|
166
|
+
|
|
167
|
+
# Collect all unique line numbers that have findings
|
|
168
|
+
all_issue_lines = set()
|
|
169
|
+
for lines in original_result.values():
|
|
170
|
+
if isinstance(lines, list):
|
|
171
|
+
all_issue_lines.update(lines)
|
|
172
|
+
|
|
173
|
+
if not all_issue_lines:
|
|
174
|
+
return sast_dict.copy()
|
|
175
|
+
|
|
176
|
+
# Parse and tokenize **once**
|
|
177
|
+
comments_by_line = get_all_comments_by_line(file_location)
|
|
178
|
+
start_to_end = get_start_to_end_lines(file_location)
|
|
179
|
+
|
|
180
|
+
# Decide which lines to KEEP
|
|
181
|
+
keep_lines = set()
|
|
182
|
+
for line in sorted(all_issue_lines):
|
|
183
|
+
if not is_suppressed(line, comments_by_line, start_to_end, match_suppression_keyword):
|
|
184
|
+
keep_lines.add(line)
|
|
185
|
+
|
|
186
|
+
# Build new result dictionary
|
|
187
|
+
new_result = {}
|
|
188
|
+
for key, value in original_result.items():
|
|
189
|
+
if isinstance(value, list):
|
|
190
|
+
filtered = [ln for ln in value if ln in keep_lines]
|
|
191
|
+
if filtered:
|
|
192
|
+
new_result[key] = filtered
|
|
193
|
+
else:
|
|
194
|
+
new_result[key] = value
|
|
195
|
+
|
|
196
|
+
# Return new full dictionary
|
|
197
|
+
filtered_dict = sast_dict.copy()
|
|
198
|
+
filtered_dict["result"] = new_result
|
|
199
|
+
return filtered_dict
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def match_suppression_keyword(comment_line):
|
|
203
|
+
"""
|
|
204
|
+
Checks if a SAST suppression marker is present in the comment.
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
MARKER_LIST = [
|
|
208
|
+
"nosec",
|
|
209
|
+
"nosemgrep",
|
|
210
|
+
"sast-ignore",
|
|
211
|
+
"ignore-sast",
|
|
212
|
+
"security-ignore",
|
|
213
|
+
"ignore-security",
|
|
214
|
+
"NOSONAR",
|
|
215
|
+
"noqa",
|
|
216
|
+
# False positive / risk handling
|
|
217
|
+
"false-positive",
|
|
218
|
+
"falsepositive",
|
|
219
|
+
"risk-accepted",
|
|
220
|
+
"security-accepted",
|
|
221
|
+
"security-reviewed",
|
|
222
|
+
"security-exception",
|
|
223
|
+
]
|
|
224
|
+
|
|
225
|
+
if not comment_line:
|
|
226
|
+
return False
|
|
227
|
+
|
|
228
|
+
normalized = " ".join(
|
|
229
|
+
word.lstrip("#").lower()
|
|
230
|
+
for word in comment_line.split()
|
|
231
|
+
)
|
|
232
|
+
tokens = re.split(r"[^\w\-]+", normalized)
|
|
233
|
+
return any(marker.lower() in tokens for marker in MARKER_LIST)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeaudit
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6.0
|
|
4
4
|
Summary: Simplified static security checks for Python
|
|
5
5
|
Project-URL: Documentation, https://github.com/nocomplexity/codeaudit#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/nocomplexity/codeaudit/issues
|
|
@@ -64,6 +64,10 @@ Python Code Audit has the following features:
|
|
|
64
64
|
|
|
65
65
|
* **Inline Issue Reporting**: Shows potential security issues with line numbers and code snippets.
|
|
66
66
|
|
|
67
|
+
|
|
68
|
+
* **External Egress Detection**: Identifies embedded API keys and logic that enables communication with remote services, helping uncover hidden data exfiltration paths.
|
|
69
|
+
|
|
70
|
+
|
|
67
71
|
* **HTML Reports**: All output is saved in simple, static HTML reports viewable in any browser.
|
|
68
72
|
|
|
69
73
|
|
|
@@ -100,6 +104,7 @@ This will show all commands:
|
|
|
100
104
|
|
|
101
105
|
Python Code Audit - A modern Python security source code analyzer based on distrust.
|
|
102
106
|
|
|
107
|
+
|
|
103
108
|
Commands to evaluate Python source code:
|
|
104
109
|
Usage: codeaudit COMMAND <directory|package> [report.html]
|
|
105
110
|
|
|
@@ -108,7 +113,7 @@ Depending on the command, you must specify a local directory, a Python file, or
|
|
|
108
113
|
Commands:
|
|
109
114
|
overview Generates an overview report of code complexity and security indicators.
|
|
110
115
|
filescan Scans Python source code or PyPI packages for security weaknesses.
|
|
111
|
-
modulescan
|
|
116
|
+
modulescan Generate a report on known vulnerabilities in Python modules and packages.
|
|
112
117
|
checks Creates an HTML report of all implemented security checks.
|
|
113
118
|
version Prints the module version. Or use codeaudit [-v] [--v] [-version] or [--version].
|
|
114
119
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
codeaudit/__about__.py,sha256=EZ0swjOPnWsY4bG29vXRMJsA2zyCpDKGUv7nXcLLL5E,144
|
|
2
|
+
codeaudit/__init__.py,sha256=YGs6qU0BVHPGtXCS-vfBDLO4TOfJDLTWMgaFDTmi_Iw,157
|
|
3
|
+
codeaudit/altairplots.py,sha256=gBXN1_wxUmjzTNizvzbOeCKvUxpClGPdZmK7ICK1x68,4531
|
|
4
|
+
codeaudit/api_interfaces.py,sha256=6GGz7k1fuSkzEXGjoqavQCmawTh0PVQNglttzSArFWI,17573
|
|
5
|
+
codeaudit/api_reporting.py,sha256=W8eutTJ0d-TENbv5cCmAOfu4GEp_RwiQ4XU5FCmfkoI,1736
|
|
6
|
+
codeaudit/checkmodules.py,sha256=aiF34KO-9HZDRgVBtSwVFdeUxT5_Ka5VtmlfgoLgNVs,5582
|
|
7
|
+
codeaudit/codeaudit.py,sha256=g2HzRX6a3fckKUhyRrk6n3-5qNdVYtZRI1gqQ-QNl10,3775
|
|
8
|
+
codeaudit/complexitycheck.py,sha256=A3_a5v-U0YQr80pWQwSVvOsY_eQtqwNkQf9Txr9mNtQ,3722
|
|
9
|
+
codeaudit/filehelpfunctions.py,sha256=-5kIymEUcc7j0bRBS4XblvE3pbi3rWjkU5O2M_tinvM,4374
|
|
10
|
+
codeaudit/htmlhelpfunctions.py,sha256=-SMsyfF7TRIfJkrUqoJuh7AoG1RVrYFsZfFljoxVHXc,3246
|
|
11
|
+
codeaudit/issuevalidations.py,sha256=zf2Gr7KpyvA05K17IX05pQy-1oQWnbapVIvcUMcbNn8,6441
|
|
12
|
+
codeaudit/privacy_lint.py,sha256=Rcefen7RswwJWnoE-Vrr2iE3zFjNoE19qW_O7LjGfN4,10264
|
|
13
|
+
codeaudit/pypi_package_scan.py,sha256=dmk3xBUL0mZ5aCIc1fRVpuI1UIx1ejnOqfc4qB04748,4730
|
|
14
|
+
codeaudit/reporting.py,sha256=AHgkbKOaAjBSh2ePZFFqm-MWdb2ZYTMmcFvOJy1wdLQ,43298
|
|
15
|
+
codeaudit/security_checks.py,sha256=IuJMo99188TgJoYfTpMQiCs3Dchw4EvCGWuwh_Cds7k,2167
|
|
16
|
+
codeaudit/simple.css,sha256=H7KT61oXJkVr9qXVrC5ME_Zph9jI-uR2IxOsXG1xs5k,4013
|
|
17
|
+
codeaudit/suppression.py,sha256=zSLarg79pahStnXFklf_ERQvDXFgOr375BtPXEVSQjA,7060
|
|
18
|
+
codeaudit/totals.py,sha256=b6OkzcMdqGKPwuGBKrwAeCxBOJxHa5FHauGWnEb-6zM,6387
|
|
19
|
+
codeaudit/data/sastchecks.csv,sha256=dZDOgpVqFz3jPWWiLI-6CXE_SmOQ9Ay6N98NV72ay5w,10122
|
|
20
|
+
codeaudit/data/secretslist.txt,sha256=BoVX6bijqaL5g-2JRGGf0x-S8NhZWtt7fzovZ1NrEK8,1905
|
|
21
|
+
codeaudit-1.6.0.dist-info/METADATA,sha256=KMLuS8-HAhww_uVHYyEgWANkx5RZJTqcPDfxZgX5bC8,7814
|
|
22
|
+
codeaudit-1.6.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
23
|
+
codeaudit-1.6.0.dist-info/entry_points.txt,sha256=7w6I8zii62nJHIIF30CRP5g1z8enMqF1pZEDdlw4HcQ,55
|
|
24
|
+
codeaudit-1.6.0.dist-info/licenses/LICENSE.txt,sha256=-5gWaMGKJ54oX8TYP7oeg2zITdTapzyWl9PP0tispuA,34674
|
|
25
|
+
codeaudit-1.6.0.dist-info/RECORD,,
|
codeaudit-1.4.2.dist-info/RECORD
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
codeaudit/__about__.py,sha256=ZFZWLshIXTzzWzLpG6F82-bf1qgOivq-oT9i9-lECak,144
|
|
2
|
-
codeaudit/__init__.py,sha256=YGs6qU0BVHPGtXCS-vfBDLO4TOfJDLTWMgaFDTmi_Iw,157
|
|
3
|
-
codeaudit/altairplots.py,sha256=gBXN1_wxUmjzTNizvzbOeCKvUxpClGPdZmK7ICK1x68,4531
|
|
4
|
-
codeaudit/api_interfaces.py,sha256=zWJrLDM8b3b2-rN0gCoPdflEFMzKUz3M7PfXtXvDpd4,15358
|
|
5
|
-
codeaudit/api_reporting.py,sha256=W8eutTJ0d-TENbv5cCmAOfu4GEp_RwiQ4XU5FCmfkoI,1736
|
|
6
|
-
codeaudit/checkmodules.py,sha256=aiF34KO-9HZDRgVBtSwVFdeUxT5_Ka5VtmlfgoLgNVs,5582
|
|
7
|
-
codeaudit/codeaudit.py,sha256=g2HzRX6a3fckKUhyRrk6n3-5qNdVYtZRI1gqQ-QNl10,3775
|
|
8
|
-
codeaudit/complexitycheck.py,sha256=A3_a5v-U0YQr80pWQwSVvOsY_eQtqwNkQf9Txr9mNtQ,3722
|
|
9
|
-
codeaudit/filehelpfunctions.py,sha256=tx7HDCyTkZuw8YieXipQXM8iRfrDfIVZyKb7vjmkEFY,4358
|
|
10
|
-
codeaudit/htmlhelpfunctions.py,sha256=-SMsyfF7TRIfJkrUqoJuh7AoG1RVrYFsZfFljoxVHXc,3246
|
|
11
|
-
codeaudit/issuevalidations.py,sha256=-WdaXT_R-P9w0JbQpJ5ngVoVhG9Yee2ri0aH5SoC1Ao,6404
|
|
12
|
-
codeaudit/pypi_package_scan.py,sha256=yxCXrRvjc4r0YsJYHvHJuJTyHC5QZl3sRQp73akCXx8,4723
|
|
13
|
-
codeaudit/reporting.py,sha256=GXIiq2fzN5vvSDjSTDKsEuR0hfEWybbvid7DYzAjsZg,30029
|
|
14
|
-
codeaudit/security_checks.py,sha256=wEO_A054zXmLccWGREi6cNADa4IgoOPxHsq-Je5iMIY,2167
|
|
15
|
-
codeaudit/simple.css,sha256=7auhDAUwjdluFIyoCskl-Vfh503prXKqftQrmo0-e_g,3565
|
|
16
|
-
codeaudit/totals.py,sha256=b6OkzcMdqGKPwuGBKrwAeCxBOJxHa5FHauGWnEb-6zM,6387
|
|
17
|
-
codeaudit/data/sastchecks.csv,sha256=fIcyZgymCtAluPta9fTEk6a9DJ2AGJczZYRPUIQuSag,9738
|
|
18
|
-
codeaudit-1.4.2.dist-info/METADATA,sha256=RtT5hL75GoLxYNav079UwxBdpMkLMfbfID-HX6Ijx_E,7628
|
|
19
|
-
codeaudit-1.4.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
20
|
-
codeaudit-1.4.2.dist-info/entry_points.txt,sha256=7w6I8zii62nJHIIF30CRP5g1z8enMqF1pZEDdlw4HcQ,55
|
|
21
|
-
codeaudit-1.4.2.dist-info/licenses/LICENSE.txt,sha256=-5gWaMGKJ54oX8TYP7oeg2zITdTapzyWl9PP0tispuA,34674
|
|
22
|
-
codeaudit-1.4.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|