Skip to content

Commit 3711882

Browse files
committed
Create an improver to collect patch texts wherever patch URL is present but text is missing
Signed-off-by: ziad hany <ziadhany2016@gmail.com>
1 parent b8f3936 commit 3711882

File tree

3 files changed

+143
-0
lines changed

3 files changed

+143
-0
lines changed

vulnerabilities/improvers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from vulnerabilities.pipelines.v2_improvers import flag_ghost_packages as flag_ghost_packages_v2
3434
from vulnerabilities.pipelines.v2_improvers import relate_severities
3535
from vulnerabilities.pipelines.v2_improvers import unfurl_version_range as unfurl_version_range_v2
36+
from vulnerabilities.pipelines.v2_improvers import fetch_patch_url as fetch_patch_url_v2
3637
from vulnerabilities.utils import create_registry
3738

3839
IMPROVERS_REGISTRY = create_registry(
@@ -71,6 +72,7 @@
7172
compute_version_rank_v2.ComputeVersionRankPipeline,
7273
compute_advisory_todo_v2.ComputeToDo,
7374
unfurl_version_range_v2.UnfurlVersionRangePipeline,
75+
fetch_patch_url_v2.FetchPatchURLImproverPipeline,
7476
compute_advisory_todo.ComputeToDo,
7577
collect_ssvc_trees.CollectSSVCPipeline,
7678
relate_severities.RelateSeveritiesPipeline,
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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 vulnerabilities.models import PackageCommitPatch, Patch
11+
from vulnerabilities.pipelines import VulnerableCodePipeline
12+
from vulnerabilities.utils import fetch_response
13+
14+
15+
class FetchPatchURLImproverPipeline(VulnerableCodePipeline):
16+
"""FetchPatchURL Improver Pipeline"""
17+
18+
pipeline_id = "fetch_patch_url"
19+
precedence = 200
20+
21+
@classmethod
22+
def steps(cls):
23+
return (
24+
cls.collect_patch_text,
25+
)
26+
27+
def fetch_patch_content(self, url):
28+
"""
29+
Fetches the text content of a patch from a URL.
30+
"""
31+
if not url:
32+
return None
33+
34+
self.log(f"Fetching `{url}`")
35+
36+
response = fetch_response(url)
37+
if response:
38+
return response.text.replace("\x00", "")
39+
40+
self.log(f"Skipping {url} due to fetch failure.")
41+
return None
42+
43+
def advisories_count(self) -> int:
44+
return (
45+
PackageCommitPatch.objects.filter(patch_text__isnull=True).count() +
46+
Patch.objects.filter(patch_text__isnull=True).count()
47+
)
48+
49+
def collect_patch_text(self):
50+
for pcp in PackageCommitPatch.objects.filter(patch_text__isnull=True):
51+
patch_url = generate_patch_url(pcp.vcs_url, pcp.commit_hash)
52+
content = self.fetch_patch_content(patch_url)
53+
if not content:
54+
continue
55+
pcp.patch_text = content
56+
pcp.save()
57+
58+
for patch in Patch.objects.filter(patch_text__isnull=True):
59+
content = self.fetch_patch_content(patch.patch_url)
60+
if not content:
61+
continue
62+
63+
patch.patch_text = content
64+
patch.save()
65+
66+
def generate_patch_url(vcs_url, commit_hash):
67+
"""
68+
Generate patch URL from VCS URL and commit hash.
69+
"""
70+
if not vcs_url or not commit_hash:
71+
return None
72+
73+
vcs_url = vcs_url.rstrip("/")
74+
75+
if vcs_url.startswith("https://github.com"):
76+
return f"{vcs_url}/commit/{commit_hash}.patch"
77+
elif vcs_url.startswith("https://gitlab.com"):
78+
return f"{vcs_url}/-/commit/{commit_hash}.patch"
79+
elif vcs_url.startswith("https://bitbucket.org"):
80+
return f"{vcs_url}/-/commit/{commit_hash}/raw"
81+
elif vcs_url.startswith("https://git.kernel.org"):
82+
return f"{vcs_url}.git/patch/?id={commit_hash}"
83+
return
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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 unittest import mock
11+
from unittest.mock import MagicMock
12+
13+
import pytest
14+
15+
from vulnerabilities.models import PackageCommitPatch, Patch
16+
from vulnerabilities.pipelines.v2_improvers.fetch_patch_url import FetchPatchURLImproverPipeline
17+
18+
19+
@pytest.mark.django_db
20+
@mock.patch("vulnerabilities.utils.requests.get")
21+
def test_collect_patch_text_success(mock_get):
22+
res1 = MagicMock(status_code=200, text="diff --git a/file1")
23+
res2 = MagicMock(status_code=200, text="diff --git a/file2")
24+
mock_get.side_effect = [res1, res2]
25+
26+
pcp = PackageCommitPatch.objects.create(
27+
vcs_url="https://github.com/nexB/vulnerablecode",
28+
commit_hash="abc1234",
29+
patch_text=None
30+
)
31+
32+
patch = Patch.objects.create(
33+
patch_url="https://gitlab.com/nexB/vulnerablecode/-/commit/def5678.patch",
34+
patch_text=None
35+
)
36+
pipeline = FetchPatchURLImproverPipeline()
37+
pipeline.collect_patch_text()
38+
39+
pcp.refresh_from_db()
40+
patch.refresh_from_db()
41+
42+
assert pcp.patch_text == "diff --git a/file1"
43+
assert patch.patch_text == "diff --git a/file2"
44+
45+
@pytest.mark.django_db
46+
@mock.patch("vulnerabilities.utils.requests.get")
47+
def test_collect_patch_text_failure(mock_get):
48+
mock_get.side_effect = Exception("Connection Error")
49+
50+
pcp = PackageCommitPatch.objects.create(
51+
vcs_url="https://github.com/nexB/vulnerablecode",
52+
commit_hash="abc1234",
53+
patch_text=None
54+
)
55+
56+
pipeline = FetchPatchURLImproverPipeline()
57+
pipeline.collect_patch_text()
58+
assert pcp.patch_text is None

0 commit comments

Comments
 (0)