Skip to content
This repository has been archived by the owner on Sep 16, 2022. It is now read-only.

CVE page #617

Merged
merged 18 commits into from
Jan 17, 2020
Merged
Show file tree
Hide file tree
Changes from 17 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 backend/device_registry/celery_tasks/ubuntu_cve.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from itertools import chain
import time

import dateutil.parser
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pls put it to appropriate group of imports

from django.conf import settings

import redis
Expand Down Expand Up @@ -291,6 +292,7 @@ def fetch_vulnerabilities():
'medium': Vulnerability.Urgency.MEDIUM,
'high': Vulnerability.Urgency.HIGH,
}.get(header['Priority'], Vulnerability.Urgency.NONE),
pub_date=dateutil.parser.parse(header['PublicDate']),
remote=None,
fix_available=(status == 'fixed'),
os_release_codename=codename
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class Migration(migrations.Migration):
('is_binary', models.BooleanField()),
('unstable_version', models.CharField(blank=True, max_length=64)),
('other_versions', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=64), blank=True, size=None)),
('urgency', models.CharField(choices=[(device_registry.models.Vulnerability.Urgency(' '), ' '), (device_registry.models.Vulnerability.Urgency('L'), 'L'), (device_registry.models.Vulnerability.Urgency('M'), 'M'), (device_registry.models.Vulnerability.Urgency('H'), 'H')], max_length=64)),
('urgency', models.CharField(max_length=64)),
('remote', models.BooleanField(null=True)),
('fix_available', models.BooleanField()),
],
Expand Down
44 changes: 44 additions & 0 deletions backend/device_registry/migrations/0079_vulnerability.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Generated by Django 2.2.6 on 2020-01-09 11:37

import device_registry.models
from django.db import migrations, models
from django.db.models import Case, CharField, Value, When


def convert_urgencies(apps, schema_editor):
urgencies = {
'Urgency.NONE': 0,
'Urgency.LOW': 1,
'Urgency.MEDIUM': 2,
'Urgency.HIGH': 3,
}
Vulnerability = apps.get_model('device_registry', 'Vulnerability')
Vulnerability.objects.update(urgency=Case(
*[When(urgency=k, then=Value(v)) for k, v in urgencies.items()],
default=Value(0)
))


class Migration(migrations.Migration):

dependencies = [
('device_registry', '0078_merge_20200107_1719'),
]

operations = [
migrations.RunPython(convert_urgencies),
migrations.AlterField(
model_name='vulnerability',
name='urgency',
field=models.PositiveSmallIntegerField(choices=[(device_registry.models.Vulnerability.Urgency(0), 0), (device_registry.models.Vulnerability.Urgency(1), 1), (device_registry.models.Vulnerability.Urgency(2), 2), (device_registry.models.Vulnerability.Urgency(3), 3)]),
),
migrations.AddField(
model_name='vulnerability',
name='pub_date',
field=models.DateField(null=True),
),
migrations.AlterUniqueTogether(
name='vulnerability',
unique_together={('os_release_codename', 'name', 'package')},
)
]
40 changes: 32 additions & 8 deletions backend/device_registry/models.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
from enum import Enum, IntEnum
import datetime
from statistics import mean
import json
import uuid
from typing import NamedTuple

from django.conf import settings
from django.db import models, transaction
from django.db.models import Q, Avg
from django.db.models import Q
a-martynovich marked this conversation as resolved.
Show resolved Hide resolved
from django.utils import timezone
from django.contrib.postgres.fields import ArrayField, JSONField
from django.core.exceptions import ObjectDoesNotExist
Expand Down Expand Up @@ -473,6 +472,27 @@ def vulnerable_packages(self):
self.os_release.get('codename') in DEBIAN_SUITES + UBUNTU_SUITES:
return self.deb_packages.filter(vulnerabilities__isnull=False).distinct().order_by('name')

@property
def cve_count(self):
"""
Count the number of high, medium and low severity CVEs for the device.
:return: A dict of {'high': N1, 'med': N2, 'low': N3} or None if no deb packages or unsupported OS.
"""

# We have no vulnerability data for OS other than Debian and Ubuntu flavors.
if not(self.deb_packages_hash and self.deb_packages.exists() and self.os_release
a-martynovich marked this conversation as resolved.
Show resolved Hide resolved
and self.os_release.get('codename') in DEBIAN_SUITES + UBUNTU_SUITES):
return
vuln_qs = Vulnerability.objects.filter(urgency__gte=Vulnerability.Urgency.LOW, debpackage__device=self,
a-martynovich marked this conversation as resolved.
Show resolved Hide resolved
fix_available=True)
severities = {
Vulnerability.Urgency.HIGH: 'high',
Vulnerability.Urgency.MEDIUM: 'med',
Vulnerability.Urgency.LOW: 'low'
}
return {severities[urgency]: vuln_qs.filter(urgency=urgency).values('name').distinct().count()
a-martynovich marked this conversation as resolved.
Show resolved Hide resolved
for urgency in severities}

def generate_recommended_actions(self, classes=None):
"""
Generate RAs for this device and store them as RecommendedAction objects in database.
Expand Down Expand Up @@ -806,6 +826,9 @@ class Meta:


class Vulnerability(models.Model):
class Meta:
unique_together = ['os_release_codename', 'name', 'package']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably that's not ok because you need to have unique names for each os and having 'package' here will break this constraint logic.

Copy link
Contributor Author

@a-martynovich a-martynovich Jan 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, for each OS there may be multiple vulnerabilities with the same name but different packages. It's when one CVE affects multiple packages.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok then


class Version:
"""Version class which uses the original APT comparison algorithm."""

Expand All @@ -826,21 +849,22 @@ def __lt__(self, other):
def __eq__(self, other):
return apt_pkg.version_compare(self.__asString, other.__asString) == 0

class Urgency(Enum):
NONE = ' '
LOW = 'L'
MEDIUM = 'M'
HIGH = 'H'
class Urgency(IntEnum):
NONE = 0
LOW = 1
MEDIUM = 2
HIGH = 3

os_release_codename = models.CharField(max_length=64, db_index=True)
name = models.CharField(max_length=64)
package = models.CharField(max_length=64, db_index=True)
is_binary = models.BooleanField()
unstable_version = models.CharField(max_length=64, blank=True)
other_versions = ArrayField(models.CharField(max_length=64), blank=True)
urgency = models.CharField(max_length=64, choices=[(tag, tag.value) for tag in Urgency])
urgency = models.PositiveSmallIntegerField(choices=[(tag, tag.value) for tag in Urgency])
remote = models.BooleanField(null=True)
fix_available = models.BooleanField()
pub_date = models.DateField(null=True)

def is_vulnerable(self, src_ver):
if self.unstable_version:
Expand Down
92 changes: 92 additions & 0 deletions backend/device_registry/templates/cve.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
{% extends "admin_base.html" %}

{% block title %}WoTT - CVE list{% endblock title %}

{% block dashboard_title %}
<h1 style="margin-bottom: 0">CVE list{% if device_name %} for {{ device_name }}{% endif %}</h1>
{% endblock dashboard_title %}

{% block admin_content %}
<!-- cve.html -->
<div class="container-fluid p-0">
<div class="card">
<div class="card-body">
<table class="table table-striped table-responsive-xs" >
<thead>
<th>CVE</th>
<th>Date</th>
<th>Severity</th>
<th>Packages Affected</th>
{% if not device_name %}
<th>Nodes Affected</th>
{% endif %}
<th>Solve</th>
</thead>
<tbody>
{% for row in table_rows %}
<tr>
<td>
<a href="{{ row.cve_link.href }}">{{ row.cve_link.text }}</a>
</td>
<td>{{ row.cve_date|date:"Y-m-d"|default:"N/A" }}</td>
<td>{{ row.severity }}</td>
<td>
{% for p in row.packages %}
{{ p.name }}
<br>
{% endfor %}
</td>
{% if not device_name %}
<td>
{% for p in row.packages %}
<a href="#" class="wott-popover">
{{ p.device_urls|length }}
<template>
{% for du in p.device_urls %}
<a href="{{ du.href }}">{{ du.text }}</a><br>
{% endfor %}
</template>
</a>
<br>
{% endfor %}
</td>
{% endif %}
<td>
{% for p in row.packages %}
<a href="#" class="wott-popover">
Instructions
<template>
Run the following command:
<pre>{{ p.upgrade_command }}</pre>
</template>
</a>
<br>
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock admin_content %}

{% block scripts %}
{{ block.super }}

<script>
$(function () {
$('[data-toggle="popover"]').popover()
});

$('.wott-popover').popover({
html: true,
trigger: 'click',
title: 'Details',
content: function() {
return $(this).children('template').html();
}
})
</script>
{% endblock scripts %}
18 changes: 6 additions & 12 deletions backend/device_registry/templates/device_info_security.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,16 @@ <h4 class="tab-title">Security</h4>
<tr>
<th scope="row">Vulnerable Packages</th>
<td>
{% with object.vulnerable_packages as vulnerable_packages %}
{% if vulnerable_packages is None %}
{% with object.cve_count as cve_count %}
{% if cve_count is None %}
N/A
{% elif vulnerable_packages.exists %}
<ul>
{% for package in vulnerable_packages %}
<li>{{ package.name }} / {{ package.version }}
({% for v in package.vulnerabilities.all %}<a href="https://security-tracker.debian.org/tracker/{{ v.name }}"
target="_blank">{{ v.name }}</a>{% if not forloop.last %}, {% endif %}{% endfor %})
</li>
{% endfor %}
</ul>
{% else %}
{% include "badge.html" with icon="check" color="success" %}
High: {{ cve_count.high }}<br>
Medium: {{ cve_count.med }}<br>
Low: {{ cve_count.low }}<br>
{% endif %}
{% endwith %}
<a href="{% url 'device_cve' object.pk %}">Detailed View</a>
</td>
</tr>
{% with object.heartbleed_vulnerable as heartbleed_vulnerable %}
Expand Down
Loading