Skip to content

Commit 6688cb6

Browse files
authored
Merge pull request #909 from nexB/prepare-release30
Prepare release 30
2 parents 7c708e8 + cc4ff4d commit 6688cb6

38 files changed

+5071
-5042
lines changed

CHANGELOG.rst

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,17 @@ Version v30.0.0
1111
are available.
1212
Because of these extensive changes, it is not possible to migrate existing imported
1313
data to the new schema. You will need instead to restart imports from an empty database
14-
or request access to the new vulnerablecode.io live instance.
15-
You can track the progress in this issue: https://github.com/nexB/vulnerablecode/issues/597
14+
or access the new public.vulnerablecode.io live instance. We also provide a database dump.
15+
16+
- You can track the progress of this refactoring in this issue:
17+
https://github.com/nexB/vulnerablecode/issues/597
1618

1719
- We added new data sources including PYSEC, GitHub and GitLab.
1820

1921
- We improved the documentation including adding development examples for importers and improvers.
2022

2123
- We removed the ability to edit relationships from the UI. The UI is now read-only.
24+
2225
- We replace the web UI with a brand new UI based on the same overall look and feel as ScanCode.io.
2326

2427
- We added support for NixOS as a Linux deployment target.
@@ -42,15 +45,17 @@ Version v30.0.0
4245
- Add new attribute `is_resolved`
4346
- Add namespace filter
4447

45-
- We have provided backward compatibility for `url` and `unresolved_vulnerabilities` for now
48+
- We have provided backward compatibility for `url` and `unresolved_vulnerabilities` for now.
49+
These will be removed in the next major version and should be considered as deprecated.
4650

47-
- There is a new experimental cpe/ API endpoint to lookup for vulnerabilities by CPE and
51+
- There is a new experimental `cpe/` API endpoint to lookup for vulnerabilities by CPE and
4852
another aliases/ endpoint to lookup for vulnerabilities by aliases. These two endpoints will be
4953
replaced by query parameters on the main vulnerabilities/ endpoint when stabilized.
5054

51-
- Added filters for vulnerabilities endpoint to get fixed packages in accordance to the details given in filters:
52-
For example, when you call the endpoint this way ``/api/vulnerabilities?type=pypi&namespace=foo&name=bar``,
53-
you will receive only fixed versioned purls of the type ``pypi``, namespace ``foo`` and name ``bar``.
55+
- Added filters for vulnerabilities endpoint to get fixed packages in accordance
56+
to the details given in filters: For example, when you call the endpoint this way
57+
``/api/vulnerabilities?type=pypi&namespace=foo&name=bar``, you will receive only
58+
fixed versioned purls of the type ``pypi``, namespace ``foo`` and name ``bar``.
5459

5560
- Package endpoint will give fixed packages of only those that
5661
matches type, name, namespace, subpath and qualifiers of the package queried.
@@ -69,10 +74,14 @@ Version v30.0.0
6974
their API Key in the REST API.
7075
Users can be created using the Django "createsuperuser" management command.
7176

77+
- The data license is now CC-BY-SA-4.0 as this is the highest common
78+
denominator license among all the data sources we collect and aggregate.
79+
80+
7281
Other:
7382

74-
- we dropped calver to use a plain semver.
75-
- we adopted vers and the new univers library to handle version ranges.
83+
- We dropped calver to use a plain semver.
84+
- We adopted vers and the new univers library to handle version ranges.
7685

7786

7887
Version v20.10

etc/scripts/backup.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
# backup current Db
3+
4+
DBDUMP=vcio-db-dump-$(date +"%Y-%m-%d_%H%M").dump
5+
echo "Backup vulnerablecode current DB to: $DBDUMP"
6+
sudo -u postgres pg_dump --format=c vulnerablecode > $DBDUMP

etc/scripts/restore.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
# backup current Db and then restore a dump
3+
4+
./backup.sh
5+
echo "Restore vulnerablecode dump from: $1"
6+
sudo -u postgres dropdb vulnerablecode
7+
sudo -u postgres createdb --encoding=utf-8 --owner=vulnerablecode vulnerablecode
8+
sudo -u postgres pg_restore --verbose -d vulnerablecode $1
9+

etc/thirdparty/virtualenv.pyz

-385 KB
Binary file not shown.
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
about_resource: virtualenv.pyz
22
name: get-virtualenv
3-
version: 20.13.3
4-
download_url: https://github.com/pypa/get-virtualenv/raw/20.13.3/public/virtualenv.pyz
3+
version: 20.16.5
4+
download_url: https://github.com/pypa/get-virtualenv/raw/20.16.5/public/virtualenv.pyz
55
description: virtualenv is a tool to create isolated Python environments.
66
homepage_url: https://github.com/pypa/virtualenv
77
license_expression: lgpl-2.1-plus AND (bsd-new OR apache-2.0) AND mit AND python AND bsd-new
@@ -10,4 +10,4 @@ copyright: Copyright (c) The Python Software Foundation and others
1010
redistribute: yes
1111
attribute: yes
1212
track_changes: yes
13-
package_url: pkg:github/pypa/get-virtualenv@20.13.1#public/virtualenv.pyz
13+
package_url: pkg:github/pypa/get-virtualenv@20.16.5#public/virtualenv.pyz

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = vulnerablecode
3-
version = 30.0.0rc3
3+
version = 30.0.0rc5
44
license = Apache-2.0 AND CC-BY-SA-4.0
55

66
# description must be on ONE line https://github.com/pypa/setuptools/issues/1390

vulnerabilities/forms.py

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,21 @@
99

1010
from django import forms
1111

12-
from vulnerabilities.models import Package
1312

13+
class PackageSearchForm(forms.Form):
1414

15-
def get_known_package_types():
16-
"""
17-
Return a list of known package types.
18-
"""
19-
pkg_types = [(i.type, i.type) for i in Package.objects.distinct("type").all()]
20-
pkg_types.append((None, "Any type"))
21-
return pkg_types
22-
23-
24-
class PackageForm(forms.Form):
25-
26-
package_name = forms.CharField(
27-
required=False, widget=forms.TextInput(attrs={"placeholder": "Package name or purl"})
15+
search = forms.CharField(
16+
required=True,
17+
widget=forms.TextInput(
18+
attrs={"placeholder": "Package name, purl or purl fragment"},
19+
),
2820
)
2921

3022

31-
class VulnerabilityForm(forms.Form):
23+
class VulnerabilitySearchForm(forms.Form):
3224

33-
vulnerability_id = forms.CharField(
34-
required=False,
25+
search = forms.CharField(
26+
required=True,
3527
widget=forms.TextInput(
3628
attrs={"placeholder": "Vulnerability id or alias such as CVE or GHSA"}
3729
),

vulnerabilities/improve_runner.py

Lines changed: 64 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,19 @@
1111
from datetime import datetime
1212
from datetime import timezone
1313
from typing import List
14-
from typing import Tuple
1514

15+
from django.core.exceptions import ValidationError
1616
from django.db import transaction
1717

18-
from vulnerabilities import models
19-
from vulnerabilities.importer import PackageURL
2018
from vulnerabilities.improver import Inference
2119
from vulnerabilities.models import Advisory
20+
from vulnerabilities.models import Alias
21+
from vulnerabilities.models import Package
22+
from vulnerabilities.models import PackageRelatedVulnerability
23+
from vulnerabilities.models import Vulnerability
24+
from vulnerabilities.models import VulnerabilityReference
25+
from vulnerabilities.models import VulnerabilityRelatedReference
26+
from vulnerabilities.models import VulnerabilitySeverity
2227

2328
logger = logging.getLogger(__name__)
2429

@@ -63,46 +68,59 @@ def process_inferences(inferences: List[Inference], advisory: Advisory, improver
6368
logger.info(f"Improving advisory id: {advisory.id}")
6469

6570
for inference in inferences:
66-
vuln = get_or_create_vulnerability_and_aliases(
67-
inference.vulnerability_id, inference.aliases, inference.summary
71+
vulnerability = get_or_create_vulnerability_and_aliases(
72+
vulnerability_id=inference.vulnerability_id,
73+
alias_names=inference.aliases,
74+
summary=inference.summary,
6875
)
69-
if not vuln:
76+
77+
if not vulnerability:
7078
logger.warn(f"Unable to get vulnerability for inference: {inference!r}")
7179
continue
7280

7381
for ref in inference.references:
74-
reference, _ = models.VulnerabilityReference.objects.get_or_create(
75-
reference_id=ref.reference_id, url=ref.url
82+
83+
reference = VulnerabilityReference.objects.get_or_none(
84+
reference_id=ref.reference_id,
85+
url=ref.url,
7686
)
7787

78-
models.VulnerabilityRelatedReference.objects.update_or_create(
79-
reference=reference, vulnerability=vuln
88+
if not reference:
89+
reference = create_valid_vulnerability_reference(
90+
reference_id=ref.reference_id,
91+
url=ref.url,
92+
)
93+
if not reference:
94+
continue
95+
96+
VulnerabilityRelatedReference.objects.update_or_create(
97+
reference=reference,
98+
vulnerability=vulnerability,
8099
)
81100

82101
for severity in ref.severities:
83-
_vs, updated = models.VulnerabilitySeverity.objects.update_or_create(
102+
_vs, updated = VulnerabilitySeverity.objects.update_or_create(
84103
scoring_system=severity.system.identifier,
85104
reference=reference,
86105
defaults={"value": str(severity.value)},
87106
)
88107
if updated:
89108
logger.info(f"Severity updated for reference {ref!r} to {severity.value!r}")
90109

91-
if inference.affected_purls:
92-
for pkg in inference.affected_purls:
93-
vulnerable_package, _ = _get_or_create_package(pkg)
94-
models.PackageRelatedVulnerability(
95-
vulnerability=vuln,
96-
package=vulnerable_package,
97-
created_by=improver_name,
98-
confidence=inference.confidence,
99-
fix=False,
100-
).update_or_create()
110+
for affected_purl in inference.affected_purls or []:
111+
vulnerable_package = Package.objects.get_or_create_from_purl(purl=affected_purl)
112+
PackageRelatedVulnerability(
113+
vulnerability=vulnerability,
114+
package=vulnerable_package,
115+
created_by=improver_name,
116+
confidence=inference.confidence,
117+
fix=False,
118+
).update_or_create()
101119

102120
if inference.fixed_purl:
103-
fixed_package, _ = _get_or_create_package(inference.fixed_purl)
104-
models.PackageRelatedVulnerability(
105-
vulnerability=vuln,
121+
fixed_package = Package.objects.get_or_create_from_purl(purl=inference.fixed_purl)
122+
PackageRelatedVulnerability(
123+
vulnerability=vulnerability,
106124
package=fixed_package,
107125
created_by=improver_name,
108126
confidence=inference.confidence,
@@ -113,26 +131,25 @@ def process_inferences(inferences: List[Inference], advisory: Advisory, improver
113131
advisory.save()
114132

115133

116-
def _get_or_create_package(p: PackageURL) -> Tuple[models.Package, bool]:
117-
query_kwargs = {}
118-
# TODO: this should be revisited as this should best be a model or manager method... and possibly streamlined
119-
query_kwargs = dict(
120-
type=p.type or "",
121-
namespace=p.namespace or "",
122-
name=p.name or "",
123-
version=p.version or "",
124-
qualifiers=p.qualifiers or {},
125-
subpath=p.subpath or "",
134+
def create_valid_vulnerability_reference(url, reference_id=None):
135+
"""
136+
Create and return a new validated VulnerabilityReference from a
137+
``url`` and ``reference_id``.
138+
Return None and log a warning if this is not a valid reference.
139+
"""
140+
reference = VulnerabilityReference(
141+
reference_id=reference_id,
142+
url=url,
126143
)
127144

128-
return models.Package.objects.get_or_create(**query_kwargs)
129-
145+
try:
146+
reference.full_clean()
147+
except ValidationError as e:
148+
logger.warning(f"Invalid vulnerability reference: {reference!r}: {e}")
149+
return
130150

131-
def _package_url_to_package(purl: PackageURL) -> models.Package:
132-
# FIXME: this is is likely creating a package from a purl?
133-
p = models.Package()
134-
p.set_package_url(purl)
135-
return p
151+
reference.save()
152+
return reference
136153

137154

138155
def get_or_create_vulnerability_and_aliases(vulnerability_id, alias_names, summary):
@@ -145,9 +162,9 @@ def get_or_create_vulnerability_and_aliases(vulnerability_id, alias_names, summa
145162
new_alias_names = set()
146163
for alias_name in alias_names:
147164
try:
148-
alias = models.Alias.objects.get(alias=alias_name)
165+
alias = Alias.objects.get(alias=alias_name)
149166
existing_vulns.add(alias.vulnerability)
150-
except models.Alias.DoesNotExist:
167+
except Alias.DoesNotExist:
151168
new_alias_names.add(alias_name)
152169

153170
# If given set of aliases point to different vulnerabilities in the
@@ -179,14 +196,14 @@ def get_or_create_vulnerability_and_aliases(vulnerability_id, alias_names, summa
179196
vulnerability = existing_alias_vuln
180197
elif vulnerability_id:
181198
try:
182-
vulnerability = models.Vulnerability.objects.get(vulnerability_id=vulnerability_id)
183-
except models.Vulnerability.DoesNotExist:
199+
vulnerability = Vulnerability.objects.get(vulnerability_id=vulnerability_id)
200+
except Vulnerability.DoesNotExist:
184201
logger.warn(
185202
f"Given vulnerability_id: {vulnerability_id} does not exist in the database"
186203
)
187204
return
188205
else:
189-
vulnerability = models.Vulnerability(summary=summary)
206+
vulnerability = Vulnerability(summary=summary)
190207
vulnerability.save()
191208

192209
if summary and summary != vulnerability.summary:
@@ -196,7 +213,7 @@ def get_or_create_vulnerability_and_aliases(vulnerability_id, alias_names, summa
196213
)
197214

198215
for alias_name in new_alias_names:
199-
alias = models.Alias(alias=alias_name, vulnerability=vulnerability)
216+
alias = Alias(alias=alias_name, vulnerability=vulnerability)
200217
alias.save()
201218
logger.info(f"New alias for {vulnerability!r}: {alias_name}")
202219

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#
2+
# Copyright (c) nexB Inc. and others. All rights reserved.
3+
# VulnerableCode is a trademark of nexB Inc.
4+
# SPDX-License-Identifier: Apache-2.0
5+
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
6+
# See https://github.com/nexB/vulnerablecode for support or download.
7+
# See https://aboutcode.org for more information about nexB OSS projects.
8+
#
9+
10+
import re
11+
from typing import Iterable
12+
13+
from django.db.models import Q
14+
from django.db.models.query import QuerySet
15+
16+
from vulnerabilities.importer import AdvisoryData
17+
from vulnerabilities.improver import Improver
18+
from vulnerabilities.improver import Inference
19+
from vulnerabilities.models import VulnerabilityReference
20+
21+
"""
22+
Improver that looks for CVE References without an id and tries to set one.
23+
"""
24+
25+
26+
class CveIdImprover(Improver):
27+
"""
28+
Add a CVE reference id when missing.
29+
Note that we only look for uppercase CVE for now
30+
"""
31+
32+
@property
33+
def interesting_advisories(self) -> QuerySet:
34+
return VulnerabilityReference.objects.filter(
35+
Q(reference_id__isnull=True) | Q(reference_id__exact=""),
36+
url__contains="nvd.nist.gov/vuln/detail/CVE-",
37+
)
38+
39+
def get_inferences(self, advisory_data: AdvisoryData) -> Iterable[Inference]:
40+
cve_pattern = re.compile(r"(CVE-\d{4}-\d{4,7})").search
41+
for ref in self.interesting_advisories:
42+
cve_match = cve_pattern(ref.url)
43+
if cve_match:
44+
cve = cve_match.group()
45+
ref.reference_id = cve
46+
ref.save()

vulnerabilities/migrations/0005_auto_20220329_0938.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# Generated by Django 4.0.2 on 2022-03-29 09:38
21

32
import hashlib
43
import json

0 commit comments

Comments
 (0)