Skip to content

Commit d87b983

Browse files
committed
Add support for vex vulnerableCode_url
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 <ziadhany2016@gmail.com>
1 parent 6cef213 commit d87b983

20 files changed

Lines changed: 2702 additions & 2 deletions

File tree

component_catalog/models.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,9 @@ def get_export_spdx_url(self):
745745
def get_export_cyclonedx_url(self):
746746
return self.get_url("export_cyclonedx")
747747

748+
def get_export_vex_url(self):
749+
return self.get_url("export_vex")
750+
748751
def get_about_files(self):
749752
"""
750753
Return the list of all AboutCode files from all the Packages
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{% extends "object_form.html" %}
2+
{% block javascripts %}
3+
{{ block.super }}
4+
{% endblock %}

component_catalog/views.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
from dejacode_toolkit.scancodeio import ScanCodeIO
7676
from dejacode_toolkit.scancodeio import get_package_download_url
7777
from dejacode_toolkit.scancodeio import get_scan_results_as_file_url
78+
from dejacode_toolkit.vex import create_auto_vex
7879
from dejacode_toolkit.vulnerablecode import VulnerableCode
7980
from dje import tasks
8081
from dje.client_data import add_client_data
@@ -857,7 +858,6 @@ def get_vulnerabilities_tab_fields(self, vulnerabilities):
857858
vulnerability_fields = self.get_vulnerability_fields(vulnerability, dataspace)
858859
fields.extend(vulnerability_fields)
859860
vulnerabilities_count += 1
860-
861861
return fields, vulnerabilities_count
862862

863863
def get_context_data(self, **kwargs):
@@ -1452,6 +1452,8 @@ def get_vulnerabilities_tab_fields(self, vulnerabilities):
14521452
fields = []
14531453
vulnerabilities_count = 0
14541454

1455+
create_auto_vex(self.object, vulnerabilities)
1456+
14551457
for entry in vulnerabilities:
14561458
unresolved = entry.get("affected_by_vulnerabilities", [])
14571459
for vulnerability in unresolved:

dejacode_toolkit/tests/test_vex.py

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
#
2+
# Copyright (c) nexB Inc. and others. All rights reserved.
3+
# DejaCode is a trademark of nexB Inc.
4+
# SPDX-License-Identifier: AGPL-3.0-only
5+
# See https://github.com/nexB/dejacode for support or download.
6+
# See https://aboutcode.org for more information about AboutCode FOSS projects.
7+
#
8+
9+
10+
import json
11+
import os
12+
13+
from django.contrib.auth import get_user_model
14+
from django.test import TestCase
15+
16+
from cyclonedx.output.json import SchemaVersion1Dot4
17+
from serializable import _SerializableJsonEncoder
18+
19+
from component_catalog.models import Package
20+
from dejacode_toolkit import vex
21+
from dejacode_toolkit.vex import VEXCycloneDX
22+
from dejacode_toolkit.vex import vulnerability_format_vcic_to_cyclonedx
23+
from dje.models import Dataspace
24+
from dje.tests import create_user
25+
from product_portfolio.models import Product
26+
from product_portfolio.models import ProductPackage
27+
from product_portfolio.models import ProductPackageVEX
28+
29+
User = get_user_model()
30+
31+
32+
class VEXTestCase(TestCase):
33+
def setUp(self):
34+
self.nexb_dataspace = Dataspace.objects.create(name="nexB")
35+
self.nexb_user = User.objects.create_superuser(
36+
"nexb_user", "test@test.com", "t3st", self.nexb_dataspace
37+
)
38+
self.basic_user = create_user("basic_user", self.nexb_dataspace)
39+
self.product1 = Product.objects.create(
40+
name="Product1 With Space", version="1.0", dataspace=self.nexb_dataspace
41+
)
42+
self.package1 = Package.objects.create(filename="package1", dataspace=self.nexb_dataspace)
43+
44+
self.productpacakge1 = ProductPackage.objects.create(
45+
product=self.product1, package=self.package1, dataspace=self.nexb_dataspace
46+
)
47+
self.vex1 = ProductPackageVEX.objects.create(
48+
dataspace=self.productpacakge1.dataspace,
49+
productpackage=self.productpacakge1,
50+
vulnerability_id="VCID-111c-u9bh-aaac",
51+
responses=["CNF"],
52+
justification="CNP",
53+
detail=(
54+
"Automated dataflow analysis and manual "
55+
"code review indicates that the vulnerable code is not reachable,"
56+
" either directly or indirectly."
57+
),
58+
)
59+
60+
def test_create_auto_vex1(self):
61+
vulnerabilities = [
62+
{
63+
"affected_by_vulnerabilities": [
64+
{
65+
"url": "http://public.vulnerablecode.io/api/vulnerabilities/121332",
66+
"vulnerability_id": "VCID-111c-u9bh-aaac",
67+
}
68+
]
69+
},
70+
{
71+
"affected_by_vulnerabilities": [
72+
{
73+
"url": "https://public.vulnerablecode.io/api/vulnerabilities/121331",
74+
"vulnerability_id": "VCID-uxf9-7c97-aaaj",
75+
}
76+
]
77+
},
78+
]
79+
assert ProductPackageVEX.objects.count() == 1
80+
vex.create_auto_vex(self.package1, vulnerabilities)
81+
assert ProductPackageVEX.objects.count() == 2
82+
83+
# run create_auto_vex agian and make sure that the databse ignore errors
84+
vex.create_auto_vex(self.package1, vulnerabilities)
85+
assert ProductPackageVEX.objects.count() == 2
86+
87+
def test_create_auto_vex2(self):
88+
# duplicated vulnerability
89+
vulnerabilities = [
90+
{
91+
"affected_by_vulnerabilities": [
92+
{
93+
"url": "http://public.vulnerablecode.io/api/vulnerabilities/121332",
94+
"vulnerability_id": "VCID-111c-u9bh-aaac",
95+
}
96+
]
97+
},
98+
{
99+
"affected_by_vulnerabilities": [
100+
{
101+
"url": "http://public.vulnerablecode.io/api/vulnerabilities/121332",
102+
"vulnerability_id": "VCID-111c-u9bh-aaac",
103+
}
104+
]
105+
},
106+
]
107+
assert ProductPackageVEX.objects.count() == 1
108+
vex.create_auto_vex(self.package1, vulnerabilities)
109+
assert ProductPackageVEX.objects.count() == 1
110+
111+
def test_get_references_and_rating(self):
112+
references = [
113+
{
114+
"reference_url": "https://nvd.nist.gov/vuln/detail/CVE-2017-1000136",
115+
"reference_id": "CVE-2017-1000136",
116+
"scores": [
117+
{
118+
"value": "5.0",
119+
"scoring_system": "cvssv2",
120+
"scoring_elements": "AV:N/AC:L/Au:N/C:P/I:N/A:N",
121+
},
122+
{
123+
"value": "5.3",
124+
"scoring_system": "cvssv3",
125+
"scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
126+
},
127+
],
128+
"url": "https://nvd.nist.gov/vuln/detail/CVE-2017-1000136",
129+
}
130+
]
131+
ref, rate = vex.get_references_and_rating(references)
132+
133+
assert json.dumps(
134+
ref,
135+
cls=_SerializableJsonEncoder,
136+
view_=SchemaVersion1Dot4,
137+
) == json.dumps(
138+
[
139+
{
140+
"id": "CVE-2017-1000136",
141+
"source": {"url": "https://nvd.nist.gov/vuln/detail/CVE-2017-1000136"},
142+
}
143+
]
144+
)
145+
146+
assert json.dumps(
147+
rate,
148+
cls=_SerializableJsonEncoder,
149+
view_=SchemaVersion1Dot4,
150+
) == json.dumps(
151+
[
152+
{
153+
"method": "CVSSv2",
154+
"score": "5.0",
155+
"source": {"url": "https://nvd.nist.gov/vuln/detail/CVE-2017-1000136"},
156+
"vector": "AV:N/AC:L/Au:N/C:P/I:N/A:N",
157+
},
158+
{
159+
"method": "CVSSv3",
160+
"score": "5.3",
161+
"source": {"url": "https://nvd.nist.gov/vuln/detail/CVE-2017-1000136"},
162+
"vector": "AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
163+
},
164+
]
165+
)
166+
167+
def test_vulnerability_format_vcic_to_cyclonedx1(self):
168+
vul_data_path = os.path.join(os.path.dirname(__file__), "testfiles", "vcio_vul1.json")
169+
with open(vul_data_path) as f:
170+
vcio_vulnerability = json.load(f)
171+
172+
vulnerability = vulnerability_format_vcic_to_cyclonedx(vcio_vulnerability, self.vex1)
173+
174+
cyclonedx_vul_data_path = os.path.join(
175+
os.path.dirname(__file__), "testfiles", "cyclonedx_vul1.json"
176+
)
177+
with open(cyclonedx_vul_data_path) as f:
178+
cyclonedx_vul = json.load(f)
179+
180+
assert json.dumps(
181+
vulnerability,
182+
cls=_SerializableJsonEncoder,
183+
view_=SchemaVersion1Dot4,
184+
) == json.dumps(cyclonedx_vul)
185+
186+
def test_vulnerability_format_vcic_to_cyclonedx2(self):
187+
vul_data_path = os.path.join(os.path.dirname(__file__), "testfiles", "vcio_vul2.json")
188+
with open(vul_data_path) as f:
189+
vcio_vulnerability = json.load(f)
190+
191+
vulnerability = vulnerability_format_vcic_to_cyclonedx(vcio_vulnerability, self.vex1)
192+
193+
cyclonedx_vul_data_path = os.path.join(
194+
os.path.dirname(__file__), "testfiles", "cyclonedx_vul2.json"
195+
)
196+
with open(cyclonedx_vul_data_path) as f:
197+
cyclonedx_vul = json.load(f)
198+
199+
assert json.dumps(
200+
vulnerability,
201+
cls=_SerializableJsonEncoder,
202+
view_=SchemaVersion1Dot4,
203+
) == json.dumps(cyclonedx_vul)
204+
205+
def test_vex_cyclonedx_export(self):
206+
vul_data_path = os.path.join(os.path.dirname(__file__), "testfiles", "vcio_vul1.json")
207+
with open(vul_data_path) as f:
208+
vcio_vulnerability = json.load(f)
209+
210+
vex_data_path = os.path.join(os.path.dirname(__file__), "testfiles", "vex1.json")
211+
with open(vex_data_path) as f:
212+
vex_data = json.load(f)
213+
214+
assert VEXCycloneDX().export([vcio_vulnerability], [self.vex1]) == json.dumps(vex_data)

0 commit comments

Comments
 (0)