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_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_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 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 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_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_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
class TestMakeVulnerabilityReport: @pytest.mark.parametrize( "test_input, expected", [ pytest.param(CVSS(version="2"), "cvss_v2", id="v2"), pytest.param(CVSS(version="2.0"), "cvss_v2", id="v2.0"), pytest.param(CVSS(version="2.1"), "cvss_v2", id="v2.1"), pytest.param(CVSS(version="2.x"), "cvss_v2", id="v2.x"), pytest.param(CVSS(version="3"), "cvss_v3", id="v3"), pytest.param(CVSS(version="3.0"), "cvss_v3", id="v3.0"), pytest.param(CVSS(version="3.3"), "cvss_v3", id="v3.3"), pytest.param(CVSS(version="3.y"), "cvss_v3", id="v3.y"), ], ) def test_to_cvss_score_valid(self, test_input, expected): test_input.base_score = 5.7 test_input.exploitability_score = 6.8 test_input.impact_score = 4.3 expected_output = { expected: { "base_score": test_input.base_score, "exploitability_score": test_input.exploitability_score, "impact_score": test_input.impact_score, } } assert api_utils.to_cvss_score(test_input) == expected_output @pytest.mark.parametrize( "test_input", [ pytest.param(CVSS(version="1"), id="v1"), pytest.param(CVSS(version="4"), id="v1"), pytest.param(CVSS(version="foo"), id="vfoo"), pytest.param(CVSS(version=""), id="blank"), pytest.param(CVSS(), id="none"), ], ) def test_to_cvss_score_invalid_version(self, test_input): assert api_utils.to_cvss_score(test_input) is None @pytest.mark.parametrize( "test_input", [ pytest.param( [NVDReference(vulnerability_id="CVE-x", cvss=[])], id="empty-list" ), pytest.param( [NVDReference(vulnerability_id="CVE-x", cvss=None)], id="none" ), pytest.param( [ NVDReference(vulnerability_id="CVE-x", cvss=[]), NVDReference(vulnerability_id="CVE-x", cvss=None), ], id="combo", ), ], ) def test_get_nvd_data_from_nvd_references_no_cvss(self, test_input): actual_output = api_utils.get_nvd_data_from_nvd_references(test_input) assert len(test_input) == len(actual_output) for input_item, output_item in zip(test_input, actual_output): assert output_item == { "id": input_item.vulnerability_id, "cvss_v2": { "base_score": -1.0, "exploitability_score": -1.0, "impact_score": -1.0, }, "cvss_v3": { "base_score": -1.0, "exploitability_score": -1.0, "impact_score": -1.0, }, } def test_get_nvd_data_from_nvd_references_multiple_cvss(self): test_input = NVDReference( vulnerability_id="CVE-x", cvss=[ CVSS( version="2.0", base_score=1.1, exploitability_score=1.2, impact_score=1.3, ), CVSS( version="3.0", base_score=2.1, exploitability_score=2.2, impact_score=2.3, ), CVSS( version="3.1", base_score=3.1, exploitability_score=3.2, impact_score=3.3, ), ], ) expected_output = { "id": test_input.vulnerability_id, "cvss_v2": { "base_score": 1.1, "exploitability_score": 1.2, "impact_score": 1.3, }, "cvss_v3": { "base_score": 3.1, "exploitability_score": 3.2, "impact_score": 3.3, }, } actual_output = api_utils.get_nvd_data_from_nvd_references([test_input]) assert len(actual_output) == 1 assert actual_output[0] == expected_output @pytest.mark.parametrize( "test_input", [ pytest.param(Vulnerability(cvss=[]), id="empty-list"), pytest.param(Vulnerability(cvss=None), id="none"), ], ) def test_get_vendor_data_from_vulnerability_no_cvss(self, test_input): assert api_utils.get_vendor_data_from_vulnerability(test_input) == [] @pytest.mark.parametrize( "test_input, expected_output", [ pytest.param( Vulnerability( vulnerability_id="CVE-x", cvss=[ CVSS( version="2.3", base_score=1.1, exploitability_score=1.2, impact_score=1.3, ) ], ), [ { "id": "CVE-x", "cvss_v2": { "base_score": 1.1, "exploitability_score": 1.2, "impact_score": 1.3, }, "cvss_v3": { "base_score": -1.0, "exploitability_score": -1.0, "impact_score": -1.0, }, } ], id="single_cvss", ), pytest.param( Vulnerability( vulnerability_id="CVE-x", cvss=[ CVSS( version="2.3", base_score=1.1, exploitability_score=1.2, impact_score=1.3, ), CVSS( version="3.1", base_score=2.1, exploitability_score=2.2, impact_score=2.3, ), ], ), [ { "id": "CVE-x", "cvss_v2": { "base_score": 1.1, "exploitability_score": 1.2, "impact_score": 1.3, }, "cvss_v3": { "base_score": -1.0, "exploitability_score": -1.0, "impact_score": -1.0, }, }, { "id": "CVE-x", "cvss_v2": { "base_score": -1.0, "exploitability_score": -1.0, "impact_score": -1.0, }, "cvss_v3": { "base_score": 2.1, "exploitability_score": 2.2, "impact_score": 2.3, }, }, ], id="multiple_cvss", ), ], ) def test_get_vendor_data_from_vulnerability(self, test_input, expected_output): actual_output = api_utils.get_vendor_data_from_vulnerability(test_input) assert len(actual_output) == len(expected_output) for actual_item, expected_item in zip(actual_output, expected_output): assert actual_item == expected_item @pytest.mark.parametrize( "test_input", [ pytest.param(Vulnerability(cvss=[]), id="empty-list"), pytest.param(Vulnerability(cvss=None), id="none"), ], ) def test_get_nvd_data_from_vulnerability_no_cvss(self, test_input): assert api_utils.get_nvd_data_from_vulnerability(test_input) == [] @pytest.mark.parametrize( "test_input, expected_output", [ pytest.param( Vulnerability( vulnerability_id="CVE-x", cvss=[ CVSS( version="2.3", base_score=1.1, exploitability_score=1.2, impact_score=1.3, ) ], ), [ { "id": "CVE-x", "cvss_v2": { "base_score": 1.1, "exploitability_score": 1.2, "impact_score": 1.3, }, "cvss_v3": { "base_score": -1.0, "exploitability_score": -1.0, "impact_score": -1.0, }, } ], id="single_cvss", ), pytest.param( Vulnerability( vulnerability_id="CVE-x", cvss=[ CVSS( version="2.3", base_score=1.1, exploitability_score=1.2, impact_score=1.3, ), CVSS( version="3.1", base_score=2.1, exploitability_score=2.2, impact_score=2.3, ), ], ), [ { "id": "CVE-x", "cvss_v2": { "base_score": 1.1, "exploitability_score": 1.2, "impact_score": 1.3, }, "cvss_v3": { "base_score": 2.1, "exploitability_score": 2.2, "impact_score": 2.3, }, }, ], id="multiple_cvss", ), ], ) def test_get_nvd_data_from_vulnerability(self, test_input, expected_output): actual_output = api_utils.get_nvd_data_from_vulnerability(test_input) assert len(actual_output) == len(expected_output) for actual_item, expected_item in zip(actual_output, expected_output): assert actual_item == expected_item @pytest.mark.parametrize( "report_type, package_type, expected", [ pytest.param("all", "apkg", True, id="all-apkg"), pytest.param("all", "gem", True, id="all-gem"), pytest.param("os", "python", False, id="os-python"), pytest.param("non-os", "dpkg", False, id="nonos-dpkg"), pytest.param("foo", "bar", True, id="foo-bar"), pytest.param("os", "bar", True, id="os-bar"), ], ) def test_is_type_match(self, report_type, package_type, expected): assert api_utils.is_type_match(report_type, package_type) == expected
class TestGrypeProvider: @pytest.mark.parametrize( "test_input", [ pytest.param( [VulnerabilityMatch(fix=FixedArtifact(wont_fix="true"))], id="str", ), pytest.param( [VulnerabilityMatch(fix=FixedArtifact(wont_fix=" "))], id="whitespace", ), pytest.param( [VulnerabilityMatch(fix=FixedArtifact(wont_fix=""))], id="blank", ), pytest.param( [VulnerabilityMatch(fix=FixedArtifact(wont_fix=False))], id="boolean_false", ), pytest.param( [VulnerabilityMatch(fix=None)], id="fix_none", ), ], ) def test_exclude_wont_fix_false(self, test_input): assert len(GrypeProvider._exclude_wont_fix(test_input)) == 1 @pytest.mark.parametrize( "test_input", [ pytest.param( [VulnerabilityMatch(fix=FixedArtifact(wont_fix=True))], id="boolean_true", ), ], ) def test_exclude_wont_fix_true(self, test_input): assert len(GrypeProvider._exclude_wont_fix(test_input)) == 0 @pytest.mark.parametrize( "test_input, expected_output", [ pytest.param( Vulnerability(vulnerability_id="CVE-xyz"), [{ "name": "foo", "version": "0.0", "type": "bar", "namespace": None, "severity": None, }], id="match-1", ), pytest.param( Vulnerability(vulnerability_id="CVE-xyz", severity="Critical", feed_group="meh"), [{ "name": "foo", "version": "0.0", "type": "bar", "namespace": "meh", "severity": "Critical", }], id="match-2", ), pytest.param( Vulnerability(vulnerability_id="CVE-pqr", severity="Critical", feed_group="meh"), [], id="no-match", ), ], ) 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 @pytest.mark.parametrize( "test_input, test_filter, expected_output", [ pytest.param( True, False, [{ "name": "foo", "version": None, "type": None, "namespace": None, "severity": None, }], id="wontfix-true_vendoronly_false", ), pytest.param( False, False, [{ "name": "foo", "version": None, "type": None, "namespace": None, "severity": None, }], id="wontfix-false_vendoronly_false", ), pytest.param( False, True, [{ "name": "foo", "version": None, "type": None, "namespace": None, "severity": None, }], id="wontfix-false_vendoronly_true", ), pytest.param( True, True, [], id="wontfix-true_vendoronly_true", ), ], ) def test_filter_vulnerability_matches_vendor_only(self, test_input, test_filter, expected_output): vuln_match = VulnerabilityMatch( vulnerability=Vulnerability(vulnerability_id="CVE-xyz"), artifact=Artifact(name="foo"), fix=FixedArtifact(wont_fix=test_input), ) results = GrypeProvider._filter_vulnerability_matches( matches=[vuln_match], vulnerability_id="CVE-xyz", severity_filter=None, namespace_filter=None, affected_package_filter=None, vendor_only=test_filter, ) assert results == expected_output @pytest.mark.parametrize( "test_input, test_filter, expected_output", [ pytest.param( "High", "Critical", [], id="no-match", ), pytest.param( "High", "High", [{ "name": "foo", "version": None, "type": None, "namespace": None, "severity": "High", }], id="match", ), ], ) def test_filter_vulnerability_matches_severity(self, test_input, test_filter, expected_output): vuln_match = VulnerabilityMatch( vulnerability=Vulnerability(vulnerability_id="CVE-xyz", severity=test_input), artifact=Artifact(name="foo"), ) results = GrypeProvider._filter_vulnerability_matches( matches=[vuln_match], vulnerability_id="CVE-xyz", severity_filter=test_filter, namespace_filter=None, affected_package_filter=None, vendor_only=None, ) assert results == expected_output @pytest.mark.parametrize( "test_input, test_filter, expected_output", [ pytest.param( "foo", "bar", [], id="no-match", ), pytest.param( "meh", "meh", [{ "name": "foo", "version": None, "type": None, "namespace": "meh", "severity": None, }], id="match", ), ], ) 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_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 TestImageVulnerabilitiesDeduplicator: @pytest.mark.parametrize( "test_input, expected_index", [ pytest.param( [ VulnerabilityMatch( vulnerability=Vulnerability( feed="vulnerabilities", feed_group="nvdv2:cves", vulnerability_id="CVE-2019-12904", ), nvd=[NVDReference(vulnerability_id="CVE-2019-12904")], ), VulnerabilityMatch( vulnerability=Vulnerability( feed="vulnerabilities", feed_group="ubuntu:20.04", vulnerability_id="CVE-2019-12904", ), nvd=[NVDReference(vulnerability_id="CVE-2019-12904")], ), ], 1, id="different-namespaces", ), pytest.param( [ VulnerabilityMatch( vulnerability=Vulnerability( feed="vulnerabilities", feed_group="nvdv2:cves", vulnerability_id="CVE-2019-12904", ), nvd=[NVDReference(vulnerability_id="CVE-2019-12904")], ), VulnerabilityMatch( vulnerability=Vulnerability( feed="vulnerabilities", feed_group="github:java", vulnerability_id="GHSA-foobar", ), nvd=[NVDReference(vulnerability_id="CVE-2019-12904")], ), ], 1, id="different-identifiers", ), pytest.param( [ VulnerabilityMatch( vulnerability=Vulnerability( feed="vulnerabilities", feed_group="github:java", vulnerability_id="GHSA-foobar", ), nvd=[NVDReference(vulnerability_id="CVE-2019-12904")], ), VulnerabilityMatch( vulnerability=Vulnerability( feed="vulnerabilities", feed_group="ubuntu:20.04", vulnerability_id="CVE-2019-12904", ), nvd=[NVDReference(vulnerability_id="CVE-2019-12904")], ), ], 1, id="non-nvd-namespaces", ), pytest.param( [ VulnerabilityMatch( vulnerability=Vulnerability( feed="vulnerabilities", feed_group="nvdv2:cves", vulnerability_id="CVE-2019-12904", ), nvd=[NVDReference(vulnerability_id="CVE-2019-12904")], ), VulnerabilityMatch( vulnerability=Vulnerability( feed="vulnerabilities", feed_group="ubuntu:20.04", vulnerability_id="CVE-2019-12904", ), nvd=[], ), ], 1, id="no-nvd-refs", ), pytest.param( [ VulnerabilityMatch( vulnerability=Vulnerability( feed="vulnerabilities", feed_group="nvdv2:cves", vulnerability_id="CVE-2019-12904", ), nvd=[NVDReference(vulnerability_id="CVE-2019-12345")], ), VulnerabilityMatch( vulnerability=Vulnerability( feed="vulnerabilities", feed_group="nvdv2:cves", vulnerability_id="CVE-2019-12904", ), nvd=[NVDReference(vulnerability_id="CVE-2019-12904")], ), VulnerabilityMatch( vulnerability=Vulnerability( feed="vulnerabilities", feed_group="github:java", vulnerability_id="GHSA-foobar", ), nvd=[ NVDReference(vulnerability_id="CVE-2019-12904"), NVDReference(vulnerability_id="CVE-2019-12345"), ], ), ], 2, id="multiple-nvd-refs", ), ], ) 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 @pytest.mark.parametrize("count", [1, 2, 3, 4, 5]) 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 @pytest.mark.parametrize( "test_input", [pytest.param([], id="empty-list"), pytest.param(None, id="none")], ) def test_execute_invalid_input(self, test_input): assert ( ImageVulnerabilitiesDeduplicator(FeedGroupRank()).execute(test_input) == list() )
class TestVulnerabilityIdentity: @pytest.mark.parametrize( "test_input", [ pytest.param( [NVDReference(vulnerability_id="CVE-abc")], id="single-nvd", ), pytest.param( [ NVDReference(vulnerability_id="CVE-abc"), NVDReference(vulnerability_id="CVE-def"), NVDReference(vulnerability_id="CVE-ghi"), ], id="multiple-nvd", ), ], ) def test_from_with_nvd(self, test_input): 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", ), ) match.nvd = test_input identity_objects = VulnerabilityIdentity.from_match(match) assert identity_objects assert isinstance(identity_objects, list) and len(identity_objects) == len( test_input ) for identity_object, input_nvd in zip(identity_objects, test_input): assert identity_object.vuln_id == input_nvd.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_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 @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( feed="hedgehog", feed_group="hedgy:thorny", vulnerability_id="foo", ), nvd=[NVDReference(vulnerability_id="CVE-abc")], ), True, id="equal-different-namespaces", ), 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="hedgehog", feed_group="hedgy:thorny", vulnerability_id="foo", ), nvd=[ NVDReference(vulnerability_id="CVE-abc"), NVDReference(vulnerability_id="CVE-def"), NVDReference(vulnerability_id="CVE-ghi"), ], ), True, id="equal-multiple-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="hedgehog", feed_group="hedgy:thorny", vulnerability_id="foo", ), nvd=[NVDReference(vulnerability_id="CVE-def")], ), False, id="not-equal", ), ], ) 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 @pytest.mark.parametrize("count", [1, 2, 3, 4, 5]) def test_hash(self, count): record = VulnerabilityIdentity( vuln_id="meh", pkg_name="blah", pkg_version="1.2.3maven", pkg_type="java", pkg_path="blah", ) test_input = [record for x in range(count)] result = set(test_input) assert result and len(result) == 1
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