Skip to content

Commit ef4e846

Browse files
committed
Store Package URL data in the Package model
Signed-off-by: Haiko Schol <hs@haikoschol.com>
1 parent af561ae commit ef4e846

8 files changed

Lines changed: 94 additions & 32 deletions

File tree

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ lxml==4.3.3
55
django==2.2.4
66
djangorestframework==3.9.2
77
django-filter==2.1.0
8+
packageurl-python==0.8.7
89

910
# Tests
1011
pytest==3.2.3

vulnerabilities/api.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ class Meta:
7272
fields = [
7373
'name',
7474
'version',
75-
'platform',
7675
'vulnerabilities',
7776
'references',
7877
]
@@ -81,4 +80,4 @@ class Meta:
8180
class PackageViewSet(viewsets.ReadOnlyModelViewSet):
8281
queryset = Package.objects.all()
8382
serializer_class = PackageSerializer
84-
filter_fields = ('name', 'version', 'platform')
83+
filter_fields = ('name', 'version')

vulnerabilities/data_dump.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from vulnerabilities.models import VulnerabilityReference
3030

3131

32-
def debian_dump(extract_data):
32+
def debian_dump(extract_data, base_release='jessie'):
3333
"""
3434
Save data scraped from Debian' security tracker.
3535
"""
@@ -45,7 +45,10 @@ def debian_dump(extract_data):
4545
pkg_name = data.get('package_name', '')
4646
package = Package.objects.create(
4747
name=pkg_name,
48+
type='deb',
49+
namespace='debian',
4850
version=data.get('version', ''),
51+
qualifiers=f'distro={base_release}',
4952
)
5053

5154
if data['status'] == 'open':
@@ -63,7 +66,10 @@ def debian_dump(extract_data):
6366
if fixed_version:
6467
package = Package.objects.create(
6568
name=pkg_name,
69+
type='deb',
70+
namespace='debian',
6671
version=fixed_version,
72+
qualifiers=f'distro={base_release}',
6773
)
6874

6975
ResolvedPackage.objects.create(
@@ -86,6 +92,8 @@ def ubuntu_dump(html):
8692
)
8793
package = Package.objects.create(
8894
name=data.get('package_name'),
95+
type='deb',
96+
namespace='ubuntu'
8997
)
9098
ImpactedPackage.objects.create(
9199
vulnerability=vulnerability,
@@ -119,12 +127,14 @@ def archlinux_dump(extract_data):
119127
VulnerabilityReference.objects.create(
120128
vulnerability=vulnerability,
121129
reference_id=vulnerability_id,
122-
url='https://security.archlinux.org/{}'.format(vulnerability_id)
130+
url=f'https://security.archlinux.org/{vulnerability_id}',
123131
)
124132

125133
for package_name in packages_name:
126134
package_affected = Package.objects.create(
127135
name=package_name,
136+
type='pacman',
137+
namespace='archlinux',
128138
version=affected_version
129139
)
130140
ImpactedPackage.objects.create(
@@ -133,10 +143,12 @@ def archlinux_dump(extract_data):
133143
)
134144
PackageReference.objects.create(
135145
package=package_affected,
136-
repository='https://security.archlinux.org/package/{}'.format(package_name)
146+
repository=f'https://security.archlinux.org/package/{package_name}',
137147
)
138148
package_fixed = Package.objects.create(
139149
name=package_name,
150+
type='pacman',
151+
namespace='archlinux',
140152
version=fixed_version
141153
)
142154
ResolvedPackage.objects.create(
@@ -145,5 +157,5 @@ def archlinux_dump(extract_data):
145157
)
146158
PackageReference.objects.create(
147159
package=package_fixed,
148-
repository='https://security.archlinux.org/package/{}'.format(package_name)
160+
repository=f'https://security.archlinux.org/package/{package_name}',
149161
)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Generated by Django 2.2.4 on 2019-09-27 14:38
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('vulnerabilities', '0005_auto_20190411_1644'),
10+
]
11+
12+
operations = [
13+
migrations.RemoveField(
14+
model_name='package',
15+
name='platform',
16+
),
17+
migrations.AddField(
18+
model_name='package',
19+
name='namespace',
20+
field=models.CharField(blank=True, help_text='Package name prefix, such as Maven groupid, Docker image owner, GitHub user or organization, etc.', max_length=255, null=True),
21+
),
22+
migrations.AddField(
23+
model_name='package',
24+
name='qualifiers',
25+
field=models.CharField(blank=True, help_text='Extra qualifying data for a package such as the name of an OS, architecture, distro, etc.', max_length=1024, null=True),
26+
),
27+
migrations.AddField(
28+
model_name='package',
29+
name='subpath',
30+
field=models.CharField(blank=True, help_text='Extra subpath within a package, relative to the package root.', max_length=200, null=True),
31+
),
32+
migrations.AddField(
33+
model_name='package',
34+
name='type',
35+
field=models.CharField(blank=True, help_text='A short code to identify the type of this package. For example: gem for a Rubygem, docker for a container, pypi for a Python Wheel or Egg, maven for a Maven Jar, deb for a Debian package, etc.', max_length=16, null=True),
36+
),
37+
migrations.AlterField(
38+
model_name='package',
39+
name='name',
40+
field=models.CharField(blank=True, help_text='Name of the package.', max_length=100, null=True),
41+
),
42+
migrations.AlterField(
43+
model_name='package',
44+
name='version',
45+
field=models.CharField(blank=True, help_text='Version of the package.', max_length=50, null=True),
46+
),
47+
]

vulnerabilities/models.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
from django.db import models
2525

26+
from packageurl.contrib.django_models import PackageURLMixin
27+
2628

2729
class Vulnerability(models.Model):
2830
"""
@@ -58,14 +60,11 @@ def __str__(self):
5860
return self.source
5961

6062

61-
class Package(models.Model):
63+
class Package(PackageURLMixin):
6264
"""
6365
A software package with minimal identifying information.
6466
Other identifiers are stored as PackageReference.
6567
"""
66-
platform = models.CharField(max_length=50, help_text='Package platform eg:maven', blank=True)
67-
name = models.CharField(max_length=50, help_text='Package name', blank=True)
68-
version = models.CharField(max_length=50, help_text='Package version', blank=True)
6968
vulnerabilities = models.ManyToManyField(to='Vulnerability', through='ImpactedPackage')
7069

7170
def __str__(self):

vulnerabilities/tests/test_api.py

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -64,23 +64,14 @@ def test_ubuntu_response(self):
6464
ubuntu_dump(extract_data)
6565
response = self.client.get('/api/packages/?name=automake', format='json')
6666

67-
expected = [{
68-
"name": "automake",
69-
"version": "",
70-
"platform": "",
71-
"vulnerabilities": [{
72-
"summary": "",
73-
"cvss": None,
74-
"references": [{
75-
"reference_id": "CVE-2012-3386",
76-
"source": "",
77-
"url": "",
78-
}]
79-
}],
80-
"references": [],
81-
}]
82-
83-
self.assertEqual(expected, response.data.get('results'))
67+
result = response.data.get('results')[0]
68+
self.assertEqual('automake', result['name'])
69+
self.assertEqual(None, result['version'])
70+
self.assertEqual(1, len(result['vulnerabilities']))
71+
72+
vuln = result['vulnerabilities'][0]
73+
self.assertEqual(1, len(vuln['references']))
74+
self.assertEqual('CVE-2012-3386', vuln['references'][0]['reference_id'])
8475

8576

8677
class TestSerializers(TestCase):

vulnerabilities/tests/test_data_dump.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,12 @@ def test_Package(self):
8888
self.assertEqual(5, Package.objects.count())
8989

9090
self.assertTrue(Package.objects.filter(name='mimetex'))
91-
self.assertTrue(Package.objects.get(name='librsync'))
91+
92+
pkg = Package.objects.get(name='librsync')
93+
self.assertEqual('0.9.7-10', pkg.version)
94+
self.assertEqual('deb', pkg.type)
95+
self.assertEqual('debian', pkg.namespace)
96+
self.assertIn('distro=jessie', pkg.qualifiers)
9297

9398
def test_ImpactedPackage(self):
9499
"""
@@ -130,7 +135,12 @@ def test_data_dump(self):
130135
"""
131136
reference = VulnerabilityReference.objects.filter(reference_id='CVE-2002-2439')[0]
132137
self.assertEqual(reference.reference_id, 'CVE-2002-2439')
133-
self.assertTrue(Package.objects.filter(name='gcc-4.6')[0].name, 'gcc-4.6')
138+
pkgs = Package.objects.filter(name='gcc-4.6')
139+
self.assertTrue(pkgs)
140+
141+
pkg = pkgs[0]
142+
self.assertEqual('deb', pkg.type)
143+
self.assertEqual('ubuntu', pkg.namespace)
134144

135145

136146
class TestArchLinuxDataDump(TestCase):
@@ -163,7 +173,12 @@ def test_Package(self):
163173
Check that all packages from the test data are stored in the database
164174
"""
165175
self.assertEqual(8, Package.objects.count())
166-
self.assertTrue(Package.objects.filter(name='wireshark-cli'))
176+
pkgs = Package.objects.filter(name='wireshark-cli')
177+
self.assertTrue(pkgs)
178+
179+
for pkg in pkgs:
180+
self.assertEqual('pacman', pkg.type)
181+
self.assertEqual('archlinux', pkg.namespace)
167182

168183
def test_PackageReference(self):
169184
"""

vulnerabilities/tests/test_models.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,10 @@ class TestPackage(TestCase):
5858
def test_package(self):
5959
Package.objects.create(
6060
name="Firefox",
61-
platform="Maven",
6261
version="1.5.4"
6362
)
6463

6564
self.assertTrue(Package.objects.get(name="Firefox"))
66-
self.assertTrue(Package.objects.get(platform="Maven"))
6765
self.assertTrue(Package.objects.get(version="1.5.4"))
6866

6967

0 commit comments

Comments
 (0)