mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-09 21:14:22 +00:00
chore: route vulnerability references to canonical URLs (#10853)
Co-authored-by: Hugo P.Brito <hugopbrito@Mac.home>
This commit is contained in:
co-authored by
Hugo P.Brito
parent
bcaa6ac488
commit
2c5d47a8cd
@@ -0,0 +1,91 @@
|
||||
from prowler.lib.utils.vulnerability_references import (
|
||||
build_finding_reference_url,
|
||||
resolve_vulnerability_reference_urls,
|
||||
)
|
||||
|
||||
|
||||
class TestBuildFindingReferenceUrl:
|
||||
def test_cve_id_returns_cve_org_url(self):
|
||||
assert (
|
||||
build_finding_reference_url("CVE-2023-1234")
|
||||
== "https://www.cve.org/CVERecord?id=CVE-2023-1234"
|
||||
)
|
||||
|
||||
def test_lowercase_cve_id_is_normalized(self):
|
||||
assert (
|
||||
build_finding_reference_url("cve-2024-9999")
|
||||
== "https://www.cve.org/CVERecord?id=CVE-2024-9999"
|
||||
)
|
||||
|
||||
def test_ghsa_id_returns_github_advisory_url(self):
|
||||
assert (
|
||||
build_finding_reference_url("GHSA-abcd-1234-efgh")
|
||||
== "https://github.com/advisories/GHSA-ABCD-1234-EFGH"
|
||||
)
|
||||
|
||||
def test_avd_prefixed_id_strips_prefix_for_hub(self):
|
||||
assert (
|
||||
build_finding_reference_url("AVD-AWS-0001")
|
||||
== "https://hub.prowler.com/check/AWS-0001"
|
||||
)
|
||||
|
||||
def test_clean_trivy_id_uses_hub_directly(self):
|
||||
assert (
|
||||
build_finding_reference_url("AWS-0104")
|
||||
== "https://hub.prowler.com/check/AWS-0104"
|
||||
)
|
||||
|
||||
def test_kubernetes_id_uses_hub(self):
|
||||
assert (
|
||||
build_finding_reference_url("AVD-K8S-0001")
|
||||
== "https://hub.prowler.com/check/K8S-0001"
|
||||
)
|
||||
|
||||
def test_dockerfile_id_uses_hub(self):
|
||||
assert (
|
||||
build_finding_reference_url("AVD-DOCKER-0001")
|
||||
== "https://hub.prowler.com/check/DOCKER-0001"
|
||||
)
|
||||
|
||||
def test_whitespace_is_trimmed(self):
|
||||
assert (
|
||||
build_finding_reference_url(" AZU-0013 ")
|
||||
== "https://hub.prowler.com/check/AZU-0013"
|
||||
)
|
||||
|
||||
|
||||
class TestResolveVulnerabilityReferenceUrls:
|
||||
def test_cve_with_cve_org_reference_uses_it(self):
|
||||
recommendation_url, additional_urls = resolve_vulnerability_reference_urls(
|
||||
vulnerability_id="CVE-2023-1234",
|
||||
references=[
|
||||
"https://avd.aquasec.com/nvd/cve-2023-1234",
|
||||
"https://www.cve.org/CVERecord?id=CVE-2023-1234",
|
||||
"https://nvd.nist.gov/vuln/detail/CVE-2023-1234",
|
||||
],
|
||||
primary_url="https://avd.aquasec.com/nvd/cve-2023-1234",
|
||||
)
|
||||
|
||||
assert recommendation_url == "https://www.cve.org/CVERecord?id=CVE-2023-1234"
|
||||
assert additional_urls == ["https://www.cve.org/CVERecord?id=CVE-2023-1234"]
|
||||
|
||||
def test_cve_without_cve_org_reference_builds_url(self):
|
||||
recommendation_url, additional_urls = resolve_vulnerability_reference_urls(
|
||||
vulnerability_id="CVE-2023-5678",
|
||||
references=["https://nvd.nist.gov/vuln/detail/CVE-2023-5678"],
|
||||
)
|
||||
|
||||
assert recommendation_url == "https://www.cve.org/CVERecord?id=CVE-2023-5678"
|
||||
assert additional_urls == ["https://www.cve.org/CVERecord?id=CVE-2023-5678"]
|
||||
|
||||
def test_non_cve_id_returns_filtered_references(self):
|
||||
recommendation_url, additional_urls = resolve_vulnerability_reference_urls(
|
||||
vulnerability_id="GHSA-abcd-1234-efgh",
|
||||
references=[
|
||||
"https://avd.aquasec.com/nvd/ghsa-abcd-1234-efgh",
|
||||
"https://github.com/advisories/GHSA-abcd-1234-efgh",
|
||||
],
|
||||
)
|
||||
|
||||
assert recommendation_url == ""
|
||||
assert additional_urls == ["https://github.com/advisories/GHSA-abcd-1234-efgh"]
|
||||
@@ -259,7 +259,13 @@ SAMPLE_TRIVY_VULNERABILITY_OUTPUT = {
|
||||
"Title": "Example vulnerability",
|
||||
"Description": "This is an example vulnerability",
|
||||
"Severity": "high",
|
||||
"PrimaryURL": "https://example.com/cve-2023-1234",
|
||||
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2023-1234",
|
||||
"References": [
|
||||
"https://avd.aquasec.com/nvd/cve-2023-1234",
|
||||
"https://nvd.nist.gov/vuln/detail/CVE-2023-1234",
|
||||
"https://www.cve.org/CVERecord?id=CVE-2023-1234",
|
||||
"https://security.example.com/advisories/CVE-2023-1234",
|
||||
],
|
||||
}
|
||||
],
|
||||
"Secrets": [],
|
||||
@@ -268,6 +274,39 @@ SAMPLE_TRIVY_VULNERABILITY_OUTPUT = {
|
||||
]
|
||||
}
|
||||
|
||||
SAMPLE_TRIVY_VULNERABILITY_WITHOUT_CVE_ORG_REFERENCE = {
|
||||
"VulnerabilityID": "CVE-2023-5678",
|
||||
"Title": "Vulnerability without cve.org reference",
|
||||
"Description": "This vulnerability includes references but no cve.org reference",
|
||||
"Severity": "high",
|
||||
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2023-5678",
|
||||
"References": [
|
||||
"https://avd.aquasec.com/nvd/cve-2023-5678",
|
||||
"https://nvd.nist.gov/vuln/detail/CVE-2023-5678",
|
||||
"https://security.example.com/advisories/CVE-2023-5678",
|
||||
],
|
||||
}
|
||||
|
||||
SAMPLE_TRIVY_VULNERABILITY_WITHOUT_REFERENCES = {
|
||||
"VulnerabilityID": "CVE-2023-9012",
|
||||
"Title": "Fallback CVE vulnerability",
|
||||
"Description": "This vulnerability requires building the URL from VulnerabilityID",
|
||||
"Severity": "medium",
|
||||
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2023-9012",
|
||||
}
|
||||
|
||||
SAMPLE_TRIVY_NON_CVE_VULNERABILITY = {
|
||||
"VulnerabilityID": "GHSA-abcd-1234-efgh",
|
||||
"Title": "Non-CVE vulnerability",
|
||||
"Description": "This advisory has no CVE identifier",
|
||||
"Severity": "high",
|
||||
"PrimaryURL": "https://avd.aquasec.com/nvd/ghsa-abcd-1234-efgh",
|
||||
"References": [
|
||||
"https://avd.aquasec.com/nvd/ghsa-abcd-1234-efgh",
|
||||
"https://github.com/advisories/GHSA-abcd-1234-efgh",
|
||||
],
|
||||
}
|
||||
|
||||
# Sample Trivy output with secrets
|
||||
SAMPLE_TRIVY_SECRET_OUTPUT = {
|
||||
"Results": [
|
||||
|
||||
@@ -20,6 +20,9 @@ from tests.providers.iac.iac_fixtures import (
|
||||
SAMPLE_KUBERNETES_CHECK,
|
||||
SAMPLE_PASSED_CHECK,
|
||||
SAMPLE_SKIPPED_CHECK,
|
||||
SAMPLE_TRIVY_NON_CVE_VULNERABILITY,
|
||||
SAMPLE_TRIVY_VULNERABILITY_WITHOUT_CVE_ORG_REFERENCE,
|
||||
SAMPLE_TRIVY_VULNERABILITY_WITHOUT_REFERENCES,
|
||||
SAMPLE_YAML_CHECK,
|
||||
get_empty_trivy_output,
|
||||
get_invalid_trivy_output,
|
||||
@@ -57,13 +60,15 @@ class TestIacProvider:
|
||||
assert isinstance(report, CheckReportIAC)
|
||||
assert report.status == "FAIL"
|
||||
|
||||
# Trivy emits "AVD-AWS-0001"; Hub indexes it without the AVD- prefix.
|
||||
expected_url = "https://hub.prowler.com/check/AWS-0001"
|
||||
assert report.check_metadata.Provider == "iac"
|
||||
assert report.check_metadata.CheckID == SAMPLE_FAILED_CHECK["ID"]
|
||||
assert report.check_metadata.CheckTitle == SAMPLE_FAILED_CHECK["Title"]
|
||||
assert report.check_metadata.Severity == "low"
|
||||
assert report.check_metadata.RelatedUrl == SAMPLE_FAILED_CHECK.get(
|
||||
"PrimaryURL", ""
|
||||
)
|
||||
assert report.check_metadata.Remediation.Recommendation.Url == expected_url
|
||||
assert report.check_metadata.RelatedUrl == ""
|
||||
assert report.check_metadata.AdditionalURLs == [expected_url]
|
||||
|
||||
def test_iac_provider_process_finding_passed(self):
|
||||
"""Test processing a passed finding"""
|
||||
@@ -79,6 +84,101 @@ class TestIacProvider:
|
||||
assert report.check_metadata.CheckTitle == SAMPLE_PASSED_CHECK["Title"]
|
||||
assert report.check_metadata.Severity == "low"
|
||||
|
||||
def test_iac_provider_process_vulnerability_prefers_cve_reference_and_filters_aqua(
|
||||
self,
|
||||
):
|
||||
"""Test CVE findings use cve.org and exclude Aqua references."""
|
||||
provider = IacProvider()
|
||||
|
||||
report = provider._process_finding(
|
||||
{
|
||||
"VulnerabilityID": "CVE-2023-1234",
|
||||
"Title": "Example vulnerability",
|
||||
"Description": "This is an example vulnerability",
|
||||
"Severity": "high",
|
||||
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2023-1234",
|
||||
"References": [
|
||||
"https://avd.aquasec.com/nvd/cve-2023-1234",
|
||||
"https://nvd.nist.gov/vuln/detail/CVE-2023-1234",
|
||||
"https://www.cve.org/CVERecord?id=CVE-2023-1234",
|
||||
"https://security.example.com/advisories/CVE-2023-1234",
|
||||
],
|
||||
},
|
||||
"package.json",
|
||||
"nodejs",
|
||||
)
|
||||
|
||||
assert (
|
||||
report.check_metadata.Remediation.Recommendation.Url
|
||||
== "https://www.cve.org/CVERecord?id=CVE-2023-1234"
|
||||
)
|
||||
assert report.check_metadata.RelatedUrl == ""
|
||||
assert report.check_metadata.AdditionalURLs == [
|
||||
"https://www.cve.org/CVERecord?id=CVE-2023-1234"
|
||||
]
|
||||
|
||||
def test_iac_provider_process_vulnerability_builds_cve_org_for_nvd_reference(
|
||||
self,
|
||||
):
|
||||
"""Test official CVE URL is built when only NVD is provided."""
|
||||
provider = IacProvider()
|
||||
|
||||
report = provider._process_finding(
|
||||
SAMPLE_TRIVY_VULNERABILITY_WITHOUT_CVE_ORG_REFERENCE,
|
||||
"package.json",
|
||||
"nodejs",
|
||||
)
|
||||
|
||||
assert (
|
||||
report.check_metadata.Remediation.Recommendation.Url
|
||||
== "https://www.cve.org/CVERecord?id=CVE-2023-5678"
|
||||
)
|
||||
assert report.check_metadata.RelatedUrl == ""
|
||||
assert report.check_metadata.AdditionalURLs == [
|
||||
"https://www.cve.org/CVERecord?id=CVE-2023-5678"
|
||||
]
|
||||
|
||||
def test_iac_provider_process_vulnerability_builds_cve_org_when_references_missing(
|
||||
self,
|
||||
):
|
||||
"""Test CVE URL is built from VulnerabilityID when references are absent."""
|
||||
provider = IacProvider()
|
||||
|
||||
report = provider._process_finding(
|
||||
SAMPLE_TRIVY_VULNERABILITY_WITHOUT_REFERENCES,
|
||||
"package.json",
|
||||
"nodejs",
|
||||
)
|
||||
|
||||
assert (
|
||||
report.check_metadata.Remediation.Recommendation.Url
|
||||
== "https://www.cve.org/CVERecord?id=CVE-2023-9012"
|
||||
)
|
||||
assert report.check_metadata.RelatedUrl == ""
|
||||
assert report.check_metadata.AdditionalURLs == [
|
||||
"https://www.cve.org/CVERecord?id=CVE-2023-9012"
|
||||
]
|
||||
|
||||
def test_iac_provider_process_non_cve_vulnerability_falls_back_to_github_advisory(
|
||||
self,
|
||||
):
|
||||
"""Non-CVE vulnerabilities (GHSA-…) point to GitHub Security Advisories."""
|
||||
provider = IacProvider()
|
||||
|
||||
report = provider._process_finding(
|
||||
SAMPLE_TRIVY_NON_CVE_VULNERABILITY,
|
||||
"package.json",
|
||||
"nodejs",
|
||||
)
|
||||
|
||||
expected_url = (
|
||||
"https://github.com/advisories/"
|
||||
f"{SAMPLE_TRIVY_NON_CVE_VULNERABILITY['VulnerabilityID'].upper()}"
|
||||
)
|
||||
assert report.check_metadata.Remediation.Recommendation.Url == expected_url
|
||||
assert report.check_metadata.RelatedUrl == ""
|
||||
assert report.check_metadata.AdditionalURLs == [expected_url]
|
||||
|
||||
@patch("subprocess.run")
|
||||
def test_iac_provider_run_scan_success(self, mock_subprocess):
|
||||
"""Test successful IAC scan with Trivy"""
|
||||
|
||||
@@ -11,6 +11,12 @@ SAMPLE_VULNERABILITY_FINDING = {
|
||||
"Title": "OpenSSL Buffer Overflow",
|
||||
"Description": "A buffer overflow vulnerability in OpenSSL allows remote attackers to execute arbitrary code.",
|
||||
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2024-1234",
|
||||
"References": [
|
||||
"https://avd.aquasec.com/nvd/cve-2024-1234",
|
||||
"https://nvd.nist.gov/vuln/detail/CVE-2024-1234",
|
||||
"https://www.cve.org/CVERecord?id=CVE-2024-1234",
|
||||
"https://security.alpinelinux.org/vuln/CVE-2024-1234",
|
||||
],
|
||||
}
|
||||
|
||||
# Sample secret finding from Trivy
|
||||
@@ -45,6 +51,50 @@ SAMPLE_UNKNOWN_SEVERITY_FINDING = {
|
||||
"Description": "An issue with unknown severity.",
|
||||
}
|
||||
|
||||
SAMPLE_VULNERABILITY_WITHOUT_CVE_ORG_REFERENCE = {
|
||||
"VulnerabilityID": "CVE-2024-5678",
|
||||
"PkgID": "libcrypto3@3.3.2-r0",
|
||||
"PkgName": "libcrypto3",
|
||||
"InstalledVersion": "3.3.2-r0",
|
||||
"FixedVersion": "3.3.2-r1",
|
||||
"Severity": "HIGH",
|
||||
"Title": "OpenSSL advisory without cve.org reference",
|
||||
"Description": "A vulnerability with references but no cve.org reference.",
|
||||
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2024-5678",
|
||||
"References": [
|
||||
"https://avd.aquasec.com/nvd/cve-2024-5678",
|
||||
"https://nvd.nist.gov/vuln/detail/CVE-2024-5678",
|
||||
"https://security.alpinelinux.org/vuln/CVE-2024-5678",
|
||||
],
|
||||
}
|
||||
|
||||
SAMPLE_CVE_WITHOUT_REFERENCES_FINDING = {
|
||||
"VulnerabilityID": "CVE-2024-9012",
|
||||
"PkgID": "busybox@1.36.1-r8",
|
||||
"PkgName": "busybox",
|
||||
"InstalledVersion": "1.36.1-r8",
|
||||
"FixedVersion": "1.36.1-r9",
|
||||
"Severity": "MEDIUM",
|
||||
"Title": "Busybox fallback CVE",
|
||||
"Description": "A vulnerability without explicit references.",
|
||||
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2024-9012",
|
||||
}
|
||||
|
||||
SAMPLE_NON_CVE_VULNERABILITY_FINDING = {
|
||||
"VulnerabilityID": "GHSA-abcd-1234-efgh",
|
||||
"PkgID": "custompkg@0.0.1",
|
||||
"PkgName": "custompkg",
|
||||
"InstalledVersion": "0.0.1",
|
||||
"Severity": "HIGH",
|
||||
"Title": "Non-CVE advisory",
|
||||
"Description": "An advisory without a CVE identifier.",
|
||||
"PrimaryURL": "https://avd.aquasec.com/nvd/ghsa-abcd-1234-efgh",
|
||||
"References": [
|
||||
"https://avd.aquasec.com/nvd/ghsa-abcd-1234-efgh",
|
||||
"https://github.com/advisories/GHSA-abcd-1234-efgh",
|
||||
],
|
||||
}
|
||||
|
||||
# Sample image SHA for testing (first 12 chars of a sha256 digest)
|
||||
SAMPLE_IMAGE_SHA = "c1aabb73d233"
|
||||
SAMPLE_IMAGE_ID = f"sha256:{SAMPLE_IMAGE_SHA}abcdef1234567890"
|
||||
|
||||
@@ -23,11 +23,14 @@ from prowler.providers.image.exceptions.exceptions import (
|
||||
)
|
||||
from prowler.providers.image.image_provider import ImageProvider
|
||||
from tests.providers.image.image_fixtures import (
|
||||
SAMPLE_CVE_WITHOUT_REFERENCES_FINDING,
|
||||
SAMPLE_IMAGE_SHA,
|
||||
SAMPLE_MISCONFIGURATION_FINDING,
|
||||
SAMPLE_NON_CVE_VULNERABILITY_FINDING,
|
||||
SAMPLE_SECRET_FINDING,
|
||||
SAMPLE_UNKNOWN_SEVERITY_FINDING,
|
||||
SAMPLE_VULNERABILITY_FINDING,
|
||||
SAMPLE_VULNERABILITY_WITHOUT_CVE_ORG_REFERENCE,
|
||||
get_empty_trivy_output,
|
||||
get_invalid_trivy_output,
|
||||
get_multi_type_trivy_output,
|
||||
@@ -148,6 +151,77 @@ class TestImageProvider:
|
||||
assert report.check_metadata.Categories == ["vulnerabilities"]
|
||||
assert report.check_metadata.RelatedUrl == ""
|
||||
|
||||
def test_process_finding_vulnerability_prefers_cve_reference_and_filters_aqua(self):
|
||||
"""Test CVE findings use cve.org and exclude Aqua references."""
|
||||
provider = _make_provider()
|
||||
|
||||
report = provider._process_finding(
|
||||
SAMPLE_VULNERABILITY_FINDING,
|
||||
"alpine:3.18",
|
||||
"alpine:3.18 (alpine 3.18.0)",
|
||||
)
|
||||
|
||||
assert (
|
||||
report.check_metadata.Remediation.Recommendation.Url
|
||||
== "https://www.cve.org/CVERecord?id=CVE-2024-1234"
|
||||
)
|
||||
assert report.check_metadata.AdditionalURLs == [
|
||||
"https://www.cve.org/CVERecord?id=CVE-2024-1234"
|
||||
]
|
||||
|
||||
def test_process_finding_vulnerability_builds_cve_org_when_only_nvd_reference(
|
||||
self,
|
||||
):
|
||||
"""Test official CVE URL is built when only NVD is provided."""
|
||||
provider = _make_provider()
|
||||
|
||||
report = provider._process_finding(
|
||||
SAMPLE_VULNERABILITY_WITHOUT_CVE_ORG_REFERENCE,
|
||||
"alpine:3.18",
|
||||
"alpine:3.18 (alpine 3.18.0)",
|
||||
)
|
||||
|
||||
assert (
|
||||
report.check_metadata.Remediation.Recommendation.Url
|
||||
== "https://www.cve.org/CVERecord?id=CVE-2024-5678"
|
||||
)
|
||||
assert report.check_metadata.AdditionalURLs == [
|
||||
"https://www.cve.org/CVERecord?id=CVE-2024-5678"
|
||||
]
|
||||
|
||||
def test_process_finding_vulnerability_builds_cve_org_when_references_missing(self):
|
||||
"""Test CVE URL is built from VulnerabilityID when references are absent."""
|
||||
provider = _make_provider()
|
||||
|
||||
report = provider._process_finding(
|
||||
SAMPLE_CVE_WITHOUT_REFERENCES_FINDING,
|
||||
"alpine:3.18",
|
||||
"alpine:3.18 (alpine 3.18.0)",
|
||||
)
|
||||
|
||||
assert (
|
||||
report.check_metadata.Remediation.Recommendation.Url
|
||||
== "https://www.cve.org/CVERecord?id=CVE-2024-9012"
|
||||
)
|
||||
assert report.check_metadata.AdditionalURLs == [
|
||||
"https://www.cve.org/CVERecord?id=CVE-2024-9012"
|
||||
]
|
||||
|
||||
def test_process_finding_non_cve_vulnerability_does_not_fallback_to_aqua(self):
|
||||
"""Test non-CVE vulnerabilities do not keep Aqua links."""
|
||||
provider = _make_provider()
|
||||
|
||||
report = provider._process_finding(
|
||||
SAMPLE_NON_CVE_VULNERABILITY_FINDING,
|
||||
"alpine:3.18",
|
||||
"alpine:3.18 (alpine 3.18.0)",
|
||||
)
|
||||
|
||||
assert report.check_metadata.Remediation.Recommendation.Url == ""
|
||||
assert report.check_metadata.AdditionalURLs == [
|
||||
"https://github.com/advisories/GHSA-abcd-1234-efgh"
|
||||
]
|
||||
|
||||
def test_process_finding_secret(self):
|
||||
"""Test processing a secret finding (identified by RuleID)."""
|
||||
provider = _make_provider()
|
||||
|
||||
Reference in New Issue
Block a user