Skip to content

Commit

Permalink
Add Known Exploited Vulnerabilities improver (#1422)
Browse files Browse the repository at this point in the history
Add a Kev test & function docstrings
Change the kev model ( known_ransomware_campaign_use from integer choices to boolean )
Add kev to api
Access the vulnerability directly from Alias
Add a tooltips and edit kev table
Add a kev in a separate tab
Solve migration conflict
Add a basic improver
squash migration files
Add a basic Known Exploited Vulnerabilities model

Signed-off-by: ziadhany <[email protected]>
  • Loading branch information
ziadhany authored Jul 15, 2024
1 parent f16228e commit c311f73
Show file tree
Hide file tree
Showing 8 changed files with 361 additions and 2 deletions.
13 changes: 13 additions & 0 deletions vulnerabilities/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from rest_framework.throttling import UserRateThrottle

from vulnerabilities.models import Alias
from vulnerabilities.models import Kev
from vulnerabilities.models import Package
from vulnerabilities.models import Vulnerability
from vulnerabilities.models import VulnerabilityReference
Expand Down Expand Up @@ -167,6 +168,12 @@ def to_representation(self, instance):
return representation


class KEVSerializer(serializers.ModelSerializer):
class Meta:
model = Kev
fields = ["date_added", "description", "required_action", "due_date", "resources_and_notes"]


class VulnerabilitySerializer(BaseResourceSerializer):
fixed_packages = MinimalPackageSerializer(
many=True, source="filtered_fixed_packages", read_only=True
Expand All @@ -175,6 +182,7 @@ class VulnerabilitySerializer(BaseResourceSerializer):

references = VulnerabilityReferenceSerializer(many=True, source="vulnerabilityreference_set")
aliases = AliasSerializer(many=True, source="alias")
kev = KEVSerializer(read_only=True)
weaknesses = WeaknessSerializer(many=True)

def to_representation(self, instance):
Expand All @@ -183,6 +191,10 @@ def to_representation(self, instance):
weaknesses = data.get("weaknesses", [])
data["weaknesses"] = [weakness for weakness in weaknesses if weakness is not None]

kev = data.get("kev", None)
if not kev:
data.pop("kev")

return data

class Meta:
Expand All @@ -196,6 +208,7 @@ class Meta:
"affected_packages",
"references",
"weaknesses",
"kev",
]


Expand Down
2 changes: 2 additions & 0 deletions vulnerabilities/improvers/__init__.py
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_kev
from vulnerabilities.improvers import vulnerability_status

IMPROVERS_REGISTRY = [
Expand All @@ -27,6 +28,7 @@
valid_versions.RubyImprover,
valid_versions.GithubOSVImprover,
vulnerability_status.VulnerabilityStatusImprover,
vulnerability_kev.VulnerabilityKevImprover,
]

IMPROVERS_REGISTRY = {x.qualified_name: x for x in IMPROVERS_REGISTRY}
66 changes: 66 additions & 0 deletions vulnerabilities/improvers/vulnerability_kev.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import logging
from typing import Iterable

from django.db.models import QuerySet
from sphinx.util import requests

from vulnerabilities.improver import Improver
from vulnerabilities.improver import Inference
from vulnerabilities.models import Advisory
from vulnerabilities.models import Alias
from vulnerabilities.models import Kev

logger = logging.getLogger(__name__)


class VulnerabilityKevImprover(Improver):
"""
Known Exploited Vulnerabilities Improver
"""

@property
def interesting_advisories(self) -> QuerySet:
# TODO Modify KEV improver to iterate over the vulnerabilities alias, not the advisory
return [Advisory.objects.first()]

def get_inferences(self, advisory_data) -> Iterable[Inference]:
"""
Fetch Kev data, iterate over it to find the vulnerability with the specified alias, and create or update
the Kev instance accordingly.
"""

kev_url = (
"https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
)
response = requests.get(kev_url)
kev_data = response.json()
if response.status_code != 200:
logger.error(
f"Failed to fetch the CISA Catalog of Known Exploited Vulnerabilities: {kev_url}"
)
return []

for kev_vul in kev_data.get("vulnerabilities", []):
alias = Alias.objects.get_or_none(alias=kev_vul["cveID"])
if not alias:
continue

vul = alias.vulnerability

if not vul:
continue

Kev.objects.update_or_create(
vulnerability=vul,
defaults={
"description": kev_vul["shortDescription"],
"date_added": kev_vul["dateAdded"],
"required_action": kev_vul["requiredAction"],
"due_date": kev_vul["dueDate"],
"resources_and_notes": kev_vul["notes"],
"known_ransomware_campaign_use": True
if kev_vul["knownRansomwareCampaignUse"] == "Known"
else False,
},
)
return []
72 changes: 72 additions & 0 deletions vulnerabilities/migrations/0057_kev.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Generated by Django 4.1.13 on 2024-05-29 19:14

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
("vulnerabilities", "0056_alter_packagechangelog_software_version_and_more"),
]

operations = [
migrations.CreateModel(
name="Kev",
fields=[
(
"id",
models.AutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
(
"date_added",
models.DateField(
blank=True,
help_text="The date the vulnerability was added to the Known Exploited Vulnerabilities (KEV) catalog in the format YYYY-MM-DD.",
null=True,
),
),
(
"description",
models.TextField(
help_text="Description of the vulnerability in the Known Exploited Vulnerabilities (KEV) catalog, usually a refinement of the original CVE description"
),
),
(
"required_action",
models.TextField(
help_text="The required action to address the vulnerability, typically to apply vendor updates or apply vendor mitigations or to discontinue use."
),
),
(
"due_date",
models.DateField(
help_text="The date the required action is due in the format YYYY-MM-DD,which applies to all USA federal civilian executive branch (FCEB) agencies,but all organizations are strongly encouraged to execute the required action."
),
),
(
"resources_and_notes",
models.TextField(
help_text="Additional notes and resources about the vulnerability, often a URL to vendor instructions."
),
),
(
"known_ransomware_campaign_use",
models.BooleanField(
default=False,
help_text="Known if this vulnerability is known to have been leveraged as part of a ransomware campaign; \n or 'Unknown' if CISA lacks confirmation that the vulnerability has been utilized for ransomware.",
),
),
(
"vulnerability",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="kev",
to="vulnerabilities.vulnerability",
),
),
],
),
]
52 changes: 50 additions & 2 deletions vulnerabilities/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1121,7 +1121,6 @@ class Meta:


class ChangeLog(models.Model):

action_time = models.DateTimeField(
# check if dates are actually UTC
default=timezone.now,
Expand Down Expand Up @@ -1261,7 +1260,6 @@ def log_action(self, package, action_type, actor_name, source_url, related_vulne


class PackageChangeLog(ChangeLog):

AFFECTED_BY = 1
FIXING = 2

Expand Down Expand Up @@ -1309,3 +1307,53 @@ def log_fixing(cls, package, importer, source_url, related_vulnerability):
source_url=source_url,
related_vulnerability=related_vulnerability,
)


class Kev(models.Model):
"""
Known Exploited Vulnerabilities
"""

vulnerability = models.OneToOneField(
Vulnerability,
on_delete=models.CASCADE,
related_name="kev",
)

date_added = models.DateField(
help_text="The date the vulnerability was added to the Known Exploited Vulnerabilities"
" (KEV) catalog in the format YYYY-MM-DD.",
null=True,
blank=True,
)

description = models.TextField(
help_text="Description of the vulnerability in the Known Exploited Vulnerabilities"
" (KEV) catalog, usually a refinement of the original CVE description"
)

required_action = models.TextField(
help_text="The required action to address the vulnerability, typically to "
"apply vendor updates or apply vendor mitigations or to discontinue use."
)

due_date = models.DateField(
help_text="The date the required action is due in the format YYYY-MM-DD,"
"which applies to all USA federal civilian executive branch (FCEB) agencies,"
"but all organizations are strongly encouraged to execute the required action."
)

resources_and_notes = models.TextField(
help_text="Additional notes and resources about the vulnerability,"
" often a URL to vendor instructions."
)

known_ransomware_campaign_use = models.BooleanField(
default=False,
help_text="""Known if this vulnerability is known to have been leveraged as part of a ransomware campaign;
or 'Unknown' if CISA lacks confirmation that the vulnerability has been utilized for ransomware.""",
)

@property
def get_known_ransomware_campaign_use_type(self):
return "Known" if self.known_ransomware_campaign_use else "Unknown"
90 changes: 90 additions & 0 deletions vulnerabilities/templates/vulnerability_details.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@
</span>
</a>
</li>
{% if vulnerability.kev %}<li data-tab="known-exploited-vulnerabilities">
<a>
<span>
Known Exploited Vulnerabilities
</span>
</a>
</li>{% endif %}
<li data-tab="history">
<a>
<span>
Expand Down Expand Up @@ -374,6 +381,89 @@
</tr>
{% endfor %}
</div>
{% if vulnerability.kev %}
<div class="tab-div content" data-content="known-exploited-vulnerabilities">
<div class="has-text-weight-bold tab-nested-div ml-1 mb-1 mt-1">
Known Exploited Vulnerabilities
</div>
<table class="table vcio-table width-100-pct mt-2">
<tbody>
<tr>
<td class="two-col-left">
<span class="has-tooltip-multiline has-tooltip-black has-tooltip-arrow has-tooltip-text-left"
data-tooltip="'Known' if this vulnerability is known to have been leveraged as part of a ransomware campaign; 'Unknown' if CISA lacks confirmation that the vulnerability has been utilized for ransomware">
Known Ransomware Campaign Use:
</span>
</td>
<td class="two-col-right">{{ vulnerability.kev.get_known_ransomware_campaign_use_type }}</td>
</tr>

{% if vulnerability.kev.description %}
<tr>
<td class="two-col-left">
<span class="has-tooltip-multiline has-tooltip-black has-tooltip-arrow has-tooltip-text-left"
data-tooltip="Description of the vulnerability in the Known Exploited Vulnerabilities
(KEV) catalog, usually a refinement of the original CVE description.">
Description:
</span>
</td>
<td class="two-col-right">{{ vulnerability.kev.description }}</td>
</tr>
{% endif %}
{% if vulnerability.kev.required_action %}
<tr>
<td class="two-col-left">
<span class="has-tooltip-multiline has-tooltip-black has-tooltip-arrow has-tooltip-text-left"
data-tooltip="The required action to address the vulnerability">
Required Action:
</span>
</td>
<td class="two-col-right">{{ vulnerability.kev.required_action }}</td>
</tr>
{% endif %}

{% if vulnerability.kev.resources_and_notes %}
<tr>
<td class="two-col-left">
<span class="has-tooltip-multiline has-tooltip-black has-tooltip-arrow has-tooltip-text-left"
data-tooltip="Any additional notes about the vulnerability">
Notes:
</span>
</td>
<td class="two-col-right">{{ vulnerability.kev.resources_and_notes }}</td>
</tr>
{% endif %}

{% if vulnerability.kev.due_date %}
<tr>
<td class="two-col-left">
<span class="has-tooltip-multiline has-tooltip-black has-tooltip-arrow has-tooltip-text-left"
data-tooltip="The date the required action is due in the format YYYY-MM-DD">
Due Date:
</span>
</td>
<td class="two-col-right">{{ vulnerability.kev.due_date }}</td>
</tr>
{% endif %}
{% if vulnerability.kev.date_added %}
<tr>
<td class="two-col-left">
<span
class="has-tooltip-multiline has-tooltip-black has-tooltip-arrow has-tooltip-text-left"
data-tooltip="The date vulnerability was added to the catalog in the format YYYY-MM-DD">
Date Added:
</span>
</td>
<td class="two-col-right">{{ vulnerability.kev.date_added }}</td>
</tr>
{% endif %}

</tbody>
</table>
{% endif %}

</div>

<div class="tab-div content" data-content="history">
<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth">
<thead>
Expand Down
Loading

0 comments on commit c311f73

Please sign in to comment.