Skip to content

Commit f641395

Browse files
authored
Merge pull request aboutcode-org#2309 from aboutcode-org/add_datasource_name
Add datasource ID
2 parents 8013df1 + 87f1bd9 commit f641395

77 files changed

Lines changed: 472 additions & 47 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ Release notes
44
next release
55
---------------------
66

7-
- WARNING: Vulnerablecode V1 API and UI has stopped supporting Ubuntu OVAL advisories, please shift to V3 API for new Ubuntu advisories.
7+
- WARNING: Vulnerablecode V1 API and UI has stopped supporting Ubuntu OVAL advisories, please shift to V3 API for new Ubuntu advisories.
8+
- Add attribute ``pipeline_id`` to AdvisoryV2 to track the pipeline that created the advisory, also rename existing ``datasource_id`` and AVIDs.
89

910
Version v38.6.0
1011
---------------------

PIPELINES-AVID.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
.. list-table:: Pipeline AVID Mapping
1+
.. list-table:: Pipeline Advisory UID Mapping
22
:header-rows: 1
33
:widths: 35 65
44

55
* - pipeline name
6-
- AVID
6+
- Advisory UID
7+
- datasource name
78
* - alpine_linux_importer_v2
89
- {package_name}/{distroversion}/{version}/{vulnerability_id}
10+
- alpine_linux
911
* - aosp_dataset_fix_commits
1012
- CVE ID of the record
1113
* - apache_httpd_importer_v2

docs/source/api_v3_usage.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ Example response:
143143
"affected_by_vulnerabilities": [
144144
{
145145
"advisory_id": "GHSA-g5vw-3h65-2q3v",
146+
"advisory_uid": "ghsa/g5vw-3h65-2q3v",
146147
"aliases": [],
147148
"weighted_severity": null,
148149
"exploitability_score": null,
@@ -189,6 +190,7 @@ Example response:
189190
"affected_by_vulnerabilities": [
190191
{
191192
"advisory_id": "GHSA-g5vw-3h65-2q3v",
193+
"advisory_uid": "ghsa/g5vw-3h65-2q3v",
192194
"aliases": [],
193195
"weighted_severity": null,
194196
"exploitability_score": null,

vulnerabilities/api_v3.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ class AdvisoryV3Serializer(serializers.ModelSerializer):
113113
weaknesses = AdvisoryWeaknessSerializer(many=True)
114114
references = AdvisoryReferenceSerializer(many=True)
115115
severities = AdvisorySeveritySerializer(many=True)
116-
advisory_id = serializers.CharField(source="avid", read_only=True)
116+
advisory_uid = serializers.CharField(source="avid", read_only=True)
117117
related_ssvc_trees = serializers.SerializerMethodField()
118118

119119
def get_related_ssvc_trees(self, obj):
@@ -143,6 +143,7 @@ class Meta:
143143
model = AdvisoryV2
144144
fields = [
145145
"advisory_id",
146+
"advisory_uid",
146147
"url",
147148
"aliases",
148149
"summary",
@@ -270,6 +271,7 @@ def get_affected_by_vulnerabilities(self, package):
270271
result.append(
271272
{
272273
"advisory_id": advisory.advisory_id.split("/")[-1],
274+
"advisory_uid": advisory.avid,
273275
"aliases": [alias.alias for alias in advisory.aliases.all()],
274276
"summary": advisory.summary,
275277
"severity": advisory.weighted_severity,
@@ -313,6 +315,7 @@ def get_fixing_vulnerabilities(self, package):
313315
results.append(
314316
{
315317
"advisory_id": advisory.advisory_id.split("/")[-1],
318+
"advisory_uid": advisory.avid,
316319
}
317320
)
318321
return results
@@ -337,6 +340,7 @@ def return_fixing_advisories_data(self, advisories):
337340
result.append(
338341
{
339342
"advisory_id": advisory.identifier,
343+
"advisory_uid": advisory.advisory.avid,
340344
}
341345
)
342346

@@ -364,6 +368,7 @@ def return_advisories_data(self, package, advisories_qs, advisories):
364368
result.append(
365369
{
366370
"advisory_id": advisory.identifier,
371+
"advisory_uid": advisory.advisory.avid,
367372
"aliases": [alias.alias for alias in advisory.aliases],
368373
"weighted_severity": advisory.weighted_severity,
369374
"exploitability": advisory.exploitability,
@@ -471,6 +476,7 @@ def create(self, request, *args, **kwargs):
471476

472477
class AffectedByAdvisoryV3Serializer(AdvisoryV3Serializer):
473478
fixed_by_packages = serializers.SerializerMethodField()
479+
advisory_uid = serializers.CharField(source="avid", read_only=True)
474480

475481
def get_fixed_by_packages(self, obj):
476482
return list(
@@ -483,6 +489,7 @@ class Meta:
483489
model = AdvisoryV2
484490
fields = [
485491
"advisory_id",
492+
"advisory_uid",
486493
"url",
487494
"aliases",
488495
"summary",
@@ -623,8 +630,8 @@ def get_affected_advisories_bulk(packages):
623630

624631
grouped.append(
625632
{
626-
"avid": primary.avid,
627633
"advisory_id": identifier,
634+
"advisory_uid": primary.avid,
628635
"aliases": aliases,
629636
"weighted_severity": weighted_severity,
630637
"exploitability": exploitability,
@@ -699,7 +706,9 @@ def get_fixing_advisories_bulk(packages):
699706
grouped = []
700707

701708
for adv_id in groups:
702-
grouped.append({"advisory_id": adv_id.split("/")[-1]})
709+
grouped.append(
710+
{"advisory_id": adv_id.split("/")[-1], "advisory_uid": adv_id.split("/")[-1]}
711+
)
703712

704713
result[package.id] = grouped
705714

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Generated by Django 5.2.11 on 2026-05-18 08:52
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("vulnerabilities", "0129_advisorypoc"),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name="advisoryv2",
15+
name="pipeline_id",
16+
field=models.CharField(
17+
blank=True,
18+
db_index=True,
19+
help_text="Unique ID for the pipeline used for this advisory .e.g.: nginx_importer_v2",
20+
max_length=200,
21+
null=True,
22+
),
23+
),
24+
]
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Generated by Django 5.2.11 on 2026-05-18 08:54
2+
3+
from django.db import migrations, models
4+
from django.db.models import F
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
("vulnerabilities", "0130_advisoryv2_pipeline_id"),
11+
]
12+
13+
def populate_pipeline_id(apps, schema_editor):
14+
Advisory = apps.get_model("vulnerabilities", "AdvisoryV2")
15+
16+
Advisory.objects.update(
17+
pipeline_id=F("datasource_id")
18+
)
19+
20+
assert not Advisory.objects.filter(pipeline_id="").exists(), "Some advisories have an empty pipeline_id after the update"
21+
22+
operations = [
23+
migrations.RunPython(populate_pipeline_id, reverse_code=migrations.RunPython.noop),
24+
migrations.AlterField(
25+
model_name="advisoryv2",
26+
name="pipeline_id",
27+
field=models.CharField(
28+
db_index=True,
29+
help_text="Unique ID for the pipeline used for this advisory .e.g.: nginx_importer_v2",
30+
max_length=200,
31+
),
32+
),
33+
migrations.AlterField(
34+
model_name="advisoryv2",
35+
name="datasource_id",
36+
field=models.CharField(
37+
db_index=True,
38+
help_text="Unique ID for the datasource used for this advisory .e.g.: nginx",
39+
max_length=200,
40+
),
41+
),
42+
]
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
from django.db import migrations
2+
from django.db.models import F
3+
from django.db.models import Value
4+
from django.db.models.functions import Concat
5+
6+
7+
def migrate_advisoryv2_datasource_ids(apps, schema_editor):
8+
"""
9+
v2 importers previously stored pipeline_id as datasource_id on AdvisoryV2.
10+
Migration 0131 copied that value into pipeline_id; update datasource_id (and avid)
11+
to each pipeline's datasource_id, matching rows by pipeline_id.
12+
"""
13+
from vulnerabilities.importers import IMPORTERS_REGISTRY
14+
from vulnerabilities.pipelines import VulnerableCodeBaseImporterPipelineV2
15+
16+
Advisory = apps.get_model("vulnerabilities", "AdvisoryV2")
17+
18+
for pipeline_class in IMPORTERS_REGISTRY.values():
19+
if not issubclass(pipeline_class, VulnerableCodeBaseImporterPipelineV2):
20+
continue
21+
22+
pipeline_id = pipeline_class.pipeline_id
23+
datasource_id = pipeline_class.datasource_id
24+
if not pipeline_id or not datasource_id:
25+
continue
26+
if pipeline_id == datasource_id:
27+
continue
28+
29+
Advisory.objects.filter(
30+
pipeline_id=pipeline_id,
31+
datasource_id=pipeline_id,
32+
).update(
33+
datasource_id=datasource_id,
34+
avid=Concat(Value(f"{datasource_id}/"), F("advisory_id")),
35+
)
36+
37+
38+
def reverse_migrate_advisoryv2_datasource_ids(apps, schema_editor):
39+
from vulnerabilities.importers import IMPORTERS_REGISTRY
40+
from vulnerabilities.pipelines import VulnerableCodeBaseImporterPipelineV2
41+
42+
Advisory = apps.get_model("vulnerabilities", "AdvisoryV2")
43+
44+
for pipeline_class in IMPORTERS_REGISTRY.values():
45+
if not issubclass(pipeline_class, VulnerableCodeBaseImporterPipelineV2):
46+
continue
47+
if "v2_importers" not in pipeline_class.__module__:
48+
continue
49+
50+
pipeline_id = pipeline_class.pipeline_id
51+
datasource_id = pipeline_class.datasource_id
52+
if not pipeline_id or not datasource_id:
53+
continue
54+
if pipeline_id == datasource_id:
55+
continue
56+
57+
Advisory.objects.filter(
58+
pipeline_id=pipeline_id,
59+
datasource_id=datasource_id,
60+
).update(
61+
datasource_id=pipeline_id,
62+
avid=Concat(Value(f"{pipeline_id}/"), F("advisory_id")),
63+
)
64+
65+
66+
class Migration(migrations.Migration):
67+
68+
dependencies = [
69+
("vulnerabilities", "0131_auto_20260518_0854"),
70+
]
71+
72+
operations = [
73+
migrations.RunPython(
74+
migrate_advisoryv2_datasource_ids,
75+
reverse_code=reverse_migrate_advisoryv2_datasource_ids,
76+
),
77+
]

vulnerabilities/models.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3058,7 +3058,15 @@ class AdvisoryV2(models.Model):
30583058
blank=False,
30593059
null=False,
30603060
db_index=True,
3061-
help_text="Unique ID for the datasource used for this advisory ." "e.g.: nginx_importer_v2",
3061+
help_text="Unique ID for the datasource used for this advisory ." "e.g.: nginx",
3062+
)
3063+
3064+
pipeline_id = models.CharField(
3065+
max_length=200,
3066+
blank=False,
3067+
null=False,
3068+
db_index=True,
3069+
help_text="Unique ID for the pipeline used for this advisory ." "e.g.: nginx_importer_v2",
30623070
)
30633071

30643072
# This is similar to a name

vulnerabilities/pipelines/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ class VulnerableCodeBaseImporterPipelineV2(VulnerableCodePipeline):
273273

274274
pipeline_id = None # Unique Pipeline ID, this should be the name of pipeline module.
275275
license_url = None
276-
datasource_name = None
276+
datasource_id = None
277277
spdx_license_expression = None
278278
repo_url = None
279279
ignorable_versions = []
@@ -319,6 +319,9 @@ def advisories_count(self) -> int:
319319
raise NotImplementedError
320320

321321
def collect_and_store_advisories(self):
322+
if not self.pipeline_id and not self.datasource_id:
323+
self.log("Pipeline must have a unique pipeline_id or datasource_id defined.")
324+
return
322325
collected_advisory_count = 0
323326
estimated_advisory_count = self.advisories_count()
324327

@@ -338,6 +341,7 @@ def collect_and_store_advisories(self):
338341
if _obj := insert_advisory_v2(
339342
advisory=advisory,
340343
pipeline_id=self.pipeline_id,
344+
datasource_id=self.datasource_id,
341345
logger=self.log,
342346
precedence=self.precedence,
343347
):

vulnerabilities/pipelines/v2_importers/alpine_linux_importer.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class AlpineLinuxImporterPipeline(VulnerableCodeBaseImporterPipelineV2):
3838

3939
pipeline_id = "alpine_linux_importer_v2"
4040
spdx_license_expression = "CC-BY-SA-4.0"
41+
datasource_id = "alpine_linux"
4142
license_url = "https://secdb.alpinelinux.org/license.txt"
4243
repo_url = "git+https://github.com/aboutcode-org/aboutcode-mirror-alpine-secdb/"
4344

0 commit comments

Comments
 (0)