Skip to content

Commit b477a11

Browse files
Merge branch 'main' into feat/enhance-advisory-grouping
2 parents 8865d9b + 963058f commit b477a11

43 files changed

Lines changed: 2209 additions & 145 deletions

Some content is hidden

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

aboutcode/federated/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,7 +1028,7 @@ def large_size_configs(cls):
10281028
"mlflow": 16,
10291029
"pub": 16,
10301030
"rpm": 16,
1031-
# Small Ecosystem all use the defaul
1031+
# Small Ecosystem all use the default
10321032
"default": 1,
10331033
}
10341034
return [
@@ -1069,7 +1069,7 @@ def medium_size_configs(cls):
10691069
"mlflow": 8,
10701070
"pub": 8,
10711071
"rpm": 8,
1072-
# Small Ecosystem all use the defaul
1072+
# Small Ecosystem all use the default
10731073
"default": 1,
10741074
}
10751075
return [
@@ -1110,7 +1110,7 @@ def small_size_configs(cls):
11101110
"mlflow": 4,
11111111
"pub": 4,
11121112
"rpm": 4,
1113-
# Small Ecosystem all use the defaul
1113+
# Small Ecosystem all use the default
11141114
"default": 1,
11151115
}
11161116
return [
@@ -1181,7 +1181,7 @@ def cluster_preset():
11811181
DataCluster(
11821182
data_kind="security_advisories",
11831183
description="VulnerableCode security advisories for each package version.",
1184-
datafile_path_template="{/namespace}/{name}/{version}/advisories.json",
1184+
datafile_path_template="{/namespace}/{name}/{version}/advisories.yml",
11851185
purl_type_configs=[PurlTypeConfig.default_config()],
11861186
data_schema_url="",
11871187
documentation_url="",

aboutcode/federated/tests/test_data/all-presets/foo/aboutcode-federated-config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -933,7 +933,7 @@ data_clusters:
933933
data_license: CC-BY-4.0
934934
data_maintainers: []
935935
- data_kind: security_advisories
936-
datafile_path_template: '{/namespace}/{name}/{version}/advisories.json'
936+
datafile_path_template: '{/namespace}/{name}/{version}/advisories.yml'
937937
purl_type_configs:
938938
- purl_type: default
939939
number_of_repos: 1

docs/source/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"https://nvd.nist.gov/products/cpe",
4141
"https://ftp.suse.com/pub/projects/security/yaml/suse-cvss-scores.yaml",
4242
"http://ftp.suse.com/pub/projects/security/yaml/",
43+
r"https://nixos\.wiki/", # NixOS wiki blocks CI bots with 403
4344
]
4445

4546
# Add any Sphinx extension module names here, as strings. They can be

vulnerabilities/importers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
)
5656
from vulnerabilities.pipelines.v2_importers import epss_importer_v2
5757
from vulnerabilities.pipelines.v2_importers import fireeye_importer_v2
58+
from vulnerabilities.pipelines.v2_importers import gentoo_importer as gentoo_importer_v2
5859
from vulnerabilities.pipelines.v2_importers import github_osv_importer as github_osv_importer_v2
5960
from vulnerabilities.pipelines.v2_importers import gitlab_importer as gitlab_importer_v2
6061
from vulnerabilities.pipelines.v2_importers import istio_importer as istio_importer_v2
@@ -110,6 +111,7 @@
110111
project_kb_msr2019_importer_v2.ProjectKBMSR2019Pipeline,
111112
ruby_importer_v2.RubyImporterPipeline,
112113
epss_importer_v2.EPSSImporterPipeline,
114+
gentoo_importer_v2.GentooImporterPipeline,
113115
nginx_importer_v2.NginxImporterPipeline,
114116
debian_importer_v2.DebianImporterPipeline,
115117
mattermost_importer_v2.MattermostImporterPipeline,

vulnerabilities/importers/fireeye.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def matcher_url(ref) -> str:
112112
"""
113113
Returns URL of the reference markup from reference url in Markdown format
114114
"""
115-
markup_regex = "\[([^\[]+)]\(\s*(http[s]?://.+)\s*\)"
115+
markup_regex = r"\[([^\[]+)]\(\s*(http[s]?://.+)\s*\)"
116116
matched_markup = re.findall(markup_regex, ref)
117117
if matched_markup:
118118
return matched_markup[0][1]

vulnerabilities/importers/gentoo.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
# See https://github.com/aboutcode-org/vulnerablecode for support or download.
77
# See https://aboutcode.org for more information about nexB OSS projects.
88
#
9-
10-
9+
import logging
1110
import re
1211
import xml.etree.ElementTree as ET
1312
from pathlib import Path
@@ -17,12 +16,15 @@
1716
from univers.version_constraint import VersionConstraint
1817
from univers.version_range import EbuildVersionRange
1918
from univers.versions import GentooVersion
19+
from univers.versions import InvalidVersion
2020

2121
from vulnerabilities.importer import AdvisoryData
2222
from vulnerabilities.importer import AffectedPackage
2323
from vulnerabilities.importer import Importer
2424
from vulnerabilities.importer import Reference
2525

26+
logger = logging.getLogger(__name__)
27+
2628

2729
class GentooImporter(Importer):
2830
repo_url = "git+https://anongit.gentoo.org/git/data/glsa.git"
@@ -104,14 +106,20 @@ def affected_and_safe_purls(affected_elem):
104106
safe_versions, affected_versions = GentooImporter.get_safe_and_affected_versions(pkg)
105107

106108
for version in safe_versions:
107-
constraints.append(
108-
VersionConstraint(version=GentooVersion(version), comparator="=").invert()
109-
)
109+
try:
110+
constraints.append(
111+
VersionConstraint(version=GentooVersion(version), comparator="=").invert()
112+
)
113+
except InvalidVersion as e:
114+
logger.error(f"Invalid safe_version {version} - error: {e}")
110115

111116
for version in affected_versions:
112-
constraints.append(
113-
VersionConstraint(version=GentooVersion(version), comparator="=")
114-
)
117+
try:
118+
constraints.append(
119+
VersionConstraint(version=GentooVersion(version), comparator="=")
120+
)
121+
except InvalidVersion as e:
122+
logger.error(f"Invalid affected_version {version} - error: {e}")
115123

116124
if not constraints:
117125
continue

vulnerabilities/improvers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
enhance_with_metasploit as enhance_with_metasploit_v2,
3232
)
3333
from vulnerabilities.pipelines.v2_improvers import flag_ghost_packages as flag_ghost_packages_v2
34+
from vulnerabilities.pipelines.v2_improvers import relate_severities
3435
from vulnerabilities.pipelines.v2_improvers import unfurl_version_range as unfurl_version_range_v2
3536
from vulnerabilities.utils import create_registry
3637

@@ -72,5 +73,6 @@
7273
unfurl_version_range_v2.UnfurlVersionRangePipeline,
7374
compute_advisory_todo.ComputeToDo,
7475
collect_ssvc_trees.CollectSSVCPipeline,
76+
relate_severities.RelateSeveritiesPipeline,
7577
]
7678
)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Generated by Django 5.2.11 on 2026-02-17 13:27
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("vulnerabilities", "0113_advisoryv2_precedence"),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name="advisoryv2",
15+
name="related_advisory_severities",
16+
field=models.ManyToManyField(
17+
help_text="Related advisories that are used to calculate the severity of this advisory.",
18+
related_name="related_to_advisory_severities",
19+
to="vulnerabilities.advisoryv2",
20+
),
21+
),
22+
]
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Generated by Django 5.2.11 on 2026-02-19 11:12
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
("vulnerabilities", "0114_advisoryv2_related_advisory_severities"),
11+
]
12+
13+
operations = [
14+
migrations.RemoveField(
15+
model_name="impactedpackage",
16+
name="affecting_packages",
17+
),
18+
migrations.RemoveField(
19+
model_name="impactedpackage",
20+
name="fixed_by_packages",
21+
),
22+
migrations.AlterField(
23+
model_name="advisoryv2",
24+
name="date_collected",
25+
field=models.DateTimeField(
26+
auto_now_add=True,
27+
db_index=True,
28+
help_text="UTC Date on which the advisory was collected",
29+
),
30+
),
31+
migrations.CreateModel(
32+
name="ImpactedPackageAffecting",
33+
fields=[
34+
(
35+
"id",
36+
models.AutoField(
37+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
38+
),
39+
),
40+
("created_at", models.DateTimeField(auto_now_add=True, db_index=True)),
41+
(
42+
"impacted_package",
43+
models.ForeignKey(
44+
on_delete=django.db.models.deletion.CASCADE,
45+
to="vulnerabilities.impactedpackage",
46+
),
47+
),
48+
(
49+
"package",
50+
models.ForeignKey(
51+
on_delete=django.db.models.deletion.CASCADE, to="vulnerabilities.packagev2"
52+
),
53+
),
54+
],
55+
options={
56+
"unique_together": {("impacted_package", "package")},
57+
},
58+
),
59+
migrations.AddField(
60+
model_name="impactedpackage",
61+
name="affecting_packages",
62+
field=models.ManyToManyField(
63+
help_text="Packages vulnerable to this impact.",
64+
related_name="affected_in_impacts",
65+
through="vulnerabilities.ImpactedPackageAffecting",
66+
to="vulnerabilities.packagev2",
67+
),
68+
),
69+
migrations.CreateModel(
70+
name="ImpactedPackageFixedBy",
71+
fields=[
72+
(
73+
"id",
74+
models.AutoField(
75+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
76+
),
77+
),
78+
("created_at", models.DateTimeField(auto_now_add=True, db_index=True)),
79+
(
80+
"impacted_package",
81+
models.ForeignKey(
82+
on_delete=django.db.models.deletion.CASCADE,
83+
to="vulnerabilities.impactedpackage",
84+
),
85+
),
86+
(
87+
"package",
88+
models.ForeignKey(
89+
on_delete=django.db.models.deletion.CASCADE, to="vulnerabilities.packagev2"
90+
),
91+
),
92+
],
93+
options={
94+
"unique_together": {("impacted_package", "package")},
95+
},
96+
),
97+
migrations.AddField(
98+
model_name="impactedpackage",
99+
name="fixed_by_packages",
100+
field=models.ManyToManyField(
101+
help_text="Packages fixing the vulnerable packages in this impact.",
102+
related_name="fixed_in_impacts",
103+
through="vulnerabilities.ImpactedPackageFixedBy",
104+
to="vulnerabilities.packagev2",
105+
),
106+
),
107+
]

vulnerabilities/models.py

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2344,13 +2344,14 @@ def save(self, *args, **kwargs):
23442344
@property
23452345
def pipeline_class(self):
23462346
"""Return the pipeline class."""
2347+
23472348
from vulnerabilities.importers import IMPORTERS_REGISTRY
23482349
from vulnerabilities.improvers import IMPROVERS_REGISTRY
2350+
from vulnerabilities.pipelines.exporters import EXPORTERS_REGISTRY
2351+
2352+
pipeline_registry = IMPORTERS_REGISTRY | IMPROVERS_REGISTRY | EXPORTERS_REGISTRY
23492353

2350-
if self.pipeline_id in IMPROVERS_REGISTRY:
2351-
return IMPROVERS_REGISTRY.get(self.pipeline_id)
2352-
if self.pipeline_id in IMPORTERS_REGISTRY:
2353-
return IMPORTERS_REGISTRY.get(self.pipeline_id)
2354+
return pipeline_registry[self.pipeline_id]
23542355

23552356
@property
23562357
def description(self):
@@ -2960,9 +2961,15 @@ class AdvisoryV2(models.Model):
29602961
)
29612962

29622963
date_published = models.DateTimeField(
2963-
blank=True, null=True, help_text="UTC Date of publication of the advisory"
2964+
blank=True,
2965+
null=True,
2966+
help_text="UTC Date of publication of the advisory",
2967+
)
2968+
date_collected = models.DateTimeField(
2969+
auto_now_add=True,
2970+
db_index=True,
2971+
help_text="UTC Date on which the advisory was collected",
29642972
)
2965-
date_collected = models.DateTimeField(help_text="UTC Date on which the advisory was collected")
29662973

29672974
original_advisory_text = models.TextField(
29682975
blank=True,
@@ -2997,6 +3004,12 @@ class AdvisoryV2(models.Model):
29973004
help_text="Precedence indicates the priority of advisory from different datasources. It is determined based on the reliability of the datasource and how close it is to the source.",
29983005
)
29993006

3007+
related_advisory_severities = models.ManyToManyField(
3008+
"AdvisoryV2",
3009+
related_name="related_to_advisory_severities",
3010+
help_text="Related advisories that are used to calculate the severity of this advisory.",
3011+
)
3012+
30003013
@property
30013014
def risk_score(self):
30023015
"""
@@ -3130,13 +3143,15 @@ class ImpactedPackage(models.Model):
31303143
affecting_packages = models.ManyToManyField(
31313144
"PackageV2",
31323145
related_name="affected_in_impacts",
3146+
through="ImpactedPackageAffecting",
31333147
help_text="Packages vulnerable to this impact.",
31343148
)
31353149

31363150
fixed_by_packages = models.ManyToManyField(
31373151
"PackageV2",
31383152
related_name="fixed_in_impacts",
3139-
help_text="Packages vulnerable to this impact.",
3153+
through="ImpactedPackageFixedBy",
3154+
help_text="Packages fixing the vulnerable packages in this impact.",
31403155
)
31413156

31423157
introduced_by_package_commit_patches = models.ManyToManyField(
@@ -3484,6 +3499,44 @@ def current_version(self):
34843499
return self.version_class(self.version)
34853500

34863501

3502+
class ImpactedPackageAffecting(models.Model):
3503+
impacted_package = models.ForeignKey(
3504+
ImpactedPackage,
3505+
on_delete=models.CASCADE,
3506+
)
3507+
package = models.ForeignKey(
3508+
PackageV2,
3509+
on_delete=models.CASCADE,
3510+
)
3511+
3512+
created_at = models.DateTimeField(
3513+
auto_now_add=True,
3514+
db_index=True,
3515+
)
3516+
3517+
class Meta:
3518+
unique_together = ("impacted_package", "package")
3519+
3520+
3521+
class ImpactedPackageFixedBy(models.Model):
3522+
impacted_package = models.ForeignKey(
3523+
ImpactedPackage,
3524+
on_delete=models.CASCADE,
3525+
)
3526+
package = models.ForeignKey(
3527+
PackageV2,
3528+
on_delete=models.CASCADE,
3529+
)
3530+
3531+
created_at = models.DateTimeField(
3532+
auto_now_add=True,
3533+
db_index=True,
3534+
)
3535+
3536+
class Meta:
3537+
unique_together = ("impacted_package", "package")
3538+
3539+
34873540
class AdvisoryExploit(models.Model):
34883541
"""
34893542
A vulnerability exploit is code used to

0 commit comments

Comments
 (0)