diff --git a/AGENTS.md b/AGENTS.md index 64279977619..8c5bbb6bc5c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -54,6 +54,7 @@ Modules in various stages of reorganization: |--------|-----------|-------------|-----|------|--------| | **url** | In module | N/A | Done | Done | **Complete** | | **location** | In module | N/A | N/A | Done | **Complete** | +| **cicd_infrastructure** | In module | N/A | Done | Done | **Complete** | | **product_type** | In dojo/models.py | Missing | Partial (views at root) | In dojo/api_v2/ | Needs work | | **test** | In dojo/models.py | Missing | Partial (views at root) | In dojo/api_v2/ | Needs work | | **engagement** | In dojo/models.py | Partial (32 lines) | Partial (views at root) | In dojo/api_v2/ | Needs work | diff --git a/dojo/authorization/api_permissions.py b/dojo/authorization/api_permissions.py index d8237c382f0..3b992af9ec4 100644 --- a/dojo/authorization/api_permissions.py +++ b/dojo/authorization/api_permissions.py @@ -19,6 +19,7 @@ from dojo.importers.auto_create_context import AutoCreateContextManager from dojo.location.models import Location from dojo.models import ( + CICDInfrastructure, Development_Environment, Endpoint, Engagement, @@ -919,6 +920,18 @@ class UserHasRegulationPermission(BaseDjangoModelPermission): } +class UserHasCICDInfrastructurePermission(BaseDjangoModelPermission): + django_model = CICDInfrastructure + # Reads are open to any authenticated user (engagement views surface CICD + # references and need to render them). Writes require elevated privileges. + request_method_permission_map = { + "POST": "add", + "PUT": "change", + "PATCH": "change", + "DELETE": "delete", + } + + def raise_no_auto_create_import_validation_error( test_title, scan_type, diff --git a/dojo/cicd_infrastructure/__init__.py b/dojo/cicd_infrastructure/__init__.py new file mode 100644 index 00000000000..0d13c6e29ca --- /dev/null +++ b/dojo/cicd_infrastructure/__init__.py @@ -0,0 +1 @@ +import dojo.cicd_infrastructure.admin # noqa: F401 diff --git a/dojo/cicd_infrastructure/admin.py b/dojo/cicd_infrastructure/admin.py new file mode 100644 index 00000000000..d80c7aa7ef8 --- /dev/null +++ b/dojo/cicd_infrastructure/admin.py @@ -0,0 +1,9 @@ +from django.contrib import admin + +from dojo.cicd_infrastructure.models import CICDInfrastructure + + +@admin.register(CICDInfrastructure) +class CICDInfrastructureAdmin(admin.ModelAdmin): + + """Admin support for the CICDInfrastructure model.""" diff --git a/dojo/cicd_infrastructure/api/__init__.py b/dojo/cicd_infrastructure/api/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dojo/cicd_infrastructure/api/serializers.py b/dojo/cicd_infrastructure/api/serializers.py new file mode 100644 index 00000000000..5d16e5251fb --- /dev/null +++ b/dojo/cicd_infrastructure/api/serializers.py @@ -0,0 +1,16 @@ +from rest_framework import serializers + +from dojo.models import CICDInfrastructure + + +class CICDInfrastructureSerializer(serializers.ModelSerializer): + class Meta: + model = CICDInfrastructure + fields = "__all__" + + def get_fields(self): + fields = super().get_fields() + if self.instance is not None: + # Disallow editing of infra type on an instance; see the matching comment on CICDInfrastructure#save() + fields["infrastructure_type"].read_only = True + return fields diff --git a/dojo/cicd_infrastructure/api/urls.py b/dojo/cicd_infrastructure/api/urls.py new file mode 100644 index 00000000000..b4bb389e7eb --- /dev/null +++ b/dojo/cicd_infrastructure/api/urls.py @@ -0,0 +1,6 @@ +from dojo.cicd_infrastructure.api.views import CICDInfrastructureViewSet + + +def add_cicd_infrastructure_urls(router): + router.register(r"cicd_infrastructure", CICDInfrastructureViewSet, basename="cicd_infrastructure") + return router diff --git a/dojo/cicd_infrastructure/api/views.py b/dojo/cicd_infrastructure/api/views.py new file mode 100644 index 00000000000..32fafb381a8 --- /dev/null +++ b/dojo/cicd_infrastructure/api/views.py @@ -0,0 +1,21 @@ +from django_filters.rest_framework import DjangoFilterBackend +from rest_framework.permissions import IsAuthenticated + +from dojo.api_v2.views import DojoModelViewSet +from dojo.authorization import api_permissions as permissions +from dojo.cicd_infrastructure.api.serializers import CICDInfrastructureSerializer +from dojo.models import CICDInfrastructure + + +# Authorization: read open to authenticated users; write requires configuration permission. +class CICDInfrastructureViewSet( + DojoModelViewSet, +): + serializer_class = CICDInfrastructureSerializer + queryset = CICDInfrastructure.objects.none() + filter_backends = (DjangoFilterBackend,) + filterset_fields = ["id", "name", "infrastructure_type"] + permission_classes = (IsAuthenticated, permissions.UserHasCICDInfrastructurePermission) + + def get_queryset(self): + return CICDInfrastructure.objects.all().order_by("id") diff --git a/dojo/cicd_infrastructure/models.py b/dojo/cicd_infrastructure/models.py new file mode 100644 index 00000000000..ed717d0087e --- /dev/null +++ b/dojo/cicd_infrastructure/models.py @@ -0,0 +1,35 @@ +from django.core.exceptions import ValidationError +from django.db import models +from django.utils.translation import gettext as _ + + +class CICDInfrastructure(models.Model): + INFRASTRUCTURE_TYPE_CHOICES = ( + ("scm_server", "SCM Server"), + ("build_server", "Build Server"), + ("orchestration", "Orchestration Engine"), + ) + + name = models.CharField(max_length=200) + description = models.CharField(max_length=2000, blank=True, default="") + url = models.URLField(max_length=2000, blank=True, default="", help_text=_("Public URL of the tool (e.g., https://jenkins.company.com)")) + infrastructure_type = models.CharField(max_length=30, choices=INFRASTRUCTURE_TYPE_CHOICES) + + class Meta: + ordering = ["name"] + unique_together = [("name", "infrastructure_type")] + + def __str__(self): + return self.name + + def save(self, *args, **kwargs): + if self.pk: + # Disallow editing of the infra type on an instance; engagement CICD FKs are scoped by infrastructure_type + # via limit_choices_to (build_server/scm_server/orchestration), so changing the type would create a + # semantic conflict between an engagement and this object. + current_type = type(self).objects.filter(pk=self.pk).values_list("infrastructure_type", flat=True).first() + if current_type is not None and current_type != self.infrastructure_type: + raise ValidationError( + {"infrastructure_type": _("infrastructure_type cannot be changed once set.")}, + ) + super().save(*args, **kwargs) diff --git a/dojo/cicd_infrastructure/ui/__init__.py b/dojo/cicd_infrastructure/ui/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dojo/cicd_infrastructure/ui/forms.py b/dojo/cicd_infrastructure/ui/forms.py new file mode 100644 index 00000000000..9f4d83f0792 --- /dev/null +++ b/dojo/cicd_infrastructure/ui/forms.py @@ -0,0 +1,15 @@ +from django import forms + +from dojo.models import CICDInfrastructure + + +class CICDInfrastructureForm(forms.ModelForm): + class Meta: + model = CICDInfrastructure + fields = ["name", "description", "url", "infrastructure_type"] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if self.instance.pk: + # Disallow editing of infra type on an instance; see the matching comment on CICDInfrastructure#save() + self.fields["infrastructure_type"].disabled = True diff --git a/dojo/cicd_infrastructure/ui/urls.py b/dojo/cicd_infrastructure/ui/urls.py new file mode 100644 index 00000000000..c1f88104fa1 --- /dev/null +++ b/dojo/cicd_infrastructure/ui/urls.py @@ -0,0 +1,10 @@ +from django.urls import re_path + +from dojo.cicd_infrastructure.ui import views + +urlpatterns = [ + re_path(r"^cicd_infrastructure/add$", views.new_cicd_infrastructure, name="add_cicd_infrastructure"), + re_path(r"^cicd_infrastructure/(?P\d+)/edit$", views.edit_cicd_infrastructure, name="edit_cicd_infrastructure"), + re_path(r"^cicd_infrastructure/(?P\d+)/delete$", views.delete_cicd_infrastructure, name="delete_cicd_infrastructure"), + re_path(r"^cicd_infrastructure$", views.cicd_infrastructure, name="cicd_infrastructure"), +] diff --git a/dojo/cicd_infrastructure/ui/views.py b/dojo/cicd_infrastructure/ui/views.py new file mode 100644 index 00000000000..fe1bf1cbe89 --- /dev/null +++ b/dojo/cicd_infrastructure/ui/views.py @@ -0,0 +1,66 @@ +import logging + +from django.contrib import messages +from django.http import HttpResponseRedirect +from django.shortcuts import get_object_or_404, render +from django.urls import reverse +from django.utils.translation import gettext as _ + +from dojo.authorization.authorization import user_has_configuration_permission_or_403 +from dojo.cicd_infrastructure.ui.forms import CICDInfrastructureForm +from dojo.models import CICDInfrastructure +from dojo.utils import add_breadcrumb + +logger = logging.getLogger(__name__) + + +def cicd_infrastructure(request): + confs = CICDInfrastructure.objects.all().order_by("name") + add_breadcrumb(title=_("CI/CD Infrastructure List"), top_level=not len(request.GET), request=request) + return render(request, "dojo/cicd_infrastructure.html", {"confs": confs}) + + +def new_cicd_infrastructure(request): + user_has_configuration_permission_or_403(request.user, "dojo.add_cicdinfrastructure") + if request.method == "POST": + form = CICDInfrastructureForm(request.POST) + if form.is_valid(): + form.save() + messages.add_message(request, messages.SUCCESS, + _("CI/CD Infrastructure successfully created."), + extra_tags="alert-success") + return HttpResponseRedirect(reverse("cicd_infrastructure")) + else: + form = CICDInfrastructureForm() + add_breadcrumb(title=_("New CI/CD Infrastructure"), top_level=False, request=request) + return render(request, "dojo/new_cicd_infrastructure.html", {"form": form}) + + +def edit_cicd_infrastructure(request, ciid): + user_has_configuration_permission_or_403(request.user, "dojo.change_cicdinfrastructure") + conf = get_object_or_404(CICDInfrastructure, pk=ciid) + if request.method == "POST": + form = CICDInfrastructureForm(request.POST, instance=conf) + if form.is_valid(): + form.save() + messages.add_message(request, messages.SUCCESS, + _("CI/CD Infrastructure successfully updated."), + extra_tags="alert-success") + return HttpResponseRedirect(reverse("cicd_infrastructure")) + else: + form = CICDInfrastructureForm(instance=conf) + add_breadcrumb(title=_("Edit CI/CD Infrastructure"), top_level=False, request=request) + return render(request, "dojo/edit_cicd_infrastructure.html", {"form": form, "conf": conf}) + + +def delete_cicd_infrastructure(request, ciid): + user_has_configuration_permission_or_403(request.user, "dojo.delete_cicdinfrastructure") + conf = get_object_or_404(CICDInfrastructure, pk=ciid) + if request.method == "POST": + conf.delete() + messages.add_message(request, messages.SUCCESS, + _("CI/CD Infrastructure successfully deleted."), + extra_tags="alert-success") + return HttpResponseRedirect(reverse("cicd_infrastructure")) + add_breadcrumb(title=_("Delete CI/CD Infrastructure"), top_level=False, request=request) + return render(request, "dojo/delete_cicd_infrastructure.html", {"conf": conf}) diff --git a/dojo/db_migrations/0269_cicd_infrastructure.py b/dojo/db_migrations/0269_cicd_infrastructure.py new file mode 100644 index 00000000000..1d70ad1ac48 --- /dev/null +++ b/dojo/db_migrations/0269_cicd_infrastructure.py @@ -0,0 +1,206 @@ +import logging + +from django.db import migrations, models +import django.db.models.deletion +import pgtrigger.compiler +import pgtrigger.migrations + +logger = logging.getLogger(__name__) + + +def migrate_tool_configs_to_cicd_infrastructure(apps, schema_editor): + """ + For each Tool_Configuration referenced by an engagement's build_server, + source_code_management_server, or orchestration_engine FK, create a + CICDInfrastructure record and point the new engagement FK to it. + """ + Engagement = apps.get_model("dojo", "Engagement") + CICDInfrastructure = apps.get_model("dojo", "CICDInfrastructure") + + field_mappings = [ + ("source_code_management_server", "cicd_scm_server", "scm_server"), + ("build_server", "cicd_build_server", "build_server"), + ("orchestration_engine", "cicd_orchestration_engine", "orchestration"), + ] + + for old_field, new_field, infra_type in field_mappings: + engagements_with_old_fk = Engagement.objects.filter( + **{f"{old_field}__isnull": False}, + ).select_related(old_field) + + for engagement in engagements_with_old_fk: + tc = getattr(engagement, old_field) + if tc is None: + continue + + cicd_infra, created = CICDInfrastructure.objects.get_or_create( + name=tc.name, + infrastructure_type=infra_type, + defaults={ + "description": tc.description or "", + "url": tc.url or "", + }, + ) + if created: + logger.info( + "Created CICDInfrastructure '%s' (type=%s) from Tool_Configuration '%s'", + cicd_infra.name, infra_type, tc.name, + ) + + setattr(engagement, new_field, cicd_infra) + engagement.save(update_fields=[new_field]) + + +class Migration(migrations.Migration): + + dependencies = [ + ("dojo", "0268_release_authorization_to_pro"), + ] + + operations = [ + # Step 1: Create CICDInfrastructure model + migrations.CreateModel( + name="CICDInfrastructure", + fields=[ + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("name", models.CharField(max_length=200)), + ("description", models.CharField(blank=True, default="", max_length=2000)), + ("url", models.URLField(blank=True, default="", help_text="Public URL of the tool (e.g., https://jenkins.company.com)", max_length=2000)), + ("infrastructure_type", models.CharField(choices=[("scm_server", "SCM Server"), ("build_server", "Build Server"), ("orchestration", "Orchestration Engine")], max_length=30)), + ], + options={ + "ordering": ["name"], + "unique_together": {("name", "infrastructure_type")}, + }, + ), + # Step 2: Remove old pgtrigger triggers FIRST. + # The triggers reference the old column names (build_server_id, etc.). If we left + # them in place, the data migration (Step 4) would still fire AFTER UPDATE on each + # engagement.save() and write rows to dojo_engagementevent that record the old + # columns (untouched, so they look like no-op updates) while missing the actual + # change to the new cicd_* columns — misleading audit history. Dropping the old + # triggers up front skips that noise; the new triggers are re-added at Step 7 + # once the column renames are in place. + pgtrigger.migrations.RemoveTrigger( + model_name='engagement', + name='insert_insert', + ), + pgtrigger.migrations.RemoveTrigger( + model_name='engagement', + name='update_update', + ), + pgtrigger.migrations.RemoveTrigger( + model_name='engagement', + name='delete_delete', + ), + # Step 3: Add new FK fields to Engagement (before removing old ones) + migrations.AddField( + model_name="engagement", + name="cicd_scm_server", + field=models.ForeignKey( + blank=True, null=True, + help_text="Source code management server used for this CI/CD engagement", + limit_choices_to={"infrastructure_type": "scm_server"}, + on_delete=django.db.models.deletion.SET_NULL, + related_name="engagements_as_scm_server", + to="dojo.cicdinfrastructure", + verbose_name="SCM Server", + ), + ), + migrations.AddField( + model_name="engagement", + name="cicd_build_server", + field=models.ForeignKey( + blank=True, null=True, + help_text="Build server used for this CI/CD engagement", + limit_choices_to={"infrastructure_type": "build_server"}, + on_delete=django.db.models.deletion.SET_NULL, + related_name="engagements_as_build_server", + to="dojo.cicdinfrastructure", + verbose_name="Build Server", + ), + ), + migrations.AddField( + model_name="engagement", + name="cicd_orchestration_engine", + field=models.ForeignKey( + blank=True, null=True, + help_text="Orchestration engine used for this CI/CD engagement", + limit_choices_to={"infrastructure_type": "orchestration"}, + on_delete=django.db.models.deletion.SET_NULL, + related_name="engagements_as_orchestration", + to="dojo.cicdinfrastructure", + verbose_name="Orchestration Engine", + ), + ), + # Step 4: Migrate data from Tool_Configuration to CICDInfrastructure + migrations.RunPython( + migrate_tool_configs_to_cicd_infrastructure, + reverse_code=migrations.RunPython.noop, + ), + # Step 4a: Force deferred FK constraint checks to fire now. Django creates FKs as + # DEFERRABLE INITIALLY DEFERRED, so each engagement.save() in Step 4 queued a + # constraint check for commit time. Without flushing those, the next ALTER TABLE + # on dojo_engagement (Step 5) trips Postgres' "pending trigger events" guard. + migrations.RunSQL( + sql="SET CONSTRAINTS ALL IMMEDIATE", + reverse_sql=migrations.RunSQL.noop, + ), + # Step 5: Remove old FK fields from Engagement + migrations.RemoveField( + model_name="engagement", + name="source_code_management_server", + ), + migrations.RemoveField( + model_name="engagement", + name="build_server", + ), + migrations.RemoveField( + model_name="engagement", + name="orchestration_engine", + ), + # Step 6: Update pghistory event table FK fields to point to CICDInfrastructure + migrations.RenameField( + model_name="engagementevent", + old_name="source_code_management_server", + new_name="cicd_scm_server", + ), + migrations.RenameField( + model_name="engagementevent", + old_name="build_server", + new_name="cicd_build_server", + ), + migrations.RenameField( + model_name="engagementevent", + old_name="orchestration_engine", + new_name="cicd_orchestration_engine", + ), + migrations.AlterField( + model_name='engagementevent', + name='cicd_scm_server', + field=models.ForeignKey(blank=True, db_constraint=False, db_index=False, help_text='Source code management server used for this CI/CD engagement', limit_choices_to={'infrastructure_type': 'scm_server'}, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to='dojo.cicdinfrastructure', verbose_name='SCM Server'), + ), + migrations.AlterField( + model_name='engagementevent', + name='cicd_build_server', + field=models.ForeignKey(blank=True, db_constraint=False, db_index=False, help_text='Build server used for this CI/CD engagement', limit_choices_to={'infrastructure_type': 'build_server'}, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to='dojo.cicdinfrastructure', verbose_name='Build Server'), + ), + migrations.AlterField( + model_name='engagementevent', + name='cicd_orchestration_engine', + field=models.ForeignKey(blank=True, db_constraint=False, db_index=False, help_text='Orchestration engine used for this CI/CD engagement', limit_choices_to={'infrastructure_type': 'orchestration'}, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to='dojo.cicdinfrastructure', verbose_name='Orchestration Engine'), + ), + # Step 7: Re-create pgtrigger triggers with new column names + pgtrigger.migrations.AddTrigger( + model_name='engagement', + trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "dojo_engagementevent" ("active", "api_test", "branch_tag", "build_id", "check_list", "cicd_build_server_id", "cicd_orchestration_engine_id", "cicd_scm_server_id", "commit_hash", "created", "deduplication_on_engagement", "description", "done_testing", "engagement_type", "first_contacted", "id", "lead_id", "name", "pen_test", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "preset_id", "product_id", "progress", "reason", "report_type_id", "requester_id", "source_code_management_uri", "status", "target_end", "target_start", "test_strategy", "threat_model", "tmodel_path", "tracker", "updated", "version") VALUES (NEW."active", NEW."api_test", NEW."branch_tag", NEW."build_id", NEW."check_list", NEW."cicd_build_server_id", NEW."cicd_orchestration_engine_id", NEW."cicd_scm_server_id", NEW."commit_hash", NEW."created", NEW."deduplication_on_engagement", NEW."description", NEW."done_testing", NEW."engagement_type", NEW."first_contacted", NEW."id", NEW."lead_id", NEW."name", NEW."pen_test", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."preset_id", NEW."product_id", NEW."progress", NEW."reason", NEW."report_type_id", NEW."requester_id", NEW."source_code_management_uri", NEW."status", NEW."target_end", NEW."target_start", NEW."test_strategy", NEW."threat_model", NEW."tmodel_path", NEW."tracker", NEW."updated", NEW."version"); RETURN NULL;', hash='a217ec77b975020749afc350ee463c5867cfea27', operation='INSERT', pgid='pgtrigger_insert_insert_125f1', table='dojo_engagement', when='AFTER')), + ), + pgtrigger.migrations.AddTrigger( + model_name='engagement', + trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD."active" IS DISTINCT FROM (NEW."active") OR OLD."api_test" IS DISTINCT FROM (NEW."api_test") OR OLD."branch_tag" IS DISTINCT FROM (NEW."branch_tag") OR OLD."build_id" IS DISTINCT FROM (NEW."build_id") OR OLD."check_list" IS DISTINCT FROM (NEW."check_list") OR OLD."cicd_build_server_id" IS DISTINCT FROM (NEW."cicd_build_server_id") OR OLD."cicd_orchestration_engine_id" IS DISTINCT FROM (NEW."cicd_orchestration_engine_id") OR OLD."cicd_scm_server_id" IS DISTINCT FROM (NEW."cicd_scm_server_id") OR OLD."commit_hash" IS DISTINCT FROM (NEW."commit_hash") OR OLD."deduplication_on_engagement" IS DISTINCT FROM (NEW."deduplication_on_engagement") OR OLD."description" IS DISTINCT FROM (NEW."description") OR OLD."done_testing" IS DISTINCT FROM (NEW."done_testing") OR OLD."engagement_type" IS DISTINCT FROM (NEW."engagement_type") OR OLD."first_contacted" IS DISTINCT FROM (NEW."first_contacted") OR OLD."id" IS DISTINCT FROM (NEW."id") OR OLD."lead_id" IS DISTINCT FROM (NEW."lead_id") OR OLD."name" IS DISTINCT FROM (NEW."name") OR OLD."pen_test" IS DISTINCT FROM (NEW."pen_test") OR OLD."preset_id" IS DISTINCT FROM (NEW."preset_id") OR OLD."product_id" IS DISTINCT FROM (NEW."product_id") OR OLD."progress" IS DISTINCT FROM (NEW."progress") OR OLD."reason" IS DISTINCT FROM (NEW."reason") OR OLD."report_type_id" IS DISTINCT FROM (NEW."report_type_id") OR OLD."requester_id" IS DISTINCT FROM (NEW."requester_id") OR OLD."source_code_management_uri" IS DISTINCT FROM (NEW."source_code_management_uri") OR OLD."status" IS DISTINCT FROM (NEW."status") OR OLD."target_end" IS DISTINCT FROM (NEW."target_end") OR OLD."target_start" IS DISTINCT FROM (NEW."target_start") OR OLD."test_strategy" IS DISTINCT FROM (NEW."test_strategy") OR OLD."threat_model" IS DISTINCT FROM (NEW."threat_model") OR OLD."tmodel_path" IS DISTINCT FROM (NEW."tmodel_path") OR OLD."tracker" IS DISTINCT FROM (NEW."tracker") OR OLD."version" IS DISTINCT FROM (NEW."version"))', func='INSERT INTO "dojo_engagementevent" ("active", "api_test", "branch_tag", "build_id", "check_list", "cicd_build_server_id", "cicd_orchestration_engine_id", "cicd_scm_server_id", "commit_hash", "created", "deduplication_on_engagement", "description", "done_testing", "engagement_type", "first_contacted", "id", "lead_id", "name", "pen_test", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "preset_id", "product_id", "progress", "reason", "report_type_id", "requester_id", "source_code_management_uri", "status", "target_end", "target_start", "test_strategy", "threat_model", "tmodel_path", "tracker", "updated", "version") VALUES (NEW."active", NEW."api_test", NEW."branch_tag", NEW."build_id", NEW."check_list", NEW."cicd_build_server_id", NEW."cicd_orchestration_engine_id", NEW."cicd_scm_server_id", NEW."commit_hash", NEW."created", NEW."deduplication_on_engagement", NEW."description", NEW."done_testing", NEW."engagement_type", NEW."first_contacted", NEW."id", NEW."lead_id", NEW."name", NEW."pen_test", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."preset_id", NEW."product_id", NEW."progress", NEW."reason", NEW."report_type_id", NEW."requester_id", NEW."source_code_management_uri", NEW."status", NEW."target_end", NEW."target_start", NEW."test_strategy", NEW."threat_model", NEW."tmodel_path", NEW."tracker", NEW."updated", NEW."version"); RETURN NULL;', hash='6a9569fa21d5d7ad16eb018bc4e6236e8401bced', operation='UPDATE', pgid='pgtrigger_update_update_65136', table='dojo_engagement', when='AFTER')), + ), + pgtrigger.migrations.AddTrigger( + model_name='engagement', + trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "dojo_engagementevent" ("active", "api_test", "branch_tag", "build_id", "check_list", "cicd_build_server_id", "cicd_orchestration_engine_id", "cicd_scm_server_id", "commit_hash", "created", "deduplication_on_engagement", "description", "done_testing", "engagement_type", "first_contacted", "id", "lead_id", "name", "pen_test", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "preset_id", "product_id", "progress", "reason", "report_type_id", "requester_id", "source_code_management_uri", "status", "target_end", "target_start", "test_strategy", "threat_model", "tmodel_path", "tracker", "updated", "version") VALUES (OLD."active", OLD."api_test", OLD."branch_tag", OLD."build_id", OLD."check_list", OLD."cicd_build_server_id", OLD."cicd_orchestration_engine_id", OLD."cicd_scm_server_id", OLD."commit_hash", OLD."created", OLD."deduplication_on_engagement", OLD."description", OLD."done_testing", OLD."engagement_type", OLD."first_contacted", OLD."id", OLD."lead_id", OLD."name", OLD."pen_test", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."preset_id", OLD."product_id", OLD."progress", OLD."reason", OLD."report_type_id", OLD."requester_id", OLD."source_code_management_uri", OLD."status", OLD."target_end", OLD."target_start", OLD."test_strategy", OLD."threat_model", OLD."tmodel_path", OLD."tracker", OLD."updated", OLD."version"); RETURN NULL;', hash='de64abfdac94fadfbe7a8cd33212b1dc26ad9600', operation='DELETE', pgid='pgtrigger_delete_delete_9f4df', table='dojo_engagement', when='AFTER')), + ), + ] diff --git a/dojo/engagement/views.py b/dojo/engagement/views.py index 0154aa5d336..3df05b63057 100644 --- a/dojo/engagement/views.py +++ b/dojo/engagement/views.py @@ -1606,8 +1606,8 @@ def get_excludes(): def get_foreign_keys(): - return ["build_server", "lead", "orchestration_engine", "preset", "product", - "report_type", "requester", "source_code_management_server"] + return ["cicd_build_server", "cicd_orchestration_engine", "cicd_scm_server", + "lead", "preset", "product", "report_type", "requester"] def csv_export(request): diff --git a/dojo/fixtures/defect_dojo_sample_data.json b/dojo/fixtures/defect_dojo_sample_data.json index 7923a91dd38..afe62b4e550 100644 --- a/dojo/fixtures/defect_dojo_sample_data.json +++ b/dojo/fixtures/defect_dojo_sample_data.json @@ -2740,8 +2740,10 @@ "api_test": true, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": true, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": null, "deduplication_on_engagement": false, @@ -2756,7 +2758,6 @@ ], "name": "1st Quarter Engagement", "notes": [], - "orchestration_engine": null, "pen_test": true, "preset": null, "product": 2, @@ -2765,7 +2766,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": null, "status": "In Progress", "tags": [], @@ -2787,8 +2787,10 @@ "api_test": true, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": true, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": null, "deduplication_on_engagement": false, @@ -2803,7 +2805,6 @@ ], "name": "April Monthly Engagement", "notes": [], - "orchestration_engine": null, "pen_test": true, "preset": null, "product": 1, @@ -2812,7 +2813,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": null, "status": "Completed", "tags": [], @@ -2834,8 +2834,10 @@ "api_test": true, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": true, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": null, "deduplication_on_engagement": false, @@ -2850,7 +2852,6 @@ ], "name": "weekly engagement", "notes": [], - "orchestration_engine": null, "pen_test": true, "preset": null, "product": 2, @@ -2859,7 +2860,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": null, "status": "Completed", "tags": [], @@ -2881,8 +2881,10 @@ "api_test": false, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2022-11-10T06:00:11.573932529Z", "deduplication_on_engagement": false, @@ -2897,7 +2899,6 @@ ], "name": "Static Scan", "notes": [], - "orchestration_engine": null, "pen_test": false, "preset": null, "product": 1, @@ -2906,7 +2907,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": null, "status": "Completed", "tags": [], @@ -2928,8 +2928,10 @@ "api_test": false, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2022-11-10T06:24:40.306932529Z", "deduplication_on_engagement": false, @@ -2944,7 +2946,6 @@ ], "name": "Quarterly PCI Scan", "notes": [], - "orchestration_engine": null, "pen_test": false, "preset": null, "product": 1, @@ -2953,7 +2954,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": null, "status": "Not Started", "tags": [ @@ -2977,8 +2977,10 @@ "api_test": true, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": true, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2022-11-10T06:35:26.062932529Z", "deduplication_on_engagement": false, @@ -2991,7 +2993,6 @@ "lead": null, "name": "Ad Hoc Engagement", "notes": [], - "orchestration_engine": null, "pen_test": true, "preset": null, "product": 2, @@ -3000,7 +3001,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": null, "status": "", "tags": [], @@ -3022,8 +3022,10 @@ "api_test": false, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2022-11-10T06:42:02.042932529Z", "deduplication_on_engagement": false, @@ -3038,7 +3040,6 @@ ], "name": "Initial Assessment", "notes": [], - "orchestration_engine": null, "pen_test": false, "preset": null, "product": 3, @@ -3047,7 +3048,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": null, "status": "Not Started", "tags": [], @@ -3069,8 +3069,10 @@ "api_test": false, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2022-11-11T03:43:46.699932529Z", "deduplication_on_engagement": false, @@ -3085,7 +3087,6 @@ ], "name": "Multiple scanners", "notes": [], - "orchestration_engine": null, "pen_test": false, "preset": null, "product": 1, @@ -3094,7 +3095,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": null, "status": "Completed", "tags": [ @@ -3118,8 +3118,10 @@ "api_test": false, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2022-11-11T03:53:22.806932529Z", "deduplication_on_engagement": false, @@ -3134,7 +3136,6 @@ ], "name": "Manual PenTest", "notes": [], - "orchestration_engine": null, "pen_test": false, "preset": null, "product": 1, @@ -3143,7 +3144,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": null, "status": "Blocked", "tags": [], @@ -3165,8 +3165,10 @@ "api_test": false, "branch_tag": "master", "build_id": "89", - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": "b8ca612dbbd45f37d62c7b9d3e9521a31438aaa6", "created": "2022-11-11T04:05:37.062932529Z", "deduplication_on_engagement": false, @@ -3181,7 +3183,6 @@ ], "name": "CI/CD Baseline Security Test", "notes": [], - "orchestration_engine": null, "pen_test": false, "preset": null, "product": 1, @@ -3190,7 +3191,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": "https://github.com/psiinon/bodgeit", "status": "Completed", "tags": [], @@ -3212,8 +3212,10 @@ "api_test": false, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2022-11-11T07:42:16.372932529Z", "deduplication_on_engagement": false, @@ -3226,7 +3228,6 @@ "lead": null, "name": "AdHoc Import - Fri, 17 Aug 2018 18:20:55", "notes": [], - "orchestration_engine": null, "pen_test": false, "preset": null, "product": 1, @@ -3235,7 +3236,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": null, "status": "In Progress", "tags": [], diff --git a/dojo/fixtures/defect_dojo_sample_data_locations.json b/dojo/fixtures/defect_dojo_sample_data_locations.json index 0b08e888e03..1c0c55dae30 100644 --- a/dojo/fixtures/defect_dojo_sample_data_locations.json +++ b/dojo/fixtures/defect_dojo_sample_data_locations.json @@ -2749,8 +2749,10 @@ "api_test": true, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": true, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": null, "deduplication_on_engagement": false, @@ -2765,7 +2767,6 @@ ], "name": "1st Quarter Engagement", "notes": [], - "orchestration_engine": null, "pen_test": true, "preset": null, "product": 2, @@ -2774,7 +2775,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": null, "status": "In Progress", "tags": [], @@ -2796,8 +2796,10 @@ "api_test": true, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": true, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": null, "deduplication_on_engagement": false, @@ -2812,7 +2814,6 @@ ], "name": "April Monthly Engagement", "notes": [], - "orchestration_engine": null, "pen_test": true, "preset": null, "product": 1, @@ -2821,7 +2822,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": null, "status": "Completed", "tags": [], @@ -2843,8 +2843,10 @@ "api_test": true, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": true, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": null, "deduplication_on_engagement": false, @@ -2859,7 +2861,6 @@ ], "name": "weekly engagement", "notes": [], - "orchestration_engine": null, "pen_test": true, "preset": null, "product": 2, @@ -2868,7 +2869,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": null, "status": "Completed", "tags": [], @@ -2890,8 +2890,10 @@ "api_test": false, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2021-12-05T12:52:07.136385054Z", "deduplication_on_engagement": false, @@ -2906,7 +2908,6 @@ ], "name": "Static Scan", "notes": [], - "orchestration_engine": null, "pen_test": false, "preset": null, "product": 1, @@ -2915,7 +2916,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": null, "status": "Completed", "tags": [], @@ -2937,8 +2937,10 @@ "api_test": false, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2021-12-05T13:16:35.869385054Z", "deduplication_on_engagement": false, @@ -2953,7 +2955,6 @@ ], "name": "Quarterly PCI Scan", "notes": [], - "orchestration_engine": null, "pen_test": false, "preset": null, "product": 1, @@ -2962,7 +2963,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": null, "status": "Not Started", "tags": [ @@ -2986,8 +2986,10 @@ "api_test": true, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": true, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2021-12-05T13:27:21.625385054Z", "deduplication_on_engagement": false, @@ -3000,7 +3002,6 @@ "lead": null, "name": "Ad Hoc Engagement", "notes": [], - "orchestration_engine": null, "pen_test": true, "preset": null, "product": 2, @@ -3009,7 +3010,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": null, "status": "", "tags": [], @@ -3031,8 +3031,10 @@ "api_test": false, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2021-12-05T13:33:57.605385054Z", "deduplication_on_engagement": false, @@ -3047,7 +3049,6 @@ ], "name": "Initial Assessment", "notes": [], - "orchestration_engine": null, "pen_test": false, "preset": null, "product": 3, @@ -3056,7 +3057,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": null, "status": "Not Started", "tags": [], @@ -3078,8 +3078,10 @@ "api_test": false, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2021-12-06T10:35:42.262385054Z", "deduplication_on_engagement": false, @@ -3094,7 +3096,6 @@ ], "name": "Multiple scanners", "notes": [], - "orchestration_engine": null, "pen_test": false, "preset": null, "product": 1, @@ -3103,7 +3104,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": null, "status": "Completed", "tags": [ @@ -3127,8 +3127,10 @@ "api_test": false, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2021-12-06T10:45:18.369385054Z", "deduplication_on_engagement": false, @@ -3143,7 +3145,6 @@ ], "name": "Manual PenTest", "notes": [], - "orchestration_engine": null, "pen_test": false, "preset": null, "product": 1, @@ -3152,7 +3153,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": null, "status": "Blocked", "tags": [], @@ -3174,8 +3174,10 @@ "api_test": false, "branch_tag": "master", "build_id": "89", - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": "b8ca612dbbd45f37d62c7b9d3e9521a31438aaa6", "created": "2021-12-06T10:57:32.625385054Z", "deduplication_on_engagement": false, @@ -3190,7 +3192,6 @@ ], "name": "CI/CD Baseline Security Test", "notes": [], - "orchestration_engine": null, "pen_test": false, "preset": null, "product": 1, @@ -3199,7 +3200,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": "https://github.com/psiinon/bodgeit", "status": "Completed", "tags": [], @@ -3221,8 +3221,10 @@ "api_test": false, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2021-12-06T14:34:11.935385054Z", "deduplication_on_engagement": false, @@ -3235,7 +3237,6 @@ "lead": null, "name": "AdHoc Import - Fri, 17 Aug 2018 18:20:55", "notes": [], - "orchestration_engine": null, "pen_test": false, "preset": null, "product": 1, @@ -3244,7 +3245,6 @@ "report_type": null, "requester": null, "risk_acceptance": [], - "source_code_management_server": null, "source_code_management_uri": null, "status": "In Progress", "tags": [], @@ -47137,8 +47137,10 @@ "api_test": true, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": true, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": null, "deduplication_on_engagement": false, @@ -47151,7 +47153,6 @@ "product_manager" ], "name": "1st Quarter Engagement", - "orchestration_engine": null, "pen_test": true, "pgh_context": null, "pgh_created_at": "2026-02-26T19:36:31.394385054Z", @@ -47163,7 +47164,6 @@ "reason": null, "report_type": null, "requester": null, - "source_code_management_server": null, "source_code_management_uri": null, "status": "In Progress", "target_end": "2025-07-01", @@ -47184,8 +47184,10 @@ "api_test": true, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": true, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": null, "deduplication_on_engagement": false, @@ -47198,7 +47200,6 @@ "admin" ], "name": "April Monthly Engagement", - "orchestration_engine": null, "pen_test": true, "pgh_context": null, "pgh_created_at": "2026-02-26T19:36:31.394385054Z", @@ -47210,7 +47211,6 @@ "reason": null, "report_type": null, "requester": null, - "source_code_management_server": null, "source_code_management_uri": null, "status": "Completed", "target_end": "2025-07-01", @@ -47231,8 +47231,10 @@ "api_test": true, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": true, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": null, "deduplication_on_engagement": false, @@ -47245,7 +47247,6 @@ "product_manager" ], "name": "weekly engagement", - "orchestration_engine": null, "pen_test": true, "pgh_context": null, "pgh_created_at": "2026-02-26T19:36:31.394385054Z", @@ -47257,7 +47258,6 @@ "reason": null, "report_type": null, "requester": null, - "source_code_management_server": null, "source_code_management_uri": null, "status": "Completed", "target_end": "2025-06-23", @@ -47278,8 +47278,10 @@ "api_test": false, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2021-12-05T12:52:07.136385054Z", "deduplication_on_engagement": false, @@ -47292,7 +47294,6 @@ "admin" ], "name": "Static Scan", - "orchestration_engine": null, "pen_test": false, "pgh_context": null, "pgh_created_at": "2026-02-26T19:36:31.394385054Z", @@ -47304,7 +47305,6 @@ "reason": null, "report_type": null, "requester": null, - "source_code_management_server": null, "source_code_management_uri": null, "status": "Completed", "target_end": "2025-11-11", @@ -47325,8 +47325,10 @@ "api_test": false, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2021-12-05T13:16:35.869385054Z", "deduplication_on_engagement": false, @@ -47339,7 +47341,6 @@ "admin" ], "name": "Quarterly PCI Scan", - "orchestration_engine": null, "pen_test": false, "pgh_context": null, "pgh_created_at": "2026-02-26T19:36:31.394385054Z", @@ -47351,7 +47352,6 @@ "reason": null, "report_type": null, "requester": null, - "source_code_management_server": null, "source_code_management_uri": null, "status": "Not Started", "target_end": "2026-01-27", @@ -47372,8 +47372,10 @@ "api_test": true, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": true, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2021-12-05T13:27:21.625385054Z", "deduplication_on_engagement": false, @@ -47384,7 +47386,6 @@ "id": 7, "lead": null, "name": "Ad Hoc Engagement", - "orchestration_engine": null, "pen_test": true, "pgh_context": null, "pgh_created_at": "2026-02-26T19:36:31.394385054Z", @@ -47396,7 +47397,6 @@ "reason": null, "report_type": null, "requester": null, - "source_code_management_server": null, "source_code_management_uri": null, "status": "", "target_end": "2025-11-04", @@ -47417,8 +47417,10 @@ "api_test": false, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2021-12-05T13:33:57.605385054Z", "deduplication_on_engagement": false, @@ -47431,7 +47433,6 @@ "admin" ], "name": "Initial Assessment", - "orchestration_engine": null, "pen_test": false, "pgh_context": null, "pgh_created_at": "2026-02-26T19:36:31.394385054Z", @@ -47443,7 +47444,6 @@ "reason": null, "report_type": null, "requester": null, - "source_code_management_server": null, "source_code_management_uri": null, "status": "Not Started", "target_end": "2025-12-28", @@ -47464,8 +47464,10 @@ "api_test": false, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2021-12-06T10:35:42.262385054Z", "deduplication_on_engagement": false, @@ -47478,7 +47480,6 @@ "admin" ], "name": "Multiple scanners", - "orchestration_engine": null, "pen_test": false, "pgh_context": null, "pgh_created_at": "2026-02-26T19:36:31.394385054Z", @@ -47490,7 +47491,6 @@ "reason": null, "report_type": null, "requester": null, - "source_code_management_server": null, "source_code_management_uri": null, "status": "Completed", "target_end": "2025-11-05", @@ -47511,8 +47511,10 @@ "api_test": false, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2021-12-06T10:45:18.369385054Z", "deduplication_on_engagement": false, @@ -47525,7 +47527,6 @@ "admin" ], "name": "Manual PenTest", - "orchestration_engine": null, "pen_test": false, "pgh_context": null, "pgh_created_at": "2026-02-26T19:36:31.394385054Z", @@ -47537,7 +47538,6 @@ "reason": null, "report_type": null, "requester": null, - "source_code_management_server": null, "source_code_management_uri": null, "status": "Blocked", "target_end": "2026-01-03", @@ -47558,8 +47558,10 @@ "api_test": false, "branch_tag": "master", "build_id": "89", - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": "b8ca612dbbd45f37d62c7b9d3e9521a31438aaa6", "created": "2021-12-06T10:57:32.625385054Z", "deduplication_on_engagement": false, @@ -47572,7 +47574,6 @@ "admin" ], "name": "CI/CD Baseline Security Test", - "orchestration_engine": null, "pen_test": false, "pgh_context": null, "pgh_created_at": "2026-02-26T19:36:31.394385054Z", @@ -47584,7 +47585,6 @@ "reason": null, "report_type": null, "requester": null, - "source_code_management_server": null, "source_code_management_uri": "https://github.com/psiinon/bodgeit", "status": "Completed", "target_end": "2025-11-12", @@ -47605,8 +47605,10 @@ "api_test": false, "branch_tag": null, "build_id": null, - "build_server": null, "check_list": false, + "cicd_build_server": null, + "cicd_orchestration_engine": null, + "cicd_scm_server": null, "commit_hash": null, "created": "2021-12-06T14:34:11.935385054Z", "deduplication_on_engagement": false, @@ -47617,7 +47619,6 @@ "id": 13, "lead": null, "name": "AdHoc Import - Fri, 17 Aug 2018 18:20:55", - "orchestration_engine": null, "pen_test": false, "pgh_context": null, "pgh_created_at": "2026-02-26T19:36:31.394385054Z", @@ -47629,7 +47630,6 @@ "reason": null, "report_type": null, "requester": null, - "source_code_management_server": null, "source_code_management_uri": null, "status": "In Progress", "target_end": "2025-11-05", diff --git a/dojo/forms.py b/dojo/forms.py index e33d7ef51c4..422d2727a75 100644 --- a/dojo/forms.py +++ b/dojo/forms.py @@ -1058,10 +1058,10 @@ def __init__(self, *args, **kwargs): del self.fields["build_id"] del self.fields["commit_hash"] del self.fields["branch_tag"] - del self.fields["build_server"] - del self.fields["source_code_management_server"] + del self.fields["cicd_scm_server"] + del self.fields["cicd_build_server"] + del self.fields["cicd_orchestration_engine"] # del self.fields['source_code_management_uri'] - del self.fields["orchestration_engine"] else: del self.fields["test_strategy"] del self.fields["status"] diff --git a/dojo/models.py b/dojo/models.py index a41f5640889..831e41d12b3 100644 --- a/dojo/models.py +++ b/dojo/models.py @@ -45,6 +45,7 @@ from titlecase import titlecase from dojo.base_models.base import BaseModel +from dojo.cicd_infrastructure.models import CICDInfrastructure from dojo.validators import cvss3_validator, cvss4_validator if TYPE_CHECKING: @@ -1472,10 +1473,10 @@ class Engagement(BaseModel): null=True, blank=True, help_text=_("Commit hash from repo"), verbose_name=_("Commit Hash")) branch_tag = models.CharField(editable=True, max_length=150, null=True, blank=True, help_text=_("Tag or branch of the product the engagement tested."), verbose_name=_("Branch/Tag")) - build_server = models.ForeignKey(Tool_Configuration, verbose_name=_("Build Server"), help_text=_("Build server responsible for CI/CD test"), null=True, blank=True, related_name="build_server", on_delete=models.CASCADE) - source_code_management_server = models.ForeignKey(Tool_Configuration, null=True, blank=True, verbose_name=_("SCM Server"), help_text=_("Source code server for CI/CD test"), related_name="source_code_management_server", on_delete=models.CASCADE) source_code_management_uri = models.URLField(max_length=600, null=True, blank=True, editable=True, verbose_name=_("Repo"), help_text=_("Resource link to source code")) - orchestration_engine = models.ForeignKey(Tool_Configuration, verbose_name=_("Orchestration Engine"), help_text=_("Orchestration service responsible for CI/CD test"), null=True, blank=True, related_name="orchestration", on_delete=models.CASCADE) + cicd_scm_server = models.ForeignKey(CICDInfrastructure, null=True, blank=True, related_name="engagements_as_scm_server", on_delete=models.SET_NULL, limit_choices_to={"infrastructure_type": "scm_server"}, verbose_name=_("SCM Server"), help_text=_("Source code management server used for this CI/CD engagement")) + cicd_build_server = models.ForeignKey(CICDInfrastructure, null=True, blank=True, related_name="engagements_as_build_server", on_delete=models.SET_NULL, limit_choices_to={"infrastructure_type": "build_server"}, verbose_name=_("Build Server"), help_text=_("Build server used for this CI/CD engagement")) + cicd_orchestration_engine = models.ForeignKey(CICDInfrastructure, null=True, blank=True, related_name="engagements_as_orchestration", on_delete=models.SET_NULL, limit_choices_to={"infrastructure_type": "orchestration"}, verbose_name=_("Orchestration Engine"), help_text=_("Orchestration engine used for this CI/CD engagement")) deduplication_on_engagement = models.BooleanField(default=False, verbose_name=_("Deduplication within this engagement only"), help_text=_("If enabled deduplication will only mark a finding in this engagement as duplicate of another finding if both findings are in this engagement. If disabled, deduplication is on the product level.")) tags = TagField(blank=True, force_lowercase=True, help_text=_("Add tags that help describe this engagement. Choose from the list or add new tags. Press Enter key to add.")) diff --git a/dojo/static/dojo/css/dojo.css b/dojo/static/dojo/css/dojo.css index 8871ec262be..dc4ec09530d 100644 --- a/dojo/static/dojo/css/dojo.css +++ b/dojo/static/dojo/css/dojo.css @@ -1894,4 +1894,14 @@ input[type=number]::-webkit-outer-spin-button { color: ButtonText; } } - \ No newline at end of file + +/* Disabled form-field styling — @tailwindcss/forms resets the browser default + disabled appearance for /