Skip to content

Commit

Permalink
Add the as_cyclonedx method on the Vulnerability model #108
Browse files Browse the repository at this point in the history
Signed-off-by: tdruez <tdruez@nexb.com>
  • Loading branch information
tdruez committed Sep 3, 2024
1 parent a8cb6f8 commit 27db675
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 17 deletions.
58 changes: 58 additions & 0 deletions component_catalog/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# See https://aboutcode.org for more information about AboutCode FOSS projects.
#

import decimal
import logging
import re
from contextlib import suppress
Expand Down Expand Up @@ -37,6 +38,7 @@
from cyclonedx.model import component as cyclonedx_component
from cyclonedx.model import contact as cyclonedx_contact
from cyclonedx.model import license as cyclonedx_license
from cyclonedx.model import vulnerability as cdx_vulnerability
from license_expression import ExpressionError
from packageurl import PackageURL
from packageurl.contrib import purl2url
Expand Down Expand Up @@ -2747,3 +2749,59 @@ def get_severity_scores(severities):
consolidated_scores.extend(score_range)

return consolidated_scores

def as_cyclonedx(self, component_bom_ref):
affects = [cdx_vulnerability.BomTarget(ref=f"urn:cdx:{component_bom_ref}")]

source_url = f"https://public.vulnerablecode.io/vulnerabilities/{self.vulnerability_id}"
source = cdx_vulnerability.VulnerabilitySource(
name="VulnerableCode",
url=source_url,
)

references = []
ratings = []
for reference in self.references:
reference_source = cdx_vulnerability.VulnerabilitySource(
url=reference.get("reference_url"),
)
references.append(
cdx_vulnerability.VulnerabilityReference(
id=reference.get("reference_id"),
source=reference_source,
)
)

for score_entry in reference.get("scores", []):
# CycloneDX only support a float value for the score field,
# where on the VulnerableCode data it can be either a score float value
# or a severity string value.
score_value = score_entry.get("value")
try:
score = decimal.Decimal(score_value)
severity = None
except decimal.DecimalException:
score = None
severity = getattr(
cdx_vulnerability.VulnerabilitySeverity,
score_value.upper(),
None,
)

ratings.append(
cdx_vulnerability.VulnerabilityRating(
source=reference_source,
score=score,
severity=severity,
vector=score_entry.get("scoring_elements"),
)
)

return cdx_vulnerability.Vulnerability(
id=self.vulnerability_id,
source=source,
description=self.summary,
affects=affects,
references=sorted(references),
ratings=ratings,
)
21 changes: 21 additions & 0 deletions component_catalog/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2734,3 +2734,24 @@ def test_vulnerability_model_queryset_count_methods(self):
)
self.assertEqual(2, qs[0].affected_packages_count)
self.assertEqual(1, qs[0].affected_products_count)

def test_vulnerability_model_as_cyclonedx(self):
response_file = self.data / "vulnerabilities" / "idna_3.6_response.json"
json_data = json.loads(response_file.read_text())
affected_by_vulnerabilities = json_data["results"][0]["affected_by_vulnerabilities"]
vulnerability1 = Vulnerability.create_from_data(
dataspace=self.dataspace,
data=affected_by_vulnerabilities[0],
)

vulnerability1_as_cdx = vulnerability1.as_cyclonedx(component_bom_ref="ref")
as_dict = json.loads(vulnerability1_as_cdx.as_json())
as_dict.pop("ratings", None) # The sorting is inconsistent
results = json.dumps(as_dict, indent=2)

expected_location = self.data / "vulnerabilities" / "idna_3.6_as_cyclonedx.json"
# Uncomment to regen the expected results
# if True:
# expected_location.write_text(results)

self.assertJSONEqual(results, expected_location.read_text())
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
{
"affects": [
{
"ref": "urn:cdx:ref"
}
],
"description": "Internationalized Domain Names in Applications (IDNA) vulnerable to denial of service from specially crafted inputs to idna.encode",
"id": "VCID-j3au-usaz-aaag",
"references": [
{
"id": "",
"source": {
"url": "https://access.redhat.com/hydra/rest/securitydata/cve/CVE-2024-3651.json"
}
},
{
"id": "",
"source": {
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-3651"
}
},
{
"id": "",
"source": {
"url": "https://ftp.suse.com/pub/projects/security/yaml/suse-cvss-scores.yaml"
}
},
{
"id": "",
"source": {
"url": "https://github.com/kjd/idna"
}
},
{
"id": "",
"source": {
"url": "https://github.com/kjd/idna/commit/1d365e17e10d72d0b7876316fc7b9ca0eebdd38d"
}
},
{
"id": "",
"source": {
"url": "https://github.com/pypa/advisory-database/tree/main/vulns/idna/PYSEC-2024-60.yaml"
}
},
{
"id": "",
"source": {
"url": "https://huntr.com/bounties/93d78d07-d791-4b39-a845-cbfabc44aadb"
}
},
{
"id": "1069127",
"source": {
"url": "https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1069127"
}
},
{
"id": "2274779",
"source": {
"url": "https://bugzilla.redhat.com/show_bug.cgi?id=2274779"
}
},
{
"id": "CVE-2024-3651",
"source": {
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-3651"
}
},
{
"id": "GHSA-jjg7-2v4v-x38h",
"source": {
"url": "https://github.com/advisories/GHSA-jjg7-2v4v-x38h"
}
},
{
"id": "GHSA-jjg7-2v4v-x38h",
"source": {
"url": "https://github.com/kjd/idna/security/advisories/GHSA-jjg7-2v4v-x38h"
}
},
{
"id": "RHSA-2024:3466",
"source": {
"url": "https://access.redhat.com/errata/RHSA-2024:3466"
}
},
{
"id": "RHSA-2024:3543",
"source": {
"url": "https://access.redhat.com/errata/RHSA-2024:3543"
}
},
{
"id": "RHSA-2024:3552",
"source": {
"url": "https://access.redhat.com/errata/RHSA-2024:3552"
}
},
{
"id": "RHSA-2024:3781",
"source": {
"url": "https://access.redhat.com/errata/RHSA-2024:3781"
}
},
{
"id": "RHSA-2024:3846",
"source": {
"url": "https://access.redhat.com/errata/RHSA-2024:3846"
}
},
{
"id": "RHSA-2024:4260",
"source": {
"url": "https://access.redhat.com/errata/RHSA-2024:4260"
}
},
{
"id": "USN-6780-1",
"source": {
"url": "https://usn.ubuntu.com/6780-1/"
}
},
{
"id": "cpe:2.3:a:kjd:internationalized_domain_names_in_applications:3.6:*:*:*:*:*:*:*",
"source": {
"url": "https://nvd.nist.gov/vuln/search/results?adv_search=true&isCpeNameSearch=true&query=cpe:2.3:a:kjd:internationalized_domain_names_in_applications:3.6:*:*:*:*:*:*:*"
}
}
],
"source": {
"name": "VulnerableCode",
"url": "https://public.vulnerablecode.io/vulnerabilities/VCID-j3au-usaz-aaag"
}
}
4 changes: 2 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,10 @@ install_requires =
attrs==23.2.0
pyrsistent==0.18.1
# CycloneDX
cyclonedx-python-lib==7.3.4
cyclonedx-python-lib==7.6.0
sortedcontainers==2.4.0
toml==0.10.2
py-serializable==1.0.3
py-serializable==1.1.0
# Git
gitpython==3.1.43
gitdb==4.0.11
Expand Down
15 changes: 0 additions & 15 deletions thirdparty/dist/cyclonedx_python_lib-7.3.4-py3-none-any.whl.ABOUT

This file was deleted.

Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit 27db675

Please sign in to comment.