diff --git a/docs/source/contributing.rst b/docs/source/contributing.rst index 1258ae687..37344f4c9 100644 --- a/docs/source/contributing.rst +++ b/docs/source/contributing.rst @@ -73,7 +73,7 @@ overlooked. We value any suggestions to improve .. tip:: Our documentation is treated like code. Make sure to check our - `writing guidelines `_ + `writing guidelines `_ to help guide new users. Other Ways @@ -87,7 +87,7 @@ questions, and interact with us and other community members on Helpful Resources ----------------- -- Review our `comprehensive guide `_ +- Review our `comprehensive guide `_ for more details on how to add quality contributions to our codebase and documentation - Check this free resource on `How to contribute to an open source project on github `_ - Follow `this wiki page `_ diff --git a/vulnerabilities/importer.py b/vulnerabilities/importer.py index 0f5befe4d..9d19b4a5c 100644 --- a/vulnerabilities/importer.py +++ b/vulnerabilities/importer.py @@ -512,14 +512,14 @@ def from_dict(cls, affected_pkg: dict): fixed_version_range = None affected_range = affected_pkg["affected_version_range"] fixed_range = affected_pkg["fixed_version_range"] - introduced_by_commit_patches = ( - affected_pkg.get("introduced_by_package_commit_patches") or [] - ) - fixed_by_commit_patches = affected_pkg.get("fixed_by_package_commit_patches") or [] + introduced_by_commit_patches = affected_pkg.get("introduced_by_commit_patches") or [] + fixed_by_commit_patches = affected_pkg.get("fixed_by_commit_patches") or [] try: - affected_version_range = VersionRange.from_string(affected_range) - fixed_version_range = VersionRange.from_string(fixed_range) + affected_version_range = ( + VersionRange.from_string(affected_range) if affected_range else None + ) + fixed_version_range = VersionRange.from_string(fixed_range) if fixed_range else None except: tb = traceback.format_exc() logger.error( @@ -527,9 +527,15 @@ def from_dict(cls, affected_pkg: dict): ) return - if not fixed_version_range and not affected_version_range: + if ( + not fixed_version_range + and not affected_version_range + and not introduced_by_commit_patches + and not fixed_by_commit_patches + ): logger.error( - f"Cannot create AffectedPackage without fixed or affected range: {affected_pkg!r}" + f"Cannot create an AffectedPackage for: {affected_pkg!r}, at least one of the following must be provided: " + "a fixed version range, an affected version range, introduced commit patches, or fixed commit patches" ) return diff --git a/vulnerabilities/models.py b/vulnerabilities/models.py index 02a3f4098..451db71eb 100644 --- a/vulnerabilities/models.py +++ b/vulnerabilities/models.py @@ -2796,6 +2796,19 @@ class Meta: ) ] + def to_dict(self): + return { + "patch_url": self.patch_url, + "patch_text": self.patch_text, + "patch_checksum": self.patch_checksum, + } + + def to_patch_data(self): + """Return `PatchData` from the Patch.""" + from vulnerabilities.importer import PatchData + + return PatchData.from_dict(self.to_dict()) + class PackageCommitPatch(models.Model): """ @@ -2823,6 +2836,14 @@ def save(self, *args, **kwargs): class Meta: unique_together = ["commit_hash", "vcs_url"] + def to_dict(self): + return { + "vcs_url": self.vcs_url, + "commit_hash": self.commit_hash, + "patch_text": self.patch_text, + "patch_checksum": self.patch_checksum, + } + class AdvisoryV2QuerySet(BaseQuerySet): def latest_for_avid(self, avid: str): @@ -3016,6 +3037,7 @@ def to_advisory_data(self) -> "AdvisoryData": impacted.to_affected_package_data() for impacted in self.impacted_packages.all() ], references_v2=[ref.to_reference_v2_data() for ref in self.references.all()], + patches=[patch.to_patch_data() for patch in self.patches.all()], date_published=self.date_published, weaknesses=[weak.cwe_id for weak in self.weaknesses.all()], severities=[sev.to_vulnerability_severity_data() for sev in self.severities.all()], @@ -3099,6 +3121,12 @@ def to_dict(self): "package": purl_to_dict(self.base_purl), "affected_version_range": self.affecting_vers, "fixed_version_range": self.fixed_vers, + "introduced_by_commit_patches": [ + commit.to_dict() for commit in self.introduced_by_package_commit_patches.all() + ], + "fixed_by_commit_patches": [ + commit.to_dict() for commit in self.fixed_by_package_commit_patches.all() + ], } def to_affected_package_data(self): diff --git a/vulnerabilities/tests/test_models.py b/vulnerabilities/tests/test_models.py index 6c15b862a..8c4e4a9e3 100644 --- a/vulnerabilities/tests/test_models.py +++ b/vulnerabilities/tests/test_models.py @@ -24,7 +24,11 @@ from vulnerabilities import models from vulnerabilities.importer import AdvisoryData from vulnerabilities.importer import AffectedPackage +from vulnerabilities.importer import AffectedPackageV2 +from vulnerabilities.importer import PackageCommitPatchData +from vulnerabilities.importer import PatchData from vulnerabilities.importer import Reference +from vulnerabilities.importer import ReferenceV2 from vulnerabilities.models import AdvisorySeverity from vulnerabilities.models import Alias from vulnerabilities.models import Package @@ -735,3 +739,34 @@ def test_constraint_none(self): scoring_system=CVSSV4, scoring_elements="CVSS:4.0/AV:P/AC:H/AT:P/PR:H/UI:A/VC:L/VI:L/VA:H/SC:H/SI:L/SA:L/E:A/CR:M/IR:M/AR:M/MAV:A/MAC:L/MAT:P/MPR:L/MVC:L/MVI:L/MVA:L/MSC:H/MSI:H/MSA:H/S:P/AU:Y/R:U/V:C/RE:M/U:Amber", ) + + +class TestAdvisoryV2Model(DjangoTestCase): + def setUp(self): + self.advisoryv2_data1 = AdvisoryData( + advisory_id="test_adv", + aliases=[], + summary="vulnerability description here", + affected_packages=[ + AffectedPackageV2( + package=PackageURL(type="pypi", name="dummy"), + affected_version_range=VersionRange.from_string("vers:pypi/>=1.0.0|<=2.0.0"), + introduced_by_commit_patches=[ + PackageCommitPatchData( + vcs_url="http://foo.bar/", commit_hash="c4eab154606e801" + ) + ], + ) + ], + references_v2=[ReferenceV2(url="https://example.com/with/more/info/CVE-2020-13371337")], + patches=[PatchData(patch_url="https://foo.bar/", patch_text="test patch")], + url="https://test.com", + ) + + def test_advisoryv2_to_advisory_data_patch_seralization(self): + from vulnerabilities.pipes.advisory import insert_advisory_v2 + + insert_advisory_v2(advisory=self.advisoryv2_data1, pipeline_id="test_pipeline") + result = models.AdvisoryV2.objects.first().to_advisory_data() + + self.assertEqual(result, self.advisoryv2_data1)