From 0cc186b6832b12476f74e1c1b9d4e039c2b5e4cb Mon Sep 17 00:00:00 2001 From: tdruez Date: Fri, 17 May 2024 11:32:50 +0400 Subject: [PATCH] Add new resolved_to field on DiscoveredDependency #1066 And refine existing fields of the model Signed-off-by: tdruez --- ...scovereddependency_resolved_to_and_more.py | 53 +++++++++++++++++++ scanpipe/models.py | 23 +++++++- .../scanpipe/tabset/tab_dependencies.html | 2 +- scanpipe/tests/test_models.py | 4 +- scanpipe/views.py | 4 +- 5 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 scanpipe/migrations/0058_discovereddependency_resolved_to_and_more.py diff --git a/scanpipe/migrations/0058_discovereddependency_resolved_to_and_more.py b/scanpipe/migrations/0058_discovereddependency_resolved_to_and_more.py new file mode 100644 index 000000000..6a277426c --- /dev/null +++ b/scanpipe/migrations/0058_discovereddependency_resolved_to_and_more.py @@ -0,0 +1,53 @@ +# Generated by Django 5.0.4 on 2024-05-17 07:11 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("scanpipe", "0057_rename_symbol_collection_pipelines"), + ] + + operations = [ + migrations.AddField( + model_name="discovereddependency", + name="resolved_to", + field=models.ForeignKey( + blank=True, + editable=False, + help_text="The package that this dependency resolves to. If empty, it indicates the dependency is unresolved.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="resolved_dependencies", + to="scanpipe.discoveredpackage", + ), + ), + migrations.AlterField( + model_name="discovereddependency", + name="datafile_resource", + field=models.ForeignKey( + blank=True, + editable=False, + help_text="The codebase resource (e.g., manifest or lockfile) that declares this dependency.", + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="declared_dependencies", + to="scanpipe.codebaseresource", + ), + ), + migrations.AlterField( + model_name="discovereddependency", + name="for_package", + field=models.ForeignKey( + blank=True, + editable=False, + help_text="The package that declares this dependency.", + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="declared_dependencies", + to="scanpipe.discoveredpackage", + ), + ), + ] diff --git a/scanpipe/models.py b/scanpipe/models.py index 7140a1bd3..e9fd15699 100644 --- a/scanpipe/models.py +++ b/scanpipe/models.py @@ -3290,6 +3290,8 @@ class DiscoveredDependency( """ A project's Discovered Dependencies are records of the dependencies used by system and application packages discovered in the code under analysis. + Dependencies are usually collected from parsed package data such as a package + manifest or lockfile. """ # Overrides the `project` field from `ProjectRelatedModel` to set the proper @@ -3306,15 +3308,32 @@ class DiscoveredDependency( ) for_package = models.ForeignKey( DiscoveredPackage, - related_name="dependencies", + related_name="declared_dependencies", + help_text=_("The package that declares this dependency."), on_delete=models.CASCADE, editable=False, blank=True, null=True, ) + resolved_to = models.ForeignKey( + DiscoveredPackage, + related_name="resolved_dependencies", + help_text=_( + "The package that this dependency resolves to. " + "If empty, it indicates the dependency is unresolved." + ), + on_delete=models.SET_NULL, + editable=False, + blank=True, + null=True, + ) datafile_resource = models.ForeignKey( CodebaseResource, - related_name="dependencies", + related_name="declared_dependencies", + help_text=_( + "The codebase resource (e.g., manifest or lockfile) that declares this " + "dependency." + ), on_delete=models.CASCADE, editable=False, blank=True, diff --git a/scanpipe/templates/scanpipe/tabset/tab_dependencies.html b/scanpipe/templates/scanpipe/tabset/tab_dependencies.html index 96d2f1c39..817e8ba6d 100644 --- a/scanpipe/templates/scanpipe/tabset/tab_dependencies.html +++ b/scanpipe/templates/scanpipe/tabset/tab_dependencies.html @@ -9,7 +9,7 @@ - {% for dependency in tab_data.fields.dependencies.value %} + {% for dependency in tab_data.fields.declared_dependencies.value %} {{ dependency.purl }} diff --git a/scanpipe/tests/test_models.py b/scanpipe/tests/test_models.py index 7e5d6c45d..7609829e1 100644 --- a/scanpipe/tests/test_models.py +++ b/scanpipe/tests/test_models.py @@ -2069,9 +2069,11 @@ def test_scanpipe_package_model_integrity_with_toolkit_package_model(self): "affected_by_vulnerabilities", "compliance_alert", "tag", + "declared_dependencies", + "resolved_dependencies", ] - package_data_only_field = ["datasource_id"] + package_data_only_field = ["datasource_id", "dependencies"] discovered_package_fields = [ field.name diff --git a/scanpipe/views.py b/scanpipe/views.py index 7e655f7e6..0d0805d53 100644 --- a/scanpipe/views.py +++ b/scanpipe/views.py @@ -1835,7 +1835,7 @@ class DiscoveredPackageDetailsView( "project_id", ), ), - "dependencies__project", + "declared_dependencies__project", ] tabset = { "essentials": { @@ -1910,7 +1910,7 @@ class DiscoveredPackageDetailsView( "template": "scanpipe/tabset/tab_resources.html", }, "dependencies": { - "fields": ["dependencies"], + "fields": ["declared_dependencies"], "icon_class": "fa-solid fa-layer-group", "template": "scanpipe/tabset/tab_dependencies.html", },