Skip to content

Commit

Permalink
Add support for vex vulnerableCode_url
Browse files Browse the repository at this point in the history
Fix Vex export test
Remove get_export_vex_url func from Package model
Add support for vulnerability encoder
Add a test for get_references_and_rating
Rename UI VEX fields
Fix Export VEX View
Rename VEX model
Add VEX Form
Fix UI bug and add the model to dataspace
Add basic VEX mapping for CycloneDX
Automate VEX creation
Add the basic Vex Form
Add the skeleton view and form for vex
Add Product VEX List view and update tab_vex
Add the basic for vex model
Add the basic skeleton for vex export

Signed-off-by: ziadhany <[email protected]>
  • Loading branch information
ziadhany committed May 4, 2024
1 parent 6cef213 commit d87b983
Show file tree
Hide file tree
Showing 20 changed files with 2,702 additions and 2 deletions.
3 changes: 3 additions & 0 deletions component_catalog/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,9 @@ def get_export_spdx_url(self):
def get_export_cyclonedx_url(self):
return self.get_url("export_cyclonedx")

def get_export_vex_url(self):
return self.get_url("export_vex")

def get_about_files(self):
"""
Return the list of all AboutCode files from all the Packages
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{% extends "object_form.html" %}
{% block javascripts %}
{{ block.super }}
{% endblock %}
4 changes: 3 additions & 1 deletion component_catalog/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
from dejacode_toolkit.scancodeio import ScanCodeIO
from dejacode_toolkit.scancodeio import get_package_download_url
from dejacode_toolkit.scancodeio import get_scan_results_as_file_url
from dejacode_toolkit.vex import create_auto_vex
from dejacode_toolkit.vulnerablecode import VulnerableCode
from dje import tasks
from dje.client_data import add_client_data
Expand Down Expand Up @@ -857,7 +858,6 @@ def get_vulnerabilities_tab_fields(self, vulnerabilities):
vulnerability_fields = self.get_vulnerability_fields(vulnerability, dataspace)
fields.extend(vulnerability_fields)
vulnerabilities_count += 1

return fields, vulnerabilities_count

def get_context_data(self, **kwargs):
Expand Down Expand Up @@ -1452,6 +1452,8 @@ def get_vulnerabilities_tab_fields(self, vulnerabilities):
fields = []
vulnerabilities_count = 0

create_auto_vex(self.object, vulnerabilities)

for entry in vulnerabilities:
unresolved = entry.get("affected_by_vulnerabilities", [])
for vulnerability in unresolved:
Expand Down
214 changes: 214 additions & 0 deletions dejacode_toolkit/tests/test_vex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
#
# Copyright (c) nexB Inc. and others. All rights reserved.
# DejaCode is a trademark of nexB Inc.
# SPDX-License-Identifier: AGPL-3.0-only
# See https://github.com/nexB/dejacode for support or download.
# See https://aboutcode.org for more information about AboutCode FOSS projects.
#


import json
import os

from django.contrib.auth import get_user_model
from django.test import TestCase

from cyclonedx.output.json import SchemaVersion1Dot4
from serializable import _SerializableJsonEncoder

from component_catalog.models import Package
from dejacode_toolkit import vex
from dejacode_toolkit.vex import VEXCycloneDX
from dejacode_toolkit.vex import vulnerability_format_vcic_to_cyclonedx
from dje.models import Dataspace
from dje.tests import create_user
from product_portfolio.models import Product
from product_portfolio.models import ProductPackage
from product_portfolio.models import ProductPackageVEX

User = get_user_model()


class VEXTestCase(TestCase):
def setUp(self):
self.nexb_dataspace = Dataspace.objects.create(name="nexB")
self.nexb_user = User.objects.create_superuser(
"nexb_user", "[email protected]", "t3st", self.nexb_dataspace
)
self.basic_user = create_user("basic_user", self.nexb_dataspace)
self.product1 = Product.objects.create(
name="Product1 With Space", version="1.0", dataspace=self.nexb_dataspace
)
self.package1 = Package.objects.create(filename="package1", dataspace=self.nexb_dataspace)

self.productpacakge1 = ProductPackage.objects.create(
product=self.product1, package=self.package1, dataspace=self.nexb_dataspace
)
self.vex1 = ProductPackageVEX.objects.create(
dataspace=self.productpacakge1.dataspace,
productpackage=self.productpacakge1,
vulnerability_id="VCID-111c-u9bh-aaac",
responses=["CNF"],
justification="CNP",
detail=(
"Automated dataflow analysis and manual "
"code review indicates that the vulnerable code is not reachable,"
" either directly or indirectly."
),
)

def test_create_auto_vex1(self):
vulnerabilities = [
{
"affected_by_vulnerabilities": [
{
"url": "http://public.vulnerablecode.io/api/vulnerabilities/121332",
"vulnerability_id": "VCID-111c-u9bh-aaac",
}
]
},
{
"affected_by_vulnerabilities": [
{
"url": "https://public.vulnerablecode.io/api/vulnerabilities/121331",
"vulnerability_id": "VCID-uxf9-7c97-aaaj",
}
]
},
]
assert ProductPackageVEX.objects.count() == 1
vex.create_auto_vex(self.package1, vulnerabilities)
assert ProductPackageVEX.objects.count() == 2

# run create_auto_vex agian and make sure that the databse ignore errors
vex.create_auto_vex(self.package1, vulnerabilities)
assert ProductPackageVEX.objects.count() == 2

def test_create_auto_vex2(self):
# duplicated vulnerability
vulnerabilities = [
{
"affected_by_vulnerabilities": [
{
"url": "http://public.vulnerablecode.io/api/vulnerabilities/121332",
"vulnerability_id": "VCID-111c-u9bh-aaac",
}
]
},
{
"affected_by_vulnerabilities": [
{
"url": "http://public.vulnerablecode.io/api/vulnerabilities/121332",
"vulnerability_id": "VCID-111c-u9bh-aaac",
}
]
},
]
assert ProductPackageVEX.objects.count() == 1
vex.create_auto_vex(self.package1, vulnerabilities)
assert ProductPackageVEX.objects.count() == 1

def test_get_references_and_rating(self):
references = [
{
"reference_url": "https://nvd.nist.gov/vuln/detail/CVE-2017-1000136",
"reference_id": "CVE-2017-1000136",
"scores": [
{
"value": "5.0",
"scoring_system": "cvssv2",
"scoring_elements": "AV:N/AC:L/Au:N/C:P/I:N/A:N",
},
{
"value": "5.3",
"scoring_system": "cvssv3",
"scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
},
],
"url": "https://nvd.nist.gov/vuln/detail/CVE-2017-1000136",
}
]
ref, rate = vex.get_references_and_rating(references)

assert json.dumps(
ref,
cls=_SerializableJsonEncoder,
view_=SchemaVersion1Dot4,
) == json.dumps(
[
{
"id": "CVE-2017-1000136",
"source": {"url": "https://nvd.nist.gov/vuln/detail/CVE-2017-1000136"},
}
]
)

assert json.dumps(
rate,
cls=_SerializableJsonEncoder,
view_=SchemaVersion1Dot4,
) == json.dumps(
[
{
"method": "CVSSv2",
"score": "5.0",
"source": {"url": "https://nvd.nist.gov/vuln/detail/CVE-2017-1000136"},
"vector": "AV:N/AC:L/Au:N/C:P/I:N/A:N",
},
{
"method": "CVSSv3",
"score": "5.3",
"source": {"url": "https://nvd.nist.gov/vuln/detail/CVE-2017-1000136"},
"vector": "AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
},
]
)

def test_vulnerability_format_vcic_to_cyclonedx1(self):
vul_data_path = os.path.join(os.path.dirname(__file__), "testfiles", "vcio_vul1.json")
with open(vul_data_path) as f:
vcio_vulnerability = json.load(f)

vulnerability = vulnerability_format_vcic_to_cyclonedx(vcio_vulnerability, self.vex1)

cyclonedx_vul_data_path = os.path.join(
os.path.dirname(__file__), "testfiles", "cyclonedx_vul1.json"
)
with open(cyclonedx_vul_data_path) as f:
cyclonedx_vul = json.load(f)

assert json.dumps(
vulnerability,
cls=_SerializableJsonEncoder,
view_=SchemaVersion1Dot4,
) == json.dumps(cyclonedx_vul)

def test_vulnerability_format_vcic_to_cyclonedx2(self):
vul_data_path = os.path.join(os.path.dirname(__file__), "testfiles", "vcio_vul2.json")
with open(vul_data_path) as f:
vcio_vulnerability = json.load(f)

vulnerability = vulnerability_format_vcic_to_cyclonedx(vcio_vulnerability, self.vex1)

cyclonedx_vul_data_path = os.path.join(
os.path.dirname(__file__), "testfiles", "cyclonedx_vul2.json"
)
with open(cyclonedx_vul_data_path) as f:
cyclonedx_vul = json.load(f)

assert json.dumps(
vulnerability,
cls=_SerializableJsonEncoder,
view_=SchemaVersion1Dot4,
) == json.dumps(cyclonedx_vul)

def test_vex_cyclonedx_export(self):
vul_data_path = os.path.join(os.path.dirname(__file__), "testfiles", "vcio_vul1.json")
with open(vul_data_path) as f:
vcio_vulnerability = json.load(f)

vex_data_path = os.path.join(os.path.dirname(__file__), "testfiles", "vex1.json")
with open(vex_data_path) as f:
vex_data = json.load(f)

assert VEXCycloneDX().export([vcio_vulnerability], [self.vex1]) == json.dumps(vex_data)
Loading

0 comments on commit d87b983

Please sign in to comment.