示例#1
0
    def _try_parse_related_vulnerabilities(
        vulns: List[Dict], ) -> List[NVDReference]:
        """
        Best effort attempt at parsing other vulnerabilities from grype response. Ignores any errors raised and chugs along
        """
        nvd_objects = []
        if isinstance(vulns, list) and vulns:
            for vuln_dict in vulns:
                try:
                    nvd_objects.append(
                        NVDReference(
                            vulnerability_id=vuln_dict.get("id"),
                            # description=vuln_dict.get("description"),
                            description=None,
                            severity=vuln_dict.get("severity"),
                            link=vuln_dict.get("dataSource"),
                            cvss=VulnerabilityMapper._try_parse_cvss(
                                vuln_dict.get("cvss", [])),
                        ))
                except (AttributeError, ValueError):
                    log.debug(
                        "Ignoring error parsing related vulnerability dict %s",
                        vuln_dict,
                    )

        return nvd_objects
示例#2
0
    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__
示例#3
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
示例#4
0
    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
示例#5
0
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
示例#6
0
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()
        )
示例#7
0
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
示例#8
0
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