diff --git a/backend/core/base_models.py b/backend/core/base_models.py index 264567870..5e1240797 100644 --- a/backend/core/base_models.py +++ b/backend/core/base_models.py @@ -46,13 +46,19 @@ def is_unique_in_scope(self, scope: models.QuerySet, fields_to_check: list) -> b # to avoid false positives as a result of the object being compared to itself if self.pk: scope = scope.exclude(pk=self.pk) - return not scope.filter( - **{ - f"{field}__iexact": getattr(self, field) - for field in fields_to_check - if hasattr(self, field) - } - ).exists() + filters = {} + for field in fields_to_check: + if hasattr(self, field): + field_value = getattr(self, field) + model_field = self._meta.get_field(field) + + # Use the appropriate lookup based on the field type + if isinstance(model_field, models.ForeignKey): + filters[f"{field}__exact"] = field_value + else: + filters[f"{field}__iexact"] = field_value + + return not scope.filter(**filters).exists() def display_path(self): pass diff --git a/backend/core/startup.py b/backend/core/startup.py index 3196379e2..2ec1362d4 100644 --- a/backend/core/startup.py +++ b/backend/core/startup.py @@ -355,6 +355,10 @@ "view_stakeholder", "change_stakeholder", "delete_stakeholder", + "add_strategicscenario", + "view_strategicscenario", + "change_strategicscenario", + "delete_strategicscenario", "add_attackpath", "view_attackpath", "change_attackpath", diff --git a/backend/ebios_rm/migrations/0008_remove_attackpath_ro_to_couple_strategicscenario_and_more.py b/backend/ebios_rm/migrations/0008_remove_attackpath_ro_to_couple_strategicscenario_and_more.py new file mode 100644 index 000000000..3c98cffa8 --- /dev/null +++ b/backend/ebios_rm/migrations/0008_remove_attackpath_ro_to_couple_strategicscenario_and_more.py @@ -0,0 +1,97 @@ +# Generated by Django 5.1.4 on 2024-12-20 08:56 + +import django.db.models.deletion +import iam.models +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("ebios_rm", "0007_ebiosrmstudy_meta"), + ("iam", "0010_user_preferences"), + ] + + operations = [ + migrations.RemoveField( + model_name="attackpath", + name="ro_to_couple", + ), + migrations.CreateModel( + name="StrategicScenario", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + ), + ), + ( + "created_at", + models.DateTimeField(auto_now_add=True, verbose_name="Created at"), + ), + ( + "updated_at", + models.DateTimeField(auto_now=True, verbose_name="Updated at"), + ), + ( + "is_published", + models.BooleanField(default=False, verbose_name="published"), + ), + ("name", models.CharField(max_length=200, verbose_name="Name")), + ( + "description", + models.TextField(blank=True, null=True, verbose_name="Description"), + ), + ("ref_id", models.CharField(blank=True, max_length=100)), + ( + "ebios_rm_study", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="strategic_scenarios", + to="ebios_rm.ebiosrmstudy", + verbose_name="EBIOS RM study", + ), + ), + ( + "folder", + models.ForeignKey( + default=iam.models.Folder.get_root_folder_id, + on_delete=django.db.models.deletion.CASCADE, + related_name="%(class)s_folder", + to="iam.folder", + ), + ), + ( + "ro_to_couple", + models.ForeignKey( + help_text="RO/TO couple from which the attach path is derived", + on_delete=django.db.models.deletion.CASCADE, + to="ebios_rm.roto", + verbose_name="RO/TO couple", + ), + ), + ], + options={ + "verbose_name": "Strategic Scenario", + "verbose_name_plural": "Strategic Scenarios", + "ordering": ["created_at"], + }, + ), + migrations.AddField( + model_name="attackpath", + name="strategic_scenario", + field=models.ForeignKey( + default="", + help_text="Strategic scenario from which the attack path is derived", + on_delete=django.db.models.deletion.CASCADE, + related_name="attack_paths", + to="ebios_rm.strategicscenario", + verbose_name="Strategic scenario", + ), + preserve_default=False, + ), + ] diff --git a/backend/ebios_rm/models.py b/backend/ebios_rm/models.py index 7f617ee9a..ba5884409 100644 --- a/backend/ebios_rm/models.py +++ b/backend/ebios_rm/models.py @@ -212,6 +212,8 @@ class FearedEvent(NameDescriptionMixin, FolderMixin): is_selected = models.BooleanField(verbose_name=_("Is selected"), default=False) justification = models.TextField(verbose_name=_("Justification"), blank=True) + fields_to_check = ["name", "ref_id"] + class Meta: verbose_name = _("Feared event") verbose_name_plural = _("Feared events") @@ -309,6 +311,8 @@ class Pertinence(models.IntegerChoices): is_selected = models.BooleanField(verbose_name=_("Is selected"), default=False) justification = models.TextField(verbose_name=_("Justification"), blank=True) + fields_to_check = ["target_objective", "risk_origin"] + def __str__(self) -> str: return f"{self.get_risk_origin_display()} - {self.target_objective}" @@ -338,7 +342,7 @@ def get_pertinence(self): def get_gravity(self): gravity = -1 for feared_event in self.feared_events.all(): - if feared_event.gravity > gravity: + if feared_event.gravity > gravity and feared_event.is_selected: gravity = feared_event.gravity return gravity @@ -420,11 +424,16 @@ class Category(models.TextChoices): is_selected = models.BooleanField(verbose_name=_("Is selected"), default=False) justification = models.TextField(verbose_name=_("Justification"), blank=True) + fields_to_check = ["entity", "category"] + class Meta: verbose_name = _("Stakeholder") verbose_name_plural = _("Stakeholders") ordering = ["created_at"] + def get_scope(self): + return self.__class__.objects.filter(ebios_rm_study=self.ebios_rm_study) + def __str__(self): return f"{self.entity.name} - {self.get_category_display()}" @@ -473,10 +482,11 @@ def get_residual_criticality_display(self) -> str: ) -class AttackPath(NameDescriptionMixin, FolderMixin): +class StrategicScenario(NameDescriptionMixin, FolderMixin): ebios_rm_study = models.ForeignKey( EbiosRMStudy, verbose_name=_("EBIOS RM study"), + related_name="strategic_scenarios", on_delete=models.CASCADE, ) ro_to_couple = models.ForeignKey( @@ -485,6 +495,51 @@ class AttackPath(NameDescriptionMixin, FolderMixin): on_delete=models.CASCADE, help_text=_("RO/TO couple from which the attach path is derived"), ) + ref_id = models.CharField(max_length=100, blank=True) + + fields_to_check = ["name", "ref_id"] + + class Meta: + verbose_name = _("Strategic Scenario") + verbose_name_plural = _("Strategic Scenarios") + ordering = ["created_at"] + + def get_scope(self): + return self.__class__.objects.filter(ebios_rm_study=self.ebios_rm_study) + + def save(self, *args, **kwargs): + self.folder = self.ebios_rm_study.folder + super().save(*args, **kwargs) + + def get_gravity_display(self): + if self.ro_to_couple.get_gravity() < 0: + return { + "abbreviation": "--", + "name": "--", + "description": "not rated", + "value": -1, + "hexcolor": "#f9fafb", + } + risk_matrix = self.ebios_rm_study.parsed_matrix + return { + **risk_matrix["impact"][self.ro_to_couple.get_gravity()], + "value": self.ro_to_couple.get_gravity(), + } + + +class AttackPath(NameDescriptionMixin, FolderMixin): + ebios_rm_study = models.ForeignKey( + EbiosRMStudy, + verbose_name=_("EBIOS RM study"), + on_delete=models.CASCADE, + ) + strategic_scenario = models.ForeignKey( + StrategicScenario, + verbose_name=_("Strategic scenario"), + on_delete=models.CASCADE, + related_name="attack_paths", + help_text=_("Strategic scenario from which the attack path is derived"), + ) stakeholders = models.ManyToManyField( Stakeholder, verbose_name=_("Stakeholders"), @@ -497,15 +552,25 @@ class AttackPath(NameDescriptionMixin, FolderMixin): is_selected = models.BooleanField(verbose_name=_("Is selected"), default=False) justification = models.TextField(verbose_name=_("Justification"), blank=True) + fields_to_check = ["name", "ref_id"] + class Meta: verbose_name = _("Attack path") verbose_name_plural = _("Attack paths") ordering = ["created_at"] + def get_scope(self): + return self.__class__.objects.filter(ebios_rm_study=self.ebios_rm_study) + def save(self, *args, **kwargs): + self.ebios_rm_study = self.strategic_scenario.ebios_rm_study self.folder = self.ebios_rm_study.folder super().save(*args, **kwargs) + @property + def ro_to_couple(self): + return self.strategic_scenario.ro_to_couple + @property def gravity(self): return self.ro_to_couple.get_gravity() diff --git a/backend/ebios_rm/serializers.py b/backend/ebios_rm/serializers.py index 0b0ec8a72..b23c78876 100644 --- a/backend/ebios_rm/serializers.py +++ b/backend/ebios_rm/serializers.py @@ -8,6 +8,7 @@ FearedEvent, RoTo, Stakeholder, + StrategicScenario, AttackPath, OperationalScenario, ) @@ -104,12 +105,8 @@ class Meta: class StakeholderWriteSerializer(BaseModelSerializer): - current_criticality = serializers.CharField( - source="get_current_criticality_display" - ) - residual_criticality = serializers.CharField( - source="get_residual_criticality_display" - ) + current_criticality = serializers.IntegerField(read_only=True) + residual_criticality = serializers.IntegerField(read_only=True) class Meta: model = Stakeholder @@ -136,10 +133,28 @@ class Meta: fields = "__all__" +class StrategicScenarioWriteSerializer(BaseModelSerializer): + class Meta: + model = StrategicScenario + exclude = ["created_at", "updated_at", "folder"] + + +class StrategicScenarioReadSerializer(BaseModelSerializer): + ebios_rm_study = FieldsRelatedField() + folder = FieldsRelatedField() + ro_to_couple = FieldsRelatedField() + gravity = serializers.JSONField(source="get_gravity_display") + attack_paths = FieldsRelatedField(many=True) + + class Meta: + model = StrategicScenario + fields = "__all__" + + class AttackPathWriteSerializer(BaseModelSerializer): class Meta: model = AttackPath - exclude = ["created_at", "updated_at", "folder"] + exclude = ["created_at", "updated_at", "folder", "ebios_rm_study"] class AttackPathReadSerializer(BaseModelSerializer): diff --git a/backend/ebios_rm/urls.py b/backend/ebios_rm/urls.py index 76f7b3e2e..cf7bcaebf 100644 --- a/backend/ebios_rm/urls.py +++ b/backend/ebios_rm/urls.py @@ -6,6 +6,7 @@ FearedEventViewSet, RoToViewSet, StakeholderViewSet, + StrategicScenarioViewSet, AttackPathViewSet, OperationalScenarioViewSet, ) @@ -16,6 +17,9 @@ router.register(r"feared-events", FearedEventViewSet, basename="feared-events") router.register(r"ro-to", RoToViewSet, basename="ro-to") router.register(r"stakeholders", StakeholderViewSet, basename="stakeholders") +router.register( + r"strategic-scenarios", StrategicScenarioViewSet, basename="strategic-scenarios" +) router.register(r"attack-paths", AttackPathViewSet, basename="attack-paths") router.register( r"operational-scenarios", diff --git a/backend/ebios_rm/views.py b/backend/ebios_rm/views.py index e84d997eb..871119cd0 100644 --- a/backend/ebios_rm/views.py +++ b/backend/ebios_rm/views.py @@ -7,6 +7,7 @@ FearedEvent, RoTo, Stakeholder, + StrategicScenario, AttackPath, OperationalScenario, ) @@ -109,12 +110,23 @@ def gravity(self, request, pk): return Response(choices) +class RoToFilter(df.FilterSet): + used = df.BooleanFilter(method="is_used", label="Used") + + def is_used(self, queryset, name, value): + if value: + return queryset.filter(strategicscenario__isnull=False) + return queryset.filter(strategicscenario__isnull=True) + + class Meta: + model = RoTo + fields = ["ebios_rm_study", "is_selected", "used"] + + class RoToViewSet(BaseModelViewSet): model = RoTo - filterset_fields = [ - "ebios_rm_study", - ] + filterset_class = RoToFilter @action(detail=False, name="Get risk origin choices", url_path="risk-origin") def risk_origin(self, request): @@ -134,6 +146,7 @@ class StakeholderViewSet(BaseModelViewSet): filterset_fields = [ "ebios_rm_study", + "is_selected", ] @action(detail=False, name="Get category choices") @@ -141,6 +154,14 @@ def category(self, request): return Response(dict(Stakeholder.Category.choices)) +class StrategicScenarioViewSet(BaseModelViewSet): + model = StrategicScenario + + filterset_fields = [ + "ebios_rm_study", + ] + + class AttackPathFilter(df.FilterSet): used = df.BooleanFilter(method="is_used", label="Used") diff --git a/enterprise/backend/enterprise_core/settings.py b/enterprise/backend/enterprise_core/settings.py index 88d783fe1..6e6ab658d 100644 --- a/enterprise/backend/enterprise_core/settings.py +++ b/enterprise/backend/enterprise_core/settings.py @@ -142,8 +142,8 @@ def set_ciso_assistant_url(_, __, event_dict): "iam", "global_settings", "tprm", - "core", "ebios_rm", + "core", "cal", "django_filters", "library", diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 794cb8416..3a82e3e6d 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -923,24 +923,24 @@ "ebiosWs5_3": "Define security measures", "ebiosWs5_4": "Assess and document residual risks", "ebiosWs5_5": "Establish risk monitoring framework", - "activity": "Activity", + "activity": "Step", "ebiosRmMatrixHelpText": "Risk matrix used as a reference for the study. Defaults to `urn:intuitem:risk:library:risk-matrix-4x4-ebios-rm`", - "activityOne": "Activity 1", - "activityTwo": "Activity 2", - "activityThree": "Activity 3", + "activityOne": "Step 1", + "activityTwo": "Step 2", + "activityThree": "Step 3", "ebiosRmStudy": "Ebios RM study", "qualifications": "Qualifications", "impacts": "Impacts", "ebiosRmStudies": "Ebios RM studies", "bringTheEvidences": "Bring the evidences", "bringTheEvidencesHelpText": "If disabled, the object will be duplicated without its evidences", - "gravity": "Gravity", + "gravity": "Severity", "existingControlsHelper": "What do you currently have to manage this risk", "extraControlsHelper": "What will you do to mitigate this risk", "existingContextHelper": "Description of the existing mitigations (this field will be deprecated soon)", "fearedEvent": "Feared event", "fearedEvents": "Feared events", - "isSelected": "Is selected", + "isSelected": "Selected", "ebiosRM": "Ebios RM", "riskOrigin": "Risk origin", "targetObjective": "Target objective", @@ -962,7 +962,7 @@ "organizedCrime": "Organized crime", "terrorist": "Terrorist", "activist": "Activist", - "professional": "Professional", + "professional": "Competitor", "amateur": "Amateur", "avenger": "Avenger", "pathological": "Pathological", @@ -1008,6 +1008,10 @@ "minor": "Minor", "operatingModesDescription": "Operating modes description", "noStakeholders": "No stakeholders", + "strategicScenario": "Strategic scenario", + "strategicScenarios": "Strategic scenarios", + "goBackToEbiosRmStudy": "Go back to Ebios RM study", + "addStrategicScenario": "Add strategic scenario", "markAsDone": "Mark as done", "markAsInProgress": "Mark as in progress" } diff --git a/frontend/messages/fr.json b/frontend/messages/fr.json index 6311995ff..a7e674606 100644 --- a/frontend/messages/fr.json +++ b/frontend/messages/fr.json @@ -797,6 +797,14 @@ "proof": "Preuve", "privacy": "Vie privée", "safety": "Sûreté", + "rto": "RTO", + "rtoHelpText": "Objectif de temps de récupération", + "rpo": "RPO", + "rpoHelpText": "Objectif du point de récupération", + "mtd": "MTD", + "mtdHelpText": "Temps d'arrêt maximal tolérable", + "securityObjectives": "Objectifs de sécurité", + "disasterRecoveryObjectives": "Objectifs de reprise d'activité", "noExpirationDateSet": "Aucune date d'expiration définie", "sumpageTotal": "Total", "sumpageActive": "Actif", @@ -858,6 +866,11 @@ "exploitable": "Exploitable", "mitigated": "Atténuée", "fixed": "Fixée", + "general": "Général", + "generalSettingsDescription": "Configurez vos paramètres ici.", + "securityObjectiveScale": "Échelle des objectifs de sécurité", + "generalSettingsUpdated": "Paramètres mis à jour", + "securityObjectiveScaleHelpText": "Choisissez votre échelle d'objectifs de sécurité (1 à 4 par défaut)", "labels": "Étiquettes", "addLabel": "Ajouter une étiquette", "labelsHelpText": "Les étiquettes sont utilisées pour catégoriser et filtrer les éléments.", @@ -871,12 +884,23 @@ "tags": "Étiquettes", "addTag": "Ajouter une étiquette", "tagsHelpText": "Les étiquettes sont utilisées pour classer et filtrer les éléments. Vous pouvez ajouter des étiquettes dans la section Extra", + "enabled": "Activé", + "hours": "Heures", + "minutes": "Minutes", + "seconds": "Secondes", + "days": "Jours", + "milliseconds": "Millisecondes", "existingMeasures": "Mesures existantes", + "youCanSetPasswordHere": "Vous pouvez définir votre mot de passe ici", "forgotPassword": "Mot de passe oublié", "ssoSettingsUpdated": "Paramètres SSO mis à jour", "scoreSemiColon": "Score:", "mappingInferenceHelpText": "Ces variables sont fixes et ne changeront pas en fonction de la source.", "priority": "Priorité", + "p1": "P1", + "p2": "P2", + "p3": "P3", + "p4": "P4", "ebiosWs1": "Atelier 1 : Cadrage et Socle de sécurité", "ebiosWs2": "Atelier 2 : Sources de risque", "ebiosWs3": "Atelier 3 : Scénarios stratégiques", @@ -900,22 +924,92 @@ "ebiosWs5_4": "Évaluer et documenter les risques résiduels", "ebiosWs5_5": "Mettre en place le cadre de suivi des risques", "activity": "Activité", + "ebiosRmMatrixHelpText": "Matrice de risque utilisée pour l'étude. La valeur par défaut est `urn:intuitem:risk:library:risk-matrix-4x4-ebios-rm`", + "activityOne": "Activité 1", + "activityTwo": "Activité 2", + "activityThree": "Activité 3", + "ebiosRmStudy": "Étude Ebios RM", + "qualifications": "Qualifications", + "impacts": "Impacts", + "ebiosRmStudies": "Études Ebios RM", "bringTheEvidences": "Apportez les preuves", "bringTheEvidencesHelpText": "Si désactivé, l'objet sera dupliqué sans ses preuves", + "gravity": "Gravité", "existingControlsHelper": "Que disposez-vous actuellement pour gérer ce risque", "extraControlsHelper": "Que ferez-vous pour atténuer ce risque", "existingContextHelper": "Description des mesures existantes (ce champ sera bientôt obsolète)", + "fearedEvent": "Événement redouté", + "fearedEvents": "Evénements redoutés", + "isSelected": "Retenu", + "ebiosRM": "Ebios RM", + "riskOrigin": "Source de risque", + "targetObjective": "Objectif visé", + "motivation": "Motivation", + "resources": "Ressources", + "pertinence": "Pertinence", + "limited": "Limité", + "significant": "Significatif", + "important": "Important", + "unlimited": "Illimité", + "strong": "Fort", + "irrelevant": "Non pertinent", + "partiallyRelevant": "Partiellement pertinent", + "fairlyRelevant": "Assez pertinent", + "highlyRelevant": "Très pertinent", + "roTo": "SR/OV", + "roToCouple": "Couple SR/OV", + "addRoto": "Ajouter un couple SR/OV", + "organizedCrime": "Crime organisé", + "terrorist": "Terroriste", + "activist": "Activiste", + "professional": "Concurrent", + "amateur": "Amateur", + "avenger": "Vengeur", + "pathological": "Pathologique", + "currentDependency": "Dépendance actuelle", + "currentPenetration": "Pénétration actuelle", + "currentMaturity": "Maturité actuelle", + "currentTrust": "Confiance actuelle", + "residualDependency": "Dépendance résiduelle", + "residualPenetration": "Pénétration résiduelle", + "residualMaturity": "Maturité résiduelle", + "residualTrust": "Confiance résiduelle", + "selected": "Retenu", + "likelihood": "Vraisemblance", + "stakeholders": "Parties prenantes", + "addAttackPath": "Ajouter un chemin d'attaque", + "attackPath": "Chemin d'attaque", + "attackPaths": "Chemins d'attaque", + "currentCriticality": "Criticité actuelle", + "residualCriticality": "Criticité résiduelle", + "notSelected": "Non retenu", "resetPasswordHere": "Vous pouvez réinitialiser votre mot de passe ici.", "resetPassword": "Réinitialiser le mot de passe", - "securityObjectives": "Objectifs de sécurité", - "disasterRecoveryObjectives": "Objectifs de reprise après sinistre", - "hours": "Heures", - "minutes": "Minutes", - "seconds": "Secondes", - "rto": "RTO", - "rtoHelpText": "Objectif de temps de récupération", - "rpo": "RPO", - "rpoHelpText": "Objectif du point de récupération", - "mtd": "MTD", - "mtdHelpText": "Temps d'arrêt maximal tolérable" + "ebiosRm": "Ebios RM", + "workshopOne": "Atelier 1", + "refIdSemiColon": "ID :", + "addFearedEvent": "Ajouter un événement redouté", + "addEbiosRMstudy": "Ajouter une étude Ebios RM", + "noAuthor": "Aucun auteur attribué", + "noReviewer": "Aucun relecteur assigné", + "selectAudit": "Sélectionner un audit", + "errorAssetGraphMustNotContainCycles": "Le graphe des actifs ne doit pas contenir de boucles.", + "addStakeholder": "Ajouter une partie prenante", + "operationalScenario": "Scénario opérationnel", + "operationalScenarioRefId": "Scénario opérationnel {refId}", + "operationalScenarios": "Scénarios opérationnels", + "addOperationalScenario": "Ajouter un scénario opérationnel", + "workshopFour": "Atelier 4", + "noThreat": "Aucune menace", + "likely": "Probable", + "unlikely": "Peu probable", + "veryLikely": "Très probable", + "certain": "Certain", + "minor": "Mineure", + "operatingModesDescription": "Description des modes opératoires", + "noStakeholders": "Aucune partie prenante", + "strategicScenario": "Scénario stratégique", + "strategicScenarios": "Scénarios stratégiques", + "goBackToEbiosRmStudy": "Retour à l'étude", + "addStrategicScenario": "Ajouter un scénario stratégique" } diff --git a/frontend/src/lib/components/CommandPalette/paletteData.ts b/frontend/src/lib/components/CommandPalette/paletteData.ts index 8903454cc..491235c04 100644 --- a/frontend/src/lib/components/CommandPalette/paletteData.ts +++ b/frontend/src/lib/components/CommandPalette/paletteData.ts @@ -34,6 +34,10 @@ export const navigationLinks: NavigationLink[] = [ label: 'riskScenarios', href: '/risk-scenarios' }, + { + label: 'ebiosRM', + href: '/ebios-rm' + }, { label: 'actionPlan', href: '/applied-controls' diff --git a/frontend/src/lib/components/DetailView/DetailView.svelte b/frontend/src/lib/components/DetailView/DetailView.svelte index 7bf94b2f9..4a2f12545 100644 --- a/frontend/src/lib/components/DetailView/DetailView.svelte +++ b/frontend/src/lib/components/DetailView/DetailView.svelte @@ -321,7 +321,7 @@ {:else} -- {/if} - {:else if value.id} + {:else if value.id && !value.hexcolor} {@const itemHref = `/${ URL_MODEL_MAP[data.urlModel]['foreignKeyFields']?.find( (item) => item.field === key diff --git a/frontend/src/lib/components/Forms/ModelForm.svelte b/frontend/src/lib/components/Forms/ModelForm.svelte index 939955bce..97393bc4b 100644 --- a/frontend/src/lib/components/Forms/ModelForm.svelte +++ b/frontend/src/lib/components/Forms/ModelForm.svelte @@ -47,6 +47,7 @@ import { createModalCache } from '$lib/utils/stores'; import FilteringLabelForm from './ModelForm/FilteringLabelForm.svelte'; import OperationalScenarioForm from './ModelForm/OperationalScenarioForm.svelte'; + import StrategicScenarioForm from './ModelForm/StrategicScenarioForm.svelte'; export let form: SuperValidated; export let invalidateAll = true; // set to false to keep form data using muliple forms on a page @@ -270,6 +271,8 @@ {:else if URLModel === 'stakeholders'} + {:else if URLModel === 'strategic-scenarios'} + {:else if URLModel === 'attack-paths'} {:else if URLModel === 'operational-scenarios'} diff --git a/frontend/src/lib/components/Forms/ModelForm/AttackPathForm.svelte b/frontend/src/lib/components/Forms/ModelForm/AttackPathForm.svelte index e6d7ab3a6..7619db182 100644 --- a/frontend/src/lib/components/Forms/ModelForm/AttackPathForm.svelte +++ b/frontend/src/lib/components/Forms/ModelForm/AttackPathForm.svelte @@ -12,17 +12,18 @@ export let cacheLocks: Record = {}; export let formDataCache: Record = {}; export let initialData: Record = {}; - export let context: 'create' | 'edit' = 'create';