From 5e46f17299b9d416e624e91a227033af297f5724 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Wed, 25 May 2022 14:35:15 -0400 Subject: [PATCH] service/osv: guard against schema changes (#288) * service/osv: guard against schema changes Signed-off-by: William Woodruff * CHANGELOG: record changes Signed-off-by: William Woodruff --- CHANGELOG.md | 3 +++ pip_audit/_service/osv.py | 9 +++++++++ test/service/test_osv.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89985aa2..64411bcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ All versions prior to 0.0.9 are untracked. connection failures to vulnerability sources was improved ([#287](https://github.com/trailofbits/pip-audit/pull/287)) +* Vulnerability sources: the OSV service is now more resilient + to schema changes ([#288](https://github.com/trailofbits/pip-audit/pull/288)) + ### Fixed * Vulnerability sources: a bug stemming from an incorrect assumption diff --git a/pip_audit/_service/osv.py b/pip_audit/_service/osv.py index c7a7b8c2..c9740401 100644 --- a/pip_audit/_service/osv.py +++ b/pip_audit/_service/osv.py @@ -79,6 +79,15 @@ def query(self, spec: Dependency) -> Tuple[Dependency, List[VulnerabilityResult] return spec, results for vuln in response_json["vulns"]: + # Sanity check: only the v1 schema is specified at the moment, + # and the code below probably won't work with future incompatible + # schemas without additional changes. + # The absence of a schema is treated as 1.0.0, per the OSV spec. + schema_version = Version(vuln.get("schema_version", "1.0.0")) + if schema_version.major != 1: + logger.warning(f"Unsupported OSV schema version: {schema_version}") + continue + id = vuln["id"] # The summary is intended to be shorter, so we prefer it over diff --git a/test/service/test_osv.py b/test/service/test_osv.py index 4723206f..752bddfe 100644 --- a/test/service/test_osv.py +++ b/test/service/test_osv.py @@ -138,6 +138,35 @@ def test_osv_skipped_dep(): assert len(vulns) == 0 +@pytest.mark.parametrize("version", ["0.0.0", "2.0.0", "2.3.4"]) +def test_osv_unsupported_schema_version(monkeypatch, version): + logger = pretend.stub(warning=pretend.call_recorder(lambda s: None)) + monkeypatch.setattr(service.osv, "logger", logger) + + payload = { + "vulns": [ + {"schema_version": version}, + ] + } + + response = pretend.stub(raise_for_status=lambda: None, json=lambda: payload) + post = pretend.call_recorder(lambda *a, **kw: response) + + osv = service.OsvService() + monkeypatch.setattr(osv.session, "post", post) + + dep = service.ResolvedDependency("foo", Version("1.0.0")) + results = dict(osv.query_all(iter([dep]))) + + assert logger.warning.calls == [pretend.call(f"Unsupported OSV schema version: {version}")] + + assert len(results) == 1 + assert dep in results + + vulns = results[dep] + assert len(vulns) == 0 + + @pytest.mark.parametrize( ["summary", "details", "description"], [