def test_from_without_nvd(self): match = VulnerabilityMatch( artifact=Artifact( name="blah", location="/usr/local/java/blah", pkg_type="java", version="1.2.3maven", ), vulnerability=Vulnerability( feed="vulnerabilities", feed_group="whatever:hello", vulnerability_id="meh", ), nvd=[], ) identity_objects = VulnerabilityIdentity.from_match(match) assert identity_objects assert isinstance(identity_objects, list) and len(identity_objects) == 1 identity_object = identity_objects[0] assert identity_object.vuln_id == match.vulnerability.vulnerability_id assert identity_object.pkg_name == match.artifact.name assert identity_object.pkg_type == match.artifact.pkg_type assert identity_object.pkg_version == match.artifact.version assert identity_object.pkg_path == match.artifact.location
def test_from(self): match = VulnerabilityMatch( artifact=Artifact( name="blah", location="/usr/local/java/blah", pkg_type="java", version="1.2.3maven", ), vulnerability=Vulnerability( feed="vulnerabilities", feed_group="whatever:hello", vulnerability_id="meh", ), nvd=[NVDReference(vulnerability_id="CVE-abc")], ) rank_strategy = FeedGroupRank() ranked_match = RankedVulnerabilityMatch.from_match(match, FeedGroupRank()) assert ranked_match assert ranked_match.vuln_id == match.vulnerability.vulnerability_id assert ranked_match.vuln_namespace == match.vulnerability.feed_group assert ranked_match.pkg_name == match.artifact.name assert ranked_match.pkg_type == match.artifact.pkg_type assert ranked_match.pkg_version == match.artifact.version assert ranked_match.pkg_path == match.artifact.location assert ranked_match.rank == rank_strategy.__default__
def test_transfer_vulnerability_timestamps_fix_observed_at( self, test_source, test_destination, expected ): random = VulnerabilityMatch( artifact=Artifact( name="blah", location="/usr/local/java/blah", pkg_type="java", version="1.2.3maven", ), vulnerability=Vulnerability( feed="vulnerabilities", feed_group="whatever:hello", vulnerability_id="meh", ), match=Match(detected_at=datetime.datetime.utcnow()), ) source = copy.deepcopy(random) source.fix = test_source destination = copy.deepcopy(random) destination.fix = test_destination results = transfer_vulnerability_timestamps( source=[source], destination=[destination] ) assert results and len(results) == 1 assert results[0].fix.observed_at == expected
def test_transfer_vulnerability_timestamps_single( self, test_source, test_destination ): random = VulnerabilityMatch( artifact=Artifact( name="blah", location="/usr/local/java/blah", pkg_type="java", version="1.2.3maven", ), vulnerability=Vulnerability( feed="vulnerabilities", feed_group="whatever:hello", vulnerability_id="meh", ), fix=FixedArtifact(), ) source = copy.deepcopy(random) source.match = Match(detected_at=test_source) destination = copy.deepcopy(random) destination.match = Match(detected_at=test_destination) results = transfer_vulnerability_timestamps( source=[source], destination=[destination] ) assert results and len(results) == 1 assert results[0].match.detected_at == test_source
def to_engine( self, result: Dict, package_mapper: PackageMapper, now: datetime.datetime, ): artifact_dict = result.get("artifact") vuln_dict = result.get("vulnerability") # parse cvss fields cvss_objs = VulnerabilityMapper._try_parse_cvss( vuln_dict.get("cvss", [])) # parse fix details fix_dict = vuln_dict.get("fix") fix_obj = FixedArtifact(versions=[], wont_fix=False, observed_at=None, advisories=[]) if fix_dict: fix_obj.versions = fix_dict.get("versions", []) fix_obj.wont_fix = (fix_dict.get("state").lower() == "wont-fix" ) # TODO format check with toolbox fix_obj.observed_at = now if fix_obj.versions else None fix_obj.advisories = VulnerabilityMapper._try_parse_advisories( fix_dict.get("advisories", [])) # parse nvd references nvd_objs = VulnerabilityMapper._try_parse_related_vulnerabilities( result.get("relatedVulnerabilities", [])) # parse package path pkg_path = (artifact_dict.get("locations")[0].get("path") if artifact_dict.get("locations") else "NA") vuln_match = VulnerabilityMatch( vulnerability=Vulnerability( vulnerability_id=vuln_dict.get("id"), description=vuln_dict.get("description"), severity=vuln_dict.get("severity"), link=vuln_dict.get("dataSource"), feed="grypedb", feed_group=vuln_dict.get("namespace"), cvss=cvss_objs, ), artifact=Artifact( name=artifact_dict.get("name"), version=artifact_dict.get("version"), pkg_type=package_mapper.engine_type, location=pkg_path, cpe=None, # vestige of the old system cpes=self._try_parse_matched_cpes(result), ), fix=fix_obj, match=Match(detected_at=now), nvd=nvd_objs, ) return vuln_match
def test_equality_constant_artifact(self, lhs, rhs, expected): artifact = Artifact( name="blah", location="/usr/local/java/blah", pkg_type="java", version="1.2.3maven", ) lhs.artifact = artifact rhs.artifact = artifact assert ( RankedVulnerabilityMatch.from_match(lhs, FeedGroupRank()) == RankedVulnerabilityMatch.from_match(rhs, FeedGroupRank()) ) == expected
def test_equality_constant_artifact(self, lhs, rhs, expected): artifact = Artifact( name="blah", location="/usr/local/java/blah", pkg_type="java", version="1.2.3maven", ) lhs.artifact = artifact rhs.artifact = artifact assert ( VulnerabilityIdentity.from_match(lhs) == VulnerabilityIdentity.from_match(rhs) ) == expected
def test_execute(self, test_input, expected_index): artifact = Artifact( name="blah", location="/usr/local/java/blah", pkg_type="java", version="1.2.3maven", ) for item in test_input: item.artifact = artifact results = ImageVulnerabilitiesDeduplicator(FeedGroupRank()).execute(test_input) assert len(results) == 1 actual = results[0].vulnerability expected = test_input[expected_index] assert actual.vulnerability_id == expected.vulnerability.vulnerability_id assert actual.feed_group == expected.vulnerability.feed_group
def test_filter_vulnerability_matches_namespace(self, test_input, test_filter, expected_output): vuln_match = VulnerabilityMatch( vulnerability=Vulnerability(vulnerability_id="CVE-xyz", feed_group=test_input), artifact=Artifact(name="foo"), ) results = GrypeProvider._filter_vulnerability_matches( matches=[vuln_match], vulnerability_id="CVE-xyz", severity_filter=None, namespace_filter=test_filter, affected_package_filter=None, vendor_only=None, ) assert results == expected_output
def test_filter_vulnerability_matches_no_optional_filters( self, test_input, expected_output): vuln_match = VulnerabilityMatch( vulnerability=test_input, artifact=Artifact( name="foo", version="0.0", pkg_type="bar", location="/usr/local/lib", ), ) results = GrypeProvider._filter_vulnerability_matches( matches=[vuln_match], vulnerability_id="CVE-xyz", severity_filter=None, namespace_filter=None, affected_package_filter=None, vendor_only=None, ) assert results == expected_output
def test_execute_absolute_duplicates(self, count): a = VulnerabilityMatch( artifact=Artifact( name="blah", location="/usr/local/java/blah", pkg_type="java", version="1.2.3maven", ), vulnerability=Vulnerability( feed="vulnerabilities", feed_group="whatever:hello", vulnerability_id="meh", ), nvd=[NVDReference(vulnerability_id="CVE-2019-12904")], ) input_matches = [a for x in range(count)] results = ImageVulnerabilitiesDeduplicator(FeedGroupRank()).execute( input_matches ) assert len(results) == 1
def test_transfer_vulnerability_timestamps_multiple(self): dest_ts = datetime.datetime.utcnow() src_ts = datetime.datetime.utcnow() - datetime.timedelta(days=1) destination = [ VulnerabilityMatch( artifact=Artifact( name="blah", location="/usr/local/java/blah", pkg_type="java", version="1.2.3maven", ), vulnerability=Vulnerability( feed="vulnerabilities", feed_group="whatever:hello", vulnerability_id="meh", ), match=Match(detected_at=dest_ts), fix=FixedArtifact(), ), VulnerabilityMatch( artifact=Artifact( name="blah", location="/usr/local/java/blah", pkg_type="java", version="1.2.3maven", ), vulnerability=Vulnerability( feed="vulnerabilities", feed_group="whatever:hello", vulnerability_id="foo", ), match=Match(detected_at=dest_ts), fix=FixedArtifact(), ), ] source = [ VulnerabilityMatch( artifact=Artifact( name="blah", location="/usr/local/java/blah", pkg_type="java", version="1.2.3maven", ), vulnerability=Vulnerability( feed="vulnerabilities", feed_group="whatever:hello", vulnerability_id="meh", ), match=Match(detected_at=src_ts), fix=FixedArtifact(), ) ] results = transfer_vulnerability_timestamps( source=source, destination=destination ) assert results and len(results) == 2 for result in results: if ( result.vulnerability.vulnerability_id == source[0].vulnerability.vulnerability_id ): assert result.match.detected_at == src_ts else: assert result.match.detected_at == dest_ts
class TestRankedVulnerabilityMatch: def test_from(self): match = VulnerabilityMatch( artifact=Artifact( name="blah", location="/usr/local/java/blah", pkg_type="java", version="1.2.3maven", ), vulnerability=Vulnerability( feed="vulnerabilities", feed_group="whatever:hello", vulnerability_id="meh", ), nvd=[NVDReference(vulnerability_id="CVE-abc")], ) rank_strategy = FeedGroupRank() ranked_match = RankedVulnerabilityMatch.from_match(match, FeedGroupRank()) assert ranked_match assert ranked_match.vuln_id == match.vulnerability.vulnerability_id assert ranked_match.vuln_namespace == match.vulnerability.feed_group assert ranked_match.pkg_name == match.artifact.name assert ranked_match.pkg_type == match.artifact.pkg_type assert ranked_match.pkg_version == match.artifact.version assert ranked_match.pkg_path == match.artifact.location assert ranked_match.rank == rank_strategy.__default__ @pytest.mark.parametrize( "lhs, rhs, expected", [ pytest.param( VulnerabilityMatch( vulnerability=Vulnerability( feed="trusty", feed_group="trusty:chameleon", vulnerability_id="meh", ), nvd=[NVDReference(vulnerability_id="CVE-abc")], ), VulnerabilityMatch( vulnerability=Vulnerability( feed="hedgehog", feed_group="hedgy:thorny", vulnerability_id="foo", ), nvd=[NVDReference(vulnerability_id="CVE-abc")], ), False, id="not-equal-different-ids", ), pytest.param( VulnerabilityMatch( vulnerability=Vulnerability( feed="trusty", feed_group="trusty:chameleon", vulnerability_id="meh", ), nvd=[ NVDReference(vulnerability_id="CVE-abc"), NVDReference(vulnerability_id="CVE-def"), NVDReference(vulnerability_id="CVE-ghi"), ], ), VulnerabilityMatch( vulnerability=Vulnerability( feed="trusty", feed_group="trusty:chameleon", vulnerability_id="meh", ), nvd=[NVDReference(vulnerability_id="CVE-abc")], ), True, id="equal-different-cvss", ), pytest.param( VulnerabilityMatch( vulnerability=Vulnerability( feed="trusty", feed_group="trusty:chameleon", vulnerability_id="meh", ), nvd=[NVDReference(vulnerability_id="CVE-abc")], ), VulnerabilityMatch( vulnerability=Vulnerability( feed="trusty", feed_group="trusty:python", vulnerability_id="meh", ), nvd=[NVDReference(vulnerability_id="CVE-abc")], ), False, id="not-equal-different-namespaces", ), ], ) def test_equality_constant_artifact(self, lhs, rhs, expected): artifact = Artifact( name="blah", location="/usr/local/java/blah", pkg_type="java", version="1.2.3maven", ) lhs.artifact = artifact rhs.artifact = artifact assert ( RankedVulnerabilityMatch.from_match(lhs, FeedGroupRank()) == RankedVulnerabilityMatch.from_match(rhs, FeedGroupRank()) ) == expected @pytest.mark.parametrize("count", [1, 2, 3, 4, 5]) def test_hash_empty_match(self, count): record = RankedVulnerabilityMatch( vuln_id="meh", vuln_namespace="trusty:chameleon", pkg_name="blah", pkg_version="1.2.3maven", pkg_type="java", pkg_path="blah", rank=100, match_obj=VulnerabilityMatch(), ) test_input = [record for x in range(count)] result = set(test_input) assert result and len(result) == 1 @pytest.mark.parametrize( "test_input", [ pytest.param( [ VulnerabilityMatch( artifact=Artifact( name="blah", location="/usr/local/java/blah", pkg_type="java", version="1.2.3maven", ), vulnerability=Vulnerability( feed="twisty", feed_group="twisty:python", vulnerability_id="meh", ), nvd=[NVDReference(vulnerability_id="CVE-abc")], ), VulnerabilityMatch( artifact=Artifact( name="foo", location="/usr/local/java/foo", pkg_type="unknown", version="1.2.3", ), vulnerability=Vulnerability( feed="tricky", feed_group="tricky:chameleon", vulnerability_id="meh", ), nvd=[NVDReference(vulnerability_id="CVE-def")], ), ], id="different-matches", ), pytest.param( [ VulnerabilityMatch( artifact=Artifact( name="blah", location="/usr/local/java/blah", pkg_type="java", version="1.2.3maven", ), vulnerability=Vulnerability( feed="twisty", feed_group="twisty:python", vulnerability_id="meh", ), nvd=[NVDReference(vulnerability_id="CVE-abc")], ), ] * 3, id="same-matches", ), ], ) def test_hash(self, test_input): vuln_rank_objects = [ RankedVulnerabilityMatch( vuln_id="meh", vuln_namespace="trusty:chameleon", pkg_name="blah", pkg_version="1.2.3maven", pkg_type="java", pkg_path="/usr/local/blah", rank=100, match_obj=item, ) for item in test_input ] result = set(vuln_rank_objects) assert result and len(result) == 1 result = list(result)[0] assert result.vuln_id == "meh" assert result.vuln_namespace == "trusty:chameleon" assert result.pkg_name == "blah" assert result.pkg_type == "java" assert result.pkg_path == "/usr/local/blah" assert result.rank == 100