-
-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathpip-audit-bulk
More file actions
executable file
·105 lines (83 loc) · 3.13 KB
/
pip-audit-bulk
File metadata and controls
executable file
·105 lines (83 loc) · 3.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#!/usr/bin/env python
# pip-audit-bulk: run pip-audit in bulk, saving (reduced) reports in JSON
import collections
import json
import os
import shutil
import subprocess
import sys
from pathlib import Path
from tempfile import NamedTemporaryFile
# This entire script is path-sensitive.
_HERE = Path(__file__).resolve().parent
_SITE = _HERE / "site"
assert _SITE.is_dir(), "missing site dir (wrong dir?)"
# formula-name -> [requirements]
_REQUIREMENTS_JSON = _SITE / "formula-requirements.json"
_REQUIREMENTS = json.loads(_REQUIREMENTS_JSON.read_text())
_AUDITS_JSON = _SITE / "formula-audits.json"
audits = {
"vulnerable": {},
"failures": [],
"skipped": [], # NOTE: currently unused
}
if "CI" in os.environ:
_SUMMARY = Path(os.environ["GITHUB_STEP_SUMMARY"])
else:
_SUMMARY = None
# {(dependency, CVE): [list of formula]}
_SUMMARY_RESULTS = collections.defaultdict(list)
assert shutil.which("osv-scanner"), "missing osv-scanner"
for formula, requirements in _REQUIREMENTS.items():
# osv-scanner wants a requirements.txt file, so we make a temporary one.
with NamedTemporaryFile(mode="w") as req_file:
req_file.write("\n".join(requirements))
req_file.flush()
result = subprocess.run(
[
"osv-scanner",
"--lockfile",
f"requirements.txt:{req_file.name}",
"--format=json",
"--config=osv-scanner-config.toml",
],
capture_output=True,
text=True,
)
# If we don't see anything on stdout, osv-scanner probably failed.
# Log it as an error.
if not result.stdout:
print(f"\t>:( osv-scanner failed: {result.stderr}", file=sys.stderr)
audits["failures"].append(formula)
continue
audit = json.loads(result.stdout)
results = audit["results"]
# If we're left with anything, dump it as a result.
if results:
seen_packages = {}
for r in results:
for p in r["packages"]:
seen_packages.setdefault(
(p["package"]["name"], p["package"]["version"]), p
)
vulnerable_packages = list(seen_packages.values())
vuln_count = 0
for p in vulnerable_packages:
vuln_count += len(p["groups"])
for v in p["groups"]:
_SUMMARY_RESULTS[(p["package"]["name"], v["ids"][0])].append(formula)
print(f"\t:-( found {vuln_count} vulnerabilities in {formula}", file=sys.stderr)
audits["vulnerable"][formula] = vulnerable_packages
else:
print(f"\t:-) no vulnerabilities found in {formula}", file=sys.stderr)
_AUDITS_JSON.write_text(json.dumps(audits, indent=2))
render = """Identified the following vulnerabilities:
| Dependency | Vulnerability | Formula |
| ---------- | ------------- | ------- |
"""
for (dep, vuln), formulas in sorted(_SUMMARY_RESULTS.items()):
formulas_rendered = ", ".join(f"`{f}`" for f in formulas)
render += f"| `{dep}` | [`{vuln}`](https://osv.dev/vulnerability/{vuln}) | {formulas_rendered} |\n"
if _SUMMARY:
with _SUMMARY.open(mode="a") as io:
print(render, file=io)