Skip to content

Commit 864a525

Browse files
committed
Refactor conversion code for pypi to be in shared utilities
1 parent a1734f5 commit 864a525

File tree

4 files changed

+72
-70
lines changed

4 files changed

+72
-70
lines changed

exasol/toolbox/nox/_dependencies.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
import tomlkit
2020
from nox import Session
2121

22-
from exasol.toolbox.security import GitHubVulnerabilityIssue
22+
from exasol.toolbox.security import (
23+
GitHubVulnerabilityIssue,
24+
from_pip_audit,
25+
)
2326

2427

2528
@dataclass(frozen=True)
@@ -388,18 +391,14 @@ def _set_to_resolve(
388391

389392
def _split_resolution_status(self) -> None:
390393
to_resolve_by_cve = {vuln.cve: vuln for vuln in self.to_resolve}
391-
cves_to_resolve = to_resolve_by_cve.keys()
394+
cves_to_resolve = set(to_resolve_by_cve.keys())
395+
392396
audit_json, _ = Audit().audit()
397+
cve_audit = {vuln.cve for vuln in from_pip_audit(json.dumps(audit_json))}
398+
399+
cves_not_resolved = cves_to_resolve.intersection(cve_audit)
393400

394-
not_resolved: list = []
395-
for dependency in audit_json["dependencies"]:
396-
for v in dependency["vulns"]:
397-
vuln_ids = set([v["id"]] + v["aliases"])
398-
if remaining_cves := (cves_to_resolve & vuln_ids):
399-
not_resolved.extend(
400-
to_resolve_by_cve[cve] for cve in remaining_cves
401-
)
402-
self.not_resolved = set(not_resolved)
401+
self.not_resolved = {to_resolve_by_cve[cve] for cve in cves_not_resolved}
403402
self.resolved = self.to_resolve - self.not_resolved
404403

405404
def get_packages(self) -> set[str]:

exasol/toolbox/security/__init__.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
dataclass,
88
)
99
from enum import Enum
10+
from typing import Iterable
1011

1112
import typer
1213

@@ -105,3 +106,58 @@ def get_link(self, package: str, vuln_id: str) -> str:
105106
VulnerabilitySource.PYSEC: "https://github.com/pypa/advisory-database/blob/main/vulns/{package}/{vuln_id}.yaml",
106107
}
107108
return map_link[self].format(package=package, vuln_id=vuln_id)
109+
110+
111+
def from_pip_audit(report: str) -> Iterable[VulnerabilityIssue]:
112+
"""
113+
Transforms the JSON output from `nox -s dependency:audit` into an iterable of
114+
`VulnerabilityIssue` objects.
115+
116+
This does not gracefully handle scenarios where:
117+
- a CVE is not initially associated with the vulnerability; however, the assumption
118+
is that such vulnerabilities will later be associated with a CVE.
119+
- the same vulnerability ID (CVE, PYSEC, GHSA, etc.) is present across
120+
multiple coordinates.
121+
122+
Input:
123+
'{"dependencies": [{"name": "<package_name>", "version": "<package_version>",
124+
"vulns": [{"id": "<vuln_id>", "fix_versions": ["<fix_version>"],
125+
"aliases": ["<vuln_id2>"], "description": "<vuln_description>"}]}]}'
126+
127+
Args:
128+
report:
129+
the JSON output of `nox -s dependency:audit` provided as a str
130+
"""
131+
report_dict = json.loads(report)
132+
dependencies = report_dict.get("dependencies", [])
133+
for dependency in dependencies:
134+
package = dependency["name"]
135+
for v in dependency["vulns"]:
136+
refs = [v["id"]] + v["aliases"]
137+
cves, cwes, links = identify_pypi_references(
138+
references=refs, package_name=package
139+
)
140+
if cves:
141+
yield VulnerabilityIssue(
142+
cve=sorted(cves)[0],
143+
cwe="None" if not cwes else ", ".join(cwes),
144+
description=v["description"],
145+
coordinates=f"{package}:{dependency['version']}",
146+
references=tuple(links),
147+
)
148+
149+
150+
def identify_pypi_references(
151+
references: list[str], package_name: str
152+
) -> tuple[list[str], list[str], list[str]]:
153+
refs: dict = {k: [] for k in VulnerabilitySource}
154+
links = []
155+
for reference in references:
156+
if source := VulnerabilitySource.from_prefix(reference.upper()):
157+
refs[source].append(reference)
158+
links.append(source.get_link(package=package_name, vuln_id=reference))
159+
return (
160+
refs[VulnerabilitySource.CVE],
161+
refs[VulnerabilitySource.CWE],
162+
links,
163+
)

exasol/toolbox/tools/security.py

Lines changed: 1 addition & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from exasol.toolbox.security import (
2222
GitHubVulnerabilityIssue,
2323
VulnerabilityIssue,
24-
VulnerabilitySource,
24+
from_pip_audit,
2525
)
2626

2727
stdout = print
@@ -82,61 +82,6 @@ def from_maven(report: str) -> Iterable[VulnerabilityIssue]:
8282
)
8383

8484

85-
def identify_pypi_references(
86-
references: list[str], package_name: str
87-
) -> tuple[list[str], list[str], list[str]]:
88-
refs: dict = {k: [] for k in VulnerabilitySource}
89-
links = []
90-
for reference in references:
91-
if source := VulnerabilitySource.from_prefix(reference.upper()):
92-
refs[source].append(reference)
93-
links.append(source.get_link(package=package_name, vuln_id=reference))
94-
return (
95-
refs[VulnerabilitySource.CVE],
96-
refs[VulnerabilitySource.CWE],
97-
links,
98-
)
99-
100-
101-
def from_pip_audit(report: str) -> Iterable[VulnerabilityIssue]:
102-
"""
103-
Transforms the JSON output from `nox -s dependency:audit` into an iterable of
104-
`VulnerabilityIssue` objects.
105-
106-
This does not gracefully handle scenarios where:
107-
- a CVE is not initially associated with the vulnerability; however, the assumption
108-
is that such vulnerabilities will later be associated with a CVE.
109-
- the same vulnerability ID (CVE, PYSEC, GHSA, etc.) is present across
110-
multiple coordinates.
111-
112-
Input:
113-
'{"dependencies": [{"name": "<package_name>", "version": "<package_version>",
114-
"vulns": [{"id": "<vuln_id>", "fix_versions": ["<fix_version>"],
115-
"aliases": ["<vuln_id2>"], "description": "<vuln_description>"}]}]}'
116-
117-
Args:
118-
report:
119-
the JSON output of `nox -s dependency:audit` provided as a str
120-
"""
121-
report_dict = json.loads(report)
122-
dependencies = report_dict.get("dependencies", [])
123-
for dependency in dependencies:
124-
package = dependency["name"]
125-
for v in dependency["vulns"]:
126-
refs = [v["id"]] + v["aliases"]
127-
cves, cwes, links = identify_pypi_references(
128-
references=refs, package_name=package
129-
)
130-
if cves:
131-
yield VulnerabilityIssue(
132-
cve=sorted(cves)[0],
133-
cwe="None" if not cwes else ", ".join(cwes),
134-
description=v["description"],
135-
coordinates=f"{package}:{dependency['version']}",
136-
references=tuple(links),
137-
)
138-
139-
14085
@dataclass(frozen=True)
14186
class SecurityIssue:
14287
file_name: str

test/unit/security_test.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
from exasol.toolbox.security import (
1212
GitHubVulnerabilityIssue,
1313
VulnerabilitySource,
14+
from_pip_audit,
15+
identify_pypi_references,
1416
)
1517
from exasol.toolbox.tools import security
1618

@@ -545,14 +547,14 @@ def test_from_prefix(prefix: str, expected):
545547
],
546548
)
547549
def test_identify_pypi_references(reference: str, expected):
548-
actual = security.identify_pypi_references([reference], package_name="dummy")
550+
actual = identify_pypi_references([reference], package_name="dummy")
549551
assert actual == expected
550552

551553

552554
class TestFromPipAudit:
553555
@staticmethod
554556
def test_no_vulnerability_returns_empty_list():
555-
actual = set(security.from_pip_audit("{}"))
557+
actual = set(from_pip_audit("{}"))
556558
assert actual == set()
557559

558560
@staticmethod
@@ -562,5 +564,5 @@ def test_convert_vulnerability_to_issue(
562564
audit_json = json.dumps(pip_audit_report)
563565
expected = {pip_audit_jinja2_issue, pip_audit_cryptography_issue}
564566

565-
actual = set(security.from_pip_audit(audit_json))
567+
actual = set(from_pip_audit(audit_json))
566568
assert actual == expected

0 commit comments

Comments
 (0)