Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…boutcode-org#708

Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>
  • Loading branch information
TG1999 committed May 3, 2022
1 parent 554384b commit 1ea8d88
Show file tree
Hide file tree
Showing 15 changed files with 271 additions and 79 deletions.
2 changes: 1 addition & 1 deletion vulnerabilities/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class VulnerabilityAdmin(admin.ModelAdmin):

@admin.register(VulnerabilityReference)
class VulnerabilityReferenceAdmin(admin.ModelAdmin):
search_fields = ["vulnerability__vulnerability_id", "reference_id", "url"]
search_fields = ["vulnerabilityrelatedreference__vulnerability__id", "reference_id", "url"]


@admin.register(Package)
Expand Down
52 changes: 41 additions & 11 deletions vulnerabilities/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from rest_framework.decorators import action
from rest_framework.response import Response

from vulnerabilities.models import Alias
from vulnerabilities.models import Package
from vulnerabilities.models import Vulnerability
from vulnerabilities.models import VulnerabilityReference
Expand All @@ -44,10 +45,11 @@ class Meta:

class VulnerabilityReferenceSerializer(serializers.ModelSerializer):
scores = VulnerabilitySeveritySerializer(many=True, source="vulnerabilityseverity_set")
reference_url = serializers.CharField(source="url")

class Meta:
model = VulnerabilityReference
fields = ["reference_id", "url", "scores"]
fields = ["reference_url", "reference_id", "scores"]


class MinimalPackageSerializer(serializers.HyperlinkedModelSerializer):
Expand All @@ -71,36 +73,64 @@ class MinimalVulnerabilitySerializer(serializers.HyperlinkedModelSerializer):

class Meta:
model = Vulnerability
fields = ["url", "vulnerability_id", "references", "summary"]
fields = ["url", "vulnerability_id", "summary", "references"]


class AliasSerializer(serializers.HyperlinkedModelSerializer):
"""
Used for nesting inside package focused APIs.
"""

class Meta:
model = Alias
fields = ["alias"]


class VulnerabilitySerializer(serializers.HyperlinkedModelSerializer):

resolved_packages = MinimalPackageSerializer(many=True, source="resolved_to", read_only=True)
unresolved_packages = MinimalPackageSerializer(
many=True, source="vulnerable_to", read_only=True
)
fixed_packages = MinimalPackageSerializer(many=True, source="resolved_to", read_only=True)
affected_packages = MinimalPackageSerializer(many=True, source="vulnerable_to", read_only=True)

references = VulnerabilityReferenceSerializer(many=True, source="vulnerabilityreference_set")
aliases = AliasSerializer(many=True, source="alias")

class Meta:
model = Vulnerability
fields = "__all__"
fields = [
"url",
"vulnerability_id",
"summary",
"aliases",
"fixed_packages",
"affected_packages",
"references",
]


class PackageSerializer(serializers.HyperlinkedModelSerializer):

unresolved_vulnerabilities = MinimalVulnerabilitySerializer(
purl = serializers.CharField(source="package_url")
affected_by_vulnerabilities = MinimalVulnerabilitySerializer(
many=True, source="vulnerable_to", read_only=True
)
resolved_vulnerabilities = MinimalVulnerabilitySerializer(
fixing_vulnerabilities = MinimalVulnerabilitySerializer(
many=True, source="resolved_to", read_only=True
)
purl = serializers.CharField(source="package_url")

class Meta:
model = Package
exclude = ["vulnerabilities"]
fields = [
"url",
"purl",
"type",
"namespace",
"name",
"version",
"qualifiers",
"subpath",
"affected_by_vulnerabilities",
"fixing_vulnerabilities",
]


class PackageFilterSet(filters.FilterSet):
Expand Down
12 changes: 3 additions & 9 deletions vulnerabilities/importers/alpine_linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ class AlpineImporter(Importer):
license_url = "https://secdb.alpinelinux.org/license.txt"

def advisory_data(self) -> Iterable[AdvisoryData]:
advisories = []
page_response_content = fetch_response(BASE_URL).content
advisory_directory_links = fetch_advisory_directory_links(page_response_content)
advisory_links = []
Expand All @@ -68,8 +67,7 @@ def advisory_data(self) -> Iterable[AdvisoryData]:
if not record["packages"]:
LOGGER.error(f'"packages" not found in {link!r}')
continue
advisories.extend(process_record(record))
return advisories
yield from process_record(record)


def fetch_response(url):
Expand Down Expand Up @@ -127,7 +125,7 @@ def check_for_attributes(record) -> bool:
return True


def process_record(record: dict) -> List[AdvisoryData]:
def process_record(record: dict) -> Iterable[AdvisoryData]:
"""
Return a list of AdvisoryData objects by processing data
present in that `record`
Expand All @@ -136,22 +134,18 @@ def process_record(record: dict) -> List[AdvisoryData]:
LOGGER.error(f'"packages" not found in this record {record!r}')
return []

advisories: List[AdvisoryData] = []

for package in record["packages"]:
if not package["pkg"]:
LOGGER.error(f'"pkg" not found in this package {package!r}')
continue
if not check_for_attributes(record):
continue
loaded_advisories = load_advisories(
yield from load_advisories(
package["pkg"],
record["distroversion"],
record["reponame"],
record["archs"],
)
advisories.extend(loaded_advisories)
return advisories


def load_advisories(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Generated by Django 4.0.3 on 2022-04-26 08:42

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


class Migration(migrations.Migration):

dependencies = [
('vulnerabilities', '0010_vulnerabilityrelatedreference_and_more'),
]

operations = [
migrations.AddField(
model_name='vulnerability',
name='packages',
field=models.ManyToManyField(through='vulnerabilities.PackageRelatedVulnerability', to='vulnerabilities.package'),
),
migrations.AlterField(
model_name='package',
name='vulnerabilities',
field=models.ManyToManyField(through='vulnerabilities.PackageRelatedVulnerability', to='vulnerabilities.vulnerability'),
),
migrations.AlterField(
model_name='packagerelatedvulnerability',
name='package',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='vulnerabilities.package'),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.0.4 on 2022-05-03 09:41

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('vulnerabilities', '0011_vulnerability_packages_alter_package_vulnerabilities_and_more'),
]

operations = [
migrations.AlterField(
model_name='vulnerability',
name='vulnerability_id',
field=models.CharField(blank=True, help_text='Unique identifier for a vulnerability in the external representation. It is prefixed with VULCOID-', max_length=20, unique=True),
),
]
22 changes: 22 additions & 0 deletions vulnerabilities/migrations/0013_auto_20220503_0941.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 4.0.4 on 2022-05-03 09:41

from django.db import migrations

from django.utils.http import int_to_base36

class Migration(migrations.Migration):

dependencies = [
('vulnerabilities', '0012_alter_vulnerability_vulnerability_id'),
]

def save_vulnerbaility_id(apps, schema_editor):
Vulnerabilities = apps.get_model("vulnerabilities", "Vulnerability")
for vulnerability in Vulnerabilities.objects.all():
if not vulnerability.vulnerability_id:
vulnerability.vulnerability_id = f"VULCOID-{int_to_base36(vulnerability.id).upper()}"
vulnerability.save()

operations = [
migrations.RunPython(save_vulnerbaility_id)
]
52 changes: 35 additions & 17 deletions vulnerabilities/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from django.core.validators import MaxValueValidator
from django.core.validators import MinValueValidator
from django.db import models
from django.utils.http import int_to_base36
from packageurl import PackageURL
from packageurl.contrib.django.models import PackageURLMixin

Expand All @@ -47,12 +48,12 @@ class Vulnerability(models.Model):
stored as ``Alias``.
"""

vulnerability_id = models.UUIDField(
default=uuid.uuid4,
editable=False,
vulnerability_id = models.CharField(
unique=True,
help_text="Unique identifier for a vulnerability in this database, assigned automatically. "
"In the external representation it is prefixed with VULCOID-",
blank=True,
max_length=20,
help_text="Unique identifier for a vulnerability in the external representation. "
"It is prefixed with VULCOID-",
)

summary = models.TextField(
Expand All @@ -63,28 +64,42 @@ class Vulnerability(models.Model):
references = models.ManyToManyField(
to="VulnerabilityReference", through="VulnerabilityRelatedReference"
)
packages = models.ManyToManyField(
to="Package",
through="PackageRelatedVulnerability",
)

@property
def vulcoid(self):
return f"VULCOID-{self.vulnerability_id}"
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if not self.vulnerability_id:
self.vulnerability_id = f"VULCOID-{int_to_base36(self.id).upper()}"
super().save(update_fields=["vulnerability_id"])

@property
def vulnerable_to(self):
"""
Return packages that are vulnerable to this vulnerability.
"""
return self.packages.filter(vulnerabilities__packagerelatedvulnerability__fix=False)
return self.packages.filter(packagerelatedvulnerability__fix=False)

@property
def resolved_to(self):
"""
Returns packages that first received patch against this vulnerability
in their particular version history.
"""
return self.packages.filter(vulnerabilities__packagerelatedvulnerability__fix=True)
return self.packages.filter(packagerelatedvulnerability__fix=True)

@property
def alias(self):
"""
Returns packages that first received patch against this vulnerability
in their particular version history.
"""
return self.aliases.all()

def __str__(self):
return self.vulcoid
return self.vulnerability_id

class Meta:
verbose_name_plural = "Vulnerabilities"
Expand Down Expand Up @@ -150,10 +165,7 @@ class Package(PackageURLMixin):
"""

vulnerabilities = models.ManyToManyField(
to="Vulnerability",
through="PackageRelatedVulnerability",
through_fields=("package", "vulnerability"),
related_name="packages",
to="Vulnerability", through="PackageRelatedVulnerability"
)

# Remove the `qualifers` and `set_package_url` overrides after
Expand Down Expand Up @@ -218,8 +230,14 @@ def __str__(self):
class PackageRelatedVulnerability(models.Model):

# TODO: Fix related_name
package = models.ForeignKey(Package, on_delete=models.CASCADE, related_name="package")
vulnerability = models.ForeignKey(Vulnerability, on_delete=models.CASCADE)
package = models.ForeignKey(
Package,
on_delete=models.CASCADE,
)
vulnerability = models.ForeignKey(
Vulnerability,
on_delete=models.CASCADE,
)
created_by = models.CharField(
max_length=100,
blank=True,
Expand Down
8 changes: 4 additions & 4 deletions vulnerabilities/templates/package_update.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ <h1 class="title">
<div class="column is-full">
<div class="card has-background-danger-light">
<header class="card-header mb-3">
<p class="card-header-title">Vulnerable To</p>
<p class="card-header-title">Affected By</p>
</header>
<div class="tags mx-3">
{% for vulnerability in impacted_vuln %}
<span class="tag is-danger is-medium">
<a href="{% url 'vulnerability_view' vulnerability.pk %}" class="has-text-white">{{vulnerability.vulnerability_id}}</a>
<a href="{% url 'vulnerability_view' vulnerability.pk %}" class="has-text-white">{{vulnerability.vulcoid}}</a>
</span>
{% endfor %}
</div>
Expand All @@ -49,12 +49,12 @@ <h1 class="title">
<div class="column is-full">
<div class="card has-background-danger-light">
<header class="card-header mb-3">
<p class="card-header-title">Safe To</p>
<p class="card-header-title">Fixing</p>
</header>
<div class="tags mx-3">
{% for vulnerability in resolved_vuln %}
<span class="tag is-primary is-medium">
<a href="{% url 'vulnerability_view' vulnerability.pk %}" class="has-text-white">{{vulnerability.vulnerability_id}}</a>
<a href="{% url 'vulnerability_view' vulnerability.pk %}" class="has-text-white">{{vulnerability.vulcoid}}</a>
</span>
{% endfor %}
</div>
Expand Down
4 changes: 2 additions & 2 deletions vulnerabilities/templates/packages.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ <h1 class="title">
<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth">
<tr>
<th>Package URL</th>
<th>Vulnerabilities</th>
<th>Patched Vulnerabilities</th>
<th>Affected By Vulnerabilities</th>
<th>Fixing Vulnerabilities</th>
</tr>
{% for package in packages %}
<tr>
Expand Down
6 changes: 3 additions & 3 deletions vulnerabilities/templates/vulnerabilities.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ <h1 class="title">
<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth">
<tr>
<th>Vulnerability ID</th>
<th>Vulnerable packages</th>
<th>Patched packages</th>
<th>Affected packages</th>
<th>Fixed packages</th>
</tr>
{% for vulnerability in vulnerabilities %}
<tr>
<td><a href="{% url 'vulnerability_view' vulnerability.pk %}">{{vulnerability.vulnerability_id}}</a></td>
<td><a href="{% url 'vulnerability_view' vulnerability.pk %}">{{vulnerability.vulcoid}}</a></td>
<td>{{vulnerability.vulnerable_package_count}}</td>
<td>{{vulnerability.patched_package_count}}</td>
</tr>
Expand Down
Loading

0 comments on commit 1ea8d88

Please sign in to comment.