Skip to content

Commit 1efb3e1

Browse files
committed
Reference #707, #695, #675 and #708
Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>
1 parent 67dda33 commit 1efb3e1

15 files changed

+276
-73
lines changed

vulnerabilities/admin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class VulnerabilityAdmin(admin.ModelAdmin):
3737

3838
@admin.register(VulnerabilityReference)
3939
class VulnerabilityReferenceAdmin(admin.ModelAdmin):
40-
search_fields = ["vulnerability__vulnerability_id", "reference_id", "url"]
40+
search_fields = ["vulnerabilityrelatedreference__vulnerability__id", "reference_id", "url"]
4141

4242

4343
@admin.register(Package)

vulnerabilities/api.py

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from rest_framework.decorators import action
3131
from rest_framework.response import Response
3232

33+
from vulnerabilities.models import Alias
3334
from vulnerabilities.models import Package
3435
from vulnerabilities.models import Vulnerability
3536
from vulnerabilities.models import VulnerabilityReference
@@ -44,10 +45,11 @@ class Meta:
4445

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

4850
class Meta:
4951
model = VulnerabilityReference
50-
fields = ["reference_id", "url", "scores"]
52+
fields = ["reference_url", "reference_id", "scores"]
5153

5254

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

7274
class Meta:
7375
model = Vulnerability
74-
fields = ["url", "vulnerability_id", "references", "summary"]
76+
fields = ["url", "vulcoid", "summary", "references"]
77+
78+
79+
class AliasSerializer(serializers.HyperlinkedModelSerializer):
80+
"""
81+
Used for nesting inside package focused APIs.
82+
"""
83+
84+
class Meta:
85+
model = Alias
86+
fields = ["alias"]
7587

7688

7789
class VulnerabilitySerializer(serializers.HyperlinkedModelSerializer):
7890

79-
resolved_packages = MinimalPackageSerializer(many=True, source="resolved_to", read_only=True)
80-
unresolved_packages = MinimalPackageSerializer(
81-
many=True, source="vulnerable_to", read_only=True
82-
)
91+
fixed_packages = MinimalPackageSerializer(many=True, source="resolved_to", read_only=True)
92+
affected_packages = MinimalPackageSerializer(many=True, source="vulnerable_to", read_only=True)
8393

8494
references = VulnerabilityReferenceSerializer(many=True, source="vulnerabilityreference_set")
95+
aliases = AliasSerializer(many=True, source="alias")
8596

8697
class Meta:
8798
model = Vulnerability
88-
fields = "__all__"
99+
fields = [
100+
"url",
101+
"vulcoid",
102+
"summary",
103+
"aliases",
104+
"fixed_packages",
105+
"affected_packages",
106+
"references",
107+
]
89108

90109

91110
class PackageSerializer(serializers.HyperlinkedModelSerializer):
92111

93-
unresolved_vulnerabilities = MinimalVulnerabilitySerializer(
112+
purl = serializers.CharField(source="package_url")
113+
affected_by_vulnerabilities = MinimalVulnerabilitySerializer(
94114
many=True, source="vulnerable_to", read_only=True
95115
)
96-
resolved_vulnerabilities = MinimalVulnerabilitySerializer(
116+
fixing_vulnerabilities = MinimalVulnerabilitySerializer(
97117
many=True, source="resolved_to", read_only=True
98118
)
99-
purl = serializers.CharField(source="package_url")
100119

101120
class Meta:
102121
model = Package
103-
exclude = ["vulnerabilities"]
122+
fields = [
123+
"url",
124+
"purl",
125+
"type",
126+
"namespace",
127+
"name",
128+
"version",
129+
"qualifiers",
130+
"subpath",
131+
"affected_by_vulnerabilities",
132+
"fixing_vulnerabilities",
133+
]
104134

105135

106136
class PackageFilterSet(filters.FilterSet):

vulnerabilities/importers/alpine_linux.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ class AlpineImporter(Importer):
5454
license_url = "https://secdb.alpinelinux.org/license.txt"
5555

5656
def advisory_data(self) -> Iterable[AdvisoryData]:
57-
advisories = []
5857
page_response_content = fetch_response(BASE_URL).content
5958
advisory_directory_links = fetch_advisory_directory_links(page_response_content)
6059
advisory_links = []
@@ -68,8 +67,7 @@ def advisory_data(self) -> Iterable[AdvisoryData]:
6867
if not record["packages"]:
6968
LOGGER.error(f'"packages" not found in {link!r}')
7069
continue
71-
advisories.extend(process_record(record))
72-
return advisories
70+
yield from process_record(record)
7371

7472

7573
def fetch_response(url):
@@ -127,7 +125,7 @@ def check_for_attributes(record) -> bool:
127125
return True
128126

129127

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

139-
advisories: List[AdvisoryData] = []
140-
141137
for package in record["packages"]:
142138
if not package["pkg"]:
143139
LOGGER.error(f'"pkg" not found in this package {package!r}')
144140
continue
145141
if not check_for_attributes(record):
146142
continue
147-
loaded_advisories = load_advisories(
143+
yield from load_advisories(
148144
package["pkg"],
149145
record["distroversion"],
150146
record["reponame"],
151147
record["archs"],
152148
)
153-
advisories.extend(loaded_advisories)
154-
return advisories
155149

156150

157151
def load_advisories(
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Generated by Django 4.0.3 on 2022-04-26 08:42
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('vulnerabilities', '0010_vulnerabilityrelatedreference_and_more'),
11+
]
12+
13+
operations = [
14+
migrations.AddField(
15+
model_name='vulnerability',
16+
name='packages',
17+
field=models.ManyToManyField(through='vulnerabilities.PackageRelatedVulnerability', to='vulnerabilities.package'),
18+
),
19+
migrations.AlterField(
20+
model_name='package',
21+
name='vulnerabilities',
22+
field=models.ManyToManyField(through='vulnerabilities.PackageRelatedVulnerability', to='vulnerabilities.vulnerability'),
23+
),
24+
migrations.AlterField(
25+
model_name='packagerelatedvulnerability',
26+
name='package',
27+
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='vulnerabilities.package'),
28+
),
29+
]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 4.0.3 on 2022-04-26 15:31
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('vulnerabilities', '0011_vulnerability_packages_alter_package_vulnerabilities_and_more'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='vulnerability',
15+
name='vulcoid',
16+
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),
17+
),
18+
]
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Generated by Django 4.0.3 on 2022-04-26 12:44
2+
3+
from django.db import migrations
4+
5+
from django.utils.http import int_to_base36
6+
7+
8+
9+
class Migration(migrations.Migration):
10+
11+
def save_vulcoid(apps, schema_editor):
12+
Vulnerabilities = apps.get_model("vulnerabilities", "Vulnerability")
13+
for vulnerability in Vulnerabilities.objects.all():
14+
if not vulnerability.vulcoid:
15+
vulnerability.vulcoid = f"VULCOID-{int_to_base36(vulnerability.id).upper()}"
16+
vulnerability.save()
17+
18+
dependencies = [
19+
('vulnerabilities', '0012_vulnerability_vulcoid'),
20+
]
21+
22+
operations = [
23+
migrations.RunPython(save_vulcoid)
24+
]

vulnerabilities/models.py

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from django.core.validators import MaxValueValidator
3030
from django.core.validators import MinValueValidator
3131
from django.db import models
32+
from django.utils.http import int_to_base36
3233
from packageurl import PackageURL
3334
from packageurl.contrib.django.models import PackageURLMixin
3435

@@ -63,25 +64,48 @@ class Vulnerability(models.Model):
6364
references = models.ManyToManyField(
6465
to="VulnerabilityReference", through="VulnerabilityRelatedReference"
6566
)
67+
packages = models.ManyToManyField(
68+
to="Package",
69+
through="PackageRelatedVulnerability",
70+
)
6671

67-
@property
68-
def vulcoid(self):
69-
return f"VULCOID-{self.vulnerability_id}"
72+
vulcoid = models.CharField(
73+
unique=True,
74+
blank=True,
75+
max_length=20,
76+
help_text="Unique identifier for a vulnerability in the external representation. "
77+
"It is prefixed with VULCOID-",
78+
)
79+
80+
def save(self, *args, **kwargs):
81+
super().save(*args, **kwargs)
82+
if not self.vulcoid:
83+
self.vulcoid = f"VULCOID-{int_to_base36(self.id).upper()}"
84+
print(f"Vulnerability {self.vulcoid} created")
85+
super().save(update_fields=["vulcoid"])
7086

7187
@property
7288
def vulnerable_to(self):
7389
"""
7490
Return packages that are vulnerable to this vulnerability.
7591
"""
76-
return self.packages.filter(vulnerabilities__packagerelatedvulnerability__fix=False)
92+
return self.packages.filter(packagerelatedvulnerability__fix=False)
7793

7894
@property
7995
def resolved_to(self):
8096
"""
8197
Returns packages that first received patch against this vulnerability
8298
in their particular version history.
8399
"""
84-
return self.packages.filter(vulnerabilities__packagerelatedvulnerability__fix=True)
100+
return self.packages.filter(packagerelatedvulnerability__fix=True)
101+
102+
@property
103+
def alias(self):
104+
"""
105+
Returns packages that first received patch against this vulnerability
106+
in their particular version history.
107+
"""
108+
return self.aliases.all()
85109

86110
def __str__(self):
87111
return self.vulcoid
@@ -150,10 +174,7 @@ class Package(PackageURLMixin):
150174
"""
151175

152176
vulnerabilities = models.ManyToManyField(
153-
to="Vulnerability",
154-
through="PackageRelatedVulnerability",
155-
through_fields=("package", "vulnerability"),
156-
related_name="packages",
177+
to="Vulnerability", through="PackageRelatedVulnerability"
157178
)
158179

159180
# Remove the `qualifers` and `set_package_url` overrides after
@@ -218,8 +239,14 @@ def __str__(self):
218239
class PackageRelatedVulnerability(models.Model):
219240

220241
# TODO: Fix related_name
221-
package = models.ForeignKey(Package, on_delete=models.CASCADE, related_name="package")
222-
vulnerability = models.ForeignKey(Vulnerability, on_delete=models.CASCADE)
242+
package = models.ForeignKey(
243+
Package,
244+
on_delete=models.CASCADE,
245+
)
246+
vulnerability = models.ForeignKey(
247+
Vulnerability,
248+
on_delete=models.CASCADE,
249+
)
223250
created_by = models.CharField(
224251
max_length=100,
225252
blank=True,

vulnerabilities/templates/package_update.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ <h1 class="title">
3030
<div class="column is-full">
3131
<div class="card has-background-danger-light">
3232
<header class="card-header mb-3">
33-
<p class="card-header-title">Vulnerable To</p>
33+
<p class="card-header-title">Affected By</p>
3434
</header>
3535
<div class="tags mx-3">
3636
{% for vulnerability in impacted_vuln %}
3737
<span class="tag is-danger is-medium">
38-
<a href="{% url 'vulnerability_view' vulnerability.pk %}" class="has-text-white">{{vulnerability.vulnerability_id}}</a>
38+
<a href="{% url 'vulnerability_view' vulnerability.pk %}" class="has-text-white">{{vulnerability.vulcoid}}</a>
3939
</span>
4040
{% endfor %}
4141
</div>
@@ -49,12 +49,12 @@ <h1 class="title">
4949
<div class="column is-full">
5050
<div class="card has-background-danger-light">
5151
<header class="card-header mb-3">
52-
<p class="card-header-title">Safe To</p>
52+
<p class="card-header-title">Fixing</p>
5353
</header>
5454
<div class="tags mx-3">
5555
{% for vulnerability in resolved_vuln %}
5656
<span class="tag is-primary is-medium">
57-
<a href="{% url 'vulnerability_view' vulnerability.pk %}" class="has-text-white">{{vulnerability.vulnerability_id}}</a>
57+
<a href="{% url 'vulnerability_view' vulnerability.pk %}" class="has-text-white">{{vulnerability.vulcoid}}</a>
5858
</span>
5959
{% endfor %}
6060
</div>

vulnerabilities/templates/packages.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ <h1 class="title">
3636
<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth">
3737
<tr>
3838
<th>Package URL</th>
39-
<th>Vulnerabilities</th>
40-
<th>Patched Vulnerabilities</th>
39+
<th>Affected By Vulnerabilities</th>
40+
<th>Fixing Vulnerabilities</th>
4141
</tr>
4242
{% for package in packages %}
4343
<tr>

vulnerabilities/templates/vulnerabilities.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ <h1 class="title">
3131
<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth">
3232
<tr>
3333
<th>Vulnerability ID</th>
34-
<th>Vulnerable packages</th>
35-
<th>Patched packages</th>
34+
<th>Affected packages</th>
35+
<th>Fixed packages</th>
3636
</tr>
3737
{% for vulnerability in vulnerabilities %}
3838
<tr>
39-
<td><a href="{% url 'vulnerability_view' vulnerability.pk %}">{{vulnerability.vulnerability_id}}</a></td>
39+
<td><a href="{% url 'vulnerability_view' vulnerability.pk %}">{{vulnerability.vulcoid}}</a></td>
4040
<td>{{vulnerability.vulnerable_package_count}}</td>
4141
<td>{{vulnerability.patched_package_count}}</td>
4242
</tr>

0 commit comments

Comments
 (0)