From 7039444ff5c8bde387d75af599e575dbdf68bf46 Mon Sep 17 00:00:00 2001 From: melinoix Date: Tue, 12 Nov 2024 15:45:27 +0100 Subject: [PATCH 01/30] begin to add ref_id to some data --- backend/core/migrations/0001_initial.py | 9 +++++++++ backend/core/models.py | 3 +++ backend/core/views.py | 2 +- .../lib/components/Forms/ModelForm/ProjectForm.svelte | 8 ++++++++ frontend/src/lib/utils/schemas.ts | 1 + frontend/src/lib/utils/types.ts | 1 + 6 files changed, 23 insertions(+), 1 deletion(-) diff --git a/backend/core/migrations/0001_initial.py b/backend/core/migrations/0001_initial.py index d696bb4df..f807a0ac1 100644 --- a/backend/core/migrations/0001_initial.py +++ b/backend/core/migrations/0001_initial.py @@ -405,6 +405,15 @@ class Migration(migrations.Migration): verbose_name="Internal reference", ), ), + ( + "ref_id", + models.CharField( + blank=True, + max_length=100, + null=True, + verbose_name="reference id", + ), + ), ( "lc_status", models.CharField( diff --git a/backend/core/models.py b/backend/core/models.py index 860c44a77..69cbad7dd 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -1146,6 +1146,9 @@ class Project(NameDescriptionMixin, FolderMixin): internal_reference = models.CharField( max_length=100, null=True, blank=True, verbose_name=_("Internal reference") ) + ref_id = models.CharField( + max_length=100, null=True, blank=True, verbose_name=_("reference id") + ) lc_status = models.CharField( max_length=20, default="in_design", diff --git a/backend/core/views.py b/backend/core/views.py index 74553cfe7..119c1bd0c 100644 --- a/backend/core/views.py +++ b/backend/core/views.py @@ -181,7 +181,7 @@ class ProjectViewSet(BaseModelViewSet): model = Project filterset_fields = ["folder", "lc_status"] - search_fields = ["name", "internal_reference", "description"] + search_fields = ["name", "ref_id", "internal_reference", "description"] @action(detail=False, name="Get status choices") def lc_status(self, request): diff --git a/frontend/src/lib/components/Forms/ModelForm/ProjectForm.svelte b/frontend/src/lib/components/Forms/ModelForm/ProjectForm.svelte index e4d9c86bf..fa5b7c4c9 100644 --- a/frontend/src/lib/components/Forms/ModelForm/ProjectForm.svelte +++ b/frontend/src/lib/components/Forms/ModelForm/ProjectForm.svelte @@ -14,6 +14,14 @@ export let initialData: Record = {}; + + []; } From 11d6649921529536e77e57ed922ba2a97181a4c4 Mon Sep 17 00:00:00 2001 From: melinoix Date: Wed, 13 Nov 2024 11:04:07 +0100 Subject: [PATCH 02/30] working ref_id on project --- backend/core/migrations/0001_initial.py | 9 --------- backend/core/migrations/0035_project_ref_id.py | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 backend/core/migrations/0035_project_ref_id.py diff --git a/backend/core/migrations/0001_initial.py b/backend/core/migrations/0001_initial.py index f807a0ac1..d696bb4df 100644 --- a/backend/core/migrations/0001_initial.py +++ b/backend/core/migrations/0001_initial.py @@ -405,15 +405,6 @@ class Migration(migrations.Migration): verbose_name="Internal reference", ), ), - ( - "ref_id", - models.CharField( - blank=True, - max_length=100, - null=True, - verbose_name="reference id", - ), - ), ( "lc_status", models.CharField( diff --git a/backend/core/migrations/0035_project_ref_id.py b/backend/core/migrations/0035_project_ref_id.py new file mode 100644 index 000000000..1f62b9793 --- /dev/null +++ b/backend/core/migrations/0035_project_ref_id.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.1 on 2024-11-13 09:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0034_fix_loaded_libraries_objects_meta'), + ] + + operations = [ + migrations.AddField( + model_name='project', + name='ref_id', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='reference id'), + ), + ] From 776fe91670c14a89cff4307f862ec9adf75b1552 Mon Sep 17 00:00:00 2001 From: melinoix Date: Wed, 13 Nov 2024 12:13:21 +0100 Subject: [PATCH 03/30] added ref_id to assessments and applied_control --- .../core/migrations/0035_project_ref_id.py | 11 ++++++----- .../migrations/0036_riskassessment_ref_id.py | 19 +++++++++++++++++++ .../migrations/0037_appliedcontrol_ref_id.py | 19 +++++++++++++++++++ .../0038_complianceassessment_ref_id.py | 19 +++++++++++++++++++ backend/core/models.py | 9 +++++++++ backend/core/views.py | 3 ++- .../ModelForm/AppliedControlPolicyForm.svelte | 7 +++++++ .../ModelForm/ComplianceAssessmentForm.svelte | 8 ++++++++ .../Forms/ModelForm/FolderForm.svelte | 18 ++++++++++++++++-- .../Forms/ModelForm/ProjectForm.svelte | 2 +- .../Forms/ModelForm/RiskAssessmentForm.svelte | 8 ++++++++ frontend/src/lib/utils/schemas.ts | 4 ++++ frontend/src/lib/utils/table.ts | 18 ++++++++++-------- 13 files changed, 128 insertions(+), 17 deletions(-) create mode 100644 backend/core/migrations/0036_riskassessment_ref_id.py create mode 100644 backend/core/migrations/0037_appliedcontrol_ref_id.py create mode 100644 backend/core/migrations/0038_complianceassessment_ref_id.py diff --git a/backend/core/migrations/0035_project_ref_id.py b/backend/core/migrations/0035_project_ref_id.py index 1f62b9793..4ea2c4b91 100644 --- a/backend/core/migrations/0035_project_ref_id.py +++ b/backend/core/migrations/0035_project_ref_id.py @@ -4,15 +4,16 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0034_fix_loaded_libraries_objects_meta'), + ("core", "0034_fix_loaded_libraries_objects_meta"), ] operations = [ migrations.AddField( - model_name='project', - name='ref_id', - field=models.CharField(blank=True, max_length=100, null=True, verbose_name='reference id'), + model_name="project", + name="ref_id", + field=models.CharField( + blank=True, max_length=100, null=True, verbose_name="reference id" + ), ), ] diff --git a/backend/core/migrations/0036_riskassessment_ref_id.py b/backend/core/migrations/0036_riskassessment_ref_id.py new file mode 100644 index 000000000..afdaced38 --- /dev/null +++ b/backend/core/migrations/0036_riskassessment_ref_id.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1.1 on 2024-11-13 10:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0035_project_ref_id"), + ] + + operations = [ + migrations.AddField( + model_name="riskassessment", + name="ref_id", + field=models.CharField( + blank=True, max_length=100, null=True, verbose_name="reference id" + ), + ), + ] diff --git a/backend/core/migrations/0037_appliedcontrol_ref_id.py b/backend/core/migrations/0037_appliedcontrol_ref_id.py new file mode 100644 index 000000000..780a9aa1b --- /dev/null +++ b/backend/core/migrations/0037_appliedcontrol_ref_id.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1.1 on 2024-11-13 10:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0036_riskassessment_ref_id"), + ] + + operations = [ + migrations.AddField( + model_name="appliedcontrol", + name="ref_id", + field=models.CharField( + blank=True, max_length=100, null=True, verbose_name="reference id" + ), + ), + ] diff --git a/backend/core/migrations/0038_complianceassessment_ref_id.py b/backend/core/migrations/0038_complianceassessment_ref_id.py new file mode 100644 index 000000000..56911359a --- /dev/null +++ b/backend/core/migrations/0038_complianceassessment_ref_id.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1.1 on 2024-11-13 11:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0037_appliedcontrol_ref_id"), + ] + + operations = [ + migrations.AddField( + model_name="complianceassessment", + name="ref_id", + field=models.CharField( + blank=True, max_length=100, null=True, verbose_name="reference id" + ), + ), + ] diff --git a/backend/core/models.py b/backend/core/models.py index 69cbad7dd..0b7f31d93 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -1299,6 +1299,9 @@ class Status(models.TextChoices): ("L", _("Large")), ("XL", _("Extra Large")), ] + ref_id = models.CharField( + max_length=100, null=True, blank=True, verbose_name=_("reference id") + ) MAP_EFFORT = {None: -1, "S": 1, "M": 2, "L": 4, "XL": 8} # todo: think about a smarter model for ranking @@ -1571,6 +1574,9 @@ class RiskAssessment(Assessment): help_text=_("WARNING! After choosing it, you will not be able to change it"), verbose_name=_("Risk matrix"), ) + ref_id = models.CharField( + max_length=100, null=True, blank=True, verbose_name=_("reference id") + ) class Meta: verbose_name = _("Risk assessment") @@ -2156,6 +2162,9 @@ class ComplianceAssessment(Assessment): selected_implementation_groups = models.JSONField( blank=True, null=True, verbose_name=_("Selected implementation groups") ) + ref_id = models.CharField( + max_length=100, null=True, blank=True, verbose_name=_("reference id") + ) # score system is suggested by the framework, but can be changed at the start of the assessment min_score = models.IntegerField(null=True, verbose_name=_("Minimum score")) max_score = models.IntegerField(null=True, verbose_name=_("Maximum score")) diff --git a/backend/core/views.py b/backend/core/views.py index 119c1bd0c..70e94e8f7 100644 --- a/backend/core/views.py +++ b/backend/core/views.py @@ -1351,6 +1351,7 @@ class FolderViewSet(BaseModelViewSet): model = Folder filterset_class = FolderFilter + search_fields = ["ref_id"] def perform_create(self, serializer): """ @@ -1781,7 +1782,7 @@ class ComplianceAssessmentViewSet(BaseModelViewSet): model = ComplianceAssessment filterset_fields = ["framework", "project", "status"] - search_fields = ["name", "description"] + search_fields = ["name", "description", "ref_id"] ordering_fields = ["name", "description"] @method_decorator(cache_page(60 * LONG_CACHE_TTL)) diff --git a/frontend/src/lib/components/Forms/ModelForm/AppliedControlPolicyForm.svelte b/frontend/src/lib/components/Forms/ModelForm/AppliedControlPolicyForm.svelte index 1dbe9fd05..a9c816aaf 100644 --- a/frontend/src/lib/components/Forms/ModelForm/AppliedControlPolicyForm.svelte +++ b/frontend/src/lib/components/Forms/ModelForm/AppliedControlPolicyForm.svelte @@ -16,6 +16,13 @@ export let initialData: Record = {}; + {#if schema.shape.category} Date: Thu, 14 Nov 2024 17:16:05 +0100 Subject: [PATCH 09/30] adjust stable version --- .../0042_project_internal_reference.py | 19 +++++++++++++++++++ backend/core/models.py | 3 +++ .../Forms/ModelForm/ProjectForm.svelte | 8 +++++++- 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 backend/core/migrations/0042_project_internal_reference.py diff --git a/backend/core/migrations/0042_project_internal_reference.py b/backend/core/migrations/0042_project_internal_reference.py new file mode 100644 index 000000000..f052c6e72 --- /dev/null +++ b/backend/core/migrations/0042_project_internal_reference.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1.1 on 2024-11-14 16:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0041_remove_project_internal_reference"), + ] + + operations = [ + migrations.AddField( + model_name="project", + name="internal_reference", + field=models.CharField( + blank=True, max_length=100, null=True, verbose_name="Internal reference" + ), + ), + ] diff --git a/backend/core/models.py b/backend/core/models.py index 38e295c7f..00f84b596 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -1144,6 +1144,9 @@ class Project(NameDescriptionMixin, FolderMixin): ("dropped", _("Dropped")), ] + internal_reference = models.CharField( + max_length=100, null=True, blank=True, verbose_name=_("Internal reference") + ) ref_id = models.CharField( max_length=100, null=True, blank=True, verbose_name=_("reference id") ) diff --git a/frontend/src/lib/components/Forms/ModelForm/ProjectForm.svelte b/frontend/src/lib/components/Forms/ModelForm/ProjectForm.svelte index bc05ebbde..ed47b5567 100644 --- a/frontend/src/lib/components/Forms/ModelForm/ProjectForm.svelte +++ b/frontend/src/lib/components/Forms/ModelForm/ProjectForm.svelte @@ -31,7 +31,13 @@ label={m.domain()} hidden={initialData.folder} /> - + Date: Mon, 18 Nov 2024 16:54:59 +0100 Subject: [PATCH 11/30] corrected project api tests --- backend/app_tests/api/test_api_projects.py | 8 ++++---- backend/core/models.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/app_tests/api/test_api_projects.py b/backend/app_tests/api/test_api_projects.py index f1ea8b58c..417d64dbe 100644 --- a/backend/app_tests/api/test_api_projects.py +++ b/backend/app_tests/api/test_api_projects.py @@ -90,7 +90,7 @@ def test_get_projects(self, test): "name": PROJECT_NAME, "description": PROJECT_DESCRIPTION, "folder": test.folder, - "internal_reference": PROJECT_REFERENCE, + "ref_id": PROJECT_REFERENCE, "lc_status": PROJECT_STATUS[0], }, { @@ -112,7 +112,7 @@ def test_create_projects(self, test): "name": PROJECT_NAME, "description": PROJECT_DESCRIPTION, "folder": str(test.folder.id), - "internal_reference": PROJECT_REFERENCE, + "ref_id": PROJECT_REFERENCE, "lc_status": PROJECT_STATUS[0], }, { @@ -137,14 +137,14 @@ def test_update_projects(self, test): "name": PROJECT_NAME, "description": PROJECT_DESCRIPTION, "folder": test.folder, - "internal_reference": PROJECT_REFERENCE, + "ref_id": PROJECT_REFERENCE, "lc_status": PROJECT_STATUS[0], }, { "name": "new " + PROJECT_NAME, "description": "new " + PROJECT_DESCRIPTION, "folder": str(folder.id), - "internal_reference": "new " + PROJECT_REFERENCE, + "ref_id": "new " + PROJECT_REFERENCE, "lc_status": status[0], }, { diff --git a/backend/core/models.py b/backend/core/models.py index 38e295c7f..429bcadee 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -1297,9 +1297,6 @@ class Status(models.TextChoices): ("L", _("Large")), ("XL", _("Extra Large")), ] - ref_id = models.CharField( - max_length=100, null=True, blank=True, verbose_name=_("reference id") - ) MAP_EFFORT = {None: -1, "S": 1, "M": 2, "L": 4, "XL": 8} # todo: think about a smarter model for ranking @@ -1310,6 +1307,9 @@ class Status(models.TextChoices): blank=True, verbose_name=_("Reference Control"), ) + ref_id = models.CharField( + max_length=100, null=True, blank=True, verbose_name=_("reference id") + ) evidences = models.ManyToManyField( Evidence, blank=True, From 11df68da7bb047919fd73b7fe4196e7a13d9b49e Mon Sep 17 00:00:00 2001 From: melinoix Date: Mon, 18 Nov 2024 17:17:19 +0100 Subject: [PATCH 12/30] merged with main --- .../core/migrations/0036_merge_20241118_1616.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 backend/core/migrations/0036_merge_20241118_1616.py diff --git a/backend/core/migrations/0036_merge_20241118_1616.py b/backend/core/migrations/0036_merge_20241118_1616.py new file mode 100644 index 000000000..4746bba1a --- /dev/null +++ b/backend/core/migrations/0036_merge_20241118_1616.py @@ -0,0 +1,14 @@ +# Generated by Django 5.1.1 on 2024-11-18 16:16 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0035_remove_project_internal_reference_and_more'), + ('core', '0035_riskscenario_existing_applied_controls'), + ] + + operations = [ + ] From d8c685892cd68834c0e5d2fdd70a6a4c46a8ff05 Mon Sep 17 00:00:00 2001 From: melinoix Date: Mon, 18 Nov 2024 17:27:21 +0100 Subject: [PATCH 13/30] corrected migrations --- ...move_project_internal_reference_and_more.py | 7 ------- .../migrations/0037_alter_project_ref_id.py | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 backend/core/migrations/0037_alter_project_ref_id.py diff --git a/backend/core/migrations/0035_remove_project_internal_reference_and_more.py b/backend/core/migrations/0035_remove_project_internal_reference_and_more.py index 3afca00a6..48d0adfc7 100644 --- a/backend/core/migrations/0035_remove_project_internal_reference_and_more.py +++ b/backend/core/migrations/0035_remove_project_internal_reference_and_more.py @@ -26,13 +26,6 @@ class Migration(migrations.Migration): blank=True, max_length=100, null=True, verbose_name="reference id" ), ), - migrations.AddField( - model_name="project", - name="ref_id", - field=models.CharField( - blank=True, max_length=100, null=True, verbose_name="reference id" - ), - ), migrations.AddField( model_name="riskassessment", name="ref_id", diff --git a/backend/core/migrations/0037_alter_project_ref_id.py b/backend/core/migrations/0037_alter_project_ref_id.py new file mode 100644 index 000000000..15ece8142 --- /dev/null +++ b/backend/core/migrations/0037_alter_project_ref_id.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.1 on 2024-11-18 16:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0036_merge_20241118_1616'), + ] + + operations = [ + migrations.AlterField( + model_name='project', + name='ref_id', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='reference id'), + ), + ] From 2740b09107eb3fae5e9d0b1e215bc0f7e2e9c5e1 Mon Sep 17 00:00:00 2001 From: melinoix Date: Mon, 18 Nov 2024 17:55:25 +0100 Subject: [PATCH 14/30] corrected migrations --- ...ove_project_internal_reference_and_more.py | 36 ------------------- .../migrations/0036_merge_20241118_1616.py | 14 -------- ...ove_project_internal_reference_and_more.py | 33 +++++++++++++++++ .../migrations/0037_alter_project_ref_id.py | 18 ---------- backend/iam/migrations/0010_folder_ref_id.py | 13 ++++--- 5 files changed, 39 insertions(+), 75 deletions(-) delete mode 100644 backend/core/migrations/0035_remove_project_internal_reference_and_more.py delete mode 100644 backend/core/migrations/0036_merge_20241118_1616.py create mode 100644 backend/core/migrations/0036_remove_project_internal_reference_and_more.py delete mode 100644 backend/core/migrations/0037_alter_project_ref_id.py diff --git a/backend/core/migrations/0035_remove_project_internal_reference_and_more.py b/backend/core/migrations/0035_remove_project_internal_reference_and_more.py deleted file mode 100644 index 48d0adfc7..000000000 --- a/backend/core/migrations/0035_remove_project_internal_reference_and_more.py +++ /dev/null @@ -1,36 +0,0 @@ -# Generated by Django 5.1.1 on 2024-11-18 15:18 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("core", "0034_fix_loaded_libraries_objects_meta"), - ] - - operations = [ - migrations.RenameField( - model_name="project", old_name="internal_reference", new_name="ref_id" - ), - migrations.AddField( - model_name="appliedcontrol", - name="ref_id", - field=models.CharField( - blank=True, max_length=100, null=True, verbose_name="reference id" - ), - ), - migrations.AddField( - model_name="complianceassessment", - name="ref_id", - field=models.CharField( - blank=True, max_length=100, null=True, verbose_name="reference id" - ), - ), - migrations.AddField( - model_name="riskassessment", - name="ref_id", - field=models.CharField( - blank=True, max_length=100, null=True, verbose_name="reference id" - ), - ), - ] diff --git a/backend/core/migrations/0036_merge_20241118_1616.py b/backend/core/migrations/0036_merge_20241118_1616.py deleted file mode 100644 index 4746bba1a..000000000 --- a/backend/core/migrations/0036_merge_20241118_1616.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 5.1.1 on 2024-11-18 16:16 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0035_remove_project_internal_reference_and_more'), - ('core', '0035_riskscenario_existing_applied_controls'), - ] - - operations = [ - ] diff --git a/backend/core/migrations/0036_remove_project_internal_reference_and_more.py b/backend/core/migrations/0036_remove_project_internal_reference_and_more.py new file mode 100644 index 000000000..e0fdd0722 --- /dev/null +++ b/backend/core/migrations/0036_remove_project_internal_reference_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 5.1.1 on 2024-11-18 16:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0035_riskscenario_existing_applied_controls'), + ] + + operations = [ + migrations.RenameField( + model_name='project', + old_name='internal_reference', + new_name='ref_id' + ), + migrations.AddField( + model_name='appliedcontrol', + name='ref_id', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='reference id'), + ), + migrations.AddField( + model_name='complianceassessment', + name='ref_id', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='reference id'), + ), + migrations.AddField( + model_name='riskassessment', + name='ref_id', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='reference id'), + ), + ] diff --git a/backend/core/migrations/0037_alter_project_ref_id.py b/backend/core/migrations/0037_alter_project_ref_id.py deleted file mode 100644 index 15ece8142..000000000 --- a/backend/core/migrations/0037_alter_project_ref_id.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.1 on 2024-11-18 16:26 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0036_merge_20241118_1616'), - ] - - operations = [ - migrations.AlterField( - model_name='project', - name='ref_id', - field=models.CharField(blank=True, max_length=100, null=True, verbose_name='reference id'), - ), - ] diff --git a/backend/iam/migrations/0010_folder_ref_id.py b/backend/iam/migrations/0010_folder_ref_id.py index 454ea528c..070db6161 100644 --- a/backend/iam/migrations/0010_folder_ref_id.py +++ b/backend/iam/migrations/0010_folder_ref_id.py @@ -1,19 +1,18 @@ -# Generated by Django 5.1.1 on 2024-11-18 15:00 +# Generated by Django 5.1.1 on 2024-11-18 16:42 from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [ - ("iam", "0009_create_allauth_emailaddress_objects"), + ('iam', '0009_create_allauth_emailaddress_objects'), ] operations = [ migrations.AddField( - model_name="folder", - name="ref_id", - field=models.CharField( - blank=True, max_length=100, null=True, verbose_name="reference id" - ), + model_name='folder', + name='ref_id', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='reference id'), ), ] From e07af5f22348a7dc3781950558872a0707b36be8 Mon Sep 17 00:00:00 2001 From: melinoix Date: Mon, 18 Nov 2024 18:05:50 +0100 Subject: [PATCH 15/30] correct migrations --- .../migrations/0037_alter_project_ref_id.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 backend/core/migrations/0037_alter_project_ref_id.py diff --git a/backend/core/migrations/0037_alter_project_ref_id.py b/backend/core/migrations/0037_alter_project_ref_id.py new file mode 100644 index 000000000..8850375c1 --- /dev/null +++ b/backend/core/migrations/0037_alter_project_ref_id.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.1 on 2024-11-18 17:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0036_remove_project_internal_reference_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='project', + name='ref_id', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='reference id'), + ), + ] From 13af3173c223df407826e406bed132b05936624e Mon Sep 17 00:00:00 2001 From: melinoix Date: Tue, 19 Nov 2024 11:19:09 +0100 Subject: [PATCH 16/30] correct migrations --- ...move_project_internal_reference_and_more.py | 7 ++++++- .../migrations/0037_alter_project_ref_id.py | 18 ------------------ backend/iam/migrations/0010_folder_ref_id.py | 2 +- 3 files changed, 7 insertions(+), 20 deletions(-) delete mode 100644 backend/core/migrations/0037_alter_project_ref_id.py diff --git a/backend/core/migrations/0036_remove_project_internal_reference_and_more.py b/backend/core/migrations/0036_remove_project_internal_reference_and_more.py index e0fdd0722..5e624a872 100644 --- a/backend/core/migrations/0036_remove_project_internal_reference_and_more.py +++ b/backend/core/migrations/0036_remove_project_internal_reference_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.1 on 2024-11-18 16:53 +# Generated by Django 5.1.1 on 2024-11-19 10:15 from django.db import migrations, models @@ -15,6 +15,11 @@ class Migration(migrations.Migration): old_name='internal_reference', new_name='ref_id' ), + migrations.AlterField( + model_name='project', + name='ref_id', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='reference id'), + ), migrations.AddField( model_name='appliedcontrol', name='ref_id', diff --git a/backend/core/migrations/0037_alter_project_ref_id.py b/backend/core/migrations/0037_alter_project_ref_id.py deleted file mode 100644 index 8850375c1..000000000 --- a/backend/core/migrations/0037_alter_project_ref_id.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.1 on 2024-11-18 17:01 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('core', '0036_remove_project_internal_reference_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='project', - name='ref_id', - field=models.CharField(blank=True, max_length=100, null=True, verbose_name='reference id'), - ), - ] diff --git a/backend/iam/migrations/0010_folder_ref_id.py b/backend/iam/migrations/0010_folder_ref_id.py index 070db6161..c5b9b577f 100644 --- a/backend/iam/migrations/0010_folder_ref_id.py +++ b/backend/iam/migrations/0010_folder_ref_id.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.1 on 2024-11-18 16:42 +# Generated by Django 5.1.1 on 2024-11-19 10:15 from django.db import migrations, models From 460c528211d32401e42dfeb0686bffcafd3ce367 Mon Sep 17 00:00:00 2001 From: melinoix Date: Tue, 19 Nov 2024 11:25:29 +0100 Subject: [PATCH 17/30] correct format --- ...ove_project_internal_reference_and_more.py | 39 +++++++++++-------- backend/iam/migrations/0010_folder_ref_id.py | 11 +++--- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/backend/core/migrations/0036_remove_project_internal_reference_and_more.py b/backend/core/migrations/0036_remove_project_internal_reference_and_more.py index 5e624a872..20c7b4f2d 100644 --- a/backend/core/migrations/0036_remove_project_internal_reference_and_more.py +++ b/backend/core/migrations/0036_remove_project_internal_reference_and_more.py @@ -4,35 +4,40 @@ class Migration(migrations.Migration): - dependencies = [ - ('core', '0035_riskscenario_existing_applied_controls'), + ("core", "0035_riskscenario_existing_applied_controls"), ] operations = [ migrations.RenameField( - model_name='project', - old_name='internal_reference', - new_name='ref_id' + model_name="project", old_name="internal_reference", new_name="ref_id" ), migrations.AlterField( - model_name='project', - name='ref_id', - field=models.CharField(blank=True, max_length=100, null=True, verbose_name='reference id'), + model_name="project", + name="ref_id", + field=models.CharField( + blank=True, max_length=100, null=True, verbose_name="reference id" + ), ), migrations.AddField( - model_name='appliedcontrol', - name='ref_id', - field=models.CharField(blank=True, max_length=100, null=True, verbose_name='reference id'), + model_name="appliedcontrol", + name="ref_id", + field=models.CharField( + blank=True, max_length=100, null=True, verbose_name="reference id" + ), ), migrations.AddField( - model_name='complianceassessment', - name='ref_id', - field=models.CharField(blank=True, max_length=100, null=True, verbose_name='reference id'), + model_name="complianceassessment", + name="ref_id", + field=models.CharField( + blank=True, max_length=100, null=True, verbose_name="reference id" + ), ), migrations.AddField( - model_name='riskassessment', - name='ref_id', - field=models.CharField(blank=True, max_length=100, null=True, verbose_name='reference id'), + model_name="riskassessment", + name="ref_id", + field=models.CharField( + blank=True, max_length=100, null=True, verbose_name="reference id" + ), ), ] diff --git a/backend/iam/migrations/0010_folder_ref_id.py b/backend/iam/migrations/0010_folder_ref_id.py index c5b9b577f..37d2fd952 100644 --- a/backend/iam/migrations/0010_folder_ref_id.py +++ b/backend/iam/migrations/0010_folder_ref_id.py @@ -4,15 +4,16 @@ class Migration(migrations.Migration): - dependencies = [ - ('iam', '0009_create_allauth_emailaddress_objects'), + ("iam", "0009_create_allauth_emailaddress_objects"), ] operations = [ migrations.AddField( - model_name='folder', - name='ref_id', - field=models.CharField(blank=True, max_length=100, null=True, verbose_name='reference id'), + model_name="folder", + name="ref_id", + field=models.CharField( + blank=True, max_length=100, null=True, verbose_name="reference id" + ), ), ] From 2165dab1eb57cc55dd66291e77b46fd69b09a580 Mon Sep 17 00:00:00 2001 From: melinoix Date: Tue, 19 Nov 2024 11:29:44 +0100 Subject: [PATCH 18/30] without folder --- backend/iam/migrations/0010_folder_ref_id.py | 19 ------------------- backend/iam/models.py | 4 ---- .../Forms/ModelForm/FolderForm.svelte | 9 --------- .../Forms/ModelForm/FolderForm.svelte | 16 ---------------- frontend/src/lib/utils/table.ts | 4 ++-- 5 files changed, 2 insertions(+), 50 deletions(-) delete mode 100644 backend/iam/migrations/0010_folder_ref_id.py diff --git a/backend/iam/migrations/0010_folder_ref_id.py b/backend/iam/migrations/0010_folder_ref_id.py deleted file mode 100644 index 37d2fd952..000000000 --- a/backend/iam/migrations/0010_folder_ref_id.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.1.1 on 2024-11-19 10:15 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("iam", "0009_create_allauth_emailaddress_objects"), - ] - - operations = [ - migrations.AddField( - model_name="folder", - name="ref_id", - field=models.CharField( - blank=True, max_length=100, null=True, verbose_name="reference id" - ), - ), - ] diff --git a/backend/iam/models.py b/backend/iam/models.py index 8434ade1f..48e800fa8 100644 --- a/backend/iam/models.py +++ b/backend/iam/models.py @@ -81,10 +81,6 @@ class ContentType(models.TextChoices): max_length=2, choices=ContentType.choices, default=ContentType.DOMAIN ) - ref_id = models.CharField( - max_length=100, null=True, blank=True, verbose_name=_("reference id") - ) - parent_folder = models.ForeignKey( "self", null=True, diff --git a/enterprise/frontend/src/lib/components/Forms/ModelForm/FolderForm.svelte b/enterprise/frontend/src/lib/components/Forms/ModelForm/FolderForm.svelte index bd9e4c10d..46ca9c739 100644 --- a/enterprise/frontend/src/lib/components/Forms/ModelForm/FolderForm.svelte +++ b/enterprise/frontend/src/lib/components/Forms/ModelForm/FolderForm.svelte @@ -4,7 +4,6 @@ import * as m from '$paraglide/messages.js'; import type { SuperValidated } from 'sveltekit-superforms'; import AutocompleteSelect from '../AutocompleteSelect.svelte'; - import TextField from '$lib/components/Forms/TextField.svelte'; export let form: SuperValidated; export let model: ModelInfo; @@ -13,14 +12,6 @@ export let initialData: Record = {}; - - - import TextField from '$lib/components/Forms/TextField.svelte'; - import type { SuperValidated } from 'sveltekit-superforms'; - import type { CacheLock } from '$lib/utils/types'; - - export let form: SuperValidated; - export let cacheLocks: Record = {}; - export let formDataCache: Record = {}; - import * as m from '$paraglide/messages.js'; - - diff --git a/frontend/src/lib/utils/table.ts b/frontend/src/lib/utils/table.ts index 5304c4b87..55f0ba724 100644 --- a/frontend/src/lib/utils/table.ts +++ b/frontend/src/lib/utils/table.ts @@ -262,8 +262,8 @@ const LIBRARY_TYPE_FILTER = { export const listViewFields: ListViewFieldsConfig = { folders: { - head: ['ref_id', 'name', 'description', 'parentDomain'], - body: ['ref_id', 'name', 'description', 'parent_folder'] + head: ['name', 'description', 'parentDomain'], + body: ['name', 'description', 'parent_folder'] }, projects: { head: ['ref_id', 'name', 'description', 'domain'], From 6e3629fa12b7d51e0ba01b811513e5de908b3e4c Mon Sep 17 00:00:00 2001 From: melinoix Date: Fri, 22 Nov 2024 11:50:14 +0100 Subject: [PATCH 19/30] correct migration conflicts --- ...re.py => 0039_remove_project_internal_reference_and_more.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename backend/core/migrations/{0036_remove_project_internal_reference_and_more.py => 0039_remove_project_internal_reference_and_more.py} (94%) diff --git a/backend/core/migrations/0036_remove_project_internal_reference_and_more.py b/backend/core/migrations/0039_remove_project_internal_reference_and_more.py similarity index 94% rename from backend/core/migrations/0036_remove_project_internal_reference_and_more.py rename to backend/core/migrations/0039_remove_project_internal_reference_and_more.py index 20c7b4f2d..58dad0828 100644 --- a/backend/core/migrations/0036_remove_project_internal_reference_and_more.py +++ b/backend/core/migrations/0039_remove_project_internal_reference_and_more.py @@ -5,7 +5,7 @@ class Migration(migrations.Migration): dependencies = [ - ("core", "0035_riskscenario_existing_applied_controls"), + ("core", "0038_asset_disaster_recovery_objectives_and_more"), ] operations = [ From 2fdab93ac973f1f4f89c28d7c2b2a7d507e11f4a Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Sat, 23 Nov 2024 16:19:23 +0100 Subject: [PATCH 20/30] Change project internal reference to ref id in tests and interfaces --- frontend/src/lib/utils/schemas.ts | 1 - frontend/src/lib/utils/types.ts | 1 - frontend/tests/functional/user-route.test.ts | 2 +- frontend/tests/utils/test-utils.ts | 6 +++--- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/frontend/src/lib/utils/schemas.ts b/frontend/src/lib/utils/schemas.ts index 03302c90c..0894207d8 100644 --- a/frontend/src/lib/utils/schemas.ts +++ b/frontend/src/lib/utils/schemas.ts @@ -70,7 +70,6 @@ export const FolderSchema = baseNamedObject({ export const ProjectSchema = baseNamedObject({ folder: z.string(), - internal_reference: z.string().optional().nullable(), ref_id: z.string().optional().nullable(), lc_status: z.string().optional().default('in_design') }); diff --git a/frontend/src/lib/utils/types.ts b/frontend/src/lib/utils/types.ts index dde93f1fa..8d11ac1d0 100644 --- a/frontend/src/lib/utils/types.ts +++ b/frontend/src/lib/utils/types.ts @@ -99,7 +99,6 @@ export interface Project { is_published: boolean; name: string; description?: string; - internal_reference?: string; ref_id?: string; compliance_assessments: Record[]; } diff --git a/frontend/tests/functional/user-route.test.ts b/frontend/tests/functional/user-route.test.ts index 21b67d623..cadb1d76b 100644 --- a/frontend/tests/functional/user-route.test.ts +++ b/frontend/tests/functional/user-route.test.ts @@ -40,7 +40,7 @@ test('user usual routine actions are working correctly', async ({ name: vars.projectName, description: vars.description, folder: vars.folderName, - internal_reference: 'Test internal reference', + ref_id: 'ref id', lc_status: 'Production' }); diff --git a/frontend/tests/utils/test-utils.ts b/frontend/tests/utils/test-utils.ts index 2f75a780c..478860ad8 100644 --- a/frontend/tests/utils/test-utils.ts +++ b/frontend/tests/utils/test-utils.ts @@ -151,7 +151,7 @@ export const test = base.extend({ { name: 'name', type: type.TEXT }, { name: 'description', type: type.TEXT }, { name: 'folder', type: type.SELECT_AUTOCOMPLETE }, - { name: 'internal_reference', type: type.TEXT }, + { name: 'ref_id', type: type.TEXT }, { name: 'lc_status', type: type.SELECT } ]); await use(pPage); @@ -375,13 +375,13 @@ export class TestContent { name: vars.projectName, description: vars.description, folder: vars.folderName, - internal_reference: 'Test internal reference', + ref_id: 'ref id', lc_status: 'Production' }, editParams: { name: '', description: '', - internal_reference: '', + ref_id: '', lc_status: 'End of life' } }, From de46e46d511fe3e2163c0a43adc873a25aadbc98 Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Sat, 23 Nov 2024 16:21:07 +0100 Subject: [PATCH 21/30] Remove internal_reference from ProjectViewSet.search_fields --- backend/core/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/core/views.py b/backend/core/views.py index cebacce0c..774481bea 100644 --- a/backend/core/views.py +++ b/backend/core/views.py @@ -185,7 +185,7 @@ class ProjectViewSet(BaseModelViewSet): model = Project filterset_fields = ["folder", "lc_status"] - search_fields = ["name", "ref_id", "internal_reference", "description"] + search_fields = ["name", "ref_id", "description"] @action(detail=False, name="Get status choices") def lc_status(self, request): From f28a9368da146dff90927f4b3390e7b823b91c53 Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Sat, 23 Nov 2024 16:21:21 +0100 Subject: [PATCH 22/30] Change project internal reference to ref id in data model docs --- documentation/architecture/data-model.md | 128 +++++++++++++++-------- 1 file changed, 87 insertions(+), 41 deletions(-) diff --git a/documentation/architecture/data-model.md b/documentation/architecture/data-model.md index 953f5a932..7ea40fa8c 100644 --- a/documentation/architecture/data-model.md +++ b/documentation/architecture/data-model.md @@ -117,7 +117,7 @@ erDiagram PROJECT { string name string description - string internal_reference + string ref_id string status } @@ -705,6 +705,7 @@ class RiskScenario { ## Global fields All models have the following fields: + - created_at: the date when the object has been created. - modified_at: the date when the object has been lastly modified. @@ -715,11 +716,13 @@ Projects are fundamental context objects defined by the entity using CISO Assist The domain is the fundamental perimeter for access control. All objects, in particular domains, within a domain, have consistent access rights. If this granularity is not sufficient, the entity shall define new domains. Note: the IAM model is based on folders. A folder has a type among: + - ROOT: the root folder, which is also called "global domain". - DOMAIN: a user-defined domain. - ENCLAVE: a invisible folder used to confine the actions of a third party. Projects have the following fields: + - Name - Description - Internal reference @@ -734,7 +737,7 @@ Assets are of type primary or support. A primary asset has no parent, a support ## Frameworks The fundamental object of CISO Assistant for compliance is the framework. It corresponds to a given standard, e.g. ISO27001:2013. It mainly contains requirements nodes. A requirement node can be assessable or not (e.g. title or informational elements are not assessable). Assessable requirement nodes can be simply called "requirements". -The structure (tree) of requirements is defined by the requirement node objects. The *parent_urn* of a requirement node can either be the URN of another requirement node or null for top-level objects. This allows to simply define the structure of a framework. An assessable requirement node can be the child of another assessable requirement node, which is very convenient for frameworks that have lists of conditions attached to a requirement. +The structure (tree) of requirements is defined by the requirement node objects. The _parent_urn_ of a requirement node can either be the URN of another requirement node or null for top-level objects. This allows to simply define the structure of a framework. An assessable requirement node can be the child of another assessable requirement node, which is very convenient for frameworks that have lists of conditions attached to a requirement. The implementation_groups field contains a comma-separated list of implementation groups where the requirement node is found, when this is relevant (e.g. for CMMC or CIS). Implementation groups are identified by their ref_id string. Implementation groups are independent, a requirement can be member of any implementation group. Implementation groups are defined in the implementation_groups_definition json field (None by default), that contains a list of objects containing the following fields (example for CMMC): @@ -774,7 +777,7 @@ Vulnerabilities are referential objects used to clarify a risk scenario and to f ## Reference controls -Reference controls are templates for Applied controls. They facilitate the creation of a applied control, and help to have consistent Applied controls. They are not mandatory to create a applied control, but recommended. +Reference controls are templates for Applied controls. They facilitate the creation of a applied control, and help to have consistent Applied controls. They are not mandatory to create a applied control, but recommended. Reference controls have a category within the following possibilities: --/Policy/Process/Technical/Physical. @@ -784,7 +787,8 @@ Reference controls have a csf_function within the following possibilities: --/Go Applied controls are fundamental objects for compliance and remediation. They can derive from a reference control, which provides better consistency, or be independent. -A applied control has the following specific fields: +A applied control has the following specific fields: + - a category (same as reference controls) - a csf_function (same as reference controls) - a status (--/planned/active/inactive) @@ -802,6 +806,7 @@ Costs are measured in a global currency/multiple that is defined in global setti ## Compliance and risk assessments Both types of assessments have common fields: + - a name - a description - a version (defined by the analyst) @@ -812,9 +817,9 @@ Both types of assessments have common fields: - a list of reviewers - a list of user-defined tags -An assessment review can be asked. When at least one principal is defined, the *done* status can only be set if a representant of each principal has reviewed and validated the assessment. +An assessment review can be asked. When at least one principal is defined, the _done_ status can only be set if a representant of each principal has reviewed and validated the assessment. -When the assessment status goes from *in progress* to *in review*, each defined reviewer is notified of the review request. +When the assessment status goes from _in progress_ to _in review_, each defined reviewer is notified of the review request. A review is deprecated if the assessment is changed. A warning shall be displayed to avoid doing that by error. The state of a review can be: created/submitted/validated/changes requested/deprecated @@ -824,11 +829,11 @@ The state of a review can be: created/submitted/validated/changes requested/depr When a compliance assessment is created, each requirement of the corresponding framework is linked to a requirement assessment object. To cover a requirement, the assessor shall link it to Applied controls. Here are the specific fields for requirement assessments: + - result: --/compliant/partially compliant/non-compliant/not applicable - score: --/. - a status: (todo/in progress/in review/done) that facilitates reporting. - The compliance assessment score is a read-only field which is calculated when at least one requirement assessment is scored. We calculate the average of scored requriement assessments (ignoring requirement assessments with an undefined score or with status not-applicable). Requirement assessments can have attached evidences. An evidence contains a name, a description, an attached file, a url link. @@ -841,6 +846,7 @@ For the sake of performance, when a change is done on the selected implementatio Note: the selection is persistent, and used in particular for reporting and analytics. The UX could provide dynamic capacity to show or hide implementation groups independently of the selection (e.g. a button "show unselected requirements"). Compliance assessments have a score scale (min_score, max_score, score definition) that is inherited from the corresponding framework. But it is possible during the creation of the assessment to specify another score scale. The following hardcoded score scales are proposed as an alternative: + - percentage (0-100%, no score definition) - CMMI (1-5, Initial/Managed/Defined/Quantitatively Managed/Optimizing) - 0-5 (0-5, no score definition) @@ -853,6 +859,7 @@ Requirement mapping sets are referential objects that describe relations between A requirement mapping set contains a unique specific attribute in json format called mapping_rules. A mapping_rules is a list of elements containing: + - a source requirement URN - a target requirement URN - a rationale giving the explanation for why a Source Document Element and a Target Document Element are related. This will be syntactic, semantic, or functional. @@ -862,6 +869,7 @@ A mapping_rules is a list of elements containing: Requirement mapping rules are used to automatically generate a draft compliance assessment for a target framework, given existing source assessments. The following inference rules are used: + - there is an order relation in results: compliant > non-compliant minor > non-compliant major - N/A or -- in source makes the mapping not usable. - when several mappings exist for a target requirement, the strongest inference result is used to determine the compliance result. @@ -873,9 +881,9 @@ The following inference rules are used: A risk assessment is based on scenarios, covered by Applied controls. Gathering the risk scenarios constitutes the "risk identification" phase. -A risk assessment has an *risk_assessment_method* field that can take the following values: 0 (risk matrix)/1 (Open FAIR). This cannot be changed once the risk assessment is created. Similarly, the risk matrix cannot be changed once the risk assessment is created. +A risk assessment has an _risk_assessment_method_ field that can take the following values: 0 (risk matrix)/1 (Open FAIR). This cannot be changed once the risk assessment is created. Similarly, the risk matrix cannot be changed once the risk assessment is created. -To analyse the risk, each scenario contains Existing Controls, current probability and impact, proposed controls, residual probability and impact. To facilitate using an assistant to estimate probability and impact, or for advanced methods like openfair, the json fields *current_risk_vector* and *residual_risk_vector* are aimed at keeping the data used to calculate to the estimation. +To analyse the risk, each scenario contains Existing Controls, current probability and impact, proposed controls, residual probability and impact. To facilitate using an assistant to estimate probability and impact, or for advanced methods like openfair, the json fields _current_risk_vector_ and _residual_risk_vector_ are aimed at keeping the data used to calculate to the estimation. A risk scenario contains a treatment option with the values --/open/mitigate/accept/avoid/transfer @@ -895,32 +903,56 @@ The definition JSON field has the following format: { "type": "risk_matrix", "fields": { - "probability" : [ - {"abbreviation": "L", "name": "Low", "description": "Unfrequent event"}, - {"abbreviation": "M", "name": "Medium", "description": "Occasional event"}, - {"abbreviation": "H", "name": "High", "description": "Frequent event"} - ], - "impact": [ - {"abbreviation": "L", "name": "Low", "description": "<100k$"}, - {"abbreviation": "M", "name": "Medium", "description": "between 100 to 1000k$"}, - {"abbreviation": "H", "name": "High", "description": ">1000k$"} - ], - "risk": [ - {"abbreviation": "L", "name": "Low", "description": "acceptable risk", "hexcolor": "#00FF00"}, - {"abbreviation": "M", "name": "Medium", "description": "risk requiring mitigation within 2 years", "hexcolor": "#FFFF00"}, - {"abbreviation": "H", "name": "High", "description": "unacceptable risk", "hexcolor": "#FF0000"} - ], - "grid": [ - [1, 2, 2], - [0, 1, 2], - [0, 0, 1]] + "probability": [ + { "abbreviation": "L", "name": "Low", "description": "Unfrequent event" }, + { + "abbreviation": "M", + "name": "Medium", + "description": "Occasional event" + }, + { "abbreviation": "H", "name": "High", "description": "Frequent event" } + ], + "impact": [ + { "abbreviation": "L", "name": "Low", "description": "<100k$" }, + { + "abbreviation": "M", + "name": "Medium", + "description": "between 100 to 1000k$" + }, + { "abbreviation": "H", "name": "High", "description": ">1000k$" } + ], + "risk": [ + { + "abbreviation": "L", + "name": "Low", + "description": "acceptable risk", + "hexcolor": "#00FF00" + }, + { + "abbreviation": "M", + "name": "Medium", + "description": "risk requiring mitigation within 2 years", + "hexcolor": "#FFFF00" + }, + { + "abbreviation": "H", + "name": "High", + "description": "unacceptable risk", + "hexcolor": "#FF0000" + } + ], + "grid": [ + [1, 2, 2], + [0, 1, 2], + [0, 0, 1] + ] } } ``` ## Risk acceptance -A risk acceptance can be asked on a list of scenarios that are part of validated risk assessments (assessment in the *done* state with at least one reviewer). It is directed to an approver that should be the risk owner. +A risk acceptance can be asked on a list of scenarios that are part of validated risk assessments (assessment in the _done_ state with at least one reviewer). It is directed to an approver that should be the risk owner. The state of a risk acceptance can be: created/submitted/accepted/rejected/revoked @@ -931,6 +963,7 @@ Once a risk acceptance is active, the correponding risk assessments are frozen. ## Libraries Libraries can contain: + - frameworks (including requirement nodes) - threats - vulnerabilities @@ -947,10 +980,10 @@ Libraries have a URN to uniquely identify them. Libraries have a locale that describes the main locale for the whole content of the library. Libraries have an integer version that completes the URN. The highest version for a given URN shall always be privileged. So: + - a library loading is performed if and only if there is no greater or equal version already loaded, for the same urn. - if a breaking change is necessary, the URN should be changed. - Libraries have a provider (which entity produced the original content), and a packager (which entity did the library). Objects in the library inherit their provider from the library's. Libraries can depend on other libraries, thanks to the "dependencies" section, that contains a list of URNs. When loading a library, CISO Assistant first loads the dependent libraries. If a dependency is missing, the loading is cancelled. @@ -964,7 +997,8 @@ Deleting a library is possible only if none of its objects is currently used. Re Frameworks (including requirement nodes), mappings, threats, vulnerabilities, reference controls and risk matrices are called "referential objects", as they constitute the basis of an assessment. Referential objects can be downloaded from a library. They are called "global referential objects" or "library objects" in that case, and they have the following characteristics: -- they have a non-null URN identifier *urn* of the form: ```urn:intuitem:::[:]```. Client-defined URNs are also possible. The framework part is present for items that are part of a framework. + +- they have a non-null URN identifier _urn_ of the form: `urn:intuitem:::[:]`. Client-defined URNs are also possible. The framework part is present for items that are part of a framework. - they are read-only in the database once imported. They can be removed only by removing the corresponding library. - they are attached to the root folder. - Everyone has the right to read them, they are "published" to all domains. @@ -972,11 +1006,13 @@ Referential objects can be downloaded from a library. They are called "global re - They have a link to their library. Conversely, a referential object with a null URN is called a "local referential object" has the following characteristics: + - it is created by a user in a given domain (not in the root folder) - it can be edited with proper permission. - The URN cannot be edited and is hidden. Referential objects have the following optional fields: + - ref_id: reference used in the standard for this object (e.g. A.5.5). - annotation: provided by the library packager or the user to clarify the meaning of the object. They can be used for search, and are displayed when available. - provider: describes where the object comes from, e.g. ISO, NIST, CIS, MITRE ATT&CK... @@ -994,9 +1030,10 @@ The library_manager role will be defined to manage library objects. ## Referential objects translation -Referential objects translations are contained inside a JSON called previously *translations*. The translation takes place directly inside the yaml at the point where the object is defined. +Referential objects translations are contained inside a JSON called previously _translations_. The translation takes place directly inside the yaml at the point where the object is defined. Example: + ```yaml { - urn: urn:intuitem:risk:req_node:iso27001-2022:4 @@ -1013,9 +1050,10 @@ Example: } ``` -Everything in the library can be translated, from the library itself to the the last object. To specify that the library is available in a language other than the default one, *translations* field has to be filled for the language(s) concerned. +Everything in the library can be translated, from the library itself to the the last object. To specify that the library is available in a language other than the default one, _translations_ field has to be filled for the language(s) concerned. Example: + ```yaml { urn: urn:intuitem:risk:library:iso27001-2022 @@ -1047,6 +1085,7 @@ All objects in CISO Assistant follow a simple and consistent RBAC IAM model, inc There are two dimensions: rights and perimeter. There granularity of rights is mapped on Django convention: + - Add - View - Change @@ -1059,6 +1098,7 @@ Practically, the Add, Change or Delete permissions require the View permission. The perimeter for access control is based on the folder concept, with all its content, including subfolders. Boolean parameters allow a finer-grain definition of the perimeter, as will be seen later. Practically, the perimeter is either: + - global, corresponding to the root folder - a domain, corresponding to a folder of level 1. @@ -1067,18 +1107,19 @@ Practically, the perimeter is either: For Access Control purpose, CISO Assistant data is organized in a tree of folders, starting from a root folder. The organization of the tree is not hardcoded, it is entirely determined by configuration. Any object in CISO Assistant is attached to a folder (including folders), either directly or indirectly through a parent object that is attached to a folder. The root folder is attached to None. A folder contains the following attributes: + - name: the short name given to the folder - description: a longer description of the folder - contentType: an enum representing the type of content. Currently GLOBAL and DOMAIN. This parameter is aimed at adjusting the UI depending of the type of content. - folder: the parent folder. None for root folder. Currently, the folder organization is as follows: + - The root folder has contentType=GLOBAL. - The root folder can only contain referential objects. - There is only one level of subfolders, each subfolder with contentType=DOMAIN. - Folders are not displayed as such, they are visible only to the programmer. - ### Roles and role assignments To simplify access control, we use a RBAC model. @@ -1093,14 +1134,15 @@ To simplify access control, we use a RBAC model. | Risk approver | like reader, but with additional capability to approve risk acceptances | | Reviewer | like reader, but with additional capability to review assessments. | - Note: a DJANGO superuser is given administrator rights automatically on startup. Principals are either: + - users - group of users Role assignements are described as a table containing the following attributes: + - user: the user that receives the role assignment (can be None) - user_group: the group that receives the role assignment (can be None) - role: the role assigned to the principal @@ -1135,6 +1177,7 @@ The goal of Third-Party Risk Management is to manage the risk incurred by a prov ### Retained approach The following approach has been retained: + - An "entity" model is added to modelize third parties in a generic way. - A third party is an entity that is provider of the entity representing the client using CISO Assistant. - An evaluation of a third party is based on a compliance assessment, to leverage a huge amount of existing models and code. @@ -1246,6 +1289,7 @@ erDiagram #### Entity An entity represents a legal entity, a corporate body, an administrative body, an association. An entity can be: + - the main subject for the current CISO Assistant instance ("main entity"). - a subisdiary of another entity. - a provider of another entity. @@ -1265,11 +1309,12 @@ An entity assessment is based on a questionnaire/compliance assessment, and/or o Typically, the main entity can use the requirement group selector to tailor the questionnaire before sending it to the third-party, then a self-assessment is done by the provider, then a review is done by the main entity. An entity assessment has the following specific fields: - - conclusion: --|blocker|warning|ok|N/A - - penetration: as defined by ebios RM - - dependency: as defined by ebios RM - - maturity: as defined by ebios RM - - trust: as defined by ebios RM + +- conclusion: --|blocker|warning|ok|N/A +- penetration: as defined by ebios RM +- dependency: as defined by ebios RM +- maturity: as defined by ebios RM +- trust: as defined by ebios RM #### Solution @@ -1309,7 +1354,7 @@ There is no link between representatives (modeling of the ecosystem) and users o - Add a "contract" category - Add a foreign key "contract" to point to a contract -The foreign key contract shall be non-null only if the category is set to "contract". The UX shall reflect this constraint. +The foreign key contract shall be non-null only if the category is set to "contract". The UX shall reflect this constraint. Note: in the future, we will use the same approach for policies. @@ -1318,6 +1363,7 @@ Note: in the future, we will use the same approach for policies. The format for question and answer json fields will evolve over time. The initial format is the following: - question: + ```json { "question": { From 2e9da4793fdf7335e5ac2e61bdbdcd7ee0c4ac87 Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Sat, 23 Nov 2024 17:56:33 +0100 Subject: [PATCH 23/30] Fix ref id field title functional test --- frontend/tests/utils/page-detail.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/tests/utils/page-detail.ts b/frontend/tests/utils/page-detail.ts index 91d0a30fa..f252621db 100644 --- a/frontend/tests/utils/page-detail.ts +++ b/frontend/tests/utils/page-detail.ts @@ -28,7 +28,7 @@ export class PageDetail extends BasePage { await this.hasTitle('Edit ' + this.item); await this.hasBreadcrumbPath(['Edit'], false); - let editedValues: { [k: string]: string } = {}; + const editedValues: { [k: string]: string } = {}; for (const key in editParams) { editedValues[key] = editParams[key] === '' ? buildParams[key] + ' edited' : editParams[key]; } @@ -78,6 +78,10 @@ export class PageDetail extends BasePage { await expect .soft(this.page.getByTestId(key.replaceAll('_', '-') + '-field-title')) .toHaveText(new RegExp('domain'.replaceAll('_', ' '), 'i')); + } else if (key === 'ref_id') { + await expect + .soft(this.page.getByTestId(key.replaceAll('_', '-') + '-field-title')) + .toHaveText('Reference ID'); } else { await expect .soft(this.page.getByTestId(key.replaceAll('_', '-') + '-field-title')) From edd2c9b3dcf6d9e8b43cca8b617a09551a6ed1a4 Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Sat, 23 Nov 2024 19:16:08 +0100 Subject: [PATCH 24/30] Change test value for project's ref id --- frontend/tests/functional/user-route.test.ts | 2 +- frontend/tests/utils/test-utils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/tests/functional/user-route.test.ts b/frontend/tests/functional/user-route.test.ts index cadb1d76b..77afaba71 100644 --- a/frontend/tests/functional/user-route.test.ts +++ b/frontend/tests/functional/user-route.test.ts @@ -40,7 +40,7 @@ test('user usual routine actions are working correctly', async ({ name: vars.projectName, description: vars.description, folder: vars.folderName, - ref_id: 'ref id', + ref_id: 'R.1234', lc_status: 'Production' }); diff --git a/frontend/tests/utils/test-utils.ts b/frontend/tests/utils/test-utils.ts index 478860ad8..2e54bbdcf 100644 --- a/frontend/tests/utils/test-utils.ts +++ b/frontend/tests/utils/test-utils.ts @@ -375,7 +375,7 @@ export class TestContent { name: vars.projectName, description: vars.description, folder: vars.folderName, - ref_id: 'ref id', + ref_id: 'R.1234', lc_status: 'Production' }, editParams: { From c275730ccfb67ced0c91c03f4735ffed6e10cb11 Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Mon, 25 Nov 2024 13:59:41 +0100 Subject: [PATCH 25/30] Fix migration conflict --- ... => 0041_add_ref_id_to_project_appliedcontrol_assessment.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename backend/core/migrations/{0039_remove_project_internal_reference_and_more.py => 0041_add_ref_id_to_project_appliedcontrol_assessment.py} (94%) diff --git a/backend/core/migrations/0039_remove_project_internal_reference_and_more.py b/backend/core/migrations/0041_add_ref_id_to_project_appliedcontrol_assessment.py similarity index 94% rename from backend/core/migrations/0039_remove_project_internal_reference_and_more.py rename to backend/core/migrations/0041_add_ref_id_to_project_appliedcontrol_assessment.py index 58dad0828..f5ea2f3ac 100644 --- a/backend/core/migrations/0039_remove_project_internal_reference_and_more.py +++ b/backend/core/migrations/0041_add_ref_id_to_project_appliedcontrol_assessment.py @@ -5,7 +5,7 @@ class Migration(migrations.Migration): dependencies = [ - ("core", "0038_asset_disaster_recovery_objectives_and_more"), + ("core", "0040_riskscenario_ref_id"), ] operations = [ From a51419d55c21a141014c6d6dc30c046deca09133 Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Mon, 25 Nov 2024 14:00:01 +0100 Subject: [PATCH 26/30] Add ref_id field to applied control detail view --- frontend/src/lib/utils/crud.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/lib/utils/crud.ts b/frontend/src/lib/utils/crud.ts index e9796ebfa..5dd599252 100644 --- a/frontend/src/lib/utils/crud.ts +++ b/frontend/src/lib/utils/crud.ts @@ -249,6 +249,7 @@ export const URL_MODEL_MAP: ModelMap = { { field: 'status' }, { field: 'created_at', type: 'datetime' }, { field: 'updated_at', type: 'datetime' }, + { field: 'ref_id' }, { field: 'name' }, { field: 'description' }, { field: 'eta', type: 'date' }, From fd82eb3068f603cf82148e1c8fb1ea2fc50fe011 Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Mon, 25 Nov 2024 14:00:22 +0100 Subject: [PATCH 27/30] Change reference to reference id in table headers --- frontend/src/lib/utils/table.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/lib/utils/table.ts b/frontend/src/lib/utils/table.ts index 44976bcb4..a893c1496 100644 --- a/frontend/src/lib/utils/table.ts +++ b/frontend/src/lib/utils/table.ts @@ -290,7 +290,7 @@ export const listViewFields: ListViewFieldsConfig = { } }, threats: { - head: ['ref', 'name', 'description', 'provider', 'domain'], + head: ['ref_id', 'name', 'description', 'provider', 'domain'], body: ['ref_id', 'name', 'description', 'provider', 'folder'], meta: ['id', 'urn'], filters: { @@ -381,7 +381,7 @@ export const listViewFields: ListViewFieldsConfig = { } }, 'reference-controls': { - head: ['ref', 'name', 'description', 'category', 'csfFunction', 'provider', 'domain'], + head: ['ref_id', 'name', 'description', 'category', 'csfFunction', 'provider', 'domain'], body: ['ref_id', 'name', 'description', 'category', 'csf_function', 'provider', 'folder'], meta: ['id', 'urn'], filters: { @@ -464,7 +464,7 @@ export const listViewFields: ListViewFieldsConfig = { } }, requirements: { - head: ['ref', 'name', 'description', 'framework'], + head: ['ref_id', 'name', 'description', 'framework'], body: ['ref_id', 'name', 'description', 'framework'], meta: ['id', 'urn'] }, From 7c75633cebb17a0313736bb8592cc4788bdcfbd6 Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Mon, 25 Nov 2024 14:03:29 +0100 Subject: [PATCH 28/30] Add ref_id field to risk assessment detail view --- .../(internal)/risk-assessments/[id=uuid]/+page.svelte | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/routes/(app)/(internal)/risk-assessments/[id=uuid]/+page.svelte b/frontend/src/routes/(app)/(internal)/risk-assessments/[id=uuid]/+page.svelte index 826f7bbbf..2991b77fc 100644 --- a/frontend/src/routes/(app)/(internal)/risk-assessments/[id=uuid]/+page.svelte +++ b/frontend/src/routes/(app)/(internal)/risk-assessments/[id=uuid]/+page.svelte @@ -162,9 +162,13 @@
    +
  • + {m.refId()}: + {risk_assessment.ref_id ?? '--'} +
  • {m.status()}: - {risk_assessment.status === null ? '--' : safeTranslate(risk_assessment.status)} + {risk_assessment.status === null ? '--' : safetranslate(risk_assessment.status)}
  • {m.authors()}: From 85826c0b55fd637b53b52d4da15be32ecf4e8175 Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Mon, 25 Nov 2024 14:08:15 +0100 Subject: [PATCH 29/30] Simplify risk assessment detail view --- .../risk-assessments/[id=uuid]/+page.svelte | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/frontend/src/routes/(app)/(internal)/risk-assessments/[id=uuid]/+page.svelte b/frontend/src/routes/(app)/(internal)/risk-assessments/[id=uuid]/+page.svelte index 2991b77fc..dd3deffbb 100644 --- a/frontend/src/routes/(app)/(internal)/risk-assessments/[id=uuid]/+page.svelte +++ b/frontend/src/routes/(app)/(internal)/risk-assessments/[id=uuid]/+page.svelte @@ -19,7 +19,6 @@ import RiskScenarioItem from '$lib/components/RiskMatrix/RiskScenarioItem.svelte'; import * as m from '$paraglide/messages'; import { languageTag } from '$paraglide/runtime'; - import { toCamelCase } from '$lib/utils/locales.js'; import { safeTranslate } from '$lib/utils/i18n.js'; export let data; @@ -161,16 +160,16 @@

-
    -
  • +
      +
    • {m.refId()}: {risk_assessment.ref_id ?? '--'}
    • -
    • +
    • {m.status()}: - {risk_assessment.status === null ? '--' : safetranslate(risk_assessment.status)} + {!risk_assessment.status ? '--' : safeTranslate(risk_assessment.status)}
    • -
    • +
    • {m.authors()}:
        {#each risk_assessment.authors as author} @@ -178,11 +177,11 @@ {/each}
    • -
    • +
    • {m.createdAt()}: {new Date(risk_assessment.created_at).toLocaleString(languageTag())}
    • -
    • +
    • {m.updatedAt()}: {new Date(risk_assessment.updated_at).toLocaleString(languageTag())}
    • @@ -204,7 +203,7 @@ >
- {risk_assessment.description ?? '-'} + {risk_assessment.description ?? '--'}
From 0713bc844780b53d5e2eb27914ab9931791542ff Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Mon, 25 Nov 2024 14:11:48 +0100 Subject: [PATCH 30/30] Add ref_id field to audit detail view --- .../(third-party)/compliance-assessments/[id=uuid]/+page.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/routes/(app)/(third-party)/compliance-assessments/[id=uuid]/+page.svelte b/frontend/src/routes/(app)/(third-party)/compliance-assessments/[id=uuid]/+page.svelte index 553aba375..08de6333b 100644 --- a/frontend/src/routes/(app)/(third-party)/compliance-assessments/[id=uuid]/+page.svelte +++ b/frontend/src/routes/(app)/(third-party)/compliance-assessments/[id=uuid]/+page.svelte @@ -204,7 +204,7 @@
- {#each Object.entries(data.compliance_assessment).filter( ([key, _]) => ['name', 'description', 'project', 'framework', 'authors', 'reviewers', 'status', 'selected_implementation_groups'].includes(key) ) as [key, value]} + {#each Object.entries(data.compliance_assessment).filter( ([key, _]) => ['ref_id', 'name', 'description', 'project', 'framework', 'authors', 'reviewers', 'status', 'selected_implementation_groups'].includes(key) ) as [key, value]}