Skip to content

Commit e090246

Browse files
authored
Merge pull request #450 from sbs2001/cpe2purl_script
Add a management command for creating cpe2purl mapping
2 parents d7ba181 + 18d8795 commit e090246

1 file changed

Lines changed: 110 additions & 0 deletions

File tree

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Copyright (c) nexB Inc. and others. All rights reserved.
2+
# http://nexb.com and https://github.com/nexB/vulnerablecode/
3+
# The VulnerableCode software is licensed under the Apache License version 2.0.
4+
# Data generated with VulnerableCode require an acknowledgment.
5+
#
6+
# You may not use this software except in compliance with the License.
7+
# You may obtain a copy of the License at: http://apache.org/licenses/LICENSE-2.0
8+
# Unless required by applicable law or agreed to in writing, software distributed
9+
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
10+
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
# specific language governing permissions and limitations under the License.
12+
#
13+
# When you publish or redistribute any data created with VulnerableCode or any VulnerableCode
14+
# derivative work, you must accompany this data with the following acknowledgment:
15+
#
16+
# Generated with VulnerableCode and provided on an "AS IS" BASIS, WITHOUT WARRANTIES
17+
# OR CONDITIONS OF ANY KIND, either express or implied. No content created from
18+
# VulnerableCode should be considered or used as legal advice. Consult an Attorney
19+
# for any legal advice.
20+
# VulnerableCode is a free software code scanning tool from nexB Inc. and others.
21+
# Visit https://github.com/nexB/vulnerablecode/ for support and download.
22+
23+
import json
24+
import os
25+
from datetime import date
26+
from itertools import chain
27+
28+
from django.core.management.base import BaseCommand
29+
30+
from vulnerabilities import models
31+
from vulnerabilities.importers.nvd import BASE_URL as nvd_base_url
32+
from vulnerabilities.importers.nvd import NVDDataSource as nvd_utils
33+
34+
35+
class Command(BaseCommand):
36+
"""
37+
This script creates a mapping of CPEs to PURLs grouped by the affecting CVE.
38+
It does this by doing the following:
39+
1. Iterate over all CVEs found in VulnerableCode's db.
40+
2. Look for the CVE being iterated upon in the NVD.
41+
3. Get the list of all CPEs which are affected by this CVE from NVD entry.
42+
4. Get the list of all PURLs which are affected by this CVE from VulnerableCode's db.
43+
5. Map the list of CPEs and PURLs from #3 and #4 together.
44+
"""
45+
46+
def add_arguments(self, parser):
47+
48+
parser.add_argument(
49+
"--vulnerable_purls_only", action="store_true", help="Map only vulnerable PURLs to CPEs"
50+
)
51+
52+
parser.add_argument(
53+
"--patched_purls_only", action="store_true", help="Map only patching PURLs to CPEs"
54+
)
55+
56+
@staticmethod
57+
def get_packages(vulnerability, vulnerable_purls_only, patched_purls_only):
58+
if vulnerable_purls_only and not patched_purls_only:
59+
return vulnerability.vulnerable_packages.all()
60+
61+
elif patched_purls_only and not vulnerable_purls_only:
62+
return vulnerability.patched_packages.all()
63+
64+
return chain(vulnerability.patched_packages.all(), vulnerability.vulnerable_packages.all())
65+
66+
def handle(self, *args, **options):
67+
current_year = date.today().year
68+
# NVD json feeds start from 2002.
69+
for year in range(2002, current_year + 1):
70+
self.stdout.write(f"Processing CPEs from year {year}")
71+
download_url = nvd_base_url.format(year)
72+
nvd_data = nvd_utils.fetch(download_url)
73+
74+
vulnerabilities = list(
75+
models.Vulnerability.objects.filter(vulnerability_id__startswith=f"CVE-{year}")
76+
.prefetch_related("vulnerable_packages")
77+
.prefetch_related("patched_packages")
78+
)
79+
80+
vulnerabilities = {
81+
vulnerability.vulnerability_id: vulnerability for vulnerability in vulnerabilities
82+
}
83+
purl_cpe_mapping = []
84+
85+
for cve_item in nvd_data["CVE_Items"]:
86+
cve_id = cve_item["cve"]["CVE_data_meta"]["ID"]
87+
if cve_id not in vulnerabilities:
88+
continue
89+
90+
purl_cpe_mapping.append({})
91+
purl_cpe_mapping[-1]["cve_id"] = cve_id
92+
purl_cpe_mapping[-1]["purls"] = []
93+
purl_cpe_mapping[-1]["cpes"] = list(nvd_utils.extract_cpes(cve_item))
94+
95+
packages = self.get_packages(
96+
vulnerabilities[cve_id],
97+
options["vulnerable_purls_only"],
98+
options["patched_purls_only"],
99+
)
100+
for package in packages:
101+
purl_cpe_mapping[-1]["purls"].append(package.package_url)
102+
103+
if not os.path.exists("cpe2purl"):
104+
os.mkdir("cpe2purl")
105+
106+
with open(os.path.join("cpe2purl", f"{year}.json"), "w") as f:
107+
json.dump(purl_cpe_mapping, f, indent=4)
108+
109+
path = os.path.abspath("cpe2purl")
110+
self.stdout.write(self.style.SUCCESS(f"Successfully created the mappings. Check {path}"))

0 commit comments

Comments
 (0)