Skip to content

Commit ca2fc64

Browse files
committed
Add support for Reference Fix Commits improver
Update the pipeline and fix the test Signed-off-by: ziad hany <ziadhany2016@gmail.com>
1 parent b8f3936 commit ca2fc64

File tree

3 files changed

+183
-0
lines changed

3 files changed

+183
-0
lines changed

vulnerabilities/importers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
from vulnerabilities.pipelines.v2_importers import ubuntu_osv_importer as ubuntu_osv_importer_v2
8383
from vulnerabilities.pipelines.v2_importers import vulnrichment_importer as vulnrichment_importer_v2
8484
from vulnerabilities.pipelines.v2_importers import xen_importer as xen_importer_v2
85+
from vulnerabilities.pipelines.v2_improvers import reference_collect_commits
8586
from vulnerabilities.utils import create_registry
8687

8788
IMPORTERS_REGISTRY = create_registry(
@@ -127,6 +128,7 @@
127128
nginx_importer.NginxImporterPipeline,
128129
pysec_importer.PyPIImporterPipeline,
129130
fireeye_importer_v2.FireeyeImporterPipeline,
131+
reference_collect_commits.CollectReferencesFixCommitsPipeline,
130132
apache_tomcat.ApacheTomcatImporter,
131133
postgresql.PostgreSQLImporter,
132134
debian.DebianImporter,
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#
2+
# Copyright (c) nexB Inc. and others. All rights reserved.
3+
# VulnerableCode is a trademark of nexB Inc.
4+
# SPDX-License-Identifier: Apache-2.0
5+
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
6+
# See https://github.com/aboutcode-org/vulnerablecode for support or download.
7+
# See https://aboutcode.org for more information about nexB OSS projects.
8+
#
9+
10+
from aboutcode.pipeline import LoopProgress
11+
from packageurl.contrib.purl2url import purl2url
12+
from packageurl.contrib.url2purl import url2purl
13+
14+
from aboutcode.federated import get_core_purl
15+
from vulnerabilities.models import AdvisoryV2
16+
from vulnerabilities.models import PackageCommitPatch
17+
from vulnerabilities.pipelines import VulnerableCodeBaseImporterPipelineV2
18+
from vulnerabilities.pipes.advisory import VCS_URLS_SUPPORTED_TYPES
19+
from vulnerabilities.tests.test_export import package
20+
from vulnerabilities.utils import is_commit
21+
22+
23+
class CollectReferencesFixCommitsPipeline(VulnerableCodeBaseImporterPipelineV2):
24+
"""
25+
Improver pipeline to scout References/Patch and create PackageCommitPatch entries.
26+
"""
27+
28+
pipeline_id = "collect_ref_fix_commits_v2"
29+
license_expression = None
30+
31+
@classmethod
32+
def steps(cls):
33+
return (cls.collect_and_store_fix_commits,)
34+
35+
def get_vcs_commit(self, url):
36+
"""Extracts and VCS URL and commit hash from URL.
37+
>> get_vcs_commit('https://github.com/aboutcode-org/vulnerablecode/commit/98e516011d6e096e25247b82fc5f196bbeecff10')
38+
('https://github.com/aboutcode-org/vulnerablecode', '98e516011d6e096e25247b82fc5f196bbeecff10')
39+
>> get_vcs_commit('https://github.com/aboutcode-org/vulnerablecode/pull/1974')
40+
None
41+
"""
42+
purl = url2purl(url)
43+
if not purl or purl.type not in VCS_URLS_SUPPORTED_TYPES:
44+
return None
45+
46+
version = getattr(purl, "version", None)
47+
if not version or not is_commit(version):
48+
return None
49+
50+
vcs_url = purl2url(get_core_purl(purl).to_string())
51+
return (vcs_url, version) if vcs_url else None
52+
53+
def collect_and_store_fix_commits(self):
54+
impacted_packages_advisories = (
55+
AdvisoryV2.objects.filter(impacted_packages__isnull=False)
56+
.prefetch_related("references", "patches", "impacted_packages")
57+
.distinct()
58+
)
59+
60+
progress = LoopProgress(
61+
total_iterations=impacted_packages_advisories.count(), logger=self.log
62+
)
63+
for adv in progress.iter(impacted_packages_advisories.paginated(per_page=500)):
64+
urls = {r.url for r in adv.references.all()} | {p.patch_url for p in adv.patches.all()}
65+
impacted_packages = list(adv.impacted_packages.all())
66+
67+
for url in urls:
68+
vcs_data = self.get_vcs_commit(url)
69+
if not vcs_data:
70+
continue
71+
72+
vcs_url, commit_hash = vcs_data
73+
package_commit_obj, _ = PackageCommitPatch.objects.get_or_create(
74+
vcs_url=vcs_url, commit_hash=commit_hash
75+
)
76+
package_commit_obj.fixed_impacted_packages.add(*impacted_packages)
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#
2+
# Copyright (c) nexB Inc. and others. All rights reserved.
3+
# VulnerableCode is a trademark of nexB Inc.
4+
# SPDX-License-Identifier: Apache-2.0
5+
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
6+
# See https://github.com/aboutcode-org/vulnerablecode for support or download.
7+
# See https://aboutcode.org for more information about nexB OSS projects.
8+
9+
from datetime import datetime
10+
11+
import pytest
12+
13+
from vulnerabilities.models import AdvisoryReference
14+
from vulnerabilities.models import AdvisoryV2
15+
from vulnerabilities.models import ImpactedPackage
16+
from vulnerabilities.models import PackageCommitPatch
17+
from vulnerabilities.models import PackageV2
18+
from vulnerabilities.pipelines.v2_improvers.reference_collect_commits import (
19+
CollectReferencesFixCommitsPipeline,
20+
)
21+
22+
23+
@pytest.mark.django_db
24+
def test_is_vcs_url_already_processed_true():
25+
advisory = AdvisoryV2.objects.create(
26+
advisory_id="CVE-2025-9999",
27+
datasource_id="test-ds",
28+
avid="test-ds/CVE-2025-9999",
29+
url="https://example.com/advisory/CVE-2025-9999",
30+
unique_content_id="11111",
31+
date_collected=datetime.now(),
32+
)
33+
package = PackageV2.objects.create(
34+
type="bar",
35+
name="foo",
36+
version="1.0",
37+
)
38+
impact = ImpactedPackage.objects.create(advisory=advisory)
39+
impact.affecting_packages.add(package)
40+
package_commit_patch = PackageCommitPatch.objects.create(
41+
vcs_url="https://github.com/user/repo/commit/6bd301819f8f69331a55ae2336c8b111fc933f3d",
42+
commit_hash="6bd301819f8f69331a55ae2336c8b111fc933f3d",
43+
)
44+
impact.fixed_by_package_commit_patches.add(package_commit_patch)
45+
46+
47+
@pytest.mark.django_db
48+
def test_collect_fix_commits_pipeline_creates_entry():
49+
advisory = AdvisoryV2.objects.create(
50+
advisory_id="CVE-2025-1000",
51+
datasource_id="test-ds",
52+
avid="test-ds/CVE-2025-1000",
53+
url="https://example.com/advisory/CVE-2025-1000",
54+
unique_content_id="11111",
55+
date_collected=datetime.now(),
56+
)
57+
package = PackageV2.objects.create(
58+
type="foo",
59+
name="testpkg",
60+
version="1.0",
61+
)
62+
reference = AdvisoryReference.objects.create(
63+
url="https://github.com/test/testpkg/commit/6bd301819f8f69331a55ae2336c8b111fc933f3d"
64+
)
65+
impact = ImpactedPackage.objects.create(advisory=advisory)
66+
impact.affecting_packages.add(package)
67+
advisory.references.add(reference)
68+
69+
pipeline = CollectReferencesFixCommitsPipeline()
70+
pipeline.collect_and_store_fix_commits()
71+
72+
package_commit_patch = PackageCommitPatch.objects.all()
73+
74+
assert package_commit_patch.count() == 1
75+
fix = package_commit_patch.first()
76+
assert fix.commit_hash == "6bd301819f8f69331a55ae2336c8b111fc933f3d"
77+
assert fix.vcs_url == "https://github.com/test/testpkg"
78+
79+
80+
@pytest.mark.django_db
81+
def test_collect_fix_commits_pipeline_skips_non_commit_urls():
82+
advisory = AdvisoryV2.objects.create(
83+
advisory_id="CVE-2025-2000",
84+
datasource_id="test-ds",
85+
avid="test-ds/CVE-2025-2000",
86+
url="https://example.com/advisory/CVE-2025-2000",
87+
unique_content_id="11111",
88+
date_collected=datetime.now(),
89+
)
90+
package = PackageV2.objects.create(
91+
type="pypi",
92+
name="otherpkg",
93+
version="2.0",
94+
)
95+
impact = ImpactedPackage.objects.create(advisory=advisory)
96+
impact.affecting_packages.add(package)
97+
98+
reference = AdvisoryReference.objects.create(
99+
url="https://github.com/test/testpkg/issues/12"
100+
) # invalid reference 1
101+
advisory.references.add(reference)
102+
103+
pipeline = CollectReferencesFixCommitsPipeline()
104+
pipeline.collect_and_store_fix_commits()
105+
assert PackageCommitPatch.objects.count() == 0

0 commit comments

Comments
 (0)