8000 Mark advisories status according to NVD advisory by TG1999 · Pull Request #1232 · aboutcode-org/vulnerablecode · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Mark advisories status according to NVD advisory #1232

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

Merged
merged 13 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions vulnerabilities/improvers/__init__.py
< 8000 table class=" diff-table js-diff-table tab-size " data-tab-size="8" data-diff-anchor="diff-3438eb93d19be1ca3a2c38b5e8916c59e3733fba465ee8250f31b892dd5dbba2" data-paste-markdown-skip> Original file line number Diff line number Diff line change Expand Up @@ -8,6 +8,7 @@ #
from vulnerabilities.improvers import valid_versions from vulnerabilities.improvers import vulnerability_status
IMPROVERS_REGISTRY = [ valid_versions.GitHubBasicImprover, Expand All @@ -23,6 +24,7 @@ valid_versions.DebianOvalImprover, valid_versions.UbuntuOvalImprover, valid_versions.OSSFuzzImprover, vulnerability_status.VulnerabilityStatusImprover, ]
IMPROVERS_REGISTRY = {x.qualified_name: x for x in IMPROVERS_REGISTRY}
87 changes: 87 additions & 0 deletions vulnerabilities/improvers/vulnerability_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#
# Copyright (c) nexB Inc. and others. All rights reserved.
# VulnerableCode is a trademark of nexB Inc.
# SPDX-License-Identifier: Apache-2.0
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
# See https://github.com/nexB/vulnerablecode for support or download.
# See https://aboutcode.org for more information about nexB OSS projects.
#


from typing import Iterable
from urllib.parse import urljoin

from django.db.models import Q
from django.db.models.query import QuerySet

from vulnerabilities.importer import AdvisoryData
from vulnerabilities.importers.nvd import NVDImporter
from vulnerabilities.improver import Improver
from vulnerabilities.improver import Inference
from vulnerabilities.models import Advisory
from vulnerabilities.models import Alias
from vulnerabilities.models import Vulnerability
from vulnerabilities.models import VulnerabilityStatusType
from vulnerabilities.utils import fetch_response
from vulnerabilities.utils import get_item

MITRE_API_URL = "https://cveawg.mitre.org/api/cve/"


class VulnerabilityStatusImprover(Improver):
"""
Update vulnerability with NVD statues
"""

@property
def interesting_advisories(self) -> QuerySet:
return (
Advisory.objects.filter(Q(created_by=NVDImporter.qualified_name))
.distinct("aliases")
.paginated()
)

def get_inferences(self, advisory_data: AdvisoryData) -> Iterable[Inference]:
"""
This is a work-around until we have new style importer and improver
and this get_inferences function updates the vulnerability status directly
# TODO: Replace this with new style improvers
"""
if not advisory_data:
return []
aliases = advisory_data.aliases
# NVD Importer only has one alias in it and this a CVE
assert len(aliases) == 1
cve_id = aliases[0]
if not cve_id.startswith("CVE"):
return []

alias = Alias.objects.get(alias=cve_id)
vulnerabilities = Vulnerability.objects.filter(aliases__alias=alias).distinct()

for vuln in vulnerabilities:
status = get_status_from_api(cve_id=cve_id)
if not status:
status = VulnerabilityStatusType.PUBLISHED
vuln.status = status
vuln.save()
return []


def get_status_from_api(cve_id):
"""
Return the CVE status from the MITRE API
"""
url = urljoin(MITRE_API_URL, cve_id)
try:
response = fetch_response(url=url)
except Exception as e:
return
response = response.json()
cve_state = get_item(response, "cveMetadata", "state") or None
tags = get_item(response, "containers", "cna", "tags") or []
if "disputed" in tags:
return VulnerabilityStatusType.DISPUTED
if cve_state and cve_state == "REJECTED":
return VulnerabilityStatusType.INVALID
return VulnerabilityStatusType.PUBLISHED
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 4.1.7 on 2023-09-29 05:26

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("vulnerabilities", "0041_remove_vulns_with_empty_aliases"),
]

operations = [
migrations.AddField(
model_name="vulnerability",
name="status",
field=models.IntegerField(
choices=[(1, "published"), (2, "disputed"), (3, "invalid")],
default=1,
),
),
]
28 changes: 23 additions & 5 deletions vulnerabilities/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@
from packageurl.contrib.django.models import without_empty_values
from rest_framework.authtoken.models import Token

from vulnerabilities.importer import AdvisoryData
from vulnerabilities.importer import AffectedPackage
from vulnerabilities.importer import Reference
from vulnerabilities.improver import MAX_CONFIDENCE
from vulnerabilities.severity_systems import SCORING_SYSTEMS
from vulnerabilities.utils import build_vcid
from vulnerabilities.utils import remove_qualifiers_and_subpath
Expand Down Expand Up @@ -153,6 +149,14 @@ def with_package_counts(self):
)


class VulnerabilityStatusType(models.IntegerChoices):
"""List of vulnerability statuses."""

PUBLISHED = 1, "Published"
DISPUTED = 2, "Disputed"
INVALID = 3, "Invalid"


class Vulnerability(models.Model):
"""
A software vulnerability with a unique identifier and alternate ``aliases``.
Expand Down Expand Up @@ -181,6 +185,10 @@ class Vulnerability(models.Model):
through="PackageRelatedVulnerability",
)

status = models.IntegerField(
choices=VulnerabilityStatusType.choices, default=VulnerabilityStatusType.PUBLISHED
)

objects = VulnerabilityQuerySet.as_manager()

class Meta:
Expand Down Expand Up @@ -230,6 +238,11 @@ def get_aliases(self):

alias = get_aliases

@property
def get_status_label(self):
label_by_status = {choice[0]: choice[1] for choice in VulnerabilityStatusType.choices}
return label_by_status.get(self.status) or VulnerabilityStatusType.PUBLISHED.label

def get_absolute_url(self):
"""
Return this Vulnerability details absolute URL.
Expand Down Expand Up @@ -653,6 +666,7 @@ class PackageRelatedVulnerability(models.Model):
"module name responsible for creating this relation. Eg:"
"vulnerabilities.importers.nginx.NginxBasicImprover",
)
from vulnerabilities.improver import MAX_CONFIDENCE

confidence = models.PositiveIntegerField(
default=MAX_CONFIDENCE,
Expand Down Expand Up @@ -852,7 +866,11 @@ def save(self, *args, **kwargs):
self.unique_content_id = checksum.hexdigest()
super().save(*args, **kwargs)

def to_advisory_data(self) -> AdvisoryData:
def to_advisory_data(self) -> "AdvisoryData":
from vulnerabilities.importer import AdvisoryData
from vulnerabilities.importer import AffectedPackage
from vulnerabilities.importer import Reference

return AdvisoryData(
aliases=self.aliases,
summary=self.summary,
Expand Down
4 changes: 4 additions & 0 deletions vulnerabilities/templates/vulnerability_details.html
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@
</td>
{% endif %}
</tr>
<tr>
<td class="two-col-left">Status</td>
<td class="two-col-right">{{ status }}</td>
</tr>
</tbody>
</table>
</div>
Expand Down
Loading
0