Skip to content

Commit

Permalink
Fix vulnerability status improver
Browse files Browse the repository at this point in the history
Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>
  • Loading branch information
TG1999 committed Sep 29, 2023
1 parent 71592af commit 00639d3
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 76 deletions.
3 changes: 0 additions & 3 deletions vulnerabilities/importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
from vulnerabilities.utils import get_reference_id
from vulnerabilities.utils import is_cve
from vulnerabilities.utils import nearest_patched_package
from vulnerabilities.models import VulnerabilityStatusType

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -249,7 +248,6 @@ class AdvisoryData:
references: List[Reference] = dataclasses.field(default_factory=list)
date_published: Optional[datetime.datetime] = None
weaknesses: List[int] = dataclasses.field(default_factory=list)
status: int = dataclasses.field(default=VulnerabilityStatusType.PUBLISHED)

def __post_init__(self):
if self.date_published and not self.date_published.tzinfo:
Expand All @@ -273,7 +271,6 @@ def to_dict(self):
"references": [ref.to_dict() for ref in self.references],
"date_published": self.date_published.isoformat() if self.date_published else None,
"weaknesses": self.weaknesses,
"status": self.status,
}

@classmethod
Expand Down
4 changes: 2 additions & 2 deletions vulnerabilities/improvers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#

from vulnerabilities.improvers import valid_versions
from vulnerabilities.improvers import rejected_cves
from vulnerabilities.improvers import vulnerability_status

IMPROVERS_REGISTRY = [
valid_versions.GitHubBasicImprover,
Expand All @@ -23,7 +23,7 @@
valid_versions.IstioImprover,
valid_versions.DebianOvalImprover,
valid_versions.UbuntuOvalImprover,
rejected_cves.RejectedCvesImprover,
vulnerability_status.VulnerabilityStatusImprover,
]

IMPROVERS_REGISTRY = {x.qualified_name: x for x in IMPROVERS_REGISTRY}
37 changes: 0 additions & 37 deletions vulnerabilities/improvers/rejected_cves.py

This file was deleted.

80 changes: 80 additions & 0 deletions vulnerabilities/improvers/vulnerability_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#
# 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.
#

import urllib.parse
from typing import Iterable

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

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


class VulnerabilityStatusImprover(Improver):
"""
Generate a translation of Advisory data - returned by the importers - into
full confidence inferences. These are basic database relationships for
unstructured data present in the Advisory model without any other
information source.
"""

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

def get_inferences(self, advisory_data: AdvisoryData) -> Iterable[Inference]:
if not advisory_data:
return []
aliases = advisory_data.aliases
aliases = Alias.objects.filter(alias__in=aliases)
vulnerabilities = Vulnerability.objects.filter(aliases__in=aliases).distinct()

cve_id = None

for alias in aliases:
if alias.startswith("CVE"):
cve_id = alias

if not cve_id:
return []

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):
url = urllib.parse.urljoin(NVD_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")
tags = get_item(response, "containers", "cna", "tags")
if "disputed" in tags:
return VulnerabilityStatusType.DISPUTED
if cve_state == "REJECTED":
return VulnerabilityStatusType.REJECTED
return VulnerabilityStatusType.PUBLISHED

This file was deleted.

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", "0040_remove_advisory_date_improved_advisory_date_imported"),
]

operations = [
migrations.AddField(
model_name="vulnerability",
name="status",
field=models.IntegerField(
choices=[(1, "published"), (2, "reserved"), (3, "disputed"), (4, "rejected")],
default=1,
),
),
]
16 changes: 10 additions & 6 deletions vulnerabilities/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,15 @@ def with_package_counts(self):
),
)


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

PUBLISHED = 1, 'published'
RESERVED = 2, 'reserved'
DISPUTED = 3, 'disputed'
REJECTED = 4, 'rejected'
PUBLISHED = 1, "published"
RESERVED = 2, "reserved"
DISPUTED = 3, "disputed"
REJECTED = 4, "rejected"


class Vulnerability(models.Model):
"""
Expand Down Expand Up @@ -183,7 +185,9 @@ class Vulnerability(models.Model):
through="PackageRelatedVulnerability",
)

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

objects = VulnerabilityQuerySet.as_manager()

Expand Down Expand Up @@ -841,7 +845,6 @@ class Advisory(models.Model):
"module name importing the advisory. Eg:"
"vulnerabilities.importers.nginx.NginxImporter",
)
status = models.IntegerField(choices=VulnerabilityStatusType, default=VulnerabilityStatusType.PUBLISHED)
objects = AdvisoryQuerySet.as_manager()

class Meta:
Expand All @@ -860,6 +863,7 @@ 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 4.1.7 on 2023-09-21 12:20

from django.db import migrations
from django.db import models


class Migration(migrations.Migration):

dependencies = [
("vulnerabilities", "0039_alter_vulnerabilityseverity_scoring_system"),
]

operations = [
migrations.AddField(
model_name="advisory",
name="status",
field=models.IntegerField(
choices=[(1, "published"), (2, "reserved"), (3, "disputed"), (4, "rejected")],
default=1,
),
),
migrations.AddField(
model_name="vulnerability",
name="status",
field=models.IntegerField(
choices=[(1, "published"), (2, "reserved"), (3, "disputed"), (4, "rejected")],
default=1,
),
),
]
10 changes: 5 additions & 5 deletions vulnerabilities/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from vulnerabilities.forms import ApiUserCreationForm
from vulnerabilities.forms import PackageSearchForm
from vulnerabilities.forms import VulnerabilitySearchForm
from vulnerabilities.models import VulnerabilityStatusType, Weakness
from vulnerabilities.models import VulnerabilityStatusType
from vulnerabilities.utils import get_severity_range
from vulnerablecode.settings import env

Expand Down Expand Up @@ -114,13 +114,13 @@ class VulnerabilityDetails(DetailView):

def get_queryset(self):
return super().get_queryset().prefetch_related("references", "aliases", "weaknesses")

def get_status(self, status):
status_by_keys = {
VulnerabilityStatus.PUBLISHED.name : "Published",
VulnerabilityStatus.REJECTED.name : "Rejected"
VulnerabilityStatusType.PUBLISHED: "Published",
VulnerabilityStatusType.REJECTED: "Rejected",
VulnerabilityStatusType.DISPUTED: "Disputed",
}
print(status, status_by_keys)
return status_by_keys[status]

def get_context_data(self, **kwargs):
Expand Down

0 comments on commit 00639d3

Please sign in to comment.