Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for VEX #72

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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", "test@test.com", "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