diff --git a/pkg/process/v5/transformers/nvd/test-fixtures/cve-2020-10729.json b/pkg/process/v5/transformers/nvd/test-fixtures/cve-2020-10729.json new file mode 100644 index 00000000..89677497 --- /dev/null +++ b/pkg/process/v5/transformers/nvd/test-fixtures/cve-2020-10729.json @@ -0,0 +1,166 @@ +{ + "cve": { + "id": "CVE-2020-10729", + "sourceIdentifier": "secalert@redhat.com", + "published": "2021-05-27T19:15:07.880", + "lastModified": "2021-12-10T19:57:06.357", + "vulnStatus": "Analyzed", + "descriptions": [ + { + "lang": "en", + "value": "A flaw was found in the use of insufficiently random values in Ansible. Two random password lookups of the same length generate the equal value as the template caching action for the same file since no re-evaluation happens. The highest threat from this vulnerability would be that all passwords are exposed at once for the file. This flaw affects Ansible Engine versions before 2.9.6." + }, + { + "lang": "es", + "value": "Se encontró un fallo en el uso de valores insuficientemente aleatorios en Ansible. Dos búsquedas de contraseñas aleatorias de la misma longitud generan el mismo valor que la acción de almacenamiento en caché de la plantilla para el mismo archivo, ya que no se realiza una reevaluación. La mayor amenaza de esta vulnerabilidad sería que todas las contraseñas estén expuestas a la vez para el archivo. Este fallo afecta a Ansible Engine versiones anteriores a 2.9.6" + } + ], + "metrics": { + "cvssMetricV31": [ + { + "source": "nvd@nist.gov", + "type": "Primary", + "cvssData": { + "version": "3.1", + "vectorString": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N", + "attackVector": "LOCAL", + "attackComplexity": "LOW", + "privilegesRequired": "LOW", + "userInteraction": "NONE", + "scope": "UNCHANGED", + "confidentialityImpact": "HIGH", + "integrityImpact": "NONE", + "availabilityImpact": "NONE", + "baseScore": 5.5, + "baseSeverity": "MEDIUM" + }, + "exploitabilityScore": 1.8, + "impactScore": 3.6 + } + ], + "cvssMetricV2": [ + { + "source": "nvd@nist.gov", + "type": "Primary", + "cvssData": { + "version": "2.0", + "vectorString": "AV:L/AC:L/Au:N/C:P/I:N/A:N", + "accessVector": "LOCAL", + "accessComplexity": "LOW", + "authentication": "NONE", + "confidentialityImpact": "PARTIAL", + "integrityImpact": "NONE", + "availabilityImpact": "NONE", + "baseScore": 2.1 + }, + "baseSeverity": "LOW", + "exploitabilityScore": 3.9, + "impactScore": 2.9, + "acInsufInfo": false, + "obtainAllPrivilege": false, + "obtainUserPrivilege": false, + "obtainOtherPrivilege": false, + "userInteractionRequired": false + } + ] + }, + "weaknesses": [ + { + "source": "nvd@nist.gov", + "type": "Primary", + "description": [ + { + "lang": "en", + "value": "CWE-330" + } + ] + }, + { + "source": "secalert@redhat.com", + "type": "Secondary", + "description": [ + { + "lang": "en", + "value": "CWE-330" + } + ] + } + ], + "configurations": [ + { + "operator": "AND", + "nodes": [ + { + "operator": "OR", + "negate": false, + "cpeMatch": [ + { + "vulnerable": true, + "criteria": "cpe:2.3:a:redhat:ansible_engine:*:*:*:*:*:*:*:*", + "versionEndExcluding": "2.9.6", + "matchCriteriaId": "EDFA8005-6FBE-4032-A499-608B7FA34F56" + } + ] + }, + { + "operator": "OR", + "negate": false, + "cpeMatch": [ + { + "vulnerable": false, + "criteria": "cpe:2.3:o:redhat:enterprise_linux:7.0:*:*:*:*:*:*:*", + "matchCriteriaId": "142AD0DD-4CF3-4D74-9442-459CE3347E3A" + }, + { + "vulnerable": false, + "criteria": "cpe:2.3:o:redhat:enterprise_linux:8.0:*:*:*:*:*:*:*", + "matchCriteriaId": "F4CFF558-3C47-480D-A2F0-BABF26042943" + } + ] + } + ] + }, + { + "nodes": [ + { + "operator": "OR", + "negate": false, + "cpeMatch": [ + { + "vulnerable": true, + "criteria": "cpe:2.3:o:debian:debian_linux:10.0:*:*:*:*:*:*:*", + "matchCriteriaId": "07B237A9-69A3-4A9C-9DA0-4E06BD37AE73" + } + ] + } + ] + } + ], + "references": [ + { + "url": "https://bugzilla.redhat.com/show_bug.cgi?id=1831089", + "source": "secalert@redhat.com", + "tags": [ + "Issue Tracking", + "Vendor Advisory" + ] + }, + { + "url": "https://github.com/ansible/ansible/issues/34144", + "source": "secalert@redhat.com", + "tags": [ + "Exploit", + "Issue Tracking", + "Third Party Advisory" + ] + }, + { + "url": "https://www.debian.org/security/2021/dsa-4950", + "source": "secalert@redhat.com", + "tags": [ + "Third Party Advisory" + ] + } + ] + } +} diff --git a/pkg/process/v5/transformers/nvd/test-fixtures/cve-2022-0543.json b/pkg/process/v5/transformers/nvd/test-fixtures/cve-2022-0543.json new file mode 100644 index 00000000..09d5c187 --- /dev/null +++ b/pkg/process/v5/transformers/nvd/test-fixtures/cve-2022-0543.json @@ -0,0 +1,183 @@ +{ + "cve": { + "id": "CVE-2022-0543", + "sourceIdentifier": "security@debian.org", + "published": "2022-02-18T20:15:17.583", + "lastModified": "2023-09-29T15:55:24.533", + "vulnStatus": "Analyzed", + "cisaExploitAdd": "2022-03-28", + "cisaActionDue": "2022-04-18", + "cisaRequiredAction": "Apply updates per vendor instructions.", + "cisaVulnerabilityName": "Debian-specific Redis Server Lua Sandbox Escape Vulnerability", + "descriptions": [ + { + "lang": "en", + "value": "It was discovered, that redis, a persistent key-value database, due to a packaging issue, is prone to a (Debian-specific) Lua sandbox escape, which could result in remote code execution." + }, + { + "lang": "es", + "value": "Se ha detectado que redis, una base de datos persistente de valores clave, debido a un problema de empaquetado, es propenso a un escape del sandbox de Lua (específico de Debian), que podría resultar en una ejecución de código remota" + } + ], + "metrics": { + "cvssMetricV31": [ + { + "source": "nvd@nist.gov", + "type": "Primary", + "cvssData": { + "version": "3.1", + "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H", + "attackVector": "NETWORK", + "attackComplexity": "LOW", + "privilegesRequired": "NONE", + "userInteraction": "NONE", + "scope": "CHANGED", + "confidentialityImpact": "HIGH", + "integrityImpact": "HIGH", + "availabilityImpact": "HIGH", + "baseScore": 10, + "baseSeverity": "CRITICAL" + }, + "exploitabilityScore": 3.9, + "impactScore": 6 + } + ], + "cvssMetricV2": [ + { + "source": "nvd@nist.gov", + "type": "Primary", + "cvssData": { + "version": "2.0", + "vectorString": "AV:N/AC:L/Au:N/C:C/I:C/A:C", + "accessVector": "NETWORK", + "accessComplexity": "LOW", + "authentication": "NONE", + "confidentialityImpact": "COMPLETE", + "integrityImpact": "COMPLETE", + "availabilityImpact": "COMPLETE", + "baseScore": 10 + }, + "baseSeverity": "HIGH", + "exploitabilityScore": 10, + "impactScore": 10, + "acInsufInfo": false, + "obtainAllPrivilege": false, + "obtainUserPrivilege": false, + "obtainOtherPrivilege": false, + "userInteractionRequired": false + } + ] + }, + "weaknesses": [ + { + "source": "nvd@nist.gov", + "type": "Primary", + "description": [ + { + "lang": "en", + "value": "CWE-862" + } + ] + } + ], + "configurations": [ + { + "operator": "AND", + "nodes": [ + { + "operator": "OR", + "negate": false, + "cpeMatch": [ + { + "vulnerable": true, + "criteria": "cpe:2.3:a:redis:redis:-:*:*:*:*:*:*:*", + "matchCriteriaId": "5EBE5E1C-C881-4A76-9E36-4FB7C48427E6" + } + ] + }, + { + "operator": "OR", + "negate": false, + "cpeMatch": [ + { + "vulnerable": false, + "criteria": "cpe:2.3:o:canonical:ubuntu_linux:20.04:*:*:*:lts:*:*:*", + "matchCriteriaId": "902B8056-9E37-443B-8905-8AA93E2447FB" + }, + { + "vulnerable": false, + "criteria": "cpe:2.3:o:canonical:ubuntu_linux:21.10:*:*:*:-:*:*:*", + "matchCriteriaId": "3D94DA3B-FA74-4526-A0A0-A872684598C6" + }, + { + "vulnerable": false, + "criteria": "cpe:2.3:o:debian:debian_linux:9.0:*:*:*:*:*:*:*", + "matchCriteriaId": "DEECE5FC-CACF-4496-A3E7-164736409252" + }, + { + "vulnerable": false, + "criteria": "cpe:2.3:o:debian:debian_linux:10.0:*:*:*:*:*:*:*", + "matchCriteriaId": "07B237A9-69A3-4A9C-9DA0-4E06BD37AE73" + }, + { + "vulnerable": false, + "criteria": "cpe:2.3:o:debian:debian_linux:11.0:*:*:*:*:*:*:*", + "matchCriteriaId": "FA6FEEC2-9F11-4643-8827-749718254FED" + } + ] + } + ] + } + ], + "references": [ + { + "url": "http://packetstormsecurity.com/files/166885/Redis-Lua-Sandbox-Escape.html", + "source": "security@debian.org", + "tags": [ + "Exploit", + "Third Party Advisory", + "VDB Entry" + ] + }, + { + "url": "https://bugs.debian.org/1005787", + "source": "security@debian.org", + "tags": [ + "Issue Tracking", + "Patch", + "Third Party Advisory" + ] + }, + { + "url": "https://lists.debian.org/debian-security-announce/2022/msg00048.html", + "source": "security@debian.org", + "tags": [ + "Mailing List", + "Third Party Advisory" + ] + }, + { + "url": "https://security.netapp.com/advisory/ntap-20220331-0004/", + "source": "security@debian.org", + "tags": [ + "Third Party Advisory" + ] + }, + { + "url": "https://www.debian.org/security/2022/dsa-5081", + "source": "security@debian.org", + "tags": [ + "Mailing List", + "Third Party Advisory" + ] + }, + { + "url": "https://www.ubercomp.com/posts/2022-01-20_redis_on_debian_rce", + "source": "security@debian.org", + "tags": [ + "Third Party Advisory" + ] + } + ] + } +} diff --git a/pkg/process/v5/transformers/nvd/test-fixtures/multiple-platforms-with-application-cpe.json b/pkg/process/v5/transformers/nvd/test-fixtures/multiple-platforms-with-application-cpe.json new file mode 100644 index 00000000..40c0fa56 --- /dev/null +++ b/pkg/process/v5/transformers/nvd/test-fixtures/multiple-platforms-with-application-cpe.json @@ -0,0 +1,142 @@ +{ + "cve": { + "id": "CVE-2023-38733", + "sourceIdentifier": "psirt@us.ibm.com", + "published": "2023-08-22T22:15:08.460", + "lastModified": "2023-08-26T02:25:42.957", + "vulnStatus": "Analyzed", + "descriptions": [ + { + "lang": "en", + "value": "\nIBM Robotic Process Automation 21.0.0 through 21.0.7.1 and 23.0.0 through 23.0.1 server could allow an authenticated user to view sensitive information from installation logs. IBM X-Force Id: 262293.\n\n" + } + ], + "metrics": { + "cvssMetricV31": [ + { + "source": "nvd@nist.gov", + "type": "Primary", + "cvssData": { + "version": "3.1", + "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N", + "attackVector": "NETWORK", + "attackComplexity": "LOW", + "privilegesRequired": "LOW", + "userInteraction": "NONE", + "scope": "UNCHANGED", + "confidentialityImpact": "LOW", + "integrityImpact": "NONE", + "availabilityImpact": "NONE", + "baseScore": 4.3, + "baseSeverity": "MEDIUM" + }, + "exploitabilityScore": 2.8, + "impactScore": 1.4 + }, + { + "source": "psirt@us.ibm.com", + "type": "Secondary", + "cvssData": { + "version": "3.1", + "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N", + "attackVector": "NETWORK", + "attackComplexity": "LOW", + "privilegesRequired": "LOW", + "userInteraction": "NONE", + "scope": "UNCHANGED", + "confidentialityImpact": "LOW", + "integrityImpact": "NONE", + "availabilityImpact": "NONE", + "baseScore": 4.3, + "baseSeverity": "MEDIUM" + }, + "exploitabilityScore": 2.8, + "impactScore": 1.4 + } + ] + }, + "weaknesses": [ + { + "source": "nvd@nist.gov", + "type": "Primary", + "description": [ + { + "lang": "en", + "value": "CWE-532" + } + ] + }, + { + "source": "psirt@us.ibm.com", + "type": "Secondary", + "description": [ + { + "lang": "en", + "value": "CWE-532" + } + ] + } + ], + "configurations": [ + { + "operator": "AND", + "nodes": [ + { + "operator": "OR", + "negate": false, + "cpeMatch": [ + { + "vulnerable": true, + "criteria": "cpe:2.3:a:ibm:robotic_process_automation:*:*:*:*:*:*:*:*", + "versionStartIncluding": "21.0.0", + "versionEndIncluding": "21.0.7.3", + "matchCriteriaId": "DDF503DD-23DC-4B22-8873-BE94BF0F1CD1" + }, + { + "vulnerable": true, + "criteria": "cpe:2.3:a:ibm:robotic_process_automation:*:*:*:*:*:*:*:*", + "versionStartIncluding": "23.0.0", + "versionEndIncluding": "23.0.3", + "matchCriteriaId": "F513AA2B-F457-408B-8D5F-EBE657439000" + } + ] + }, + { + "operator": "OR", + "negate": false, + "cpeMatch": [ + { + "vulnerable": false, + "criteria": "cpe:2.3:a:redhat:openshift:-:*:*:*:*:*:*:*", + "matchCriteriaId": "F08E234C-BDCF-4B41-87B9-96BD5578CBBF" + }, + { + "vulnerable": false, + "criteria": "cpe:2.3:o:microsoft:windows:-:*:*:*:*:*:*:*", + "matchCriteriaId": "A2572D17-1DE6-457B-99CC-64AFD54487EA" + } + ] + } + ] + } + ], + "references": [ + { + "url": "https://exchange.xforce.ibmcloud.com/vulnerabilities/262293", + "source": "psirt@us.ibm.com", + "tags": [ + "VDB Entry", + "Vendor Advisory" + ] + }, + { + "url": "https://www.ibm.com/support/pages/node/7028223", + "source": "psirt@us.ibm.com", + "tags": [ + "Patch", + "Vendor Advisory" + ] + } + ] + } +} diff --git a/pkg/process/v5/transformers/nvd/transform.go b/pkg/process/v5/transformers/nvd/transform.go index c77ffd35..72d92bbd 100644 --- a/pkg/process/v5/transformers/nvd/transform.go +++ b/pkg/process/v5/transformers/nvd/transform.go @@ -46,10 +46,10 @@ func Transform(vulnerability unmarshal.NVDVulnerability) ([]data.Entry, error) { cpes.Add(grypeNamespace.Resolver().Normalize(m.Criteria)) } - if p.PlatformCPE != nil { + if p.PlatformCPE != "" { qualifiers = []qualifier.Qualifier{platformcpe.Qualifier{ Kind: "platform-cpe", - CPE: *p.PlatformCPE, + CPE: p.PlatformCPE, }} } diff --git a/pkg/process/v5/transformers/nvd/transform_test.go b/pkg/process/v5/transformers/nvd/transform_test.go index f7f03621..493d10b7 100644 --- a/pkg/process/v5/transformers/nvd/transform_test.go +++ b/pkg/process/v5/transformers/nvd/transform_test.go @@ -324,6 +324,269 @@ func TestParseAllNVDVulnerabilityEntries(t *testing.T) { }, }, }, + { + name: "CVE-2022-0543 multiple platforms", + numEntries: 1, + fixture: "test-fixtures/cve-2022-0543.json", + vulns: []grypeDB.Vulnerability{ + { + ID: "CVE-2022-0543", + PackageName: "redis", + Namespace: "nvd:cpe", + PackageQualifiers: []qualifier.Qualifier{platformcpe.Qualifier{ + Kind: "platform-cpe", + CPE: "cpe:2.3:o:canonical:ubuntu_linux:20.04:*:*:*:lts:*:*:*", + }}, + VersionConstraint: "", + VersionFormat: "unknown", + CPEs: []string{"cpe:2.3:a:redis:redis:-:*:*:*:*:*:*:*"}, + RelatedVulnerabilities: nil, + Fix: grypeDB.Fix{State: "unknown"}, + Advisories: nil, + }, + { + ID: "CVE-2022-0543", + PackageName: "redis", + Namespace: "nvd:cpe", + PackageQualifiers: []qualifier.Qualifier{platformcpe.Qualifier{ + Kind: "platform-cpe", + CPE: "cpe:2.3:o:canonical:ubuntu_linux:21.10:*:*:*:-:*:*:*", + }}, + VersionConstraint: "", + VersionFormat: "unknown", + CPEs: []string{"cpe:2.3:a:redis:redis:-:*:*:*:*:*:*:*"}, + RelatedVulnerabilities: nil, + Fix: grypeDB.Fix{State: "unknown"}, + Advisories: nil, + }, + { + ID: "CVE-2022-0543", + PackageName: "redis", + Namespace: "nvd:cpe", + PackageQualifiers: []qualifier.Qualifier{platformcpe.Qualifier{ + Kind: "platform-cpe", + CPE: "cpe:2.3:o:debian:debian_linux:10.0:*:*:*:*:*:*:*", + }}, + VersionConstraint: "", + VersionFormat: "unknown", + CPEs: []string{"cpe:2.3:a:redis:redis:-:*:*:*:*:*:*:*"}, + RelatedVulnerabilities: nil, + Fix: grypeDB.Fix{State: "unknown"}, + Advisories: nil, + }, + { + ID: "CVE-2022-0543", + PackageName: "redis", + Namespace: "nvd:cpe", + PackageQualifiers: []qualifier.Qualifier{platformcpe.Qualifier{ + Kind: "platform-cpe", + CPE: "cpe:2.3:o:debian:debian_linux:11.0:*:*:*:*:*:*:*", + }}, + VersionConstraint: "", + VersionFormat: "unknown", + CPEs: []string{"cpe:2.3:a:redis:redis:-:*:*:*:*:*:*:*"}, + RelatedVulnerabilities: nil, + Fix: grypeDB.Fix{State: "unknown"}, + Advisories: nil, + }, + { + ID: "CVE-2022-0543", + PackageName: "redis", + Namespace: "nvd:cpe", + PackageQualifiers: []qualifier.Qualifier{platformcpe.Qualifier{ + Kind: "platform-cpe", + CPE: "cpe:2.3:o:debian:debian_linux:9.0:*:*:*:*:*:*:*", + }}, + VersionConstraint: "", + VersionFormat: "unknown", + CPEs: []string{"cpe:2.3:a:redis:redis:-:*:*:*:*:*:*:*"}, + RelatedVulnerabilities: nil, + Fix: grypeDB.Fix{State: "unknown"}, + Advisories: nil, + }, + }, + metadata: grypeDB.VulnerabilityMetadata{ + ID: "CVE-2022-0543", + Namespace: "nvd:cpe", + DataSource: "https://nvd.nist.gov/vuln/detail/CVE-2022-0543", + RecordSource: "nvdv2:nvdv2:cves", + Severity: "Critical", + URLs: []string{ + "http://packetstormsecurity.com/files/166885/Redis-Lua-Sandbox-Escape.html", + "https://bugs.debian.org/1005787", + "https://lists.debian.org/debian-security-announce/2022/msg00048.html", + "https://security.netapp.com/advisory/ntap-20220331-0004/", + "https://www.debian.org/security/2022/dsa-5081", + "https://www.ubercomp.com/posts/2022-01-20_redis_on_debian_rce", + }, + Description: "It was discovered, that redis, a persistent key-value database, due to a packaging issue, is prone to a (Debian-specific) Lua sandbox escape, which could result in remote code execution.", + Cvss: []grypeDB.Cvss{ + { + VendorMetadata: nil, + Metrics: grypeDB.NewCvssMetrics(10, 10, 10), + Vector: "AV:N/AC:L/Au:N/C:C/I:C/A:C", + Version: "2.0", + Source: "nvd@nist.gov", + Type: "Primary", + }, + { + VendorMetadata: nil, + Metrics: grypeDB.NewCvssMetrics(10, 3.9, 6), + Vector: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H", + Version: "3.1", + Source: "nvd@nist.gov", + Type: "Primary", + }, + }, + }, + }, + { + name: "CVE-2020-10729 multiple platforms omitted top level config", + numEntries: 1, + fixture: "test-fixtures/cve-2020-10729.json", + vulns: []grypeDB.Vulnerability{ + { + ID: "CVE-2020-10729", + PackageName: "ansible_engine", + Namespace: "nvd:cpe", + PackageQualifiers: []qualifier.Qualifier{platformcpe.Qualifier{ + Kind: "platform-cpe", + CPE: "cpe:2.3:o:redhat:enterprise_linux:7.0:*:*:*:*:*:*:*", + }}, + VersionConstraint: "< 2.9.6", + VersionFormat: "unknown", + CPEs: []string{"cpe:2.3:a:redhat:ansible_engine:*:*:*:*:*:*:*:*"}, + RelatedVulnerabilities: nil, + Fix: grypeDB.Fix{State: "unknown"}, + Advisories: nil, + }, + { + ID: "CVE-2020-10729", + PackageName: "ansible_engine", + Namespace: "nvd:cpe", + PackageQualifiers: []qualifier.Qualifier{platformcpe.Qualifier{ + Kind: "platform-cpe", + CPE: "cpe:2.3:o:redhat:enterprise_linux:8.0:*:*:*:*:*:*:*", + }}, + VersionConstraint: "< 2.9.6", + VersionFormat: "unknown", + CPEs: []string{"cpe:2.3:a:redhat:ansible_engine:*:*:*:*:*:*:*:*"}, + RelatedVulnerabilities: nil, + Fix: grypeDB.Fix{State: "unknown"}, + Advisories: nil, + }, + }, + metadata: grypeDB.VulnerabilityMetadata{ + ID: "CVE-2020-10729", + Namespace: "nvd:cpe", + DataSource: "https://nvd.nist.gov/vuln/detail/CVE-2020-10729", + RecordSource: "nvdv2:nvdv2:cves", + Severity: "Medium", + URLs: []string{ + "https://bugzilla.redhat.com/show_bug.cgi?id=1831089", + "https://github.com/ansible/ansible/issues/34144", + "https://www.debian.org/security/2021/dsa-4950", + }, + Description: "A flaw was found in the use of insufficiently random values in Ansible. Two random password lookups of the same length generate the equal value as the template caching action for the same file since no re-evaluation happens. The highest threat from this vulnerability would be that all passwords are exposed at once for the file. This flaw affects Ansible Engine versions before 2.9.6.", + Cvss: []grypeDB.Cvss{ + { + VendorMetadata: nil, + Metrics: grypeDB.NewCvssMetrics( + 2.1, + 3.9, + 2.9, + ), + Vector: "AV:L/AC:L/Au:N/C:P/I:N/A:N", + Version: "2.0", + Source: "nvd@nist.gov", + Type: "Primary", + }, + { + VendorMetadata: nil, + Metrics: grypeDB.NewCvssMetrics( + 5.5, + 1.8, + 3.6, + ), + Vector: "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N", + Version: "3.1", + Source: "nvd@nist.gov", + Type: "Primary", + }, + }, + }, + }, + { + name: "multiple platforms some are application", + numEntries: 2, + fixture: "test-fixtures/multiple-platforms-with-application-cpe.json", + vulns: []grypeDB.Vulnerability{ + { + ID: "CVE-2023-38733", + PackageName: "robotic_process_automation", + Namespace: "nvd:cpe", + PackageQualifiers: []qualifier.Qualifier{platformcpe.Qualifier{ + Kind: "platform-cpe", + CPE: "cpe:2.3:a:redhat:openshift:-:*:*:*:*:*:*:*", + }}, + VersionConstraint: ">= 21.0.0, <= 21.0.7.3 || >= 23.0.0, <= 23.0.3", + VersionFormat: "unknown", + CPEs: []string{"cpe:2.3:a:ibm:robotic_process_automation:*:*:*:*:*:*:*:*"}, + RelatedVulnerabilities: nil, + Fix: grypeDB.Fix{ + State: "unknown", + }, + Advisories: nil, + }, + { + ID: "CVE-2023-38733", + PackageName: "robotic_process_automation", + Namespace: "nvd:cpe", + PackageQualifiers: []qualifier.Qualifier{platformcpe.Qualifier{ + Kind: "platform-cpe", + CPE: "cpe:2.3:o:microsoft:windows:-:*:*:*:*:*:*:*", + }}, + VersionConstraint: ">= 21.0.0, <= 21.0.7.3 || >= 23.0.0, <= 23.0.3", + VersionFormat: "unknown", + CPEs: []string{"cpe:2.3:a:ibm:robotic_process_automation:*:*:*:*:*:*:*:*"}, + RelatedVulnerabilities: nil, + Fix: grypeDB.Fix{ + State: "unknown", + }, + Advisories: nil, + }, + }, + metadata: grypeDB.VulnerabilityMetadata{ + ID: "CVE-2023-38733", + Namespace: "nvd:cpe", + DataSource: "https://nvd.nist.gov/vuln/detail/CVE-2023-38733", + RecordSource: "nvdv2:nvdv2:cves", + Severity: "Medium", + URLs: []string{ + "https://exchange.xforce.ibmcloud.com/vulnerabilities/262293", + "https://www.ibm.com/support/pages/node/7028223", + }, + Description: "\nIBM Robotic Process Automation 21.0.0 through 21.0.7.1 and 23.0.0 through 23.0.1 server could allow an authenticated user to view sensitive information from installation logs. IBM X-Force Id: 262293.\n\n", + Cvss: []grypeDB.Cvss{ + { + VendorMetadata: nil, + Metrics: grypeDB.NewCvssMetrics(4.3, 2.8, 1.4), + Vector: "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N", + Version: "3.1", + Source: "nvd@nist.gov", + Type: "Primary", + }, + { + VendorMetadata: nil, + Metrics: grypeDB.NewCvssMetrics(4.3, 2.8, 1.4), + Vector: "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N", + Version: "3.1", + Source: "psirt@us.ibm.com", + Type: "Secondary", + }, + }, + }, + }, } for _, test := range tests { diff --git a/pkg/process/v5/transformers/nvd/unique_pkg.go b/pkg/process/v5/transformers/nvd/unique_pkg.go index 46612f22..7e01c019 100644 --- a/pkg/process/v5/transformers/nvd/unique_pkg.go +++ b/pkg/process/v5/transformers/nvd/unique_pkg.go @@ -20,18 +20,18 @@ type pkgCandidate struct { Product string Vendor string TargetSoftware string - PlatformCPE *string + PlatformCPE string } func (p pkgCandidate) String() string { - if p.PlatformCPE == nil { + if p.PlatformCPE == "" { return fmt.Sprintf("%s|%s|%s", p.Vendor, p.Product, p.TargetSoftware) } - return fmt.Sprintf("%s|%s|%s|%s", p.Vendor, p.Product, p.TargetSoftware, *p.PlatformCPE) + return fmt.Sprintf("%s|%s|%s|%s", p.Vendor, p.Product, p.TargetSoftware, p.PlatformCPE) } -func newPkgCandidate(match nvd.CpeMatch, platformCPE *string) (*pkgCandidate, error) { +func newPkgCandidate(match nvd.CpeMatch, platformCPE string) (*pkgCandidate, error) { // we are only interested in packages that are vulnerable (not related to secondary match conditioning) if !match.Vulnerable { return nil, nil @@ -63,14 +63,101 @@ func findUniquePkgs(cfgs ...nvd.Configuration) uniquePkgTracker { return set } -func determinePlatformCPEAndNodes(c nvd.Configuration) (*string, []nvd.Node) { - var platformCPE *string +func platformPackageCandidates(set uniquePkgTracker, c nvd.Configuration) bool { + nodes := c.Nodes + /* + Turn a configuration like this: + (AND + (OR (cpe:2.3:a:redis:...whatever) (cpe:2.3.:something:...whatever) + (OR (cpe:2.3:o:debian:9....) (cpe:2.3:o:ubuntu:22..)) + ) + Into a configuration like this: + (OR + (AND (cpe:2.3:a:redis:...whatever) (cpe:2.3:o:debian:9...)) + (AND (cpe:2.3:a:redis:...whatever) (cpe:2.3:o:ubuntu:22...)) + (AND (cpe:2.3:a:something:...whatever) (cpe:2.3:o:debian:9...)) + (AND (cpe:2.3:a:something:...whatever) (cpe:2.3:o:ubuntu:22...)) + ) + Because in schema v5, rows in Grype DB can only have zero or one platform CPE + constraint. + */ + if len(nodes) != 2 || c.Operator == nil || *c.Operator != nvd.And { + return false + } + var platformsNode nvd.Node + var applicationNode nvd.Node + for _, n := range nodes { + if anyHardwareCPEPresent(n) { + return false + } + if allCPEsVulnerable(n) { + applicationNode = n + } + if noCPEsVulnerable(n) { + platformsNode = n + } + } + if platformsNode.Operator != nvd.Or || len(platformsNode.CpeMatch) < 2 { + return false + } + if applicationNode.Operator != nvd.Or { + return false + } + result := false + matches := platformsNode.CpeMatch + for _, application := range applicationNode.CpeMatch { + for _, maybePlatform := range matches { + platform := maybePlatform.Criteria + candidate, err := newPkgCandidate(application, platform) + if err != nil { + log.Debugf("unable processing uniquePkg with multiple platforms: %v", err) + continue + } + if candidate == nil { + continue + } + set.Add(*candidate, application) + result = true + } + } + return result +} + +func anyHardwareCPEPresent(n nvd.Node) bool { + for _, c := range n.CpeMatch { + parts := strings.Split(c.Criteria, ":") + if len(parts) < 3 || parts[2] == "h" { + return true + } + } + return false +} + +func allCPEsVulnerable(node nvd.Node) bool { + for _, c := range node.CpeMatch { + if !c.Vulnerable { + return false + } + } + return true +} + +func noCPEsVulnerable(node nvd.Node) bool { + for _, c := range node.CpeMatch { + if c.Vulnerable { + return false + } + } + return true +} + +func determinePlatformCPEAndNodes(c nvd.Configuration) (string, []nvd.Node) { + var platformCPE string nodes := c.Nodes - // Only retrieve a platform CPE in very specific cases if len(nodes) == 2 && c.Operator != nil && *c.Operator == nvd.And { if len(nodes[1].CpeMatch) == 1 && !nodes[1].CpeMatch[0].Vulnerable { - platformCPE = &nodes[1].CpeMatch[0].Criteria + platformCPE = nodes[1].CpeMatch[0].Criteria nodes = []nvd.Node{nodes[0]} } } @@ -83,8 +170,11 @@ func _findUniquePkgs(set uniquePkgTracker, c nvd.Configuration) { return } - platformCPE, nodes := determinePlatformCPEAndNodes(c) + if platformPackageCandidates(set, c) { + return + } + platformCPE, nodes := determinePlatformCPEAndNodes(c) for _, node := range nodes { for _, match := range node.CpeMatch { candidate, err := newPkgCandidate(match, platformCPE) diff --git a/pkg/process/v5/transformers/nvd/unique_pkg_test.go b/pkg/process/v5/transformers/nvd/unique_pkg_test.go index beb050fc..a684ef90 100644 --- a/pkg/process/v5/transformers/nvd/unique_pkg_test.go +++ b/pkg/process/v5/transformers/nvd/unique_pkg_test.go @@ -3,7 +3,10 @@ package nvd import ( "testing" + "github.com/google/go-cmp/cmp" "github.com/sergi/go-diff/diffmatchpatch" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/anchore/grype-db/pkg/provider/unmarshal/nvd" ) @@ -20,6 +23,7 @@ func TestFindUniquePkgs(t *testing.T) { tests := []struct { name string nodes []nvd.Node + operator *nvd.Operator expected uniquePkgTracker }{ { @@ -226,11 +230,91 @@ func TestFindUniquePkgs(t *testing.T) { }, }), }, + { + name: "cpe with multiple platforms", + operator: opRef(nvd.And), + nodes: []nvd.Node{ + { + Negate: boolRef(false), + Operator: nvd.Or, + CpeMatch: []nvd.CpeMatch{ + { + Criteria: "cpe:2.3:o:canonical:ubuntu_linux:20.04:*:*:*:lts:*:*:*", + MatchCriteriaID: "902B8056-9E37-443B-8905-8AA93E2447FB", + Vulnerable: false, + }, + { + Criteria: "cpe:2.3:o:canonical:ubuntu_linux:21.10:*:*:*:-:*:*:*", + MatchCriteriaID: "3D94DA3B-FA74-4526-A0A0-A872684598C6", + Vulnerable: false, + }, + { + Criteria: "cpe:2.3:o:debian:debian_linux:9.0:*:*:*:*:*:*:*", + MatchCriteriaID: "DEECE5FC-CACF-4496-A3E7-164736409252", + Vulnerable: false, + }, + { + Criteria: "cpe:2.3:o:debian:debian_linux:10.0:*:*:*:*:*:*:*", + MatchCriteriaID: "07B237A9-69A3-4A9C-9DA0-4E06BD37AE73", + Vulnerable: false, + }, + { + Criteria: "cpe:2.3:o:debian:debian_linux:11.0:*:*:*:*:*:*:*", + MatchCriteriaID: "FA6FEEC2-9F11-4643-8827-749718254FED", + Vulnerable: false, + }, + }, + }, + { + Negate: boolRef(false), + Operator: nvd.Or, + CpeMatch: []nvd.CpeMatch{ + { + Criteria: "cpe:2.3:a:redis:redis:-:*:*:*:*:*:*:*", + MatchCriteriaID: "5EBE5E1C-C881-4A76-9E36-4FB7C48427E6", + Vulnerable: true, + }, + }, + }, + }, + expected: newUniquePkgTrackerFromSlice([]pkgCandidate{ + { + Product: "redis", + Vendor: "redis", + TargetSoftware: ANY, + PlatformCPE: "cpe:2.3:o:canonical:ubuntu_linux:20.04:*:*:*:lts:*:*:*", + }, + { + Product: "redis", + Vendor: "redis", + TargetSoftware: ANY, + PlatformCPE: "cpe:2.3:o:canonical:ubuntu_linux:21.10:*:*:*:-:*:*:*", + }, + { + Product: "redis", + Vendor: "redis", + TargetSoftware: ANY, + PlatformCPE: "cpe:2.3:o:debian:debian_linux:9.0:*:*:*:*:*:*:*", + }, + { + Product: "redis", + Vendor: "redis", + TargetSoftware: ANY, + PlatformCPE: "cpe:2.3:o:debian:debian_linux:10.0:*:*:*:*:*:*:*", + }, + { + Product: "redis", + Vendor: "redis", + TargetSoftware: ANY, + PlatformCPE: "cpe:2.3:o:debian:debian_linux:11.0:*:*:*:*:*:*:*", + }, + }), + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - actual := findUniquePkgs(nvd.Configuration{Nodes: test.nodes}) + actual := findUniquePkgs(nvd.Configuration{Nodes: test.nodes, Operator: test.operator}) missing, extra := test.expected.Diff(actual) if len(missing) != 0 { for _, c := range missing { @@ -245,7 +329,6 @@ func TestFindUniquePkgs(t *testing.T) { } }) } - } func strRef(s string) *string { @@ -349,5 +432,205 @@ func TestBuildConstraints(t *testing.T) { } }) } +} + +func Test_UniquePackageTrackerHandlesOnlyPlatformDiff(t *testing.T) { + candidates := []pkgCandidate{ + { + Product: "redis", + Vendor: "redis", + TargetSoftware: ANY, + PlatformCPE: "cpe:2.3:o:canonical:ubuntu_linux:20.04:*:*:*:lts:*:*:*", + }, + { + Product: "redis", + Vendor: "redis", + TargetSoftware: ANY, + PlatformCPE: "cpe:2.3:o:canonical:ubuntu_linux:21.10:*:*:*:-:*:*:*", + }, + { + Product: "redis", + Vendor: "redis", + TargetSoftware: ANY, + PlatformCPE: "cpe:2.3:o:debian:debian_linux:9.0:*:*:*:*:*:*:*", + }, + { + Product: "redis", + Vendor: "redis", + TargetSoftware: ANY, + PlatformCPE: "cpe:2.3:o:debian:debian_linux:10.0:*:*:*:*:*:*:*", + }, + { + Product: "redis", + Vendor: "redis", + TargetSoftware: ANY, + PlatformCPE: "cpe:2.3:o:debian:debian_linux:11.0:*:*:*:*:*:*:*", + }, + } + cpeMatch := nvd.CpeMatch{ + Criteria: "cpe:2.3:a:redis:redis:-:*:*:*:*:*:*:*", + MatchCriteriaID: "5EBE5E1C-C881-4A76-9E36-4FB7C48427E6", + } + applicationNode := nvd.CpeMatch{ + Criteria: "cpe:2.3:a:redis:redis:-:*:*:*:*:*:*:*", + MatchCriteriaID: "some-uuid", + Vulnerable: true, + } + tracker := newUniquePkgTracker() + for _, c := range candidates { + candidate, err := newPkgCandidate(applicationNode, c.PlatformCPE) + require.NoError(t, err) + tracker.Add(*candidate, cpeMatch) + } + assert.Len(t, tracker, len(candidates)) +} + +func TestPlatformPackageCandidates(t *testing.T) { + type testCase struct { + name string + config nvd.Configuration + wantChanged bool + wantSet uniquePkgTracker + } + tests := []testCase{ + { + name: "application X platform", + config: nvd.Configuration{ + Negate: nil, + Nodes: []nvd.Node{ + { + CpeMatch: []nvd.CpeMatch{ + { + Vulnerable: true, + Criteria: "cpe:2.3:a:some-vendor:some-app:*:*:*:*:*:*:*:*", + }, + { + Vulnerable: true, + Criteria: "cpe:2.3:a:some-vendor:other-app:*:*:*:*:*:*:*:*", + }, + }, + Negate: nil, + Operator: nvd.Or, + }, + { + CpeMatch: []nvd.CpeMatch{ + { + Vulnerable: false, + Criteria: "cpe:2.3:o:some-vendor:some-platform:*:*:*:*:*:*:*:*", + }, + { + Vulnerable: false, + Criteria: "cpe:2.3:o:some-vendor:other-platform:*:*:*:*:*:*:*:*", + }, + }, + Negate: nil, + Operator: nvd.Or, + }, + }, + Operator: opRef(nvd.And), + }, + wantChanged: true, + wantSet: newUniquePkgTrackerFromSlice( + []pkgCandidate{ + mustNewPackage(t, nvd.CpeMatch{ + Vulnerable: true, + Criteria: "cpe:2.3:a:some-vendor:some-app:*:*:*:*:*:*:*:*", + }, "cpe:2.3:o:some-vendor:some-platform:*:*:*:*:*:*:*:*"), + mustNewPackage(t, nvd.CpeMatch{ + Vulnerable: true, + Criteria: "cpe:2.3:a:some-vendor:other-app:*:*:*:*:*:*:*:*", + }, "cpe:2.3:o:some-vendor:some-platform:*:*:*:*:*:*:*:*"), + mustNewPackage(t, nvd.CpeMatch{ + Vulnerable: true, + Criteria: "cpe:2.3:a:some-vendor:some-app:*:*:*:*:*:*:*:*", + }, "cpe:2.3:o:some-vendor:other-platform:*:*:*:*:*:*:*:*"), + mustNewPackage(t, nvd.CpeMatch{ + Vulnerable: true, + Criteria: "cpe:2.3:a:some-vendor:other-app:*:*:*:*:*:*:*:*", + }, "cpe:2.3:o:some-vendor:other-platform:*:*:*:*:*:*:*:*"), + }, + ), + }, + { + name: "top-level OR is excluded", + config: nvd.Configuration{ + Operator: opRef(nvd.Or), + }, + wantChanged: false, + wantSet: newUniquePkgTracker(), + }, + { + name: "top-level nil op is excluded", + config: nvd.Configuration{ + Operator: nil, + }, + wantChanged: false, + }, + { + name: "single hardware node results in exclusion", + config: nvd.Configuration{ + Negate: nil, + Nodes: []nvd.Node{ + { + CpeMatch: []nvd.CpeMatch{ + { + Vulnerable: true, + Criteria: "cpe:2.3:a:some-vendor:some-app:*:*:*:*:*:*:*:*", + }, + { + Vulnerable: true, + Criteria: "cpe:2.3:a:some-vendor:other-app:*:*:*:*:*:*:*:*", + }, + }, + Negate: nil, + Operator: nvd.Or, + }, + { + CpeMatch: []nvd.CpeMatch{ + { + Vulnerable: false, + Criteria: "cpe:2.3:o:some-vendor:some-platform:*:*:*:*:*:*:*:*", + }, + { + Vulnerable: false, + Criteria: "cpe:2.3:h:some-vendor:some-device:*:*:*:*:*:*:*:*", + }, + }, + Negate: nil, + Operator: nvd.Or, + }, + }, + Operator: opRef(nvd.And), + }, + wantChanged: false, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + set := newUniquePkgTracker() + result := platformPackageCandidates(set, tc.config) + assert.Equal(t, result, tc.wantChanged) + if tc.wantSet == nil { + tc.wantSet = newUniquePkgTracker() + } + if diff := cmp.Diff(tc.wantSet.All(), set.All()); diff != "" { + t.Errorf("unexpected diff (-want +got)\n%s", diff) + } + }) + + } +} + +func opRef(op nvd.Operator) *nvd.Operator { + return &op +} + +func boolRef(b bool) *bool { + return &b +} +func mustNewPackage(t *testing.T, match nvd.CpeMatch, platformCPE string) pkgCandidate { + p, err := newPkgCandidate(match, platformCPE) + require.NoError(t, err) + return *p }