Skip to content

Commit 1b0538c

Browse files
authored
Merge branch 'main' into nginx-migration
2 parents 512f39a + bcccb03 commit 1b0538c

28 files changed

Lines changed: 1922 additions & 23435 deletions

docs/source/contributing.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ resources to help you get started.
1717
Do Your Homework
1818
----------------
1919

20-
Before adding a contribution or create a new issue, take a look at the projects
20+
Before adding a contribution or create a new issue, take a look at the project's
2121
`README <https://github.com/aboutcode-org/vulnerablecode>`_, read through our
2222
`documentation <https://vulnerablecode.readthedocs.io/en/latest/>`_,
2323
and browse existing `issues <https://github.com/aboutcode-org/vulnerablecode/issues>`_,
@@ -73,7 +73,7 @@ overlooked. We value any suggestions to improve
7373

7474
.. tip::
7575
Our documentation is treated like code. Make sure to check our
76-
`writing guidelines <https://scancode-toolkit.readthedocs.io/en/latest/contribute/contrib_doc.html>`_
76+
`writing guidelines <https://scancode-toolkit.readthedocs.io/en/stable/getting-started/contribute/contributing-docs.html>`_
7777
to help guide new users.
7878

7979
Other Ways
@@ -87,7 +87,7 @@ questions, and interact with us and other community members on
8787
Helpful Resources
8888
-----------------
8989

90-
- Review our `comprehensive guide <https://scancode-toolkit.readthedocs.io/en/latest/contribute/index.html>`_
90+
- Review our `comprehensive guide <https://scancode-toolkit.readthedocs.io/en/stable/getting-started/contribute/index.html>`_
9191
for more details on how to add quality contributions to our codebase and documentation
9292
- Check this free resource on `How to contribute to an open source project on github <https://egghead.io/lessons/javascript-identifying-how-to-contribute-to-an-open-source-project-on-github>`_
9393
- Follow `this wiki page <https://aboutcode.readthedocs.io/en/latest/contributing/writing_good_commit_messages.html>`_

docs/source/tutorial_add_importer_pipeline.rst

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ At this point, an example importer will look like this:
152152
.. code-block:: python
153153
:caption: vulnerabilities/pipelines/example_importer.py
154154
:linenos:
155-
:emphasize-lines: 16-17, 20-21, 23-24
155+
:emphasize-lines: 17-18, 21-22, 24-25
156156
157157
from vulnerabilities.pipelines import VulnerableCodeBaseImporterPipeline
158158
@@ -165,6 +165,7 @@ At this point, an example importer will look like this:
165165
license_url = "https://exmaple.org/license/"
166166
spdx_license_expression = "CC-BY-4.0"
167167
importer_name = "Example Importer"
168+
run_once = False
168169
169170
@classmethod
170171
def steps(cls):
@@ -196,7 +197,7 @@ version management from `univers <https://github.com/aboutcode-org/univers>`_.
196197
.. code-block:: python
197198
:caption: vulnerabilities/pipelines/example_importer.py
198199
:linenos:
199-
:emphasize-lines: 34-35, 37-40
200+
:emphasize-lines: 35-36, 38-41
200201
201202
from datetime import datetime
202203
from datetime import timezone
@@ -223,6 +224,7 @@ version management from `univers <https://github.com/aboutcode-org/univers>`_.
223224
license_url = "https://example.org/license/"
224225
spdx_license_expression = "CC-BY-4.0"
225226
importer_name = "Example Importer"
227+
run_once = False
226228
227229
@classmethod
228230
def steps(cls):
@@ -303,6 +305,17 @@ version management from `univers <https://github.com/aboutcode-org/univers>`_.
303305
Implement ``on_failure`` to handle cleanup in case of pipeline failure.
304306
Cleanup of downloaded archives or cloned repos is necessary to avoid potential resource leakage.
305307

308+
.. tip::
309+
310+
Set ``run_once`` to ``True`` if pipeline is meant to be run once.
311+
312+
- To rerun onetime pipeline, reset ``is_active`` to ``True`` via a migration, pipeline will
313+
run one more time and then deactivate.
314+
315+
- To convert a onetime pipeline to a regular pipeline, set the ``run_once`` class variable
316+
to ``False`` and reset ``is_active` field to ``True`` via a migration.
317+
318+
306319
.. note::
307320

308321
| Use ``make valid`` to format your new code using black and isort automatically.

vulnerabilities/importer.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -512,24 +512,30 @@ def from_dict(cls, affected_pkg: dict):
512512
fixed_version_range = None
513513
affected_range = affected_pkg["affected_version_range"]
514514
fixed_range = affected_pkg["fixed_version_range"]
515-
introduced_by_commit_patches = (
516-
affected_pkg.get("introduced_by_package_commit_patches") or []
517-
)
518-
fixed_by_commit_patches = affected_pkg.get("fixed_by_package_commit_patches") or []
515+
introduced_by_commit_patches = affected_pkg.get("introduced_by_commit_patches") or []
516+
fixed_by_commit_patches = affected_pkg.get("fixed_by_commit_patches") or []
519517

520518
try:
521-
affected_version_range = VersionRange.from_string(affected_range)
522-
fixed_version_range = VersionRange.from_string(fixed_range)
519+
affected_version_range = (
520+
VersionRange.from_string(affected_range) if affected_range else None
521+
)
522+
fixed_version_range = VersionRange.from_string(fixed_range) if fixed_range else None
523523
except:
524524
tb = traceback.format_exc()
525525
logger.error(
526526
f"Cannot create AffectedPackage with invalid or unknown range: {affected_pkg!r} with error: {tb!r}"
527527
)
528528
return
529529

530-
if not fixed_version_range and not affected_version_range:
530+
if (
531+
not fixed_version_range
532+
and not affected_version_range
533+
and not introduced_by_commit_patches
534+
and not fixed_by_commit_patches
535+
):
531536
logger.error(
532-
f"Cannot create AffectedPackage without fixed or affected range: {affected_pkg!r}"
537+
f"Cannot create an AffectedPackage for: {affected_pkg!r}, at least one of the following must be provided: "
538+
"a fixed version range, an affected version range, introduced commit patches, or fixed commit patches"
533539
)
534540
return
535541

@@ -575,6 +581,10 @@ class AdvisoryData:
575581
original_advisory_text: Optional[str] = None
576582

577583
def __post_init__(self):
584+
if self.advisory_id and self.advisory_id in self.aliases:
585+
raise ValueError(
586+
f"advisory_id {self.advisory_id} should not be present in aliases {self.aliases}"
587+
)
578588
if self.summary:
579589
self.summary = self.clean_summary(self.summary)
580590

vulnerabilities/importers/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@
6060
from vulnerabilities.pipelines.v2_importers import nvd_importer as nvd_importer_v2
6161
from vulnerabilities.pipelines.v2_importers import oss_fuzz as oss_fuzz_v2
6262
from vulnerabilities.pipelines.v2_importers import postgresql_importer as postgresql_importer_v2
63+
from vulnerabilities.pipelines.v2_importers import (
64+
project_kb_msr2019_importer as project_kb_msr2019_importer_v2,
65+
)
66+
from vulnerabilities.pipelines.v2_importers import (
67+
project_kb_statements_importer as project_kb_statements_importer_v2,
68+
)
6369
from vulnerabilities.pipelines.v2_importers import pypa_importer as pypa_importer_v2
6470
from vulnerabilities.pipelines.v2_importers import pysec_importer as pysec_importer_v2
6571
from vulnerabilities.pipelines.v2_importers import redhat_importer as redhat_importer_v2
@@ -88,6 +94,8 @@
8894
github_osv_importer_v2.GithubOSVImporterPipeline,
8995
redhat_importer_v2.RedHatImporterPipeline,
9096
aosp_importer_v2.AospImporterPipeline,
97+
project_kb_statements_importer_v2.ProjectKBStatementsPipeline,
98+
project_kb_msr2019_importer_v2.ProjectKBMSR2019Pipeline,
9199
ruby_importer_v2.RubyImporterPipeline,
92100
epss_importer_v2.EPSSImporterPipeline,
93101
nginx_importer_v2.NginxImporterPipeline,

vulnerabilities/management/commands/run_scheduler.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,21 @@
1717

1818

1919
def init_pipeline_scheduled():
20-
"""Initialize schedule jobs for active PipelineSchedule."""
21-
active_pipeline_qs = models.PipelineSchedule.objects.filter(is_active=True).order_by(
22-
"created_date"
23-
)
24-
for pipeline_schedule in active_pipeline_qs:
25-
if scheduled_job_exists(pipeline_schedule.schedule_work_id):
26-
continue
27-
new_id = pipeline_schedule.create_new_job()
28-
pipeline_schedule.schedule_work_id = new_id
29-
pipeline_schedule.save(update_fields=["schedule_work_id"])
20+
"""
21+
Initialize schedule jobs for active PipelineSchedule.
22+
- Create new schedule if there is no schedule for active pipeline
23+
- Create new schedule if schedule is corrupted for an active pipeline
24+
- Delete schedule for inactive pipeline
25+
"""
26+
pipeline_qs = models.PipelineSchedule.objects.order_by("created_date")
27+
for pipeline in pipeline_qs:
28+
reset_schedule = pipeline.is_active != bool(pipeline.schedule_work_id)
29+
if not scheduled_job_exists(pipeline.schedule_work_id):
30+
reset_schedule = True
31+
32+
if reset_schedule:
33+
pipeline.schedule_work_id = pipeline.create_new_job()
34+
pipeline.save(update_fields=["schedule_work_id"])
3035

3136

3237
class Command(rqscheduler.Command):
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Generated by Django 4.2.22 on 2026-01-08 13:41
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("vulnerabilities", "0109_alter_advisoryseverity_scoring_elements_and_more"),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name="pipelineschedule",
15+
name="is_run_once",
16+
field=models.BooleanField(
17+
db_index=True,
18+
default=False,
19+
help_text="When set to True, this Pipeline will run only once.",
20+
),
21+
),
22+
]

vulnerabilities/models.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2273,6 +2273,13 @@ class PipelineSchedule(models.Model):
22732273
),
22742274
)
22752275

2276+
is_run_once = models.BooleanField(
2277+
null=False,
2278+
db_index=True,
2279+
default=False,
2280+
help_text=("When set to True, this Pipeline will run only once."),
2281+
)
2282+
22762283
live_logging = models.BooleanField(
22772284
null=False,
22782285
db_index=True,
@@ -2789,6 +2796,19 @@ class Meta:
27892796
)
27902797
]
27912798

2799+
def to_dict(self):
2800+
return {
2801+
"patch_url": self.patch_url,
2802+
"patch_text": self.patch_text,
2803+
"patch_checksum": self.patch_checksum,
2804+
}
2805+
2806+
def to_patch_data(self):
2807+
"""Return `PatchData` from the Patch."""
2808+
from vulnerabilities.importer import PatchData
2809+
2810+
return PatchData.from_dict(self.to_dict())
2811+
27922812

27932813
class PackageCommitPatch(models.Model):
27942814
"""
@@ -2816,6 +2836,14 @@ def save(self, *args, **kwargs):
28162836
class Meta:
28172837
unique_together = ["commit_hash", "vcs_url"]
28182838

2839+
def to_dict(self):
2840+
return {
2841+
"vcs_url": self.vcs_url,
2842+
"commit_hash": self.commit_hash,
2843+
"patch_text": self.patch_text,
2844+
"patch_checksum": self.patch_checksum,
2845+
}
2846+
28192847

28202848
class AdvisoryV2QuerySet(BaseQuerySet):
28212849
def latest_for_avid(self, avid: str):
@@ -3009,6 +3037,7 @@ def to_advisory_data(self) -> "AdvisoryData":
30093037
impacted.to_affected_package_data() for impacted in self.impacted_packages.all()
30103038
],
30113039
references_v2=[ref.to_reference_v2_data() for ref in self.references.all()],
3040+
patches=[patch.to_patch_data() for patch in self.patches.all()],
30123041
date_published=self.date_published,
30133042
weaknesses=[weak.cwe_id for weak in self.weaknesses.all()],
30143043
severities=[sev.to_vulnerability_severity_data() for sev in self.severities.all()],
@@ -3092,6 +3121,12 @@ def to_dict(self):
30923121
"package": purl_to_dict(self.base_purl),
30933122
"affected_version_range": self.affecting_vers,
30943123
"fixed_version_range": self.fixed_vers,
3124+
"introduced_by_commit_patches": [
3125+
commit.to_dict() for commit in self.introduced_by_package_commit_patches.all()
3126+
],
3127+
"fixed_by_commit_patches": [
3128+
commit.to_dict() for commit in self.fixed_by_package_commit_patches.all()
3129+
],
30953130
}
30963131

30973132
def to_affected_package_data(self):

vulnerabilities/pipelines/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ class VulnerableCodeBaseImporterPipeline(VulnerableCodePipeline):
169169
importer_name = None
170170
advisory_confidence = MAX_CONFIDENCE
171171

172+
# When set to true pipeline is run only once.
173+
# To rerun onetime pipeline reset is_active field to True via migration.
174+
run_once = False
175+
172176
@classmethod
173177
def steps(cls):
174178
return (
@@ -262,6 +266,10 @@ class VulnerableCodeBaseImporterPipelineV2(VulnerableCodePipeline):
262266
repo_url = None
263267
ignorable_versions = []
264268

269+
# When set to true pipeline is run only once.
270+
# To rerun onetime pipeline reset is_active field to True via migration.
271+
run_once = False
272+
265273
@classmethod
266274
def steps(cls):
267275
return (

vulnerabilities/pipelines/v2_importers/aosp_importer.py

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,9 @@
1616
from fetchcode.vcs import fetch_via_vcs
1717

1818
from vulnerabilities.importer import AdvisoryData
19-
from vulnerabilities.importer import AffectedPackageV2
20-
from vulnerabilities.importer import PackageCommitPatchData
21-
from vulnerabilities.importer import PatchData
22-
from vulnerabilities.importer import ReferenceV2
2319
from vulnerabilities.importer import VulnerabilitySeverity
2420
from vulnerabilities.pipelines import VulnerableCodeBaseImporterPipelineV2
25-
from vulnerabilities.pipes.advisory import classify_patch_source
21+
from vulnerabilities.pipes.advisory import append_patch_classifications
2622
from vulnerabilities.severity_systems import GENERIC
2723

2824

@@ -90,23 +86,14 @@ def collect_advisories(self):
9086
patch_url = commit_data.get("patchUrl")
9187
commit_id = commit_data.get("commitId")
9288

93-
base_purl, patch_objs = classify_patch_source(
89+
append_patch_classifications(
9490
url=patch_url,
9591
commit_hash=commit_id,
9692
patch_text=None,
93+
affected_packages=affected_packages,
94+
references=references,
95+
patches=patches,
9796
)
98-
for patch_obj in patch_objs:
99-
if isinstance(patch_obj, PackageCommitPatchData):
100-
fixed_commit = patch_obj
101-
affected_package = AffectedPackageV2(
102-
package=base_purl,
103-
fixed_by_commit_patches=[fixed_commit],
104-
)
105-
affected_packages.append(affected_package)
106-
elif isinstance(patch_obj, PatchData):
107-
patches.append(patch_obj)
108-
elif isinstance(patch_obj, ReferenceV2):
109-
references.append(patch_obj)
11097

11198
url = (
11299
"https://raw.githubusercontent.com/quarkslab/aosp_dataset/refs/heads/master/cves/"

0 commit comments

Comments
 (0)