From 7039444ff5c8bde387d75af599e575dbdf68bf46 Mon Sep 17 00:00:00 2001 From: melinoix Date: Tue, 12 Nov 2024 15:45:27 +0100 Subject: [PATCH 001/107] 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 002/107] 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 003/107] 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 009/107] 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 011/107] 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 012/107] 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 013/107] 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 014/107] 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 015/107] 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 016/107] 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 017/107] 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 018/107] 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 f26f33249e26cc2aab9401f884453573a2fb77d7 Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Thu, 21 Nov 2024 11:28:22 +0100 Subject: [PATCH 019/107] feat: improve mapping inference additional information --- backend/core/models.py | 2 ++ frontend/messages/en.json | 4 +++- .../[id=uuid]/+page.svelte | 18 ++++++++++++++++++ .../[id=uuid]/edit/+page.svelte | 18 ++++++++++++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/backend/core/models.py b/backend/core/models.py index ea1f89097..80c04dcee 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -2612,6 +2612,8 @@ def compute_requirement_assessments_results( "str": str(ref), "id": str(ref.id), "coverage": mapping.coverage, + "score": ref.score, + "observation": ref.observation }, # "mappings": [mapping.id for mapping in mappings], } diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 7eae4709f..4a9082d82 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -864,5 +864,7 @@ "existingMeasures": "Existing measures", "youCanSetPasswordHere": "You can set your password here", "forgotPassword": "Forgot password", - "ssoSettingsUpdated": "SSO settings updated" + "ssoSettingsUpdated": "SSO settings updated", + "scoreSemiColon": "Score:", + "mappingInferenceHelpText": "These variables are fixed and will not change according to the source requirement" } diff --git a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/+page.svelte b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/+page.svelte index 7f757b20c..584608558 100644 --- a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/+page.svelte +++ b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/+page.svelte @@ -192,6 +192,7 @@ {m.mappingInference()}

+ {m.mappingInferenceHelpText()}
  • @@ -220,6 +221,23 @@ {safeTranslate(mappingInference.result)}

    + {#if mappingInference.sourceRequirementAssessment.score} +

    + {m.scoreSemiColon()} + + {mappingInference.sourceRequirementAssessment.score} + +

    + {/if} + {#if mappingInference.sourceRequirementAssessment.observation} +

    + {m.observationSemiColon()} + {mappingInference.sourceRequirementAssessment.observation} +

    + {/if} {#if mappingInference.annotation}

    {m.annotationColon()} diff --git a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte index c9da5a0f9..9755174b5 100644 --- a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte +++ b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte @@ -327,6 +327,7 @@ {m.mappingInference()}

    + {m.mappingInferenceHelpText()}
    • @@ -353,6 +354,23 @@ {safeTranslate(mappingInference.result)}

      + {#if mappingInference.sourceRequirementAssessment.score} +

      + {m.scoreSemiColon()} + + {mappingInference.sourceRequirementAssessment.score} + +

      + {/if} + {#if mappingInference.sourceRequirementAssessment.observation} +

      + {m.observationSemiColon()} + {mappingInference.sourceRequirementAssessment.observation} +

      + {/if} {#if mappingInference.annotation}

      {m.annotationColon()} From 8fbb5f2dc17b4fdf6270efe03389297e9aefeac0 Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Thu, 21 Nov 2024 11:28:55 +0100 Subject: [PATCH 020/107] chore: format --- backend/core/models.py | 2 +- .../requirement-assessments/[id=uuid]/+page.svelte | 14 ++++++++------ .../[id=uuid]/edit/+page.svelte | 14 ++++++++------ 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/backend/core/models.py b/backend/core/models.py index 80c04dcee..49739f90b 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -2613,7 +2613,7 @@ def compute_requirement_assessments_results( "id": str(ref.id), "coverage": mapping.coverage, "score": ref.score, - "observation": ref.observation + "observation": ref.observation, }, # "mappings": [mapping.id for mapping in mappings], } diff --git a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/+page.svelte b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/+page.svelte index 584608558..5cf149894 100644 --- a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/+page.svelte +++ b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/+page.svelte @@ -192,7 +192,9 @@ {m.mappingInference()}

      - {m.mappingInferenceHelpText()} + {m.mappingInferenceHelpText()}
      • @@ -224,16 +226,16 @@ {#if mappingInference.sourceRequirementAssessment.score}

        {m.scoreSemiColon()} - + {mappingInference.sourceRequirementAssessment.score}

        {/if} {#if mappingInference.sourceRequirementAssessment.observation} -

        +

        {m.observationSemiColon()} {mappingInference.sourceRequirementAssessment.observation}

        diff --git a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte index 9755174b5..d8c801152 100644 --- a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte +++ b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte @@ -327,7 +327,9 @@ {m.mappingInference()}

        - {m.mappingInferenceHelpText()} + {m.mappingInferenceHelpText()}
        • @@ -357,16 +359,16 @@ {#if mappingInference.sourceRequirementAssessment.score}

          {m.scoreSemiColon()} - + {mappingInference.sourceRequirementAssessment.score}

          {/if} {#if mappingInference.sourceRequirementAssessment.observation} -

          +

          {m.observationSemiColon()} {mappingInference.sourceRequirementAssessment.observation}

          From b6576d7c245ba2dbafa3d32a3621dd3d10af87a2 Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene <90701924+Mohamed-Hacene@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:37:25 +0100 Subject: [PATCH 021/107] =?UTF-8?q?chore:=20update=20translations=20with?= =?UTF-8?q?=20Fink=20=F0=9F=90=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/messages/ar.json | 4 +++- frontend/messages/de.json | 4 +++- frontend/messages/en.json | 2 +- frontend/messages/es.json | 4 +++- frontend/messages/fr.json | 4 +++- frontend/messages/hi.json | 4 +++- frontend/messages/it.json | 4 +++- frontend/messages/nl.json | 4 +++- frontend/messages/pl.json | 4 +++- frontend/messages/pt.json | 4 +++- frontend/messages/ro.json | 4 +++- frontend/messages/sv.json | 4 +++- frontend/messages/ur.json | 4 +++- 13 files changed, 37 insertions(+), 13 deletions(-) diff --git a/frontend/messages/ar.json b/frontend/messages/ar.json index cb07753a8..6ce175daa 100644 --- a/frontend/messages/ar.json +++ b/frontend/messages/ar.json @@ -858,5 +858,7 @@ "tags": "العلامات", "addTag": "إضافة علامة", "tagsHelpText": "تُستخدم العلامات لتصنيف العناصر وتصفيتها. يمكنك إضافة علامات في قسم \"إضافي\"", - "forgotPassword": "هل نسيت كلمة السر" + "forgotPassword": "هل نسيت كلمة السر", + "scoreSemiColon": "نتيجة:", + "mappingInferenceHelpText": "هذه المتغيرات ثابتة ولن تتغير اعتمادًا على المصدر." } diff --git a/frontend/messages/de.json b/frontend/messages/de.json index b7f339d14..192654b27 100644 --- a/frontend/messages/de.json +++ b/frontend/messages/de.json @@ -857,5 +857,7 @@ "tags": "Schlagwörter", "addTag": "Tag hinzufügen", "tagsHelpText": "Tags werden zum Kategorisieren und Filtern der Elemente verwendet. Sie können Tags im Abschnitt Extra hinzufügen", - "forgotPassword": "Passwort vergessen" + "forgotPassword": "Passwort vergessen", + "scoreSemiColon": "Punktzahl:", + "mappingInferenceHelpText": "Diese Variablen sind fest und ändern sich je nach Quelle nicht." } diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 4a9082d82..649863755 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -866,5 +866,5 @@ "forgotPassword": "Forgot password", "ssoSettingsUpdated": "SSO settings updated", "scoreSemiColon": "Score:", - "mappingInferenceHelpText": "These variables are fixed and will not change according to the source requirement" + "mappingInferenceHelpText": "These variables are fixed and will not change depending on the source." } diff --git a/frontend/messages/es.json b/frontend/messages/es.json index ab4e031a1..7583751c0 100644 --- a/frontend/messages/es.json +++ b/frontend/messages/es.json @@ -857,5 +857,7 @@ "tags": "Etiquetas", "addTag": "Agregar etiqueta", "tagsHelpText": "Las etiquetas se utilizan para categorizar y filtrar los elementos. Puedes agregar etiquetas en la sección Extra", - "forgotPassword": "Has olvidado tu contraseña" + "forgotPassword": "Has olvidado tu contraseña", + "scoreSemiColon": "Puntaje:", + "mappingInferenceHelpText": "Estas variables son fijas y no cambiarán dependiendo de la fuente." } diff --git a/frontend/messages/fr.json b/frontend/messages/fr.json index 64029f31c..df379043d 100644 --- a/frontend/messages/fr.json +++ b/frontend/messages/fr.json @@ -864,5 +864,7 @@ "existingMeasures": "Mesures existantes", "youCanSetPasswordHere": "Vous pouvez définir votre mot de passe ici", "forgotPassword": "Mot de passe oublié", - "ssoSettingsUpdated": "Paramètres SSO mis à jour" + "ssoSettingsUpdated": "Paramètres SSO mis à jour", + "scoreSemiColon": "Score:", + "mappingInferenceHelpText": "Ces variables sont fixes et ne changeront pas en fonction de la source." } diff --git a/frontend/messages/hi.json b/frontend/messages/hi.json index 46d799ee9..3f1609eae 100644 --- a/frontend/messages/hi.json +++ b/frontend/messages/hi.json @@ -857,5 +857,7 @@ "tags": "टैग", "addTag": "टैग जोड़ें", "tagsHelpText": "टैग का उपयोग आइटम को वर्गीकृत और फ़िल्टर करने के लिए किया जाता है। आप अतिरिक्त अनुभाग में टैग जोड़ सकते हैं", - "forgotPassword": "पासवर्ड भूल गए" + "forgotPassword": "पासवर्ड भूल गए", + "scoreSemiColon": "अंक:", + "mappingInferenceHelpText": "ये चर निश्चित हैं और स्रोत के आधार पर परिवर्तित नहीं होंगे।" } diff --git a/frontend/messages/it.json b/frontend/messages/it.json index 0f4ce9a0e..0ba84f8a8 100644 --- a/frontend/messages/it.json +++ b/frontend/messages/it.json @@ -857,5 +857,7 @@ "tags": "Etichette", "addTag": "Aggiungi tag", "tagsHelpText": "I tag vengono utilizzati per categorizzare e filtrare gli elementi. Puoi aggiungere tag nella sezione Extra", - "forgotPassword": "Ha dimenticato la password" + "forgotPassword": "Ha dimenticato la password", + "scoreSemiColon": "Punto:", + "mappingInferenceHelpText": "Queste variabili sono fisse e non cambiano a seconda della fonte." } diff --git a/frontend/messages/nl.json b/frontend/messages/nl.json index adbe6dea2..a71993899 100644 --- a/frontend/messages/nl.json +++ b/frontend/messages/nl.json @@ -857,5 +857,7 @@ "tags": "Labels", "addTag": "Tag toevoegen", "tagsHelpText": "Tags worden gebruikt om de items te categoriseren en te filteren. U kunt tags toevoegen in de sectie Extra", - "forgotPassword": "Wachtwoord vergeten" + "forgotPassword": "Wachtwoord vergeten", + "scoreSemiColon": "Punt:", + "mappingInferenceHelpText": "Deze variabelen zijn vast en veranderen niet, afhankelijk van de bron." } diff --git a/frontend/messages/pl.json b/frontend/messages/pl.json index 44c357ad4..93de8ea82 100644 --- a/frontend/messages/pl.json +++ b/frontend/messages/pl.json @@ -857,5 +857,7 @@ "tags": "Tagi", "addTag": "Dodaj tag", "tagsHelpText": "Tagi służą do kategoryzowania i filtrowania elementów. Możesz dodać tagi w sekcji Extra", - "forgotPassword": "Zapomniałem hasła" + "forgotPassword": "Zapomniałem hasła", + "scoreSemiColon": "Wynik:", + "mappingInferenceHelpText": "Te zmienne są stałe i nie zmieniają się w zależności od źródła." } diff --git a/frontend/messages/pt.json b/frontend/messages/pt.json index 4019d7f32..26574841d 100644 --- a/frontend/messages/pt.json +++ b/frontend/messages/pt.json @@ -857,5 +857,7 @@ "tags": "Etiquetas", "addTag": "Adicionar etiqueta", "tagsHelpText": "As tags são usadas para categorizar e filtrar os itens. Você pode adicionar tags na seção Extra", - "forgotPassword": "Esqueceu sua senha" + "forgotPassword": "Esqueceu sua senha", + "scoreSemiColon": "Pontuação:", + "mappingInferenceHelpText": "Essas variáveis são fixas e não mudarão dependendo da fonte." } diff --git a/frontend/messages/ro.json b/frontend/messages/ro.json index b5a3bf290..df49db0e1 100644 --- a/frontend/messages/ro.json +++ b/frontend/messages/ro.json @@ -857,5 +857,7 @@ "tags": "Etichete", "addTag": "Adăugați etichetă", "tagsHelpText": "Etichetele sunt folosite pentru a clasifica și filtra articolele. Puteți adăuga etichete în secțiunea Extra", - "forgotPassword": "Aţi uitat parola" + "forgotPassword": "Aţi uitat parola", + "scoreSemiColon": "Scor:", + "mappingInferenceHelpText": "Aceste variabile sunt fixe și nu se vor modifica în funcție de sursă." } diff --git a/frontend/messages/sv.json b/frontend/messages/sv.json index f6dd37618..4dae9785d 100644 --- a/frontend/messages/sv.json +++ b/frontend/messages/sv.json @@ -861,5 +861,7 @@ "existingMeasures": "Befintliga kontroller", "youCanSetPasswordHere": "Du kan ställa in ditt lösenord här", "forgotPassword": "Glömt lösenord", - "ssoSettingsUpdated": "SSO-inställningar uppdaterade" + "ssoSettingsUpdated": "SSO-inställningar uppdaterade", + "scoreSemiColon": "Göra:", + "mappingInferenceHelpText": "Dessa variabler är fasta och kommer inte att ändras beroende på källan." } diff --git a/frontend/messages/ur.json b/frontend/messages/ur.json index 7b5197f9b..56fb1e1c0 100644 --- a/frontend/messages/ur.json +++ b/frontend/messages/ur.json @@ -857,5 +857,7 @@ "tags": "ٹیگز", "addTag": "ٹیگ شامل کریں۔", "tagsHelpText": "ٹیگز اشیاء کی درجہ بندی اور فلٹر کرنے کے لیے استعمال ہوتے ہیں۔ آپ اضافی سیکشن میں ٹیگ شامل کر سکتے ہیں۔", - "forgotPassword": "پاس ورڈ بھول گئے۔" + "forgotPassword": "پاس ورڈ بھول گئے۔", + "scoreSemiColon": "سکور:", + "mappingInferenceHelpText": "یہ متغیرات طے شدہ ہیں اور ماخذ کے لحاظ سے تبدیل نہیں ہوں گے۔" } From 6e3629fa12b7d51e0ba01b811513e5de908b3e4c Mon Sep 17 00:00:00 2001 From: melinoix Date: Fri, 22 Nov 2024 11:50:14 +0100 Subject: [PATCH 022/107] 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 75d679538820adf7fc2b41efa61296d482110b81 Mon Sep 17 00:00:00 2001 From: Abderrahmane Smimite Date: Fri, 22 Nov 2024 16:20:14 +0100 Subject: [PATCH 023/107] starting point --- .../core/migrations/0039_historicalmetric.py | 49 ++++++++++++++++ backend/core/models.py | 56 +++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 backend/core/migrations/0039_historicalmetric.py diff --git a/backend/core/migrations/0039_historicalmetric.py b/backend/core/migrations/0039_historicalmetric.py new file mode 100644 index 000000000..3dc6a94bd --- /dev/null +++ b/backend/core/migrations/0039_historicalmetric.py @@ -0,0 +1,49 @@ +# Generated by Django 5.1.1 on 2024-11-22 14:38 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0038_asset_disaster_recovery_objectives_and_more"), + ] + + operations = [ + migrations.CreateModel( + name="HistoricalMetric", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("date", models.DateField(db_index=True, verbose_name="Date")), + ("data", models.JSONField(verbose_name="Historical Data")), + ("model", models.TextField(db_index=True, verbose_name="Model")), + ( + "object_id", + models.UUIDField(db_index=True, verbose_name="Object ID"), + ), + ( + "updated_at", + models.DateTimeField(auto_now=True, verbose_name="Updated at"), + ), + ], + options={ + "indexes": [ + models.Index( + fields=["model", "object_id", "date"], + name="core_histor_model_e05191_idx", + ), + models.Index( + fields=["date", "model"], name="core_histor_date_ddb7df_idx" + ), + ], + "unique_together": {("model", "object_id", "date")}, + }, + ), + ] diff --git a/backend/core/models.py b/backend/core/models.py index 2421f0d07..99aaa8fa3 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -1741,6 +1741,34 @@ class Status(models.TextChoices): fields_to_check = ["name"] +## historical data +class HistoricalMetric(models.Model): + date = models.DateField(verbose_name=_("Date"), db_index=True) + data = models.JSONField(verbose_name=_("Historical Data")) + model = models.TextField(verbose_name=_("Model"), db_index=True) + object_id = models.UUIDField(verbose_name=_("Object ID"), db_index=True) + updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated at")) + + class Meta: + unique_together = ("model", "object_id", "date") + indexes = [ + models.Index(fields=["model", "object_id", "date"]), + models.Index(fields=["date", "model"]), + ] + + @classmethod + def update_daily_metric(cls, model, object_id, data): + """ + Upsert method to update or create a daily metric. Should be generic enough for other metrics. + """ + return cls.objects.update_or_create( + model=model, + object_id=object_id, + date=now().date(), + defaults={"data": data}, + ) + + ########################### Secondary objects ######################### @@ -2405,12 +2433,36 @@ class Meta: verbose_name = _("Compliance assessment") verbose_name_plural = _("Compliance assessments") + def upsert_daily_metrics(self): + per_status = dict() + per_result = dict() + for item in self.get_requirements_status_count(): + per_status[item[1]] = item[0] + + for item in self.get_requirements_result_count(): + per_result[item[1]] = item[0] + total = RequirementAssessment.objects.filter(compliance_assessment=self).count() + data = { + "reqs": { + "total": total, + "per_status": per_status, + "per_result": per_result, + "progress_perc": self.progress(), + "score": self.get_global_score(), + }, + } + + HistoricalMetric.update_daily_metric( + model=self.__class__.__name__, object_id=self.id, data=data + ) + def save(self, *args, **kwargs) -> None: if self.min_score is None: self.min_score = self.framework.min_score self.max_score = self.framework.max_score self.scores_definition = self.framework.scores_definition super().save(*args, **kwargs) + self.upsert_daily_metrics() def create_requirement_assessments( self, baseline: Self | None = None @@ -2980,6 +3032,10 @@ class Meta: verbose_name = _("Requirement assessment") verbose_name_plural = _("Requirement assessments") + def save(self, *args, **kwargs) -> None: + super().save(*args, **kwargs) + self.compliance_assessment.upsert_daily_metrics() + ########################### RiskAcesptance is a domain object relying on secondary objects ######################### From 42c17599187ab27e85d163b5c37e33eb29ac33ba Mon Sep 17 00:00:00 2001 From: Axxiar Date: Fri, 22 Nov 2024 18:01:09 +0100 Subject: [PATCH 024/107] chore: serialize urn & parent_urn in requirement assessment --- backend/core/serializers.py | 1 + .../[id=uuid]/edit/+page.server.ts | 10 ++++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/backend/core/serializers.py b/backend/core/serializers.py index 68e5f8bd5..0020da471 100644 --- a/backend/core/serializers.py +++ b/backend/core/serializers.py @@ -613,6 +613,7 @@ class RequirementAssessmentReadSerializer(BaseModelSerializer): compliance_assessment = FieldsRelatedField() folder = FieldsRelatedField() assessable = serializers.BooleanField(source="requirement.assessable") + requirement = FieldsRelatedField(["urn", "parent_urn"]) class Meta: model = RequirementAssessment diff --git a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.server.ts b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.server.ts index a3fd98236..2be95e97e 100644 --- a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.server.ts +++ b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.server.ts @@ -29,12 +29,10 @@ export const load = (async ({ fetch, params }) => { } const requirementAssessment = await fetchJson(endpoint); - const [compliance_assessment_score, requirement] = await Promise.all([ - fetchJson( - `${baseUrl}/compliance-assessments/${requirementAssessment.compliance_assessment.id}/global_score/` - ), - fetchJson(`${baseUrl}/requirement-nodes/${requirementAssessment.requirement}/`) - ]); + const requirement = requirementAssessment.requirement; + const compliance_assessment_score = await fetchJson( + `${baseUrl}/compliance-assessments/${requirementAssessment.compliance_assessment.id}/global_score/` + ); const parent = await fetchJson( `${baseUrl}/requirement-nodes/?urn=${requirement.parent_urn}` From d18c0f4f3c32a71f8bbd5aa47f9532085367a1bf Mon Sep 17 00:00:00 2001 From: Abderrahmane Smimite Date: Fri, 22 Nov 2024 20:18:01 +0100 Subject: [PATCH 025/107] risk assessment tracking --- backend/core/models.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/backend/core/models.py b/backend/core/models.py index 99aaa8fa3..849a4b4b9 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -1836,9 +1836,38 @@ class Meta: verbose_name = _("Risk assessment") verbose_name_plural = _("Risk assessments") + def upsert_daily_metrics(self): + per_treatment = self.get_per_treatment() + + total = RiskScenario.objects.filter(risk_assessment=self).count() + data = { + "scenarios": { + "total": total, + "per_treatment": per_treatment, + }, + } + + HistoricalMetric.update_daily_metric( + model=self.__class__.__name__, object_id=self.id, data=data + ) + def __str__(self) -> str: return f"{self.name} - {self.version}" + def get_per_treatment(self) -> dict: + output = dict() + for treatment in RiskScenario.TREATMENT_OPTIONS: + output[treatment[0]] = ( + RiskScenario.objects.filter(risk_assessment=self) + .filter(treatment=treatment[0]) + .count() + ) + return output + + def save(self, *args, **kwargs) -> None: + super().save(*args, **kwargs) + self.upsert_daily_metrics() + @property def path_display(self) -> str: return f"{self.project.folder}/{self.project}/{self.name} - {self.version}" @@ -2413,6 +2442,7 @@ def save(self, *args, **kwargs): else: self.residual_level = -1 super(RiskScenario, self).save(*args, **kwargs) + self.risk_assessment.upsert_daily_metrics() class ComplianceAssessment(Assessment): From abe3a316434bd10a612fbe0977c64acb32578867 Mon Sep 17 00:00:00 2001 From: Abderrahmane Smimite Date: Fri, 22 Nov 2024 23:07:47 +0100 Subject: [PATCH 026/107] basic graph --- .../components/Chart/TimeSeriesChart.svelte | 89 +++++++++++++++++++ .../experimental/timeseries/+page.svelte | 11 +++ 2 files changed, 100 insertions(+) create mode 100644 frontend/src/lib/components/Chart/TimeSeriesChart.svelte create mode 100644 frontend/src/routes/(app)/(internal)/experimental/timeseries/+page.svelte diff --git a/frontend/src/lib/components/Chart/TimeSeriesChart.svelte b/frontend/src/lib/components/Chart/TimeSeriesChart.svelte new file mode 100644 index 000000000..d0a30000f --- /dev/null +++ b/frontend/src/lib/components/Chart/TimeSeriesChart.svelte @@ -0,0 +1,89 @@ + + +
          diff --git a/frontend/src/routes/(app)/(internal)/experimental/timeseries/+page.svelte b/frontend/src/routes/(app)/(internal)/experimental/timeseries/+page.svelte new file mode 100644 index 000000000..5ca69d020 --- /dev/null +++ b/frontend/src/routes/(app)/(internal)/experimental/timeseries/+page.svelte @@ -0,0 +1,11 @@ + + +
          +
          + +
          +
          From bbc981c6b694ea69de9823b6c4986ac5b5728a02 Mon Sep 17 00:00:00 2001 From: Abderrahmane Smimite Date: Sat, 23 Nov 2024 11:47:13 +0100 Subject: [PATCH 027/107] wip --- backend/core/views.py | 10 +++++++++ .../components/Chart/TimeSeriesChart.svelte | 22 +++++++------------ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/backend/core/views.py b/backend/core/views.py index 846345235..342739ed9 100644 --- a/backend/core/views.py +++ b/backend/core/views.py @@ -17,6 +17,8 @@ from pathlib import Path import humanize +from icecream import ic + from django.utils.decorators import method_decorator from django.views.decorators.cache import cache_page from django.views.decorators.vary import vary_on_cookie @@ -2200,6 +2202,14 @@ def create_suggested_applied_controls(request, pk): requirement_assessment.create_applied_controls_from_suggestions() return Response(status=status.HTTP_200_OK) + @action(detail=True, methods=["get"], url_path="progress_ts") + def progress_ts(self, request, pk): + data = HistoricalMetric.objects.filter( + model="ComplianceAssessment", object_id=pk + ) + ic(data) + return Response(status=status.HTTP_200_OK) + class RequirementAssessmentViewSet(BaseModelViewSet): """ diff --git a/frontend/src/lib/components/Chart/TimeSeriesChart.svelte b/frontend/src/lib/components/Chart/TimeSeriesChart.svelte index d0a30000f..97d77752f 100644 --- a/frontend/src/lib/components/Chart/TimeSeriesChart.svelte +++ b/frontend/src/lib/components/Chart/TimeSeriesChart.svelte @@ -14,9 +14,7 @@ // specify chart configuration item and data var option = { - title: { - text: 'Assessment progress' - }, + grid: { show: false }, tooltip: { trigger: 'axis', formatter: function (params) { @@ -32,22 +30,23 @@ }, xAxis: { type: 'time', - boundaryGap: false, - splitNumber: 3, // Approximate number of splits to show + splitNumber: 3, axisPointer: { snap: true } }, yAxis: { type: 'value', - boundaryGap: [0, '30%'] + boundaryGap: [0, '5%'], + splitLine: { show: false } }, series: [ { name: 'Requirements assessed', type: 'line', + symbol: 'none', areaStyle: { - opacity: 1, + opacity: 0.8, color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, @@ -62,13 +61,8 @@ smooth: true, data: [ [new Date('2024-01-01'), 0], - [new Date('2024-01-02'), 1], - [new Date('2024-01-03'), 4], - [new Date('2024-02-15'), 23], - [new Date('2024-03-01'), 41], - [new Date('2024-03-03'), 51], - [new Date('2024-03-10'), 60], - [new Date('2024-04-03'), 51] + [new Date('2024-01-02'), 10], + [new Date('2024-01-03'), 14] ] } ] From 2fdab93ac973f1f4f89c28d7c2b2a7d507e11f4a Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Sat, 23 Nov 2024 16:19:23 +0100 Subject: [PATCH 028/107] 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 029/107] 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 030/107] 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 031/107] 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 8db7e64c3ad5972e1b402a5b4a740e299c2736a5 Mon Sep 17 00:00:00 2001 From: Abderrahmane Smimite Date: Sat, 23 Nov 2024 19:10:32 +0100 Subject: [PATCH 032/107] wip --- backend/core/views.py | 27 +++++++++++++++---- .../components/Chart/TimeSeriesChart.svelte | 5 ++-- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/backend/core/views.py b/backend/core/views.py index 342739ed9..e6030ca6b 100644 --- a/backend/core/views.py +++ b/backend/core/views.py @@ -2204,11 +2204,28 @@ def create_suggested_applied_controls(request, pk): @action(detail=True, methods=["get"], url_path="progress_ts") def progress_ts(self, request, pk): - data = HistoricalMetric.objects.filter( - model="ComplianceAssessment", object_id=pk - ) - ic(data) - return Response(status=status.HTTP_200_OK) + try: + raw = ( + HistoricalMetric.objects.filter( + model="ComplianceAssessment", object_id=pk + ) + .annotate(progress=F("data__reqs__progress_perc")) + .values("date", "progress") + .order_by("date") + ) + + # Transform the data into the required format + formatted_data = [ + [entry["date"].isoformat(), entry["progress"]] for entry in raw + ] + + return Response({"data": formatted_data}) + + except HistoricalMetric.DoesNotExist: + return Response( + {"error": "No metrics found for this assessment"}, + status=status.HTTP_404_NOT_FOUND, + ) class RequirementAssessmentViewSet(BaseModelViewSet): diff --git a/frontend/src/lib/components/Chart/TimeSeriesChart.svelte b/frontend/src/lib/components/Chart/TimeSeriesChart.svelte index 97d77752f..30f53a76d 100644 --- a/frontend/src/lib/components/Chart/TimeSeriesChart.svelte +++ b/frontend/src/lib/components/Chart/TimeSeriesChart.svelte @@ -60,9 +60,8 @@ }, smooth: true, data: [ - [new Date('2024-01-01'), 0], - [new Date('2024-01-02'), 10], - [new Date('2024-01-03'), 14] + ['2024-11-22', 36], + ['2024-11-23', 37] ] } ] From edd2c9b3dcf6d9e8b43cca8b617a09551a6ed1a4 Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Sat, 23 Nov 2024 19:16:08 +0100 Subject: [PATCH 033/107] 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 034/107] 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 035/107] 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 036/107] 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 037/107] 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 038/107] 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 039/107] 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]}
          Date: Mon, 25 Nov 2024 14:14:55 +0100 Subject: [PATCH 040/107] feat: copy score and observation for full coverage --- backend/core/models.py | 66 +++++++++++++------ .../[id=uuid]/+page.svelte | 17 ----- .../[id=uuid]/edit/+page.svelte | 17 ----- 3 files changed, 45 insertions(+), 55 deletions(-) diff --git a/backend/core/models.py b/backend/core/models.py index 49739f90b..33105d301 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -2568,56 +2568,77 @@ def compute_requirement_assessments_results( RequirementAssessment.Result.PARTIALLY_COMPLIANT, RequirementAssessment.Result.COMPLIANT, ) + + def assign_attributes(target, attributes): + """ + Helper function to assign attributes to a target object. + Only assigns if the attribute is not None. + """ + keys = ["result", "status", "score", "is_scored", "observation"] + for key, value in zip(keys, attributes): + if value is not None: + setattr(target, key, value) + for requirement_assessment in self.requirement_assessments.all(): mappings = mapping_set.mappings.filter( target_requirement=requirement_assessment.requirement ) inferences = [] refs = [] + + # Filter for full coverage relationships if applicable if mappings.filter( relationship__in=RequirementMapping.FULL_COVERAGE_RELATIONSHIPS ).exists(): mappings = mappings.filter( relationship__in=RequirementMapping.FULL_COVERAGE_RELATIONSHIPS ) + for mapping in mappings: source_requirement_assessment = RequirementAssessment.objects.get( compliance_assessment=source_assessment, requirement=mapping.source_requirement, ) - inferred_result, inferred_status = requirement_assessment.infer_result( + inferred_result = requirement_assessment.infer_result( mapping=mapping, source_requirement_assessment=source_requirement_assessment, ) - if inferred_result in result_order: - inferences.append((inferred_result, inferred_status)) + if inferred_result.get("result") in result_order: + inferences.append( + ( + inferred_result.get("result"), + inferred_result.get("status"), + inferred_result.get("score"), + inferred_result.get("is_scored"), + inferred_result.get("observation"), + ) + ) refs.append(source_requirement_assessment) + if inferences: if len(inferences) == 1: - requirement_assessment.result = inferences[0][0] - if inferences[0][1]: - requirement_assessment.status = inferences[0][1] + selected_inference = inferences[0] ref = refs[0] else: - lowest_result = min( + selected_inference = min( inferences, key=lambda x: result_order.index(x[0]) ) - requirement_assessment.result = lowest_result[0] - if lowest_result[1]: - requirement_assessment.status = lowest_result[1] - ref = refs[inferences.index(lowest_result)] + ref = refs[inferences.index(selected_inference)] + + assign_attributes(requirement_assessment, selected_inference) + requirement_assessment.mapping_inference = { "result": requirement_assessment.result, "source_requirement_assessment": { "str": str(ref), "id": str(ref.id), "coverage": mapping.coverage, - "score": ref.score, - "observation": ref.observation, }, # "mappings": [mapping.id for mapping in mappings], } + requirement_assessments.append(requirement_assessment) + return requirement_assessments def progress(self) -> int: @@ -2715,24 +2736,27 @@ def get_requirement_description(self) -> str: def infer_result( self, mapping: RequirementMapping, source_requirement_assessment: Self - ) -> str | None: + ) -> dict | None: if mapping.coverage == RequirementMapping.Coverage.FULL: - return ( - source_requirement_assessment.result, - source_requirement_assessment.status, - ) + return { + "result": source_requirement_assessment.result, + "status": source_requirement_assessment.status, + "score": source_requirement_assessment.score, + "is_scored": source_requirement_assessment.is_scored, + "observation": source_requirement_assessment.observation, + } if mapping.coverage == RequirementMapping.Coverage.PARTIAL: if source_requirement_assessment.result in ( RequirementAssessment.Result.COMPLIANT, RequirementAssessment.Result.PARTIALLY_COMPLIANT, ): - return (RequirementAssessment.Result.PARTIALLY_COMPLIANT, None) + return {"result": RequirementAssessment.Result.PARTIALLY_COMPLIANT} if ( source_requirement_assessment.result == RequirementAssessment.Result.NON_COMPLIANT ): - return (RequirementAssessment.Result.NON_COMPLIANT, None) - return (None, None) + return {"result": RequirementAssessment.Result.NON_COMPLIANT} + return {} def create_applied_controls_from_suggestions(self) -> list[AppliedControl]: applied_controls: list[AppliedControl] = [] diff --git a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/+page.svelte b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/+page.svelte index 5cf149894..f93a8e3fc 100644 --- a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/+page.svelte +++ b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/+page.svelte @@ -223,23 +223,6 @@ {safeTranslate(mappingInference.result)}

          - {#if mappingInference.sourceRequirementAssessment.score} -

          - {m.scoreSemiColon()} - - {mappingInference.sourceRequirementAssessment.score} - -

          - {/if} - {#if mappingInference.sourceRequirementAssessment.observation} -

          - {m.observationSemiColon()} - {mappingInference.sourceRequirementAssessment.observation} -

          - {/if} {#if mappingInference.annotation}

          {m.annotationColon()} diff --git a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte index d8c801152..38a1039ea 100644 --- a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte +++ b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte @@ -356,23 +356,6 @@ {safeTranslate(mappingInference.result)}

          - {#if mappingInference.sourceRequirementAssessment.score} -

          - {m.scoreSemiColon()} - - {mappingInference.sourceRequirementAssessment.score} - -

          - {/if} - {#if mappingInference.sourceRequirementAssessment.observation} -

          - {m.observationSemiColon()} - {mappingInference.sourceRequirementAssessment.observation} -

          - {/if} {#if mappingInference.annotation}

          {m.annotationColon()} From 0db4d10c1dbdfe36b4375f6ecba1c36d12f1abd6 Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Mon, 25 Nov 2024 15:54:52 +0100 Subject: [PATCH 041/107] feat: add community update script --- update-ciso-assistant.sh | 61 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100755 update-ciso-assistant.sh diff --git a/update-ciso-assistant.sh b/update-ciso-assistant.sh new file mode 100755 index 000000000..354dbe958 --- /dev/null +++ b/update-ciso-assistant.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +# Script to update Docker images for CISO Assistant (always using 'latest') + +# Variables +DB_FILE="db/ciso-assistant.sqlite3" +BACKUP_FILE="ciso-assistant-backup.sqlite3" +BACKEND_IMAGE="ghcr.io/intuitem/ciso-assistant-community/backend:latest" +FRONTEND_IMAGE="ghcr.io/intuitem/ciso-assistant-community/frontend:latest" + +echo "Using backend image: $BACKEND_IMAGE" +echo "Using frontend image: $FRONTEND_IMAGE" + +# Backup the database +if [ -f "$DB_FILE" ]; then + echo "Backing up the database file..." + cp "$DB_FILE" "$BACKUP_FILE" + if [ $? -eq 0 ]; then + echo "Database backed up to $BACKUP_FILE." + else + echo "Error: Failed to back up the database file." + exit 1 + fi +else + echo "Error: Database file $DB_FILE does not exist. Skipping backup." + exit 1 +fi + +# Stop running containers +echo "Stopping Docker containers..." +docker compose stop + +# Remove existing container instances +echo "Removing container instances..." +docker compose rm -f + +# Remove old images (any version) +echo "Removing all old backend and frontend images..." +docker images | grep "ghcr.io/intuitem/ciso-assistant-community/backend" | awk '{print $3}' | xargs -r docker rmi -f +docker images | grep "ghcr.io/intuitem/ciso-assistant-community/frontend" | awk '{print $3}' | xargs -r docker rmi -f + +# Start containers with updated images +echo "Starting Docker containers with updated images..." +docker compose up -d + +# Restore the database +if [ -f "$BACKUP_FILE" ]; then + echo "Restoring the database file..." + cp "$BACKUP_FILE" "$DB_FILE" + if [ $? -eq 0 ]; then + echo "Database restored to $DB_FILE." + else + echo "Error: Failed to restore the database file." + exit 1 + fi +else + echo "Error: Backup file $BACKUP_FILE does not exist. Unable to restore the database." + exit 1 +fi + +echo "Update and restoration process completed successfully." From 28bc24f3d2203bdec88d6d175d8e08688a2b7a82 Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Mon, 25 Nov 2024 16:00:08 +0100 Subject: [PATCH 042/107] fix: codefactor --- update-ciso-assistant.sh | 57 +++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/update-ciso-assistant.sh b/update-ciso-assistant.sh index 354dbe958..48db16120 100755 --- a/update-ciso-assistant.sh +++ b/update-ciso-assistant.sh @@ -4,7 +4,7 @@ # Variables DB_FILE="db/ciso-assistant.sqlite3" -BACKUP_FILE="ciso-assistant-backup.sqlite3" +BACKUP_FILE="../ciso-assistant-backup.sqlite3" BACKEND_IMAGE="ghcr.io/intuitem/ciso-assistant-community/backend:latest" FRONTEND_IMAGE="ghcr.io/intuitem/ciso-assistant-community/frontend:latest" @@ -14,8 +14,7 @@ echo "Using frontend image: $FRONTEND_IMAGE" # Backup the database if [ -f "$DB_FILE" ]; then echo "Backing up the database file..." - cp "$DB_FILE" "$BACKUP_FILE" - if [ $? -eq 0 ]; then + if cp "$DB_FILE" "$BACKUP_FILE"; then echo "Database backed up to $BACKUP_FILE." else echo "Error: Failed to back up the database file." @@ -28,26 +27,60 @@ fi # Stop running containers echo "Stopping Docker containers..." -docker compose stop +if docker compose stop; then + echo "Containers stopped successfully." +else + echo "Error: Failed to stop Docker containers." + exit 1 +fi # Remove existing container instances echo "Removing container instances..." -docker compose rm -f +if docker compose rm -f; then + echo "Container instances removed successfully." +else + echo "Error: Failed to remove container instances." + exit 1 +fi # Remove old images (any version) echo "Removing all old backend and frontend images..." -docker images | grep "ghcr.io/intuitem/ciso-assistant-community/backend" | awk '{print $3}' | xargs -r docker rmi -f -docker images | grep "ghcr.io/intuitem/ciso-assistant-community/frontend" | awk '{print $3}' | xargs -r docker rmi -f +if docker images | grep "ghcr.io/intuitem/ciso-assistant-community/backend" | awk '{print $3}' | xargs -r docker rmi -f; then + echo "Old backend images removed successfully." +else + echo "Error: Failed to remove old backend images." + exit 1 +fi + +if docker images | grep "ghcr.io/intuitem/ciso-assistant-community/frontend" | awk '{print $3}' | xargs -r docker rmi -f; then + echo "Old frontend images removed successfully." +else + echo "Error: Failed to remove old frontend images." + exit 1 +fi + +# Pull the latest images +echo "Pulling latest images..." +if docker pull "$BACKEND_IMAGE" && docker pull "$FRONTEND_IMAGE"; then + echo "Images pulled successfully." +else + echo "Error: Failed to pull the latest images." + exit 1 +fi -# Start containers with updated images +# Start containers with updated images echo "Starting Docker containers with updated images..." -docker compose up -d +if docker compose up -d; then + echo "Containers started successfully." +else + echo "Error: Failed to start Docker containers." + exit 1 +fi # Restore the database if [ -f "$BACKUP_FILE" ]; then echo "Restoring the database file..." - cp "$BACKUP_FILE" "$DB_FILE" - if [ $? -eq 0 ]; then + if cp "$BACKUP_FILE" "$DB_FILE"; then echo "Database restored to $DB_FILE." else echo "Error: Failed to restore the database file." @@ -58,4 +91,4 @@ else exit 1 fi -echo "Update and restoration process completed successfully." +echo "Update and restoration process completed From 3bb80f22b8d1f79fc94d2545ebee41b9cd7f9251 Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Mon, 25 Nov 2024 16:22:36 +0100 Subject: [PATCH 043/107] chore: remove db restoration --- update-ciso-assistant.sh | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/update-ciso-assistant.sh b/update-ciso-assistant.sh index 48db16120..57456ad80 100755 --- a/update-ciso-assistant.sh +++ b/update-ciso-assistant.sh @@ -77,18 +77,4 @@ else exit 1 fi -# Restore the database -if [ -f "$BACKUP_FILE" ]; then - echo "Restoring the database file..." - if cp "$BACKUP_FILE" "$DB_FILE"; then - echo "Database restored to $DB_FILE." - else - echo "Error: Failed to restore the database file." - exit 1 - fi -else - echo "Error: Backup file $BACKUP_FILE does not exist. Unable to restore the database." - exit 1 -fi - echo "Update and restoration process completed From 6343c5c4a81f653773934c79ea8fb10f7e61d1ca Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Mon, 25 Nov 2024 16:31:19 +0100 Subject: [PATCH 044/107] chore: improve end message --- update-ciso-assistant.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/update-ciso-assistant.sh b/update-ciso-assistant.sh index 57456ad80..c37960d53 100755 --- a/update-ciso-assistant.sh +++ b/update-ciso-assistant.sh @@ -77,4 +77,4 @@ else exit 1 fi -echo "Update and restoration process completed +echo "Update process completed From 4a83c747821cdc76c6a965795a14d35718fdf6ef Mon Sep 17 00:00:00 2001 From: Abderrahmane Smimite Date: Tue, 26 Nov 2024 00:34:08 +0100 Subject: [PATCH 045/107] wip --- 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 e6030ca6b..a4f871c8a 100644 --- a/backend/core/views.py +++ b/backend/core/views.py @@ -17,7 +17,7 @@ from pathlib import Path import humanize -from icecream import ic +# from icecream import ic from django.utils.decorators import method_decorator from django.views.decorators.cache import cache_page From 4a640e6176c81d80504cbafbd9895bfd1d7547a6 Mon Sep 17 00:00:00 2001 From: Abderrahmane Smimite Date: Tue, 26 Nov 2024 09:39:13 +0100 Subject: [PATCH 046/107] Update migrations order --- .../{0039_historicalmetric.py => 0041_historicalmetric.py} | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) rename backend/core/migrations/{0039_historicalmetric.py => 0041_historicalmetric.py} (92%) diff --git a/backend/core/migrations/0039_historicalmetric.py b/backend/core/migrations/0041_historicalmetric.py similarity index 92% rename from backend/core/migrations/0039_historicalmetric.py rename to backend/core/migrations/0041_historicalmetric.py index 3dc6a94bd..1c8d0f05f 100644 --- a/backend/core/migrations/0039_historicalmetric.py +++ b/backend/core/migrations/0041_historicalmetric.py @@ -1,11 +1,12 @@ -# Generated by Django 5.1.1 on 2024-11-22 14:38 +# Generated by Django 5.1.1 on 2024-11-26 08:38 from django.db import migrations, models class Migration(migrations.Migration): + dependencies = [ - ("core", "0038_asset_disaster_recovery_objectives_and_more"), + ("core", "0040_riskscenario_ref_id"), ] operations = [ From 2c7d89d301bca5f53dc3b0ecddc82562108db9bd Mon Sep 17 00:00:00 2001 From: Abderrahmane Smimite Date: Tue, 26 Nov 2024 09:46:29 +0100 Subject: [PATCH 047/107] Formatting --- backend/core/migrations/0041_historicalmetric.py | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/core/migrations/0041_historicalmetric.py b/backend/core/migrations/0041_historicalmetric.py index 1c8d0f05f..49fcf6f98 100644 --- a/backend/core/migrations/0041_historicalmetric.py +++ b/backend/core/migrations/0041_historicalmetric.py @@ -4,7 +4,6 @@ class Migration(migrations.Migration): - dependencies = [ ("core", "0040_riskscenario_ref_id"), ] From 5989866aa8ec7f5a367367dafd4a1a643d81d7ba Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Tue, 26 Nov 2024 11:16:51 +0100 Subject: [PATCH 048/107] chore: optimization --- update-ciso-assistant.sh | 80 ++++++---------------------------------- 1 file changed, 12 insertions(+), 68 deletions(-) diff --git a/update-ciso-assistant.sh b/update-ciso-assistant.sh index c37960d53..dc585fe84 100755 --- a/update-ciso-assistant.sh +++ b/update-ciso-assistant.sh @@ -1,80 +1,24 @@ #!/bin/bash -# Script to update Docker images for CISO Assistant (always using 'latest') - # Variables DB_FILE="db/ciso-assistant.sqlite3" BACKUP_FILE="../ciso-assistant-backup.sqlite3" BACKEND_IMAGE="ghcr.io/intuitem/ciso-assistant-community/backend:latest" FRONTEND_IMAGE="ghcr.io/intuitem/ciso-assistant-community/frontend:latest" -echo "Using backend image: $BACKEND_IMAGE" -echo "Using frontend image: $FRONTEND_IMAGE" - -# Backup the database -if [ -f "$DB_FILE" ]; then - echo "Backing up the database file..." - if cp "$DB_FILE" "$BACKUP_FILE"; then - echo "Database backed up to $BACKUP_FILE." - else - echo "Error: Failed to back up the database file." - exit 1 - fi -else - echo "Error: Database file $DB_FILE does not exist. Skipping backup." - exit 1 -fi - -# Stop running containers -echo "Stopping Docker containers..." -if docker compose stop; then - echo "Containers stopped successfully." -else - echo "Error: Failed to stop Docker containers." - exit 1 -fi - -# Remove existing container instances -echo "Removing container instances..." -if docker compose rm -f; then - echo "Container instances removed successfully." -else - echo "Error: Failed to remove container instances." - exit 1 -fi - -# Remove old images (any version) -echo "Removing all old backend and frontend images..." -if docker images | grep "ghcr.io/intuitem/ciso-assistant-community/backend" | awk '{print $3}' | xargs -r docker rmi -f; then - echo "Old backend images removed successfully." -else - echo "Error: Failed to remove old backend images." - exit 1 -fi +# Backup database +[ -f "$DB_FILE" ] && cp "$DB_FILE" "$BACKUP_FILE" || { echo "Database not found. Skipping."; exit 1; } -if docker images | grep "ghcr.io/intuitem/ciso-assistant-community/frontend" | awk '{print $3}' | xargs -r docker rmi -f; then - echo "Old frontend images removed successfully." -else - echo "Error: Failed to remove old frontend images." - exit 1 -fi +# Update Docker images +docker compose down +docker images | grep -E "ghcr.io/intuitem/ciso-assistant-community/(backend|frontend).*latest" | awk '{print $3}' | xargs -r docker rmi -f +docker pull "$BACKEND_IMAGE" "$FRONTEND_IMAGE" -# Pull the latest images -echo "Pulling latest images..." -if docker pull "$BACKEND_IMAGE" && docker pull "$FRONTEND_IMAGE"; then - echo "Images pulled successfully." -else - echo "Error: Failed to pull the latest images." - exit 1 -fi +# Start containers +docker compose up -d -# Start containers with updated images -echo "Starting Docker containers with updated images..." -if docker compose up -d; then - echo "Containers started successfully." -else - echo "Error: Failed to start Docker containers." - exit 1 -fi +# Run Django migrations +BACKEND_CONTAINER=$(docker ps --filter "ancestor=$BACKEND_IMAGE" --format "{{.ID}}") +[ -n "$BACKEND_CONTAINER" ] && docker exec "$BACKEND_CONTAINER" poetry run python manage.py migrate || { echo "Migration failed."; exit 1; } -echo "Update process completed +echo "Update and migration completed successfully." From b922d9aeffdb8294d70ceb48b45d604543d6ffb3 Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Tue, 26 Nov 2024 15:02:28 +0100 Subject: [PATCH 049/107] feat: improve update script --- docker-compose.yml | 4 +- update-ciso-assistant.sh | 85 ++++++++++++++++++++++++++++++++++------ 2 files changed, 75 insertions(+), 14 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index d723deee3..6b070a695 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: backend: container_name: backend - image: ghcr.io/intuitem/ciso-assistant-community/backend:latest + image: ghcr.io/intuitem/ciso-assistant-community/backend:${VERSION:-latest} restart: always environment: - ALLOWED_HOSTS=backend,localhost @@ -19,7 +19,7 @@ services: - PROTOCOL_HEADER=x-forwarded-proto - HOST_HEADER=x-forwarded-host - image: ghcr.io/intuitem/ciso-assistant-community/frontend:latest + image: ghcr.io/intuitem/ciso-assistant-community/frontend:${VERSION:-latest} depends_on: - backend diff --git a/update-ciso-assistant.sh b/update-ciso-assistant.sh index dc585fe84..bb5ff9451 100755 --- a/update-ciso-assistant.sh +++ b/update-ciso-assistant.sh @@ -2,23 +2,84 @@ # Variables DB_FILE="db/ciso-assistant.sqlite3" -BACKUP_FILE="../ciso-assistant-backup.sqlite3" -BACKEND_IMAGE="ghcr.io/intuitem/ciso-assistant-community/backend:latest" -FRONTEND_IMAGE="ghcr.io/intuitem/ciso-assistant-community/frontend:latest" +BACKUP_DIR="../ciso-assistant-backups" +VERSION=${1:-latest} +CURRENT_VERSION=$(docker ps -a --format "{{.Image}}" | grep "ghcr.io/intuitem/ciso-assistant-community/backend" | head -n 1 | cut -d ':' -f2 || echo "latest") +BACKUP_FILE="$BACKUP_DIR/ciso-assistant-backup-$CURRENT_VERSION.sqlite3" +RESTORE_BACKUP_FILE="$BACKUP_DIR/ciso-assistant-backup-$VERSION.sqlite3" +BACKEND_IMAGE="ghcr.io/intuitem/ciso-assistant-community/backend:$VERSION" +FRONTEND_IMAGE="ghcr.io/intuitem/ciso-assistant-community/frontend:$VERSION" -# Backup database -[ -f "$DB_FILE" ] && cp "$DB_FILE" "$BACKUP_FILE" || { echo "Database not found. Skipping."; exit 1; } +echo "Using backend image: $BACKEND_IMAGE" +echo "Using frontend image: $FRONTEND_IMAGE" -# Update Docker images +# Create backup directory if it doesn't exist +if [ ! -d "$BACKUP_DIR" ]; then + echo "Creating backup directory at $BACKUP_DIR..." + mkdir -p "$BACKUP_DIR" || { echo "Error: Failed to create backup directory."; exit 1; } + echo "Backup directory created." +fi + +# Backup the current database (overwrite if exists) +if [ -f "$DB_FILE" ]; then + echo "Backing up the database for current version '$CURRENT_VERSION' (overwriting existing backup)..." + cp "$DB_FILE" "$BACKUP_FILE" || { echo "Error: Database backup failed."; exit 1; } + echo "Database backed up to $BACKUP_FILE." +else + echo "Database file not found. Skipping backup." +fi + +# Stop and remove containers (after backup) docker compose down -docker images | grep -E "ghcr.io/intuitem/ciso-assistant-community/(backend|frontend).*latest" | awk '{print $3}' | xargs -r docker rmi -f -docker pull "$BACKEND_IMAGE" "$FRONTEND_IMAGE" -# Start containers -docker compose up -d +# Remove all images +docker images | grep -E "ghcr.io/intuitem/ciso-assistant-community/(backend|frontend)" | awk '{print $3}' | xargs -r docker rmi -f + +# Choice to restore the database from the selected version's backup if it exists +if [ -f "$RESTORE_BACKUP_FILE" ]; then + read -p "Backup found for version '$VERSION'. Do you want to restore the database from this backup? (y/n): " restore_choice + if [[ "$restore_choice" =~ ^[Yy]$ ]]; then + echo "Restoring database from backup for version '$VERSION'..." + cp "$RESTORE_BACKUP_FILE" "$DB_FILE" || { echo "Error: Database restoration failed."; exit 1; } + echo "Database restored from $RESTORE_BACKUP_FILE." + else + echo "Skipping database restoration." + if [ "$(echo -e "$VERSION\n$CURRENT_VERSION" | sort -V | head -n 1)" == "$CURRENT_VERSION" ] && ["$VERSION" != "$CURRENT_VERSION"]; then + NEW_DB=true + fi + fi +else + echo "No backup found for version '$VERSION'. Proceeding with a new database." + NEW_DB=true +fi + +# If using a version other than 'latest', remove the database to avoid migration conflicts (only if NEW_DB is not true) +if [ "$NEW_DB" = true ]; then + echo "Removing database file to create a fresh one for version '$VERSION'..." + rm -f "$DB_FILE" || { echo "Error: Failed to remove database file."; exit 1; } + echo "Database file removed." +fi + +# Pull and run the specified version +docker pull "$BACKEND_IMAGE" "$FRONTEND_IMAGE" +VERSION=$VERSION docker compose up -d # Run Django migrations BACKEND_CONTAINER=$(docker ps --filter "ancestor=$BACKEND_IMAGE" --format "{{.ID}}") -[ -n "$BACKEND_CONTAINER" ] && docker exec "$BACKEND_CONTAINER" poetry run python manage.py migrate || { echo "Migration failed."; exit 1; } +if [ -n "$BACKEND_CONTAINER" ]; then + echo "Running Django migrations..." + docker exec "$BACKEND_CONTAINER" poetry run python manage.py migrate || docker exec "$BACKEND_CONTAINER" python manage.py migrate || { echo "Error: Migration failed."; exit 1; } + echo "Migrations completed successfully." + + # Create superuser interactively if the database is new and the version is older + if [ "$NEW_DB" = true ]; then + echo "Initializing your superuser account..." + docker compose exec backend poetry run python manage.py createsuperuser || docker exec -it "$BACKEND_CONTAINER" python manage.py createsuperuser || { echo "Error: Failed to create superuser."; exit 1; } + echo "Superuser created successfully." + fi +else + echo "Error: Backend container not found. Cannot run migrations or create superuser." + exit 1 +fi -echo "Update and migration completed successfully." +echo "Update and migration for version '$VERSION' completed successfully." From 1c8c342666a0c90df139f85c1796472a300b3bda Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Tue, 26 Nov 2024 15:07:11 +0100 Subject: [PATCH 050/107] docs: write update-ciso-assistant manual --- update-ciso-assistant.sh | 52 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/update-ciso-assistant.sh b/update-ciso-assistant.sh index bb5ff9451..bf891e442 100755 --- a/update-ciso-assistant.sh +++ b/update-ciso-assistant.sh @@ -1,5 +1,57 @@ #!/bin/bash +# --------------------------------------------------------------------------- +# Manual: Database Backup and Update Script for CISO Assistant +# --------------------------------------------------------------------------- +# This script is used to backup, restore, and update the CISO Assistant database +# and Docker containers. It supports database backup and restoration, +# version migration, and Docker image management. Below is a detailed guide +# on how the script works and how to use it. +# +# Usage: +# ./update-ciso-assistant.sh [OPTIONS] +# +# Options: +# --help, -h Display this help message +# [version] Specify the version of CISO Assistant to update to (default: 'latest') +# +# Example: +# ./update-ciso-assistant.sh 1.0.0 +# This will update CISO Assistant to version 1.0.0, backing up and restoring +# the database as needed. +# +# Prerequisites: +# - Docker should be installed and running +# - Docker Compose should be available +# - Poetry (if used for the backend) should be available in the Docker container +# +# --------------------------------------------------------------------------- + +# Display help message if --help or -h is passed as an argument +if [[ "$1" == "--help" || "$1" == "-h" ]]; then + cat < Date: Tue, 26 Nov 2024 15:08:53 +0100 Subject: [PATCH 051/107] chore: add missing spaces --- update-ciso-assistant.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/update-ciso-assistant.sh b/update-ciso-assistant.sh index bf891e442..cea9b7f95 100755 --- a/update-ciso-assistant.sh +++ b/update-ciso-assistant.sh @@ -96,7 +96,7 @@ if [ -f "$RESTORE_BACKUP_FILE" ]; then echo "Database restored from $RESTORE_BACKUP_FILE." else echo "Skipping database restoration." - if [ "$(echo -e "$VERSION\n$CURRENT_VERSION" | sort -V | head -n 1)" == "$CURRENT_VERSION" ] && ["$VERSION" != "$CURRENT_VERSION"]; then + if [ "$(echo -e "$VERSION\n$CURRENT_VERSION" | sort -V | head -n 1)" == "$CURRENT_VERSION" ] && [ "$VERSION" != "$CURRENT_VERSION" ]; then NEW_DB=true fi fi From 7f4c487be3ab81d3e63b9541ba7cba151e1d97a4 Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Wed, 27 Nov 2024 12:10:06 +0100 Subject: [PATCH 052/107] style: fix coverage text color in additional information --- .../requirement-assessments/[id=uuid]/+page.svelte | 2 +- .../requirement-assessments/[id=uuid]/edit/+page.svelte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/+page.svelte b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/+page.svelte index f93a8e3fc..33163029d 100644 --- a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/+page.svelte +++ b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/+page.svelte @@ -208,7 +208,7 @@

          {m.coverageColon()} - + {safeTranslate( toCamelCase(mappingInference.sourceRequirementAssessment.coverage) )} diff --git a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte index 38a1039ea..8b247aecc 100644 --- a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte +++ b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte @@ -343,7 +343,7 @@

          {m.coverageColon()} - + {safeTranslate(mappingInference.sourceRequirementAssessment.coverage)}

          From 52b3c0c78251b1d51429a3ca2734409a1b189d47 Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Wed, 27 Nov 2024 12:38:37 +0100 Subject: [PATCH 053/107] feat: apply mapping for not assessed/applicable requirements --- backend/core/models.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/backend/core/models.py b/backend/core/models.py index 5f0a25184..2751460d9 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -2778,6 +2778,8 @@ def compute_requirement_assessments_results( ) -> list["RequirementAssessment"]: requirement_assessments: list[RequirementAssessment] = [] result_order = ( + RequirementAssessment.Result.NOT_ASSESSED, + RequirementAssessment.Result.NOT_APPLICABLE, RequirementAssessment.Result.NON_COMPLIANT, RequirementAssessment.Result.PARTIALLY_COMPLIANT, RequirementAssessment.Result.COMPLIANT, @@ -2952,13 +2954,21 @@ def infer_result( self, mapping: RequirementMapping, source_requirement_assessment: Self ) -> dict | None: if mapping.coverage == RequirementMapping.Coverage.FULL: - return { - "result": source_requirement_assessment.result, - "status": source_requirement_assessment.status, - "score": source_requirement_assessment.score, - "is_scored": source_requirement_assessment.is_scored, - "observation": source_requirement_assessment.observation, - } + if (source_requirement_assessment.compliance_assessment.min_score == self.compliance_assessment.min_score and + source_requirement_assessment.compliance_assessment.max_score == self.compliance_assessment.max_score): + return { + "result": source_requirement_assessment.result, + "status": source_requirement_assessment.status, + "score": source_requirement_assessment.score, + "is_scored": source_requirement_assessment.is_scored, + "observation": source_requirement_assessment.observation, + } + else: + return { + "result": source_requirement_assessment.result, + "status": source_requirement_assessment.status, + "observation": source_requirement_assessment.observation, + } if mapping.coverage == RequirementMapping.Coverage.PARTIAL: if source_requirement_assessment.result in ( RequirementAssessment.Result.COMPLIANT, From 18e283d47bbd59cb1d4cb8c9e101f3ff729e8009 Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Wed, 27 Nov 2024 12:38:55 +0100 Subject: [PATCH 054/107] chore: run format --- backend/core/models.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/backend/core/models.py b/backend/core/models.py index 2751460d9..188719ea2 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -2954,21 +2954,25 @@ def infer_result( self, mapping: RequirementMapping, source_requirement_assessment: Self ) -> dict | None: if mapping.coverage == RequirementMapping.Coverage.FULL: - if (source_requirement_assessment.compliance_assessment.min_score == self.compliance_assessment.min_score and - source_requirement_assessment.compliance_assessment.max_score == self.compliance_assessment.max_score): - return { - "result": source_requirement_assessment.result, - "status": source_requirement_assessment.status, - "score": source_requirement_assessment.score, - "is_scored": source_requirement_assessment.is_scored, - "observation": source_requirement_assessment.observation, - } - else: - return { - "result": source_requirement_assessment.result, - "status": source_requirement_assessment.status, - "observation": source_requirement_assessment.observation, - } + if ( + source_requirement_assessment.compliance_assessment.min_score + == self.compliance_assessment.min_score + and source_requirement_assessment.compliance_assessment.max_score + == self.compliance_assessment.max_score + ): + return { + "result": source_requirement_assessment.result, + "status": source_requirement_assessment.status, + "score": source_requirement_assessment.score, + "is_scored": source_requirement_assessment.is_scored, + "observation": source_requirement_assessment.observation, + } + else: + return { + "result": source_requirement_assessment.result, + "status": source_requirement_assessment.status, + "observation": source_requirement_assessment.observation, + } if mapping.coverage == RequirementMapping.Coverage.PARTIAL: if source_requirement_assessment.result in ( RequirementAssessment.Result.COMPLIANT, From f02526830babd5d9a38170f09e88fe349c6ec8db Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Wed, 27 Nov 2024 13:58:00 +0100 Subject: [PATCH 055/107] feat: add enterprise update and make script simpler --- .../update-ciso-assistant-enterprise.sh | 1 + update-ciso-assistant.sh | 151 ++++-------------- 2 files changed, 32 insertions(+), 120 deletions(-) create mode 100755 enterprise/update-ciso-assistant-enterprise.sh diff --git a/enterprise/update-ciso-assistant-enterprise.sh b/enterprise/update-ciso-assistant-enterprise.sh new file mode 100755 index 000000000..d8ec56384 --- /dev/null +++ b/enterprise/update-ciso-assistant-enterprise.sh @@ -0,0 +1 @@ +./update-ciso-assistant.sh enterprise \ No newline at end of file diff --git a/update-ciso-assistant.sh b/update-ciso-assistant.sh index cea9b7f95..ea7b67eb5 100755 --- a/update-ciso-assistant.sh +++ b/update-ciso-assistant.sh @@ -1,137 +1,48 @@ -#!/bin/bash +#! /usr/bin/env bash -# --------------------------------------------------------------------------- -# Manual: Database Backup and Update Script for CISO Assistant -# --------------------------------------------------------------------------- -# This script is used to backup, restore, and update the CISO Assistant database -# and Docker containers. It supports database backup and restoration, -# version migration, and Docker image management. Below is a detailed guide -# on how the script works and how to use it. -# -# Usage: -# ./update-ciso-assistant.sh [OPTIONS] -# -# Options: -# --help, -h Display this help message -# [version] Specify the version of CISO Assistant to update to (default: 'latest') -# -# Example: -# ./update-ciso-assistant.sh 1.0.0 -# This will update CISO Assistant to version 1.0.0, backing up and restoring -# the database as needed. -# -# Prerequisites: -# - Docker should be installed and running -# - Docker Compose should be available -# - Poetry (if used for the backend) should be available in the Docker container -# -# --------------------------------------------------------------------------- +VERSION=${1:-community} -# Display help message if --help or -h is passed as an argument -if [[ "$1" == "--help" || "$1" == "-h" ]]; then - cat < /dev/null -# Run Django migrations -BACKEND_CONTAINER=$(docker ps --filter "ancestor=$BACKEND_IMAGE" --format "{{.ID}}") -if [ -n "$BACKEND_CONTAINER" ]; then - echo "Running Django migrations..." - docker exec "$BACKEND_CONTAINER" poetry run python manage.py migrate || docker exec "$BACKEND_CONTAINER" python manage.py migrate || { echo "Error: Migration failed."; exit 1; } - echo "Migrations completed successfully." +# Start the containers +docker compose up -d - # Create superuser interactively if the database is new and the version is older - if [ "$NEW_DB" = true ]; then - echo "Initializing your superuser account..." - docker compose exec backend poetry run python manage.py createsuperuser || docker exec -it "$BACKEND_CONTAINER" python manage.py createsuperuser || { echo "Error: Failed to create superuser."; exit 1; } - echo "Superuser created successfully." - fi -else - echo "Error: Backend container not found. Cannot run migrations or create superuser." - exit 1 -fi +# Wait for the database to be ready +echo "Giving some time for the database to be ready, please wait ..." +sleep 5 -echo "Update and migration for version '$VERSION' completed successfully." +# Apply migrations +BACKEND_CONTAINER=$(docker ps --filter "ancestor=$BACKEND_IMAGE" --format "{{.Names}}") +docker compose exec "$BACKEND_CONTAINER" poetry run python manage.py migrate +echo "CISO assistant updated successfully" From b78d8ca7c8bd0f25ae04ae68dde3a12c17d6ba51 Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Wed, 27 Nov 2024 13:59:01 +0100 Subject: [PATCH 056/107] chore: undo docker compose yaml modifications --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 6b070a695..d723deee3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: backend: container_name: backend - image: ghcr.io/intuitem/ciso-assistant-community/backend:${VERSION:-latest} + image: ghcr.io/intuitem/ciso-assistant-community/backend:latest restart: always environment: - ALLOWED_HOSTS=backend,localhost @@ -19,7 +19,7 @@ services: - PROTOCOL_HEADER=x-forwarded-proto - HOST_HEADER=x-forwarded-host - image: ghcr.io/intuitem/ciso-assistant-community/frontend:${VERSION:-latest} + image: ghcr.io/intuitem/ciso-assistant-community/frontend:latest depends_on: - backend From 7211fe8fdcd4b8e66ba7fc6c708f4c50762e3464 Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Wed, 27 Nov 2024 16:09:42 +0100 Subject: [PATCH 057/107] feat: serialize requirement node inside requirement assessment --- backend/core/models.py | 34 +++++++++++++++++++ backend/core/serializers.py | 2 +- .../[id=uuid]/edit/+page.server.ts | 6 ++-- .../[id=uuid]/edit/+page.svelte | 8 ++--- 4 files changed, 42 insertions(+), 8 deletions(-) diff --git a/backend/core/models.py b/backend/core/models.py index abc4048ae..ef37115a1 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -1048,6 +1048,40 @@ class RequirementNode(ReferentialObjectMixin, I18nObjectMixin): null=True, blank=True, verbose_name=_("Typical evidence") ) question = models.JSONField(blank=True, null=True, verbose_name=_("Question")) + + @property + def associated_reference_controls(self): + _reference_controls = self.reference_controls.all() + reference_controls = [] + for control in _reference_controls: + reference_controls.append( + {"str": control.display_long, "urn": control.urn, "id": control.id} + ) + return reference_controls + + @property + def associated_threats(self): + _threats = self.threats.all() + threats = [] + for control in _threats: + threats.append( + {"str": control.display_long, "urn": control.urn, "id": control.id} + ) + return threats + + @property + def parent_requirement(self): + parent_requirement = RequirementNode.objects.filter(urn=self.parent_urn).first() + if not parent_requirement: + return None + return { + "str": parent_requirement.display_long, + "urn": parent_requirement.urn, + "id": parent_requirement.id, + "ref_id": parent_requirement.ref_id, + "name": parent_requirement.name, + "description": parent_requirement.description + } class Meta: verbose_name = _("RequirementNode") diff --git a/backend/core/serializers.py b/backend/core/serializers.py index 0020da471..da0c3e1e0 100644 --- a/backend/core/serializers.py +++ b/backend/core/serializers.py @@ -613,7 +613,7 @@ class RequirementAssessmentReadSerializer(BaseModelSerializer): compliance_assessment = FieldsRelatedField() folder = FieldsRelatedField() assessable = serializers.BooleanField(source="requirement.assessable") - requirement = FieldsRelatedField(["urn", "parent_urn"]) + requirement = FieldsRelatedField(["urn", "annotation", "name", "description", "typical_evidence", "ref_id", "associated_reference_controls", "associated_threats", "parent_requirement"]) class Meta: model = RequirementAssessment diff --git a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.server.ts b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.server.ts index 2be95e97e..05a8fb4a0 100644 --- a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.server.ts +++ b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.server.ts @@ -29,14 +29,14 @@ export const load = (async ({ fetch, params }) => { } const requirementAssessment = await fetchJson(endpoint); + console.log(endpoint); + console.log(requirementAssessment); const requirement = requirementAssessment.requirement; const compliance_assessment_score = await fetchJson( `${baseUrl}/compliance-assessments/${requirementAssessment.compliance_assessment.id}/global_score/` ); - const parent = await fetchJson( - `${baseUrl}/requirement-nodes/?urn=${requirement.parent_urn}` - ).then((res) => res.results[0]); + const parent = requirementAssessment.requirement.parent_requirement const model = getModelInfo(URLModel); const object = { ...requirementAssessment }; diff --git a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte index c9da5a0f9..5cbcc1e6a 100644 --- a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte +++ b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte @@ -6,13 +6,13 @@ export let data: PageData; export let form: ActionData; - const threats = data.requirement.threats; - const reference_controls = data.requirement.reference_controls; + const threats = data.requirementAssessment.requirement.associated_threats ?? []; + const reference_controls = data.requirementAssessment.requirement.associated_reference_controls ?? []; const annotation = data.requirement.annotation; const typical_evidence = data.requirement.typical_evidence; - const has_threats = threats && threats.length > 0; - const has_reference_controls = reference_controls && reference_controls.length > 0; + const has_threats = threats.length > 0; + const has_reference_controls = reference_controls.length > 0; import { page } from '$app/stores'; import AutocompleteSelect from '$lib/components/Forms/AutocompleteSelect.svelte'; From 2196e00e44d3ec89e60464b65b3cd35789f90121 Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Wed, 27 Nov 2024 16:10:08 +0100 Subject: [PATCH 058/107] chore: run format --- backend/core/models.py | 8 ++++---- backend/core/serializers.py | 14 +++++++++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/backend/core/models.py b/backend/core/models.py index ef37115a1..6ce373d54 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -1048,7 +1048,7 @@ class RequirementNode(ReferentialObjectMixin, I18nObjectMixin): null=True, blank=True, verbose_name=_("Typical evidence") ) question = models.JSONField(blank=True, null=True, verbose_name=_("Question")) - + @property def associated_reference_controls(self): _reference_controls = self.reference_controls.all() @@ -1058,7 +1058,7 @@ def associated_reference_controls(self): {"str": control.display_long, "urn": control.urn, "id": control.id} ) return reference_controls - + @property def associated_threats(self): _threats = self.threats.all() @@ -1068,7 +1068,7 @@ def associated_threats(self): {"str": control.display_long, "urn": control.urn, "id": control.id} ) return threats - + @property def parent_requirement(self): parent_requirement = RequirementNode.objects.filter(urn=self.parent_urn).first() @@ -1080,7 +1080,7 @@ def parent_requirement(self): "id": parent_requirement.id, "ref_id": parent_requirement.ref_id, "name": parent_requirement.name, - "description": parent_requirement.description + "description": parent_requirement.description, } class Meta: diff --git a/backend/core/serializers.py b/backend/core/serializers.py index da0c3e1e0..413e40497 100644 --- a/backend/core/serializers.py +++ b/backend/core/serializers.py @@ -613,7 +613,19 @@ class RequirementAssessmentReadSerializer(BaseModelSerializer): compliance_assessment = FieldsRelatedField() folder = FieldsRelatedField() assessable = serializers.BooleanField(source="requirement.assessable") - requirement = FieldsRelatedField(["urn", "annotation", "name", "description", "typical_evidence", "ref_id", "associated_reference_controls", "associated_threats", "parent_requirement"]) + requirement = FieldsRelatedField( + [ + "urn", + "annotation", + "name", + "description", + "typical_evidence", + "ref_id", + "associated_reference_controls", + "associated_threats", + "parent_requirement", + ] + ) class Meta: model = RequirementAssessment From cca4fd714cb80d8524455843131c7e9237fddae6 Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Wed, 27 Nov 2024 16:17:39 +0100 Subject: [PATCH 059/107] feat: remove requirement node view for third parties --- backend/core/startup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/core/startup.py b/backend/core/startup.py index 0b5294dc5..f3faad47d 100644 --- a/backend/core/startup.py +++ b/backend/core/startup.py @@ -345,7 +345,6 @@ "view_complianceassessment", "view_requirementassessment", "change_requirementassessment", - "view_requirementnode", "view_evidence", "add_evidence", "change_evidence", From 28c50ce0edad0514760c5c44f034135e26abba44 Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Wed, 27 Nov 2024 16:18:06 +0100 Subject: [PATCH 060/107] fix: add evidence in enclave from evidence list view --- frontend/src/lib/utils/crud.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/lib/utils/crud.ts b/frontend/src/lib/utils/crud.ts index 5e6be76e5..2d357cf6e 100644 --- a/frontend/src/lib/utils/crud.ts +++ b/frontend/src/lib/utils/crud.ts @@ -420,7 +420,7 @@ export const URL_MODEL_MAP: ModelMap = { verboseNamePlural: 'Evidences', fileFields: ['attachment'], foreignKeyFields: [ - { field: 'folder', urlModel: 'folders', urlParams: 'content_type=DO&content_type=GL' }, + { field: 'folder', urlModel: 'folders', urlParams: 'content_type=DO&content_type=GL&content_type=EN' }, { field: 'applied_controls', urlModel: 'applied-controls' } ] }, From b6ec102955560e173ce16761c957e5bd338deff6 Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Wed, 27 Nov 2024 12:13:14 +0100 Subject: [PATCH 061/107] Set up huey - Install huey v2.5.2 - Configure huey with sqlite implementation --- backend/ciso_assistant/settings.py | 12 ++++++ backend/poetry.lock | 60 ++++++++---------------------- backend/pyproject.toml | 1 + 3 files changed, 29 insertions(+), 44 deletions(-) diff --git a/backend/ciso_assistant/settings.py b/backend/ciso_assistant/settings.py index b0e821d19..32b1a25d3 100644 --- a/backend/ciso_assistant/settings.py +++ b/backend/ciso_assistant/settings.py @@ -153,6 +153,7 @@ def set_ciso_assistant_url(_, __, event_dict): "allauth.socialaccount", "allauth.socialaccount.providers.saml", "allauth.mfa", + "huey.contrib.djhuey", ] MIDDLEWARE = [ @@ -224,6 +225,7 @@ def set_ciso_assistant_url(_, __, event_dict): "MIN_REFRESH_INTERVAL": 60, } + # Empty outside of debug mode so that allauth middleware does not raise an error STATIC_URL = "" @@ -372,6 +374,16 @@ def set_ciso_assistant_url(_, __, event_dict): # OTHER SETTINGS } +HUEY = { + "huey_class": "huey.SqliteHuey", # Huey implementation to use. + "name": "huey-ciso-assistant", # Use db name for huey. + "results": True, # Store return values of tasks. + "store_none": False, # If a task returns None, do not save to results. + "immediate": DEBUG, # If DEBUG=True, run synchronously. + "utc": True, # Use UTC for all times internally. + "filename": "db/huey.sqlite3", +} + # SSO with allauth ACCOUNT_USER_MODEL_USERNAME_FIELD = None diff --git a/backend/poetry.lock b/backend/poetry.lock index 1fe15109f..33acc7be3 100644 --- a/backend/poetry.lock +++ b/backend/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "argon2-cffi" @@ -107,10 +107,6 @@ files = [ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"}, {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, @@ -123,14 +119,8 @@ files = [ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"}, {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, @@ -141,24 +131,8 @@ files = [ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"}, {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, - {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"}, - {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"}, - {file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"}, - {file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"}, {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, @@ -168,10 +142,6 @@ files = [ {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"}, {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, @@ -183,10 +153,6 @@ files = [ {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"}, {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, @@ -199,10 +165,6 @@ files = [ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"}, {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, @@ -215,10 +177,6 @@ files = [ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"}, {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, @@ -984,6 +942,20 @@ chardet = ["chardet (>=2.2)"] genshi = ["genshi"] lxml = ["lxml"] +[[package]] +name = "huey" +version = "2.5.2" +description = "huey, a little task queue" +optional = false +python-versions = "*" +files = [ + {file = "huey-2.5.2.tar.gz", hash = "sha256:df33db474c05414ed40ee2110e9df692369871734da22d74ffb035a4bd74047f"}, +] + +[package.extras] +backends = ["redis (>=3.0.0)"] +redis = ["redis (>=3.0.0)"] + [[package]] name = "humanize" version = "4.11.0" @@ -2396,4 +2368,4 @@ test = ["pytest"] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "5f38fdc11bf9e530472b19a6527ef3a33008ab252e82e8e78fbd86414065f10a" +content-hash = "821aaf8fbb21d8500884b69f44180e50accade9ff10587752ac69d9cd4e248ac" diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 967f41b76..40144abed 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -32,6 +32,7 @@ python-magic = "0.4.27" pytz = "2024.2" fido2 = "^1.1.3" humanize = "^4.11.0" +huey = "^2.5.2" [tool.poetry.group.dev.dependencies] pytest-django = "4.8.0" From a08367f439c6b492b5862856529494c757092af3 Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Wed, 27 Nov 2024 16:30:32 +0100 Subject: [PATCH 062/107] chore: run format --- frontend/src/lib/utils/crud.ts | 6 +++++- .../requirement-assessments/[id=uuid]/edit/+page.server.ts | 2 +- .../requirement-assessments/[id=uuid]/edit/+page.svelte | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/frontend/src/lib/utils/crud.ts b/frontend/src/lib/utils/crud.ts index 2d357cf6e..8b3ef7458 100644 --- a/frontend/src/lib/utils/crud.ts +++ b/frontend/src/lib/utils/crud.ts @@ -420,7 +420,11 @@ export const URL_MODEL_MAP: ModelMap = { verboseNamePlural: 'Evidences', fileFields: ['attachment'], foreignKeyFields: [ - { field: 'folder', urlModel: 'folders', urlParams: 'content_type=DO&content_type=GL&content_type=EN' }, + { + field: 'folder', + urlModel: 'folders', + urlParams: 'content_type=DO&content_type=GL&content_type=EN' + }, { field: 'applied_controls', urlModel: 'applied-controls' } ] }, diff --git a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.server.ts b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.server.ts index 05a8fb4a0..5ff6cb8b7 100644 --- a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.server.ts +++ b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.server.ts @@ -36,7 +36,7 @@ export const load = (async ({ fetch, params }) => { `${baseUrl}/compliance-assessments/${requirementAssessment.compliance_assessment.id}/global_score/` ); - const parent = requirementAssessment.requirement.parent_requirement + const parent = requirementAssessment.requirement.parent_requirement; const model = getModelInfo(URLModel); const object = { ...requirementAssessment }; diff --git a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte index 5cbcc1e6a..9758d2c76 100644 --- a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte +++ b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte @@ -7,7 +7,8 @@ export let form: ActionData; const threats = data.requirementAssessment.requirement.associated_threats ?? []; - const reference_controls = data.requirementAssessment.requirement.associated_reference_controls ?? []; + const reference_controls = + data.requirementAssessment.requirement.associated_reference_controls ?? []; const annotation = data.requirement.annotation; const typical_evidence = data.requirement.typical_evidence; From 4cd65d5c486dc11fee55615fe6ff396b8ee9970e Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Wed, 27 Nov 2024 16:23:06 +0100 Subject: [PATCH 063/107] Add huey service to docker compose files --- docker-compose-build.yml | 17 +++++++++++++++++ docker-compose-pg.yml | 15 +++++++++++++++ docker-compose-prod.yml | 15 +++++++++++++++ docker-compose-remote-api.yml | 15 +++++++++++++++ docker-compose-remote.yml | 15 +++++++++++++++ docker-compose-traefik.yml | 15 +++++++++++++++ docker-compose.yml | 15 +++++++++++++++ enterprise/docker-compose-build.yml | 17 +++++++++++++++++ enterprise/docker-compose-pg.yml | 15 +++++++++++++++ enterprise/docker-compose-remote-api.yml | 15 +++++++++++++++ enterprise/docker-compose-remote.yml | 15 +++++++++++++++ enterprise/docker-compose.yml | 15 +++++++++++++++ frontend/tests/docker-compose.e2e-tests.yml | 15 +++++++++++++++ 13 files changed, 199 insertions(+) diff --git a/docker-compose-build.yml b/docker-compose-build.yml index efc6b204b..5fb7e4209 100644 --- a/docker-compose-build.yml +++ b/docker-compose-build.yml @@ -12,6 +12,23 @@ services: volumes: - ./db:/code/db + huey: + container_name: huey + build: + context: ./backend + dockerfile: Dockerfile + restart: always + environment: + - ALLOWED_HOSTS=backend,localhost + - DJANGO_DEBUG=False + volumes: + - ./db:/code/db + entrypoint: + - /bin/sh + - -c + - | + poetry run python manage.py run_huey + frontend: container_name: frontend environment: diff --git a/docker-compose-pg.yml b/docker-compose-pg.yml index be5fc52e0..dba7835fb 100644 --- a/docker-compose-pg.yml +++ b/docker-compose-pg.yml @@ -16,6 +16,21 @@ services: volumes: - ./db:/code/db + huey: + container_name: huey + image: ghcr.io/intuitem/ciso-assistant-community/backend:latest + restart: always + environment: + - ALLOWED_HOSTS=backend,localhost + - DJANGO_DEBUG=False + volumes: + - ./db:/code/db + entrypoint: + - /bin/sh + - -c + - | + poetry run python manage.py run_huey + frontend: container_name: frontend environment: diff --git a/docker-compose-prod.yml b/docker-compose-prod.yml index f71579b92..5bf39fe4d 100644 --- a/docker-compose-prod.yml +++ b/docker-compose-prod.yml @@ -11,6 +11,21 @@ services: volumes: - ./db:/code/db + huey: + container_name: huey + image: ghcr.io/intuitem/ciso-assistant-community/backend:latest + restart: always + environment: + - ALLOWED_HOSTS=backend,localhost + - DJANGO_DEBUG=False + volumes: + - ./db:/code/db + entrypoint: + - /bin/sh + - -c + - | + poetry run python manage.py run_huey + frontend: container_name: frontend environment: diff --git a/docker-compose-remote-api.yml b/docker-compose-remote-api.yml index c61695b75..19c40840f 100644 --- a/docker-compose-remote-api.yml +++ b/docker-compose-remote-api.yml @@ -11,6 +11,21 @@ services: volumes: - ./db:/code/db + huey: + container_name: huey + image: ghcr.io/intuitem/ciso-assistant-community/backend:latest + restart: always + environment: + - ALLOWED_HOSTS=backend,localhost + - DJANGO_DEBUG=False + volumes: + - ./db:/code/db + entrypoint: + - /bin/sh + - -c + - | + poetry run python manage.py run_huey + frontend: container_name: frontend environment: diff --git a/docker-compose-remote.yml b/docker-compose-remote.yml index cec4d86b1..4366180d2 100644 --- a/docker-compose-remote.yml +++ b/docker-compose-remote.yml @@ -11,6 +11,21 @@ services: volumes: - ./db:/code/db + huey: + container_name: huey + image: ghcr.io/intuitem/ciso-assistant-community/backend:latest + restart: always + environment: + - ALLOWED_HOSTS=backend,localhost + - DJANGO_DEBUG=False + volumes: + - ./db:/code/db + entrypoint: + - /bin/sh + - -c + - | + poetry run python manage.py run_huey + frontend: container_name: frontend environment: diff --git a/docker-compose-traefik.yml b/docker-compose-traefik.yml index 58b6b226d..e13f36a8d 100644 --- a/docker-compose-traefik.yml +++ b/docker-compose-traefik.yml @@ -22,6 +22,21 @@ services: networks: - front + huey: + container_name: huey + image: ghcr.io/intuitem/ciso-assistant-community/backend:latest + restart: always + environment: + - ALLOWED_HOSTS=backend,localhost + - DJANGO_DEBUG=False + volumes: + - ./db:/code/db + entrypoint: + - /bin/sh + - -c + - | + poetry run python manage.py run_huey + frontend: container_name: frontend restart: unless-stopped diff --git a/docker-compose.yml b/docker-compose.yml index d723deee3..277606864 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,6 +11,21 @@ services: volumes: - ./db:/code/db + huey: + container_name: huey + image: ghcr.io/intuitem/ciso-assistant-community/backend:latest + restart: always + environment: + - ALLOWED_HOSTS=backend,localhost + - DJANGO_DEBUG=False + volumes: + - ./db:/code/db + entrypoint: + - /bin/sh + - -c + - | + poetry run python manage.py run_huey + frontend: container_name: frontend environment: diff --git a/enterprise/docker-compose-build.yml b/enterprise/docker-compose-build.yml index 56c2d9cb9..4ad962972 100644 --- a/enterprise/docker-compose-build.yml +++ b/enterprise/docker-compose-build.yml @@ -13,6 +13,23 @@ services: volumes: - ./db:/code/db + huey: + container_name: huey + build: + context: ../ + dockerfile: ./enterprise/backend/Dockerfile + restart: always + environment: + - ALLOWED_HOSTS=backend,localhost + - DJANGO_DEBUG=False + volumes: + - ./db:/code/db + entrypoint: + - /bin/sh + - -c + - | + poetry run python manage.py run_huey + frontend: container_name: frontend environment: diff --git a/enterprise/docker-compose-pg.yml b/enterprise/docker-compose-pg.yml index 0d483ef15..da9cd8f66 100644 --- a/enterprise/docker-compose-pg.yml +++ b/enterprise/docker-compose-pg.yml @@ -16,6 +16,21 @@ services: volumes: - ./db:/code/db + huey: + container_name: huey + image: ghcr.io/intuitem/ciso-assistant-community/backend:latest + restart: always + environment: + - ALLOWED_HOSTS=backend,localhost + - DJANGO_DEBUG=False + volumes: + - ./db:/code/db + entrypoint: + - /bin/sh + - -c + - | + poetry run python manage.py run_huey + frontend: container_name: frontend environment: diff --git a/enterprise/docker-compose-remote-api.yml b/enterprise/docker-compose-remote-api.yml index 58efa4510..17aeb8a12 100644 --- a/enterprise/docker-compose-remote-api.yml +++ b/enterprise/docker-compose-remote-api.yml @@ -11,6 +11,21 @@ services: volumes: - ./db:/code/db + huey: + container_name: huey + image: ghcr.io/intuitem/ciso-assistant-community/backend:latest + restart: always + environment: + - ALLOWED_HOSTS=backend,localhost + - DJANGO_DEBUG=False + volumes: + - ./db:/code/db + entrypoint: + - /bin/sh + - -c + - | + poetry run python manage.py run_huey + frontend: container_name: frontend environment: diff --git a/enterprise/docker-compose-remote.yml b/enterprise/docker-compose-remote.yml index 50fb12b6e..ed410bf33 100644 --- a/enterprise/docker-compose-remote.yml +++ b/enterprise/docker-compose-remote.yml @@ -11,6 +11,21 @@ services: volumes: - ./db:/code/db + huey: + container_name: huey + image: ghcr.io/intuitem/ciso-assistant-community/backend:latest + restart: always + environment: + - ALLOWED_HOSTS=backend,localhost + - DJANGO_DEBUG=False + volumes: + - ./db:/code/db + entrypoint: + - /bin/sh + - -c + - | + poetry run python manage.py run_huey + frontend: container_name: frontend environment: diff --git a/enterprise/docker-compose.yml b/enterprise/docker-compose.yml index a17805a67..ab78faf90 100644 --- a/enterprise/docker-compose.yml +++ b/enterprise/docker-compose.yml @@ -13,6 +13,21 @@ services: volumes: - ./db:/code/db + huey: + container_name: huey + image: ghcr.io/intuitem/ciso-assistant-community/backend:latest + restart: always + environment: + - ALLOWED_HOSTS=backend,localhost + - DJANGO_DEBUG=False + volumes: + - ./db:/code/db + entrypoint: + - /bin/sh + - -c + - | + poetry run python manage.py run_huey + frontend: container_name: frontend environment: diff --git a/frontend/tests/docker-compose.e2e-tests.yml b/frontend/tests/docker-compose.e2e-tests.yml index 625114970..127f2f3bb 100644 --- a/frontend/tests/docker-compose.e2e-tests.yml +++ b/frontend/tests/docker-compose.e2e-tests.yml @@ -15,6 +15,21 @@ services: ports: - 8080:8000 + huey: + container_name: huey + image: ghcr.io/intuitem/ciso-assistant-community/backend:latest + restart: always + environment: + - ALLOWED_HOSTS=backend,localhost + - DJANGO_DEBUG=False + volumes: + - ./db:/code/db + entrypoint: + - /bin/sh + - -c + - | + poetry run python manage.py run_huey + tests: container_name: functional-tests environment: From 04ca202beb64b37a1d920ce2702ecb60ad163d5e Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Wed, 27 Nov 2024 17:52:33 +0100 Subject: [PATCH 064/107] fix: api tests --- .../api/test_api_requirement_assessments.py | 42 ++++++++++++++++++- backend/core/serializers.py | 1 + .../[id=uuid]/edit/+page.server.ts | 2 - 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/backend/app_tests/api/test_api_requirement_assessments.py b/backend/app_tests/api/test_api_requirement_assessments.py index ef49c83e1..0623212de 100644 --- a/backend/app_tests/api/test_api_requirement_assessments.py +++ b/backend/app_tests/api/test_api_requirement_assessments.py @@ -124,7 +124,26 @@ def test_get_requirement_assessments(self, test): "id": str(compliance_assessment.id), "str": compliance_assessment.name, }, - "requirement": str(RequirementNode.objects.all()[0].id), + "requirement": { + "str": str(RequirementNode.objects.all()[0]), + "id": str(RequirementNode.objects.all()[0].id), + "urn": RequirementNode.objects.all()[0].urn, + "annotation": RequirementNode.objects.all()[0].annotation, + "name": RequirementNode.objects.all()[0].name, + "description": RequirementNode.objects.all()[0].description, + "typical_evidence": RequirementNode.objects.all()[0].typical_evidence, + "ref_id": RequirementNode.objects.all()[0].ref_id, + "associated_reference_controls": RequirementNode.objects.all()[0].associated_reference_controls, + "associated_threats": RequirementNode.objects.all()[0].associated_threats, + "parent_requirement": { + "str": RequirementNode.objects.all()[0].parent_requirement.get("str"), + "urn": RequirementNode.objects.all()[0].parent_requirement.get("urn"), + "id": str(RequirementNode.objects.all()[0].parent_requirement.get("id")), + "ref_id": RequirementNode.objects.all()[0].parent_requirement.get("ref_id"), + "name": RequirementNode.objects.all()[0].parent_requirement.get("name"), + "description": RequirementNode.objects.all()[0].parent_requirement.get("description"), + } if RequirementNode.objects.all()[0].parent_requirement else None, + }, }, base_count=-1, user_group=test.user_group, @@ -210,7 +229,26 @@ def test_update_requirement_assessments(self, test): "id": str(compliance_assessment.id), "str": compliance_assessment.name, }, - "requirement": str(RequirementNode.objects.all()[0].id), + "requirement": { + "str": str(RequirementNode.objects.all()[0]), + "id": str(RequirementNode.objects.all()[0].id), + "urn": RequirementNode.objects.all()[0].urn, + "annotation": RequirementNode.objects.all()[0].annotation, + "name": RequirementNode.objects.all()[0].name, + "description": RequirementNode.objects.all()[0].description, + "typical_evidence": RequirementNode.objects.all()[0].typical_evidence, + "ref_id": RequirementNode.objects.all()[0].ref_id, + "associated_reference_controls": RequirementNode.objects.all()[0].associated_reference_controls, + "associated_threats": RequirementNode.objects.all()[0].associated_threats, + "parent_requirement": { + "str": RequirementNode.objects.all()[0].parent_requirement.get("str"), + "urn": RequirementNode.objects.all()[0].parent_requirement.get("urn"), + "id": str(RequirementNode.objects.all()[0].parent_requirement.get("id")), + "ref_id": RequirementNode.objects.all()[0].parent_requirement.get("ref_id"), + "name": RequirementNode.objects.all()[0].parent_requirement.get("name"), + "description": RequirementNode.objects.all()[0].parent_requirement.get("description"), + } if RequirementNode.objects.all()[0].parent_requirement else None, + }, }, user_group=test.user_group, ) diff --git a/backend/core/serializers.py b/backend/core/serializers.py index 413e40497..6771fd4ce 100644 --- a/backend/core/serializers.py +++ b/backend/core/serializers.py @@ -615,6 +615,7 @@ class RequirementAssessmentReadSerializer(BaseModelSerializer): assessable = serializers.BooleanField(source="requirement.assessable") requirement = FieldsRelatedField( [ + "id", "urn", "annotation", "name", diff --git a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.server.ts b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.server.ts index 5ff6cb8b7..08e68fed1 100644 --- a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.server.ts +++ b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.server.ts @@ -29,8 +29,6 @@ export const load = (async ({ fetch, params }) => { } const requirementAssessment = await fetchJson(endpoint); - console.log(endpoint); - console.log(requirementAssessment); const requirement = requirementAssessment.requirement; const compliance_assessment_score = await fetchJson( `${baseUrl}/compliance-assessments/${requirementAssessment.compliance_assessment.id}/global_score/` From ea572b159e655b3cef92048738178ad7b5ed8b16 Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Wed, 27 Nov 2024 17:53:49 +0100 Subject: [PATCH 065/107] chore: typo in api tests --- .../api/test_api_requirement_assessments.py | 84 ++++++++++++++----- 1 file changed, 64 insertions(+), 20 deletions(-) diff --git a/backend/app_tests/api/test_api_requirement_assessments.py b/backend/app_tests/api/test_api_requirement_assessments.py index 0623212de..12dad4661 100644 --- a/backend/app_tests/api/test_api_requirement_assessments.py +++ b/backend/app_tests/api/test_api_requirement_assessments.py @@ -131,18 +131,40 @@ def test_get_requirement_assessments(self, test): "annotation": RequirementNode.objects.all()[0].annotation, "name": RequirementNode.objects.all()[0].name, "description": RequirementNode.objects.all()[0].description, - "typical_evidence": RequirementNode.objects.all()[0].typical_evidence, + "typical_evidence": RequirementNode.objects.all()[ + 0 + ].typical_evidence, "ref_id": RequirementNode.objects.all()[0].ref_id, - "associated_reference_controls": RequirementNode.objects.all()[0].associated_reference_controls, - "associated_threats": RequirementNode.objects.all()[0].associated_threats, + "associated_reference_controls": RequirementNode.objects.all()[ + 0 + ].associated_reference_controls, + "associated_threats": RequirementNode.objects.all()[ + 0 + ].associated_threats, "parent_requirement": { - "str": RequirementNode.objects.all()[0].parent_requirement.get("str"), - "urn": RequirementNode.objects.all()[0].parent_requirement.get("urn"), - "id": str(RequirementNode.objects.all()[0].parent_requirement.get("id")), - "ref_id": RequirementNode.objects.all()[0].parent_requirement.get("ref_id"), - "name": RequirementNode.objects.all()[0].parent_requirement.get("name"), - "description": RequirementNode.objects.all()[0].parent_requirement.get("description"), - } if RequirementNode.objects.all()[0].parent_requirement else None, + "str": RequirementNode.objects.all()[0].parent_requirement.get( + "str" + ), + "urn": RequirementNode.objects.all()[0].parent_requirement.get( + "urn" + ), + "id": str( + RequirementNode.objects.all()[0].parent_requirement.get( + "id" + ) + ), + "ref_id": RequirementNode.objects.all()[ + 0 + ].parent_requirement.get("ref_id"), + "name": RequirementNode.objects.all()[0].parent_requirement.get( + "name" + ), + "description": RequirementNode.objects.all()[ + 0 + ].parent_requirement.get("description"), + } + if RequirementNode.objects.all()[0].parent_requirement + else None, }, }, base_count=-1, @@ -236,18 +258,40 @@ def test_update_requirement_assessments(self, test): "annotation": RequirementNode.objects.all()[0].annotation, "name": RequirementNode.objects.all()[0].name, "description": RequirementNode.objects.all()[0].description, - "typical_evidence": RequirementNode.objects.all()[0].typical_evidence, + "typical_evidence": RequirementNode.objects.all()[ + 0 + ].typical_evidence, "ref_id": RequirementNode.objects.all()[0].ref_id, - "associated_reference_controls": RequirementNode.objects.all()[0].associated_reference_controls, - "associated_threats": RequirementNode.objects.all()[0].associated_threats, + "associated_reference_controls": RequirementNode.objects.all()[ + 0 + ].associated_reference_controls, + "associated_threats": RequirementNode.objects.all()[ + 0 + ].associated_threats, "parent_requirement": { - "str": RequirementNode.objects.all()[0].parent_requirement.get("str"), - "urn": RequirementNode.objects.all()[0].parent_requirement.get("urn"), - "id": str(RequirementNode.objects.all()[0].parent_requirement.get("id")), - "ref_id": RequirementNode.objects.all()[0].parent_requirement.get("ref_id"), - "name": RequirementNode.objects.all()[0].parent_requirement.get("name"), - "description": RequirementNode.objects.all()[0].parent_requirement.get("description"), - } if RequirementNode.objects.all()[0].parent_requirement else None, + "str": RequirementNode.objects.all()[0].parent_requirement.get( + "str" + ), + "urn": RequirementNode.objects.all()[0].parent_requirement.get( + "urn" + ), + "id": str( + RequirementNode.objects.all()[0].parent_requirement.get( + "id" + ) + ), + "ref_id": RequirementNode.objects.all()[ + 0 + ].parent_requirement.get("ref_id"), + "name": RequirementNode.objects.all()[0].parent_requirement.get( + "name" + ), + "description": RequirementNode.objects.all()[ + 0 + ].parent_requirement.get("description"), + } + if RequirementNode.objects.all()[0].parent_requirement + else None, }, }, user_group=test.user_group, From 7819b144eb74ed6654e4e0c31483144af734ec19 Mon Sep 17 00:00:00 2001 From: Monsieur Sw4g Date: Wed, 27 Nov 2024 19:58:41 +0100 Subject: [PATCH 066/107] Convert stringified version to int --- backend/library/libraries/enisa-5g-scm-v1.3.yaml | 2 +- backend/library/libraries/hds-v2023-a.yaml | 2 +- backend/library/libraries/ict-minimal.yaml | 2 +- .../libraries/mapping-ccb-cff-2023-03-01-to-iso27001-2022.yaml | 2 +- .../libraries/mapping-cjis-policy-5.9.4-to-cjis-policy-5.9.yaml | 2 +- .../libraries/mapping-iso27001-2022-to-secnumcloud-3.2.yaml | 2 +- .../libraries/mapping-secnumcloud-3.2-to-iso27001-2022.yaml | 2 +- backend/library/libraries/nist-sp-800-66-rev2.yaml | 2 +- backend/library/libraries/tisax-v5.1.yaml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/library/libraries/enisa-5g-scm-v1.3.yaml b/backend/library/libraries/enisa-5g-scm-v1.3.yaml index a947a3394..c142bef82 100644 --- a/backend/library/libraries/enisa-5g-scm-v1.3.yaml +++ b/backend/library/libraries/enisa-5g-scm-v1.3.yaml @@ -9,7 +9,7 @@ copyright: "The Matrix is provided on an \u2018as is\u2019 basis. ENISA is not r \ for the information contained in the Matrix, including the use that might be made\ \ of this information, or the content of any external sources referenced in the\ \ Matrix.\n" -version: '2' +version: 2 provider: ENISA packager: intuitem objects: diff --git a/backend/library/libraries/hds-v2023-a.yaml b/backend/library/libraries/hds-v2023-a.yaml index 81ee2a49e..e64ca13dc 100644 --- a/backend/library/libraries/hds-v2023-a.yaml +++ b/backend/library/libraries/hds-v2023-a.yaml @@ -5,7 +5,7 @@ name: HDS v2023-A description: "R\xE9f\xE9rentiel de certification H\xE9bergeur de donn\xE9es de sant\xE9\ \ (HDS) - Exigences" copyright: "Loi fran\xE7aise et europ\xE9enne" -version: '1' +version: 1 provider: "Agence du Num\xE9rique en Sant\xE9" packager: intuitem objects: diff --git a/backend/library/libraries/ict-minimal.yaml b/backend/library/libraries/ict-minimal.yaml index 765b230a6..8754b7212 100644 --- a/backend/library/libraries/ict-minimal.yaml +++ b/backend/library/libraries/ict-minimal.yaml @@ -4,7 +4,7 @@ ref_id: ict-minimal name: ICT - Minimum standard description: Minimum standard for improving ICT resilience - Version may 2023 copyright: Creative Commons BY. -version: '1' +version: 1 provider: Swiss FONES packager: intuitem translations: diff --git a/backend/library/libraries/mapping-ccb-cff-2023-03-01-to-iso27001-2022.yaml b/backend/library/libraries/mapping-ccb-cff-2023-03-01-to-iso27001-2022.yaml index 2a6851832..b32fc99ce 100644 --- a/backend/library/libraries/mapping-ccb-cff-2023-03-01-to-iso27001-2022.yaml +++ b/backend/library/libraries/mapping-ccb-cff-2023-03-01-to-iso27001-2022.yaml @@ -5,7 +5,7 @@ name: CCB-CFF-2023-03-01 -> ISO/IEC 27001:2022 description: Mapping from CCB CyberFundamentals Framework to International standard ISO/IEC 27001:2022 copyright: intuitem -version: '1' +version: 1 provider: CCB packager: intuitem dependencies: diff --git a/backend/library/libraries/mapping-cjis-policy-5.9.4-to-cjis-policy-5.9.yaml b/backend/library/libraries/mapping-cjis-policy-5.9.4-to-cjis-policy-5.9.yaml index e6288ab9e..9c424ee7c 100644 --- a/backend/library/libraries/mapping-cjis-policy-5.9.4-to-cjis-policy-5.9.yaml +++ b/backend/library/libraries/mapping-cjis-policy-5.9.4-to-cjis-policy-5.9.yaml @@ -5,7 +5,7 @@ name: CJIS-POLICY-5.9.4 -> CJIS-POLICY-5.9.5 description: Mapping from Criminal Justice Information Services (CJIS) Security Policy to Criminal Justice Information Services (CJIS) Security Policy 5.9.5 copyright: intuitem -version: '1' +version: 1 provider: intuitem packager: intuitem dependencies: diff --git a/backend/library/libraries/mapping-iso27001-2022-to-secnumcloud-3.2.yaml b/backend/library/libraries/mapping-iso27001-2022-to-secnumcloud-3.2.yaml index 033048444..fbc6ab392 100644 --- a/backend/library/libraries/mapping-iso27001-2022-to-secnumcloud-3.2.yaml +++ b/backend/library/libraries/mapping-iso27001-2022-to-secnumcloud-3.2.yaml @@ -6,7 +6,7 @@ description: "Correspondance entre la norme internationale ISO/IEC 27001:2022 et \ r\xE9f\xE9rentiel d\u2019exigences de prestataires de services d\u2019informatique\ \ en nuage (SecNumCloud 3.2)" copyright: https://www.iso.org/standard/27001 - ANSSI - ANS -version: '1' +version: 1 provider: ISO/IEC - ANSSI - ANS (annexe 1 du document HDS 2.0) packager: protocolpaladin dependencies: diff --git a/backend/library/libraries/mapping-secnumcloud-3.2-to-iso27001-2022.yaml b/backend/library/libraries/mapping-secnumcloud-3.2-to-iso27001-2022.yaml index 5757c0044..f8c2c25cf 100644 --- a/backend/library/libraries/mapping-secnumcloud-3.2-to-iso27001-2022.yaml +++ b/backend/library/libraries/mapping-secnumcloud-3.2-to-iso27001-2022.yaml @@ -6,7 +6,7 @@ description: "Correspondance entre le r\xE9f\xE9rentiel d\u2019exigences de pres \ de services d\u2019informatique en nuage (SecNumCloud 3.2) et la norme internationale\ \ ISO/IEC 27001:2022" copyright: https://www.iso.org/standard/27001 - ANSSI - ANS -version: '1' +version: 1 provider: ISO/IEC - ANSSI - ANS (annexe 1 du document HDS 2.0) packager: protocolpaladin dependencies: diff --git a/backend/library/libraries/nist-sp-800-66-rev2.yaml b/backend/library/libraries/nist-sp-800-66-rev2.yaml index 642cf412b..0abb9c32a 100644 --- a/backend/library/libraries/nist-sp-800-66-rev2.yaml +++ b/backend/library/libraries/nist-sp-800-66-rev2.yaml @@ -10,7 +10,7 @@ description: 'Implementing the Health Insurance Portability and Accountability A ' copyright: With the exception of material marked as copyrighted, information presented on NIST sites are considered public information and may be distributed or copied. -version: '1' +version: 1 provider: NIST packager: intuitem objects: diff --git a/backend/library/libraries/tisax-v5.1.yaml b/backend/library/libraries/tisax-v5.1.yaml index 1acb92ea8..b6cb73318 100644 --- a/backend/library/libraries/tisax-v5.1.yaml +++ b/backend/library/libraries/tisax-v5.1.yaml @@ -21,7 +21,7 @@ copyright: "Publisher: VERBAND DER AUTOMOBILINDUSTRIE e.\_V. (VDA, German Associ \ Creative Commons Attribution - No Derivative Works 4.0 International Public License.\ \ In addition, You are granted the right to distribute derivatives under certain\ \ terms." -version: '1' +version: 1 provider: VDA packager: intuitem objects: From 17e159f0062ec7f8af0db76c12e44afcbd56d87b Mon Sep 17 00:00:00 2001 From: Abderrahmane Smimite Date: Thu, 28 Nov 2024 10:44:45 +0100 Subject: [PATCH 067/107] starting point --- frontend/src/lib/components/DataViz/Card.svelte | 4 ++-- .../routes/(app)/(internal)/analytics/+page.svelte | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/src/lib/components/DataViz/Card.svelte b/frontend/src/lib/components/DataViz/Card.svelte index e2c7818eb..ad465bb7c 100644 --- a/frontend/src/lib/components/DataViz/Card.svelte +++ b/frontend/src/lib/components/DataViz/Card.svelte @@ -1,7 +1,7 @@
          @@ -133,14 +122,14 @@ let:option {allowUserOptions} > - {#if option.suggested} - {option.label} - (suggested) - {:else if translateOptions} - {safeTranslate(option.label)} - {:else} - {option.label} - {/if} + {#if option.suggested} + {option.label} + (suggested) + {:else if translateOptions && option.label} + {safeTranslate(option.label)} + {:else} + {option.label || option} + {/if}
          {#if helpText} From 515de011377c432bc746bdc05ba9faadc2fb03f5 Mon Sep 17 00:00:00 2001 From: Mohamed-Hacene Date: Thu, 28 Nov 2024 12:43:49 +0100 Subject: [PATCH 072/107] chore: format --- .../components/Forms/AutocompleteSelect.svelte | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/src/lib/components/Forms/AutocompleteSelect.svelte b/frontend/src/lib/components/Forms/AutocompleteSelect.svelte index a66fa5d2f..8e71e96f4 100644 --- a/frontend/src/lib/components/Forms/AutocompleteSelect.svelte +++ b/frontend/src/lib/components/Forms/AutocompleteSelect.svelte @@ -122,14 +122,14 @@ let:option {allowUserOptions} > - {#if option.suggested} - {option.label} - (suggested) - {:else if translateOptions && option.label} - {safeTranslate(option.label)} - {:else} - {option.label || option} - {/if} + {#if option.suggested} + {option.label} + (suggested) + {:else if translateOptions && option.label} + {safeTranslate(option.label)} + {:else} + {option.label || option} + {/if}
          {#if helpText} From d93ff1b94908e3d48ed5a230ee3d2087271a5e37 Mon Sep 17 00:00:00 2001 From: Abderrahmane Smimite Date: Thu, 28 Nov 2024 18:01:53 +0100 Subject: [PATCH 073/107] updating links for compliance --- backend/core/helpers.py | 15 +++++++-- frontend/messages/en.json | 17 +++++----- frontend/messages/fr.json | 31 ++++++++++--------- .../(app)/(internal)/analytics/+page.svelte | 22 +++++++++---- 4 files changed, 53 insertions(+), 32 deletions(-) diff --git a/backend/core/helpers.py b/backend/core/helpers.py index 08d8dfdf2..6a3ec34b2 100644 --- a/backend/core/helpers.py +++ b/backend/core/helpers.py @@ -15,6 +15,9 @@ from iam.models import Folder, Permission, RoleAssignment, User from library.helpers import get_referential_translation +from statistics import mean +import math + from .models import * from .utils import camel_case @@ -889,6 +892,10 @@ def viewable_items(model): viewable_controls = viewable_items(AppliedControl) controls_count = viewable_controls.count() + progress_avg = math.ceil( + mean([x.progress() for x in viewable_items(ComplianceAssessment)]) + ) + data = { "controls": { "total": controls_count, @@ -908,17 +915,19 @@ def viewable_items(model): "acceptances": viewable_items(RiskAcceptance).count(), }, "compliance": { + "used_frameworks": viewable_items(ComplianceAssessment) + .values("framework_id") + .distinct() + .count(), "audits": viewable_items(ComplianceAssessment).count(), "active_audits": viewable_items(ComplianceAssessment) .filter(status__in=["in_progress", "in_review", "done"]) .count(), "evidences": viewable_items(Evidence).count(), - "compliant_items": viewable_items(RequirementAssessment) - .filter(result="compliant") - .count(), "non_compliant_items": viewable_items(RequirementAssessment) .filter(result="non_compliant") .count(), + "progress_avg": progress_avg, }, "audits_stats": build_audits_stats(user), "csf_functions": csf_functions(user), diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 42a0fa288..08e008306 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -808,14 +808,15 @@ "sumpageToDo": "to do", "sumpageInProgress": "in progress", "sumpageOnHold": "on hold", - "sumpageActiveAudits": "active audits", - "sumpageCompliantItems": "compliant items", - "sumpageNonCompliantItems": "non compliant items", - "sumpageEvidences": "evidences", - "sumpageAssessments": "assessments", - "sumpageScenarios": "scenarios", - "sumpageMappedThreats": "mapped threats", - "sumpageRiskAccepted": "risks accepted", + "sumpageActiveAudits": "Active audits", + "sumpageCompliantItems": "Compliant items", + "sumpageNonCompliantItems": "Non compliant items", + "sumpageEvidences": "Evidences", + "sumpageAvgProgress": "Average progress", + "sumpageAssessments": "Assessments", + "sumpageScenarios": "Scenarios", + "sumpageMappedThreats": "Mapped threats", + "sumpageRiskAccepted": "Risks accepted", "sumpageSectionControls": "controls", "sumpageTitleComplianceOverview": "Compliance overview", "sumpageTitleCurrentRisks": "Current risks", diff --git a/frontend/messages/fr.json b/frontend/messages/fr.json index dbe72b3a1..7b7cb9255 100644 --- a/frontend/messages/fr.json +++ b/frontend/messages/fr.json @@ -791,21 +791,22 @@ "fillSSOSLOURLx509cert": "Option 2 : Remplissez l'URL SSO, l'URL SLO et le certificat x509", "licenseAboutToExpireWarning": "Il reste {days_left} jours avant l'expiration de votre licence.", "noExpirationDateSet": "Aucune date d'expiration définie", - "sumpageTotal": "total", - "sumpageActive": "actif", - "sumpageDeprecated": "obsolète", - "sumpageToDo": "à faire", - "sumpageInProgress": "en cours", - "sumpageOnHold": "en attente", - "sumpageActiveAudits": "audits actifs", - "sumpageCompliantItems": "items conformes", - "sumpageNonCompliantItems": "items non conformes", - "sumpageEvidences": "preuves", - "sumpageAssessments": "évaluations", - "sumpageScenarios": "scénarios", - "sumpageMappedThreats": "menaces mappées", - "sumpageRiskAccepted": "risques acceptés", - "sumpageSectionControls": "mesures", + "sumpageTotal": "Total", + "sumpageActive": "Actif", + "sumpageDeprecated": "Obsolète", + "sumpageToDo": "A faire", + "sumpageInProgress": "En cours", + "sumpageOnHold": "En attente", + "sumpageActiveAudits": "Audits actifs", + "sumpageCompliantItems": "Exigences conformes", + "sumpageNonCompliantItems": "Exigences non conformes", + "sumpageEvidences": "Preuves", + "sumpageAvgProgress": "Avancement moyenné", + "sumpageAssessments": "Evaluations", + "sumpageScenarios": "Scénarios", + "sumpageMappedThreats": "Menaces mappées", + "sumpageRiskAccepted": "Risques acceptés", + "sumpageSectionControls": "Mesures", "sumpageTitleComplianceOverview": "Aperçu conformité", "sumpageTitleCurrentRisks": "Risques actuels", "sumpageTitleResidualRisks": "Risques résiduels", diff --git a/frontend/src/routes/(app)/(internal)/analytics/+page.svelte b/frontend/src/routes/(app)/(internal)/analytics/+page.svelte index 1f0dff0aa..2591c640c 100644 --- a/frontend/src/routes/(app)/(internal)/analytics/+page.svelte +++ b/frontend/src/routes/(app)/(internal)/analytics/+page.svelte @@ -201,20 +201,30 @@ />
          + +
          -
          + Date: Thu, 28 Nov 2024 18:27:37 +0100 Subject: [PATCH 074/107] more links on widgets --- frontend/messages/fr.json | 2 +- .../src/routes/(app)/(internal)/analytics/+page.svelte | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/messages/fr.json b/frontend/messages/fr.json index 7b7cb9255..4b6a31d9f 100644 --- a/frontend/messages/fr.json +++ b/frontend/messages/fr.json @@ -802,7 +802,7 @@ "sumpageNonCompliantItems": "Exigences non conformes", "sumpageEvidences": "Preuves", "sumpageAvgProgress": "Avancement moyenné", - "sumpageAssessments": "Evaluations", + "sumpageAssessments": "Analyses", "sumpageScenarios": "Scénarios", "sumpageMappedThreats": "Menaces mappées", "sumpageRiskAccepted": "Risques acceptés", diff --git a/frontend/src/routes/(app)/(internal)/analytics/+page.svelte b/frontend/src/routes/(app)/(internal)/analytics/+page.svelte index 2591c640c..9d40aa763 100644 --- a/frontend/src/routes/(app)/(internal)/analytics/+page.svelte +++ b/frontend/src/routes/(app)/(internal)/analytics/+page.svelte @@ -259,7 +259,7 @@ Date: Thu, 28 Nov 2024 19:37:15 +0100 Subject: [PATCH 075/107] stable point --- .../src/lib/components/SideBar/navData.ts | 502 +++++----- frontend/src/lib/utils/table.ts | 866 +++++++++--------- 2 files changed, 683 insertions(+), 685 deletions(-) diff --git a/frontend/src/lib/components/SideBar/navData.ts b/frontend/src/lib/components/SideBar/navData.ts index fd27bb952..5395e691e 100644 --- a/frontend/src/lib/components/SideBar/navData.ts +++ b/frontend/src/lib/components/SideBar/navData.ts @@ -1,258 +1,252 @@ export const navData = { - items: [ - // { - // name: 'Home', - // items: [ - // { - // name: 'Home', - // fa_icon: 'fa-solid fa-house', - // href: '/' - // }, - // { name: 'Quick start', fa_icon: 'fa-solid fa-plane', href: '/quick-start' } - // ] - // }, - { - name: 'overview', - items: [ - { - name: 'analytics', - fa_icon: 'fa-solid fa-gauge', - href: '/analytics', - permissions: [ - 'view_project', - 'view_riskscenario', - 'view_referencecontrol', - 'view_assessment', - 'view_riskassessment' - ] - }, - { - name: 'myAssignments', - fa_icon: 'fa-solid fa-list-check', - href: '/my-assignments', - permissions: [ - 'view_project', - 'view_riskscenario', - 'view_referencecontrol', - 'view_assessment', - 'view_riskassessment' - ] - } - ] - }, - { - name: 'organization', - items: [ - { - name: 'domains', - fa_icon: 'fa-solid fa-sitemap', - href: '/folders', - exclude: ['BI-RL-TPR'] - }, - { - name: 'projects', - fa_icon: 'fa-solid fa-cubes', - href: '/projects' - }, - { - name: 'users', - fa_icon: 'fa-solid fa-user', - href: '/users' - }, - { - name: 'userGroups', - fa_icon: 'fa-solid fa-users', - href: '/user-groups' - }, - { - name: 'roleAssignments', - fa_icon: 'fa-solid fa-user-tag', - href: '/role-assignments' - }, - { - name: 'assets', - fa_icon: 'fa-solid fa-gem', - href: '/assets' - } - ] - }, - { - name: 'catalog', - items: [ - { - name: 'frameworks', - fa_icon: 'fa-solid fa-book', - href: '/frameworks' - }, - { - name: 'threats', - fa_icon: 'fa-solid fa-biohazard', - href: '/threats' - }, - { - name: 'referenceControls', - fa_icon: 'fa-solid fa-gears', - href: '/reference-controls' - }, - { - name: 'requirementMappingSets', - fa_icon: 'fa-solid fa-diagram-project', - href: '/requirement-mapping-sets' - }, - { - name: 'riskMatrices', - fa_icon: 'fa-solid fa-table-cells-large', - href: '/risk-matrices' - } - ] - }, - { - name: 'operations', - items: [ - { - name: 'appliedControls', - fa_icon: 'fa-solid fa-fire-extinguisher', - href: '/applied-controls' - }, - { - name: 'calendar', - fa_icon: 'fa-solid fa-calendar-days', - href: '/calendar', - permissions: ['view_appliedcontrol', 'view_riskacceptance', 'view_riskassessment'] - }, - { - name: 'xRays', - fa_icon: 'fa-solid fa-bolt', - href: '/x-rays', - permissions: ['view_riskassessment', 'view_assessment'] - } - ] - }, - { - name: 'governance', - items: [ - { - name: 'libraries', - fa_icon: 'fa-solid fa-folder-plus', - href: '/libraries', - permissions: ['add_threat', 'add_riskmatrix', 'add_referencecontrol', 'add_framework'] - }, - { - name: 'policies', - fa_icon: 'fa-solid fa-book', - href: '/policies', - permissions: ['view_appliedcontrol'] - }, - { - name: 'riskAcceptances', - fa_icon: 'fa-solid fa-signature', - href: '/risk-acceptances' - } - ] - }, - { - name: 'risk', - items: [ - { - name: 'riskAssessments', - fa_icon: 'fa-solid fa-magnifying-glass-chart', - href: '/risk-assessments' - }, - { - name: 'riskScenarios', - fa_icon: 'fa-solid fa-clone', - href: '/risk-scenarios' - }, - { - name: 'scoringAssistant', - fa_icon: 'fa-solid fa-star-half-stroke', - href: '/scoring-assistant', - permissions: ['view_riskmatrix'] - }, - { - name: 'vulnerabilities', - // What is the best icon between "fa-triangle-exclamation" and "fa-skull-crossbones" for a vulnerability ? - fa_icon: 'fa-solid fa-triangle-exclamation', - href: '/vulnerabilities' - } - ] - }, + items: [ + // { + // name: 'Home', + // items: [ + // { + // name: 'Home', + // fa_icon: 'fa-solid fa-house', + // href: '/' + // }, + // { name: 'Quick start', fa_icon: 'fa-solid fa-plane', href: '/quick-start' } + // ] + // }, + { + name: 'overview', + items: [ + { + name: 'analytics', + fa_icon: 'fa-solid fa-gauge', + href: '/analytics', + permissions: [ + 'view_project', + 'view_riskscenario', + 'view_referencecontrol', + 'view_assessment', + 'view_riskassessment' + ] + }, + { + name: 'myAssignments', + fa_icon: 'fa-solid fa-list-check', + href: '/my-assignments', + permissions: [ + 'view_project', + 'view_riskscenario', + 'view_referencecontrol', + 'view_assessment', + 'view_riskassessment' + ] + } + ] + }, + { + name: 'organization', + items: [ + { + name: 'domains', + fa_icon: 'fa-solid fa-sitemap', + href: '/folders', + exclude: ['BI-RL-TPR'] + }, + { + name: 'projects', + fa_icon: 'fa-solid fa-cubes', + href: '/projects' + }, + { + name: 'users', + fa_icon: 'fa-solid fa-user', + href: '/users' + }, + { + name: 'userGroups', + fa_icon: 'fa-solid fa-users', + href: '/user-groups' + }, + { + name: 'roleAssignments', + fa_icon: 'fa-solid fa-user-tag', + href: '/role-assignments' + }, + { + name: 'assets', + fa_icon: 'fa-solid fa-gem', + href: '/assets' + } + ] + }, + { + name: 'catalog', + items: [ + { + name: 'frameworks', + fa_icon: 'fa-solid fa-book', + href: '/frameworks' + }, + { + name: 'threats', + fa_icon: 'fa-solid fa-biohazard', + href: '/threats' + }, + { + name: 'referenceControls', + fa_icon: 'fa-solid fa-gears', + href: '/reference-controls' + }, + { + name: 'requirementMappingSets', + fa_icon: 'fa-solid fa-diagram-project', + href: '/requirement-mapping-sets' + }, + { + name: 'riskMatrices', + fa_icon: 'fa-solid fa-table-cells-large', + href: '/risk-matrices' + } + ] + }, + { + name: 'operations', + items: [ + { + name: 'appliedControls', + fa_icon: 'fa-solid fa-fire-extinguisher', + href: '/applied-controls' + }, + { + name: 'calendar', + fa_icon: 'fa-solid fa-calendar-days', + href: '/calendar', + permissions: ['view_appliedcontrol', 'view_riskacceptance', 'view_riskassessment'] + }, + { + name: 'xRays', + fa_icon: 'fa-solid fa-bolt', + href: '/x-rays', + permissions: ['view_riskassessment', 'view_assessment'] + } + ] + }, + { + name: 'governance', + items: [ + { + name: 'libraries', + fa_icon: 'fa-solid fa-folder-plus', + href: '/libraries', + permissions: ['add_threat', 'add_riskmatrix', 'add_referencecontrol', 'add_framework'] + }, + { + name: 'policies', + fa_icon: 'fa-solid fa-book', + href: '/policies', + permissions: ['view_appliedcontrol'] + }, + { + name: 'riskAcceptances', + fa_icon: 'fa-solid fa-signature', + href: '/risk-acceptances' + } + ] + }, + { + name: 'risk', + items: [ + { + name: 'riskAssessments', + fa_icon: 'fa-solid fa-magnifying-glass-chart', + href: '/risk-assessments' + }, + { + name: 'riskScenarios', + fa_icon: 'fa-solid fa-clone', + href: '/risk-scenarios' + }, + { + name: 'scoringAssistant', + fa_icon: 'fa-solid fa-star-half-stroke', + href: '/scoring-assistant', + permissions: ['view_riskmatrix'] + }, + { + name: 'vulnerabilities', + // What is the best icon between "fa-triangle-exclamation" and "fa-skull-crossbones" for a vulnerability ? + fa_icon: 'fa-solid fa-triangle-exclamation', + href: '/vulnerabilities' + } + ] + }, - { - name: 'compliance', - items: [ - { - name: 'complianceAssessments', - fa_icon: 'fa-solid fa-certificate', - href: '/compliance-assessments' - }, - { - name: 'evidences', - fa_icon: 'fa-solid fa-receipt', - href: '/evidences' - } - ] - }, + { + name: 'compliance', + items: [ + { + name: 'complianceAssessments', + fa_icon: 'fa-solid fa-certificate', + href: '/compliance-assessments' + }, + { + name: 'evidences', + fa_icon: 'fa-solid fa-receipt', + href: '/evidences' + } + ] + }, - { - name: 'thirdPartyCategory', - items: [ - { - name: 'entities', - fa_icon: 'fa-solid fa-building', - href: '/entities' - }, - { - name: 'entityAssessments', - fa_icon: 'fa-solid fa-clipboard-list', - href: '/entity-assessments' - }, - { - name: 'representatives', - fa_icon: 'fa-solid fa-user-tie', - href: '/representatives' - }, - { - name: 'solutions', - fa_icon: 'fa-solid fa-box', - href: '/solutions' - } - ] - }, - { - name: 'extra', - items: [ - { - name: 'inspect', - fa_icon: 'fa-brands fa-searchengin', - href: '/x-rays/inspect', - permissions: ['view_riskassessment', 'view_assessment'] - }, - { - name: 'backupRestore', - fa_icon: 'fa-solid fa-floppy-disk', - href: '/backup-restore', - permissions: ['backup'] - }, - { - name: 'labels', - fa_icon: 'fa-solid fa-tag', - href: '/filtering-labels', - permissions: ['view_filteringlabel'] - }, - { - name: 'settings', - fa_icon: 'fa-solid fa-cog', - href: '/settings', - permissions: ['change_globalsettings'] - }, - { - name: 'Experimental', - fa_icon: 'fa-solid fa-flask', - href: '/experimental', - permissions: ['change_globalsettings'] - } - ] - } - ] + { + name: 'thirdPartyCategory', + items: [ + { + name: 'entities', + fa_icon: 'fa-solid fa-building', + href: '/entities' + }, + { + name: 'entityAssessments', + fa_icon: 'fa-solid fa-clipboard-list', + href: '/entity-assessments' + }, + { + name: 'representatives', + fa_icon: 'fa-solid fa-user-tie', + href: '/representatives' + }, + { + name: 'solutions', + fa_icon: 'fa-solid fa-box', + href: '/solutions' + } + ] + }, + { + name: 'extra', + items: [ + { + name: 'labels', + fa_icon: 'fa-solid fa-tag', + href: '/filtering-labels', + permissions: ['view_filteringlabel'] + }, + { + name: 'settings', + fa_icon: 'fa-solid fa-cog', + href: '/settings', + permissions: ['change_globalsettings'] + }, + { + name: 'backupRestore', + fa_icon: 'fa-solid fa-floppy-disk', + href: '/backup-restore', + permissions: ['backup'] + }, + { + name: 'Experimental', + fa_icon: 'fa-solid fa-flask', + href: '/experimental', + permissions: ['change_globalsettings'] + } + ] + } + ] }; diff --git a/frontend/src/lib/utils/table.ts b/frontend/src/lib/utils/table.ts index a893c1496..3cab58679 100644 --- a/frontend/src/lib/utils/table.ts +++ b/frontend/src/lib/utils/table.ts @@ -6,210 +6,212 @@ import { LOCALE_DISPLAY_MAP } from './constants'; import type { Row } from '@vincjo/datatables'; interface ListViewFilterConfig { - component: ComponentType; - filter?: (columnValue: any, value: any) => boolean; - getColumn?: (row: Row) => Row[keyof Row]; - filterProps?: (rows: any[], field: string) => { [key: string]: any }; - extraProps?: { [key: string]: any }; - alwaysDisplay?: boolean; - alwaysDefined?: boolean; - hide?: boolean; + component: ComponentType; + filter?: (columnValue: any, value: any) => boolean; + getColumn?: (row: Row) => Row[keyof Row]; + filterProps?: (rows: any[], field: string) => { [key: string]: any }; + extraProps?: { [key: string]: any }; + alwaysDisplay?: boolean; + alwaysDefined?: boolean; + hide?: boolean; } interface ListViewFieldsConfig { - [key: string]: { - head: string[]; - body: string[]; - meta?: string[]; - breadcrumb_link_disabled?: boolean; - filters?: { - [key: string]: ListViewFilterConfig; - }; - }; + [key: string]: { + head: string[]; + body: string[]; + meta?: string[]; + breadcrumb_link_disabled?: boolean; + filters?: { + [key: string]: ListViewFilterConfig; + }; + }; } const PROJECT_STATUS_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.lc_status, - extraProps: { - defaultOptionName: 'status' - }, - alwaysDisplay: true + component: SelectFilter, + getColumn: (row) => row.lc_status, + extraProps: { + defaultOptionName: 'status' + }, + alwaysDisplay: true }; const DOMAIN_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.folder?.str, - alwaysDefined: true, - extraProps: { - defaultOptionName: 'domain' - } + component: SelectFilter, + getColumn: (row) => row.folder?.str, + alwaysDefined: true, + extraProps: { + defaultOptionName: 'domain' + } }; const LABELS_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => { - return row.filtering_labels && row.filtering_labels.length > 0 - ? row.filtering_labels?.map((filtering_label) => filtering_label.str) - : ['']; - }, - alwaysDefined: true, - extraProps: { - defaultOptionName: 'filtering_labels' - } + component: SelectFilter, + getColumn: (row) => { + return row.filtering_labels && row.filtering_labels.length > 0 + ? row.filtering_labels?.map((filtering_label) => filtering_label.str) + : ['']; + }, + alwaysDefined: true, + extraProps: { + defaultOptionName: 'filtering_labels' + } }; const PRIORITY_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.priority, - alwaysDisplay: true, - extraProps: { - defaultOptionName: 'priority' - } + component: SelectFilter, + getColumn: (row) => row.priority, + alwaysDisplay: true, + extraProps: { + defaultOptionName: 'priority' + } }; const DOMAIN_FILTER_FROM_PROJECT: ListViewFilterConfig = { - ...DOMAIN_FILTER, - getColumn: (row) => row.project?.folder.str + ...DOMAIN_FILTER, + getColumn: (row) => row.project?.folder.str }; const PROJECT_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.project?.str, - extraProps: { - defaultOptionName: 'project' // Make translations - } + component: SelectFilter, + getColumn: (row) => row.project?.str, + extraProps: { + defaultOptionName: 'project' // Make translations + } }; const STATUS_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.status, - extraProps: { - defaultOptionName: 'status' - }, - alwaysDisplay: true + component: SelectFilter, + getColumn: (row) => row.status, + extraProps: { + defaultOptionName: 'status' + }, + alwaysDisplay: true }; const TREATMENT_FILTER: ListViewFilterConfig = { - // I could make a function just make the code less repeatitive and long for nothing - component: SelectFilter, - getColumn: (row) => row.treatment, - extraProps: { - defaultOptionName: 'treatment' - } + // I could make a function just make the code less repeatitive and long for nothing + component: SelectFilter, + getColumn: (row) => row.treatment, + extraProps: { + defaultOptionName: 'treatment' + } }; const STATE_FILTER: ListViewFilterConfig = { - // I could make a function just make the code less repeatitive and long for nothing - component: SelectFilter, - getColumn: (row) => row.state, - extraProps: { - defaultOptionName: 'state' - } + // I could make a function just make the code less repeatitive and long for nothing + component: SelectFilter, + getColumn: (row) => row.state, + extraProps: { + defaultOptionName: 'state' + } }; const APPROVER_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => { - if (row.first_name && row.last_name) { - return `${row.first_name} ${row.last_name}`; - } - return row.approver?.str; // This display the email in the approver filter, is this a problem because of email leak risks ? - }, - extraProps: { - defaultOptionName: 'approver' - } + component: SelectFilter, + getColumn: (row) => { + if (row.first_name && row.last_name) { + return `${row.first_name} ${row.last_name}`; + } + return row.approver?.str; // This display the email in the approver filter, is this a problem because of email leak risks ? + }, + extraProps: { + defaultOptionName: 'approver' + } }; const RISK_ASSESSMENT_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.risk_assessment.name, - extraProps: { - defaultOptionName: 'riskAssessment' - } + component: SelectFilter, + getColumn: (row) => row.risk_assessment.name, + extraProps: { + defaultOptionName: 'riskAssessment' + } }; + + const PROVIDER_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => { - return row.provider; - }, - extraProps: { - defaultOptionName: 'provider' - } + component: SelectFilter, + getColumn: (row) => { + return row.provider; + }, + extraProps: { + defaultOptionName: 'provider' + } }; const THREAT_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => (row.threats?.length ? row.threats.map((t) => t.str) : null), - extraProps: { - defaultOptionName: 'threat' - } + component: SelectFilter, + getColumn: (row) => (row.threats?.length ? row.threats.map((t) => t.str) : null), + extraProps: { + defaultOptionName: 'threat' + } }; const ASSET_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => (row.assets?.length ? row.assets.map((t) => t.str) : null), - extraProps: { - defaultOptionName: 'asset' - }, - alwaysDisplay: true + component: SelectFilter, + getColumn: (row) => (row.assets?.length ? row.assets.map((t) => t.str) : null), + extraProps: { + defaultOptionName: 'asset' + }, + alwaysDisplay: true }; const FRAMEWORK_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.framework.ref_id, - extraProps: { - defaultOptionName: 'framework' // Make translations - } + component: SelectFilter, + getColumn: (row) => row.framework.ref_id, + extraProps: { + defaultOptionName: 'framework' // Make translations + } }; const LANGUAGE_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.locales, - extraProps: { - defaultOptionName: 'language', // Make translations - optionLabels: LOCALE_DISPLAY_MAP - } + component: SelectFilter, + getColumn: (row) => row.locales, + extraProps: { + defaultOptionName: 'language', // Make translations + optionLabels: LOCALE_DISPLAY_MAP + } }; const ASSET_TYPE_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.type, - extraProps: { - defaultOptionName: 'type' // Make translations - }, - alwaysDisplay: true + component: SelectFilter, + getColumn: (row) => row.type, + extraProps: { + defaultOptionName: 'type' // Make translations + }, + alwaysDisplay: true }; const CATEGORY_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.category, - extraProps: { - defaultOptionName: 'category' // Make translations - }, - alwaysDisplay: true + component: SelectFilter, + getColumn: (row) => row.category, + extraProps: { + defaultOptionName: 'category' // Make translations + }, + alwaysDisplay: true }; const CSF_FUNCTION_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.csf_function, - extraProps: { - defaultOptionName: 'csfFunction' // Make translations - }, - alwaysDisplay: true + component: SelectFilter, + getColumn: (row) => row.csf_function, + extraProps: { + defaultOptionName: 'csfFunction' // Make translations + }, + alwaysDisplay: true }; const OWNER_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => { - const owner = row?.meta?.owner; - return owner && owner.length ? owner.map((o) => o.str) : null; - }, - extraProps: { - defaultOptionName: 'owner' - }, - alwaysDisplay: true + component: SelectFilter, + getColumn: (row) => { + const owner = row?.meta?.owner; + return owner && owner.length ? owner.map((o) => o.str) : null; + }, + extraProps: { + defaultOptionName: 'owner' + }, + alwaysDisplay: true }; /* const HAS_RISK_MATRIX_FILTER: ListViewFilterConfig = { component: CheckboxFilter, @@ -228,297 +230,299 @@ const OWNER_FILTER: ListViewFilterConfig = { }; */ const LIBRARY_TYPE_FILTER = { - component: SelectFilter, - getColumn: (row) => { - const overviewKeys = new Set(row.overview?.map((overviewRow) => overviewRow.split(':')[0])); - const libraryDatatypeSet = new Set([ - 'framework', - 'risk_matrix', - 'threats', - 'requirement_mapping_set', - 'reference_controls' - ]); - const datatypes = [...libraryDatatypeSet].filter((datatype) => overviewKeys.has(datatype)); - return datatypes; - }, - extraProps: { - defaultOptionName: 'objectType' - }, - alwaysDisplay: true + component: SelectFilter, + getColumn: (row) => { + const overviewKeys = new Set(row.overview?.map((overviewRow) => overviewRow.split(':')[0])); + const libraryDatatypeSet = new Set([ + 'framework', + 'risk_matrix', + 'threats', + 'requirement_mapping_set', + 'reference_controls' + ]); + const datatypes = [...libraryDatatypeSet].filter((datatype) => overviewKeys.has(datatype)); + return datatypes; + }, + extraProps: { + defaultOptionName: 'objectType' + }, + alwaysDisplay: true }; export const listViewFields: ListViewFieldsConfig = { - folders: { - head: ['name', 'description', 'parentDomain'], - body: ['name', 'description', 'parent_folder'] - }, - projects: { - head: ['ref_id', 'name', 'description', 'domain'], - body: ['ref_id', 'name', 'description', 'folder'], - filters: { - folder: DOMAIN_FILTER, - lc_status: PROJECT_STATUS_FILTER - } - }, - 'filtering-labels': { - head: ['label'], - body: ['label'] - }, - 'risk-matrices': { - head: ['name', 'description', 'provider', 'domain'], - body: ['name', 'description', 'provider', 'folder'], - meta: ['id', 'urn'], - filters: { - folder: DOMAIN_FILTER - } - }, - vulnerabilities: { - head: ['ref_id', 'name', 'description', 'applied_controls', 'folder', 'labels'], - body: ['ref_id', 'name', 'description', 'applied_controls', 'folder', 'filtering_labels'], - filters: { - folder: DOMAIN_FILTER, - filtering_labels: LABELS_FILTER - } - }, - 'risk-assessments': { - head: ['ref_id', 'name', 'riskMatrix', 'description', 'riskScenarios', 'project'], - body: ['ref_id', 'str', 'risk_matrix', 'description', 'risk_scenarios_count', 'project'], - filters: { - folder: { ...DOMAIN_FILTER_FROM_PROJECT, alwaysDisplay: true }, - project: PROJECT_FILTER, - status: { ...STATUS_FILTER, alwaysDisplay: true } - } - }, - threats: { - head: ['ref_id', 'name', 'description', 'provider', 'domain'], - body: ['ref_id', 'name', 'description', 'provider', 'folder'], - meta: ['id', 'urn'], - filters: { - folder: DOMAIN_FILTER, - provider: PROVIDER_FILTER - } - }, - 'risk-scenarios': { - head: [ - 'ref_id', - 'name', - 'threats', - 'riskAssessment', - 'existingAppliedControls', - 'currentLevel', - 'extraAppliedControls', - 'residualLevel' - ], - body: [ - 'ref_id', - 'name', - 'threats', - 'risk_assessment', - 'existing_applied_controls', - 'current_level', - 'applied_controls', - 'residual_level' - ], - filters: { - folder: { ...DOMAIN_FILTER_FROM_PROJECT, alwaysDisplay: true }, - project: { ...PROJECT_FILTER, alwaysDisplay: true }, - treatment: { ...TREATMENT_FILTER, alwaysDisplay: true }, - risk_assessment: RISK_ASSESSMENT_FILTER, - threats: THREAT_FILTER, - assets: ASSET_FILTER - } - }, - 'risk-acceptances': { - head: ['name', 'description', 'riskScenarios'], - body: ['name', 'description', 'risk_scenarios'], - filters: { - folder: DOMAIN_FILTER, - state: STATE_FILTER, - approver: APPROVER_FILTER - } - }, - 'applied-controls': { - head: [ - 'ref_id', - 'name', - 'description', - 'category', - 'csfFunction', - 'eta', - 'owner', - 'domain', - 'referenceControl' - ], - body: [ - 'ref_id', - 'name', - 'description', - 'category', - 'csf_function', - 'eta', - 'owner', - 'folder', - 'reference_control' - ], - filters: { - folder: DOMAIN_FILTER, - status: STATUS_FILTER, - category: CATEGORY_FILTER, - csf_function: CSF_FUNCTION_FILTER, - owner: OWNER_FILTER, - priority: PRIORITY_FILTER - } - }, - policies: { - head: ['name', 'description', 'csfFunction', 'eta', 'owner', 'domain', 'referenceControl'], - body: ['name', 'description', 'csf_function', 'eta', 'owner', 'folder', 'reference_control'], - filters: { - folder: DOMAIN_FILTER, - status: STATUS_FILTER, - csf_function: CSF_FUNCTION_FILTER, - owner: OWNER_FILTER, - priority: PRIORITY_FILTER - } - }, - 'reference-controls': { - head: ['ref_id', 'name', 'description', 'category', 'csfFunction', 'provider', 'domain'], - body: ['ref_id', 'name', 'description', 'category', 'csf_function', 'provider', 'folder'], - meta: ['id', 'urn'], - filters: { - folder: { ...DOMAIN_FILTER, alwaysDisplay: true }, - category: CATEGORY_FILTER, - provider: PROVIDER_FILTER, - csf_function: CSF_FUNCTION_FILTER - } - }, - assets: { - head: [ - 'name', - 'description', - 'businessValue', - 'securityObjectives', - 'disasterRecoveryObjectives', - 'owner', - 'domain' - ], - body: [ - 'name', - 'description', - 'business_value', - 'security_objectives', - 'disaster_recovery_objectives', - 'owner', - 'folder' - ], - filters: { - folder: DOMAIN_FILTER, - type: ASSET_TYPE_FILTER - } - }, - users: { - head: ['email', 'firstName', 'lastName'], - body: ['email', 'first_name', 'last_name'] - }, - 'user-groups': { - head: ['name'], - body: ['localization_dict'], - meta: ['id', 'builtin'] - }, - roles: { - head: ['name', 'description'], - body: ['name', 'description'] - }, - 'role-assignments': { - head: ['user', 'userGroup', 'role', 'perimeter'], - body: ['user', 'user_group', 'role', 'perimeter_folders'] - }, - frameworks: { - head: ['name', 'description', 'provider', 'complianceAssessments', 'domain'], - body: ['name', 'description', 'provider', 'compliance_assessments', 'folder'], - meta: ['id', 'urn'], - filters: { - folder: DOMAIN_FILTER, - provider: PROVIDER_FILTER - } - }, - 'compliance-assessments': { - head: ['ref_id', 'name', 'framework', 'description', 'project'], - body: ['ref_id', 'name', 'framework', 'description', 'project'], - filters: { - folder: { ...DOMAIN_FILTER_FROM_PROJECT, alwaysDisplay: true }, // alwaysDisplay shoudln't be mandatory here something is wrong - project: PROJECT_FILTER, - framework: FRAMEWORK_FILTER, - status: STATUS_FILTER - } - }, - 'requirement-assessments': { - head: ['name', 'description', 'complianceAssessment'], - body: ['name', 'description', 'compliance_assessment'], - breadcrumb_link_disabled: true - }, - evidences: { - head: ['name', 'file', 'size', 'description'], - body: ['name', 'attachment', 'size', 'description'], - filters: { - folder: { ...DOMAIN_FILTER, alwaysDisplay: true } // This filter should also be displayed even without alwaysDisplay - } - }, - requirements: { - head: ['ref_id', 'name', 'description', 'framework'], - body: ['ref_id', 'name', 'description', 'framework'], - meta: ['id', 'urn'] - }, - libraries: { - head: ['provider', 'name', 'description', 'language', 'overview'], - body: ['provider', 'name', 'description', 'locales', 'overview'] - }, - 'stored-libraries': { - head: ['provider', 'name', 'description', 'language', 'overview'], - body: ['provider', 'name', 'description', 'locales', 'overview'], - filters: { - locales: LANGUAGE_FILTER, - provider: PROVIDER_FILTER, - objectType: LIBRARY_TYPE_FILTER - } - }, - 'loaded-libraries': { - head: ['provider', 'name', 'description', 'language', 'overview'], - body: ['provider', 'name', 'description', 'locales', 'overview'], - filters: { - locales: LANGUAGE_FILTER, - provider: PROVIDER_FILTER, - objectType: LIBRARY_TYPE_FILTER - } - }, - 'sso-settings': { - head: ['name', 'provider', 'providerId'], - body: ['name', 'provider', 'provider_id'] - }, - 'requirement-mapping-sets': { - head: ['sourceFramework', 'targetFramework'], - body: ['source_framework', 'target_framework'] - }, - entities: { - head: ['name', 'description', 'domain', 'ownedFolders'], - body: ['name', 'description', 'folder', 'owned_folders'], - filters: { - folder: DOMAIN_FILTER - } - }, - 'entity-assessments': { - head: ['name', 'description', 'project', 'entity'], - body: ['name', 'description', 'project', 'entity'], - filters: { - project: PROJECT_FILTER, - status: STATUS_FILTER - } - }, - solutions: { - head: ['name', 'description', 'providerEntity', 'recipientEntity', 'criticality'], - body: ['name', 'description', 'provider_entity', 'recipient_entity', 'criticality'] - }, - representatives: { - head: ['email', 'entity', 'role'], - body: ['email', 'entity', 'role'] - } + folders: { + head: ['name', 'description', 'parentDomain'], + body: ['name', 'description', 'parent_folder'] + }, + projects: { + head: ['ref_id', 'name', 'description', 'domain'], + body: ['ref_id', 'name', 'description', 'folder'], + filters: { + folder: DOMAIN_FILTER, + lc_status: PROJECT_STATUS_FILTER + } + }, + 'filtering-labels': { + head: ['label'], + body: ['label'] + }, + 'risk-matrices': { + head: ['name', 'description', 'provider', 'domain'], + body: ['name', 'description', 'provider', 'folder'], + meta: ['id', 'urn'], + filters: { + folder: DOMAIN_FILTER + } + }, + vulnerabilities: { + head: ['ref_id', 'name', 'description', 'applied_controls', 'folder', 'labels'], + body: ['ref_id', 'name', 'description', 'applied_controls', 'folder', 'filtering_labels'], + filters: { + folder: DOMAIN_FILTER, + filtering_labels: LABELS_FILTER + } + }, + 'risk-assessments': { + head: ['ref_id', 'name', 'riskMatrix', 'description', 'riskScenarios', 'project'], + body: ['ref_id', 'str', 'risk_matrix', 'description', 'risk_scenarios_count', 'project'], + filters: { + folder: { ...DOMAIN_FILTER_FROM_PROJECT, alwaysDisplay: true }, + project: PROJECT_FILTER, + status: { ...STATUS_FILTER, alwaysDisplay: true } + } + }, + threats: { + head: ['ref_id', 'name', 'description', 'provider', 'domain'], + body: ['ref_id', 'name', 'description', 'provider', 'folder'], + meta: ['id', 'urn'], + filters: { + folder: DOMAIN_FILTER, + provider: PROVIDER_FILTER + } + }, + 'risk-scenarios': { + head: [ + 'ref_id', + 'name', + 'threats', + 'riskAssessment', + 'existingAppliedControls', + 'currentLevel', + 'extraAppliedControls', + 'residualLevel' + ], + body: [ + 'ref_id', + 'name', + 'threats', + 'risk_assessment', + 'existing_applied_controls', + 'current_level', + 'applied_controls', + 'residual_level' + ], + filters: { + folder: { ...DOMAIN_FILTER_FROM_PROJECT, alwaysDisplay: true }, + project: { ...PROJECT_FILTER, alwaysDisplay: true }, + treatment: { ...TREATMENT_FILTER, alwaysDisplay: true }, + risk_assessment: RISK_ASSESSMENT_FILTER, + threats: THREAT_FILTER, + assets: ASSET_FILTER + } + }, + 'risk-acceptances': { + head: ['name', 'description', 'riskScenarios'], + body: ['name', 'description', 'risk_scenarios'], + filters: { + folder: DOMAIN_FILTER, + state: STATE_FILTER, + approver: APPROVER_FILTER + } + }, + 'applied-controls': { + head: [ + 'ref_id', + 'name', + 'priority', + 'status', + 'category', + 'csfFunction', + 'eta', + 'owner', + 'domain', + 'referenceControl' + ], + body: [ + 'ref_id', + 'name', + 'priority', + 'status', + 'category', + 'csf_function', + 'eta', + 'owner', + 'folder', + 'reference_control' + ], + filters: { + folder: DOMAIN_FILTER, + status: STATUS_FILTER, + category: CATEGORY_FILTER, + csf_function: CSF_FUNCTION_FILTER, + owner: OWNER_FILTER, + priority: PRIORITY_FILTER + } + }, + policies: { + head: ['name', 'description', 'csfFunction', 'eta', 'owner', 'domain', 'referenceControl'], + body: ['name', 'description', 'csf_function', 'eta', 'owner', 'folder', 'reference_control'], + filters: { + folder: DOMAIN_FILTER, + status: STATUS_FILTER, + csf_function: CSF_FUNCTION_FILTER, + owner: OWNER_FILTER, + priority: PRIORITY_FILTER + } + }, + 'reference-controls': { + head: ['ref_id', 'name', 'description', 'category', 'csfFunction', 'provider', 'domain'], + body: ['ref_id', 'name', 'description', 'category', 'csf_function', 'provider', 'folder'], + meta: ['id', 'urn'], + filters: { + folder: { ...DOMAIN_FILTER, alwaysDisplay: true }, + category: CATEGORY_FILTER, + provider: PROVIDER_FILTER, + csf_function: CSF_FUNCTION_FILTER + } + }, + assets: { + head: [ + 'name', + 'type', + 'description', + 'securityObjectives', + 'disasterRecoveryObjectives', + 'owner', + 'domain' + ], + body: [ + 'name', + 'type', + 'description', + 'security_objectives', + 'disaster_recovery_objectives', + 'owner', + 'folder' + ], + filters: { + folder: DOMAIN_FILTER, + type: ASSET_TYPE_FILTER + } + }, + users: { + head: ['email', 'firstName', 'lastName'], + body: ['email', 'first_name', 'last_name'] + }, + 'user-groups': { + head: ['name'], + body: ['localization_dict'], + meta: ['id', 'builtin'] + }, + roles: { + head: ['name', 'description'], + body: ['name', 'description'] + }, + 'role-assignments': { + head: ['user', 'userGroup', 'role', 'perimeter'], + body: ['user', 'user_group', 'role', 'perimeter_folders'] + }, + frameworks: { + head: ['name', 'description', 'provider', 'complianceAssessments', 'domain'], + body: ['name', 'description', 'provider', 'compliance_assessments', 'folder'], + meta: ['id', 'urn'], + filters: { + folder: DOMAIN_FILTER, + provider: PROVIDER_FILTER + } + }, + 'compliance-assessments': { + head: ['ref_id', 'name', 'framework', 'description', 'project'], + body: ['ref_id', 'name', 'framework', 'description', 'project'], + filters: { + folder: { ...DOMAIN_FILTER_FROM_PROJECT, alwaysDisplay: true }, // alwaysDisplay shoudln't be mandatory here something is wrong + project: PROJECT_FILTER, + framework: FRAMEWORK_FILTER, + status: STATUS_FILTER + } + }, + 'requirement-assessments': { + head: ['name', 'description', 'complianceAssessment'], + body: ['name', 'description', 'compliance_assessment'], + breadcrumb_link_disabled: true + }, + evidences: { + head: ['name', 'file', 'size', 'description'], + body: ['name', 'attachment', 'size', 'description'], + filters: { + folder: { ...DOMAIN_FILTER, alwaysDisplay: true } // This filter should also be displayed even without alwaysDisplay + } + }, + requirements: { + head: ['ref_id', 'name', 'description', 'framework'], + body: ['ref_id', 'name', 'description', 'framework'], + meta: ['id', 'urn'] + }, + libraries: { + head: ['provider', 'name', 'description', 'language', 'overview'], + body: ['provider', 'name', 'description', 'locales', 'overview'] + }, + 'stored-libraries': { + head: ['provider', 'name', 'description', 'language', 'overview'], + body: ['provider', 'name', 'description', 'locales', 'overview'], + filters: { + locales: LANGUAGE_FILTER, + provider: PROVIDER_FILTER, + objectType: LIBRARY_TYPE_FILTER + } + }, + 'loaded-libraries': { + head: ['provider', 'name', 'description', 'language', 'overview'], + body: ['provider', 'name', 'description', 'locales', 'overview'], + filters: { + locales: LANGUAGE_FILTER, + provider: PROVIDER_FILTER, + objectType: LIBRARY_TYPE_FILTER + } + }, + 'sso-settings': { + head: ['name', 'provider', 'providerId'], + body: ['name', 'provider', 'provider_id'] + }, + 'requirement-mapping-sets': { + head: ['sourceFramework', 'targetFramework'], + body: ['source_framework', 'target_framework'] + }, + entities: { + head: ['name', 'description', 'domain', 'ownedFolders'], + body: ['name', 'description', 'folder', 'owned_folders'], + filters: { + folder: DOMAIN_FILTER + } + }, + 'entity-assessments': { + head: ['name', 'description', 'project', 'entity'], + body: ['name', 'description', 'project', 'entity'], + filters: { + project: PROJECT_FILTER, + status: STATUS_FILTER + } + }, + solutions: { + head: ['name', 'description', 'providerEntity', 'recipientEntity', 'criticality'], + body: ['name', 'description', 'provider_entity', 'recipient_entity', 'criticality'] + }, + representatives: { + head: ['email', 'entity', 'role'], + body: ['email', 'entity', 'role'] + } }; From 84243ac654ef9253ee737f0334f00885044f5f17 Mon Sep 17 00:00:00 2001 From: Abderrahmane Smimite Date: Thu, 28 Nov 2024 20:11:08 +0100 Subject: [PATCH 076/107] one more column --- frontend/src/lib/utils/table.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/lib/utils/table.ts b/frontend/src/lib/utils/table.ts index 3cab58679..a2cca1164 100644 --- a/frontend/src/lib/utils/table.ts +++ b/frontend/src/lib/utils/table.ts @@ -303,6 +303,7 @@ export const listViewFields: ListViewFieldsConfig = { 'risk-scenarios': { head: [ 'ref_id', + 'treatment', 'name', 'threats', 'riskAssessment', @@ -313,6 +314,7 @@ export const listViewFields: ListViewFieldsConfig = { ], body: [ 'ref_id', + 'treatment', 'name', 'threats', 'risk_assessment', From 6eaee7665611567e527ecaf96e4dd7e7ce506e28 Mon Sep 17 00:00:00 2001 From: Abderrahmane Smimite Date: Thu, 28 Nov 2024 20:53:42 +0100 Subject: [PATCH 077/107] Formatter --- .../src/lib/components/SideBar/navData.ts | 496 +++++----- frontend/src/lib/utils/table.ts | 872 +++++++++--------- 2 files changed, 683 insertions(+), 685 deletions(-) diff --git a/frontend/src/lib/components/SideBar/navData.ts b/frontend/src/lib/components/SideBar/navData.ts index 5395e691e..5177acbc4 100644 --- a/frontend/src/lib/components/SideBar/navData.ts +++ b/frontend/src/lib/components/SideBar/navData.ts @@ -1,252 +1,252 @@ export const navData = { - items: [ - // { - // name: 'Home', - // items: [ - // { - // name: 'Home', - // fa_icon: 'fa-solid fa-house', - // href: '/' - // }, - // { name: 'Quick start', fa_icon: 'fa-solid fa-plane', href: '/quick-start' } - // ] - // }, - { - name: 'overview', - items: [ - { - name: 'analytics', - fa_icon: 'fa-solid fa-gauge', - href: '/analytics', - permissions: [ - 'view_project', - 'view_riskscenario', - 'view_referencecontrol', - 'view_assessment', - 'view_riskassessment' - ] - }, - { - name: 'myAssignments', - fa_icon: 'fa-solid fa-list-check', - href: '/my-assignments', - permissions: [ - 'view_project', - 'view_riskscenario', - 'view_referencecontrol', - 'view_assessment', - 'view_riskassessment' - ] - } - ] - }, - { - name: 'organization', - items: [ - { - name: 'domains', - fa_icon: 'fa-solid fa-sitemap', - href: '/folders', - exclude: ['BI-RL-TPR'] - }, - { - name: 'projects', - fa_icon: 'fa-solid fa-cubes', - href: '/projects' - }, - { - name: 'users', - fa_icon: 'fa-solid fa-user', - href: '/users' - }, - { - name: 'userGroups', - fa_icon: 'fa-solid fa-users', - href: '/user-groups' - }, - { - name: 'roleAssignments', - fa_icon: 'fa-solid fa-user-tag', - href: '/role-assignments' - }, - { - name: 'assets', - fa_icon: 'fa-solid fa-gem', - href: '/assets' - } - ] - }, - { - name: 'catalog', - items: [ - { - name: 'frameworks', - fa_icon: 'fa-solid fa-book', - href: '/frameworks' - }, - { - name: 'threats', - fa_icon: 'fa-solid fa-biohazard', - href: '/threats' - }, - { - name: 'referenceControls', - fa_icon: 'fa-solid fa-gears', - href: '/reference-controls' - }, - { - name: 'requirementMappingSets', - fa_icon: 'fa-solid fa-diagram-project', - href: '/requirement-mapping-sets' - }, - { - name: 'riskMatrices', - fa_icon: 'fa-solid fa-table-cells-large', - href: '/risk-matrices' - } - ] - }, - { - name: 'operations', - items: [ - { - name: 'appliedControls', - fa_icon: 'fa-solid fa-fire-extinguisher', - href: '/applied-controls' - }, - { - name: 'calendar', - fa_icon: 'fa-solid fa-calendar-days', - href: '/calendar', - permissions: ['view_appliedcontrol', 'view_riskacceptance', 'view_riskassessment'] - }, - { - name: 'xRays', - fa_icon: 'fa-solid fa-bolt', - href: '/x-rays', - permissions: ['view_riskassessment', 'view_assessment'] - } - ] - }, - { - name: 'governance', - items: [ - { - name: 'libraries', - fa_icon: 'fa-solid fa-folder-plus', - href: '/libraries', - permissions: ['add_threat', 'add_riskmatrix', 'add_referencecontrol', 'add_framework'] - }, - { - name: 'policies', - fa_icon: 'fa-solid fa-book', - href: '/policies', - permissions: ['view_appliedcontrol'] - }, - { - name: 'riskAcceptances', - fa_icon: 'fa-solid fa-signature', - href: '/risk-acceptances' - } - ] - }, - { - name: 'risk', - items: [ - { - name: 'riskAssessments', - fa_icon: 'fa-solid fa-magnifying-glass-chart', - href: '/risk-assessments' - }, - { - name: 'riskScenarios', - fa_icon: 'fa-solid fa-clone', - href: '/risk-scenarios' - }, - { - name: 'scoringAssistant', - fa_icon: 'fa-solid fa-star-half-stroke', - href: '/scoring-assistant', - permissions: ['view_riskmatrix'] - }, - { - name: 'vulnerabilities', - // What is the best icon between "fa-triangle-exclamation" and "fa-skull-crossbones" for a vulnerability ? - fa_icon: 'fa-solid fa-triangle-exclamation', - href: '/vulnerabilities' - } - ] - }, + items: [ + // { + // name: 'Home', + // items: [ + // { + // name: 'Home', + // fa_icon: 'fa-solid fa-house', + // href: '/' + // }, + // { name: 'Quick start', fa_icon: 'fa-solid fa-plane', href: '/quick-start' } + // ] + // }, + { + name: 'overview', + items: [ + { + name: 'analytics', + fa_icon: 'fa-solid fa-gauge', + href: '/analytics', + permissions: [ + 'view_project', + 'view_riskscenario', + 'view_referencecontrol', + 'view_assessment', + 'view_riskassessment' + ] + }, + { + name: 'myAssignments', + fa_icon: 'fa-solid fa-list-check', + href: '/my-assignments', + permissions: [ + 'view_project', + 'view_riskscenario', + 'view_referencecontrol', + 'view_assessment', + 'view_riskassessment' + ] + } + ] + }, + { + name: 'organization', + items: [ + { + name: 'domains', + fa_icon: 'fa-solid fa-sitemap', + href: '/folders', + exclude: ['BI-RL-TPR'] + }, + { + name: 'projects', + fa_icon: 'fa-solid fa-cubes', + href: '/projects' + }, + { + name: 'users', + fa_icon: 'fa-solid fa-user', + href: '/users' + }, + { + name: 'userGroups', + fa_icon: 'fa-solid fa-users', + href: '/user-groups' + }, + { + name: 'roleAssignments', + fa_icon: 'fa-solid fa-user-tag', + href: '/role-assignments' + }, + { + name: 'assets', + fa_icon: 'fa-solid fa-gem', + href: '/assets' + } + ] + }, + { + name: 'catalog', + items: [ + { + name: 'frameworks', + fa_icon: 'fa-solid fa-book', + href: '/frameworks' + }, + { + name: 'threats', + fa_icon: 'fa-solid fa-biohazard', + href: '/threats' + }, + { + name: 'referenceControls', + fa_icon: 'fa-solid fa-gears', + href: '/reference-controls' + }, + { + name: 'requirementMappingSets', + fa_icon: 'fa-solid fa-diagram-project', + href: '/requirement-mapping-sets' + }, + { + name: 'riskMatrices', + fa_icon: 'fa-solid fa-table-cells-large', + href: '/risk-matrices' + } + ] + }, + { + name: 'operations', + items: [ + { + name: 'appliedControls', + fa_icon: 'fa-solid fa-fire-extinguisher', + href: '/applied-controls' + }, + { + name: 'calendar', + fa_icon: 'fa-solid fa-calendar-days', + href: '/calendar', + permissions: ['view_appliedcontrol', 'view_riskacceptance', 'view_riskassessment'] + }, + { + name: 'xRays', + fa_icon: 'fa-solid fa-bolt', + href: '/x-rays', + permissions: ['view_riskassessment', 'view_assessment'] + } + ] + }, + { + name: 'governance', + items: [ + { + name: 'libraries', + fa_icon: 'fa-solid fa-folder-plus', + href: '/libraries', + permissions: ['add_threat', 'add_riskmatrix', 'add_referencecontrol', 'add_framework'] + }, + { + name: 'policies', + fa_icon: 'fa-solid fa-book', + href: '/policies', + permissions: ['view_appliedcontrol'] + }, + { + name: 'riskAcceptances', + fa_icon: 'fa-solid fa-signature', + href: '/risk-acceptances' + } + ] + }, + { + name: 'risk', + items: [ + { + name: 'riskAssessments', + fa_icon: 'fa-solid fa-magnifying-glass-chart', + href: '/risk-assessments' + }, + { + name: 'riskScenarios', + fa_icon: 'fa-solid fa-clone', + href: '/risk-scenarios' + }, + { + name: 'scoringAssistant', + fa_icon: 'fa-solid fa-star-half-stroke', + href: '/scoring-assistant', + permissions: ['view_riskmatrix'] + }, + { + name: 'vulnerabilities', + // What is the best icon between "fa-triangle-exclamation" and "fa-skull-crossbones" for a vulnerability ? + fa_icon: 'fa-solid fa-triangle-exclamation', + href: '/vulnerabilities' + } + ] + }, - { - name: 'compliance', - items: [ - { - name: 'complianceAssessments', - fa_icon: 'fa-solid fa-certificate', - href: '/compliance-assessments' - }, - { - name: 'evidences', - fa_icon: 'fa-solid fa-receipt', - href: '/evidences' - } - ] - }, + { + name: 'compliance', + items: [ + { + name: 'complianceAssessments', + fa_icon: 'fa-solid fa-certificate', + href: '/compliance-assessments' + }, + { + name: 'evidences', + fa_icon: 'fa-solid fa-receipt', + href: '/evidences' + } + ] + }, - { - name: 'thirdPartyCategory', - items: [ - { - name: 'entities', - fa_icon: 'fa-solid fa-building', - href: '/entities' - }, - { - name: 'entityAssessments', - fa_icon: 'fa-solid fa-clipboard-list', - href: '/entity-assessments' - }, - { - name: 'representatives', - fa_icon: 'fa-solid fa-user-tie', - href: '/representatives' - }, - { - name: 'solutions', - fa_icon: 'fa-solid fa-box', - href: '/solutions' - } - ] - }, - { - name: 'extra', - items: [ - { - name: 'labels', - fa_icon: 'fa-solid fa-tag', - href: '/filtering-labels', - permissions: ['view_filteringlabel'] - }, - { - name: 'settings', - fa_icon: 'fa-solid fa-cog', - href: '/settings', - permissions: ['change_globalsettings'] - }, - { - name: 'backupRestore', - fa_icon: 'fa-solid fa-floppy-disk', - href: '/backup-restore', - permissions: ['backup'] - }, - { - name: 'Experimental', - fa_icon: 'fa-solid fa-flask', - href: '/experimental', - permissions: ['change_globalsettings'] - } - ] - } - ] + { + name: 'thirdPartyCategory', + items: [ + { + name: 'entities', + fa_icon: 'fa-solid fa-building', + href: '/entities' + }, + { + name: 'entityAssessments', + fa_icon: 'fa-solid fa-clipboard-list', + href: '/entity-assessments' + }, + { + name: 'representatives', + fa_icon: 'fa-solid fa-user-tie', + href: '/representatives' + }, + { + name: 'solutions', + fa_icon: 'fa-solid fa-box', + href: '/solutions' + } + ] + }, + { + name: 'extra', + items: [ + { + name: 'labels', + fa_icon: 'fa-solid fa-tag', + href: '/filtering-labels', + permissions: ['view_filteringlabel'] + }, + { + name: 'settings', + fa_icon: 'fa-solid fa-cog', + href: '/settings', + permissions: ['change_globalsettings'] + }, + { + name: 'backupRestore', + fa_icon: 'fa-solid fa-floppy-disk', + href: '/backup-restore', + permissions: ['backup'] + }, + { + name: 'Experimental', + fa_icon: 'fa-solid fa-flask', + href: '/experimental', + permissions: ['change_globalsettings'] + } + ] + } + ] }; diff --git a/frontend/src/lib/utils/table.ts b/frontend/src/lib/utils/table.ts index a2cca1164..a46a2ffab 100644 --- a/frontend/src/lib/utils/table.ts +++ b/frontend/src/lib/utils/table.ts @@ -6,212 +6,210 @@ import { LOCALE_DISPLAY_MAP } from './constants'; import type { Row } from '@vincjo/datatables'; interface ListViewFilterConfig { - component: ComponentType; - filter?: (columnValue: any, value: any) => boolean; - getColumn?: (row: Row) => Row[keyof Row]; - filterProps?: (rows: any[], field: string) => { [key: string]: any }; - extraProps?: { [key: string]: any }; - alwaysDisplay?: boolean; - alwaysDefined?: boolean; - hide?: boolean; + component: ComponentType; + filter?: (columnValue: any, value: any) => boolean; + getColumn?: (row: Row) => Row[keyof Row]; + filterProps?: (rows: any[], field: string) => { [key: string]: any }; + extraProps?: { [key: string]: any }; + alwaysDisplay?: boolean; + alwaysDefined?: boolean; + hide?: boolean; } interface ListViewFieldsConfig { - [key: string]: { - head: string[]; - body: string[]; - meta?: string[]; - breadcrumb_link_disabled?: boolean; - filters?: { - [key: string]: ListViewFilterConfig; - }; - }; + [key: string]: { + head: string[]; + body: string[]; + meta?: string[]; + breadcrumb_link_disabled?: boolean; + filters?: { + [key: string]: ListViewFilterConfig; + }; + }; } const PROJECT_STATUS_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.lc_status, - extraProps: { - defaultOptionName: 'status' - }, - alwaysDisplay: true + component: SelectFilter, + getColumn: (row) => row.lc_status, + extraProps: { + defaultOptionName: 'status' + }, + alwaysDisplay: true }; const DOMAIN_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.folder?.str, - alwaysDefined: true, - extraProps: { - defaultOptionName: 'domain' - } + component: SelectFilter, + getColumn: (row) => row.folder?.str, + alwaysDefined: true, + extraProps: { + defaultOptionName: 'domain' + } }; const LABELS_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => { - return row.filtering_labels && row.filtering_labels.length > 0 - ? row.filtering_labels?.map((filtering_label) => filtering_label.str) - : ['']; - }, - alwaysDefined: true, - extraProps: { - defaultOptionName: 'filtering_labels' - } + component: SelectFilter, + getColumn: (row) => { + return row.filtering_labels && row.filtering_labels.length > 0 + ? row.filtering_labels?.map((filtering_label) => filtering_label.str) + : ['']; + }, + alwaysDefined: true, + extraProps: { + defaultOptionName: 'filtering_labels' + } }; const PRIORITY_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.priority, - alwaysDisplay: true, - extraProps: { - defaultOptionName: 'priority' - } + component: SelectFilter, + getColumn: (row) => row.priority, + alwaysDisplay: true, + extraProps: { + defaultOptionName: 'priority' + } }; const DOMAIN_FILTER_FROM_PROJECT: ListViewFilterConfig = { - ...DOMAIN_FILTER, - getColumn: (row) => row.project?.folder.str + ...DOMAIN_FILTER, + getColumn: (row) => row.project?.folder.str }; const PROJECT_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.project?.str, - extraProps: { - defaultOptionName: 'project' // Make translations - } + component: SelectFilter, + getColumn: (row) => row.project?.str, + extraProps: { + defaultOptionName: 'project' // Make translations + } }; const STATUS_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.status, - extraProps: { - defaultOptionName: 'status' - }, - alwaysDisplay: true + component: SelectFilter, + getColumn: (row) => row.status, + extraProps: { + defaultOptionName: 'status' + }, + alwaysDisplay: true }; const TREATMENT_FILTER: ListViewFilterConfig = { - // I could make a function just make the code less repeatitive and long for nothing - component: SelectFilter, - getColumn: (row) => row.treatment, - extraProps: { - defaultOptionName: 'treatment' - } + // I could make a function just make the code less repeatitive and long for nothing + component: SelectFilter, + getColumn: (row) => row.treatment, + extraProps: { + defaultOptionName: 'treatment' + } }; const STATE_FILTER: ListViewFilterConfig = { - // I could make a function just make the code less repeatitive and long for nothing - component: SelectFilter, - getColumn: (row) => row.state, - extraProps: { - defaultOptionName: 'state' - } + // I could make a function just make the code less repeatitive and long for nothing + component: SelectFilter, + getColumn: (row) => row.state, + extraProps: { + defaultOptionName: 'state' + } }; const APPROVER_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => { - if (row.first_name && row.last_name) { - return `${row.first_name} ${row.last_name}`; - } - return row.approver?.str; // This display the email in the approver filter, is this a problem because of email leak risks ? - }, - extraProps: { - defaultOptionName: 'approver' - } + component: SelectFilter, + getColumn: (row) => { + if (row.first_name && row.last_name) { + return `${row.first_name} ${row.last_name}`; + } + return row.approver?.str; // This display the email in the approver filter, is this a problem because of email leak risks ? + }, + extraProps: { + defaultOptionName: 'approver' + } }; const RISK_ASSESSMENT_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.risk_assessment.name, - extraProps: { - defaultOptionName: 'riskAssessment' - } + component: SelectFilter, + getColumn: (row) => row.risk_assessment.name, + extraProps: { + defaultOptionName: 'riskAssessment' + } }; - - const PROVIDER_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => { - return row.provider; - }, - extraProps: { - defaultOptionName: 'provider' - } + component: SelectFilter, + getColumn: (row) => { + return row.provider; + }, + extraProps: { + defaultOptionName: 'provider' + } }; const THREAT_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => (row.threats?.length ? row.threats.map((t) => t.str) : null), - extraProps: { - defaultOptionName: 'threat' - } + component: SelectFilter, + getColumn: (row) => (row.threats?.length ? row.threats.map((t) => t.str) : null), + extraProps: { + defaultOptionName: 'threat' + } }; const ASSET_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => (row.assets?.length ? row.assets.map((t) => t.str) : null), - extraProps: { - defaultOptionName: 'asset' - }, - alwaysDisplay: true + component: SelectFilter, + getColumn: (row) => (row.assets?.length ? row.assets.map((t) => t.str) : null), + extraProps: { + defaultOptionName: 'asset' + }, + alwaysDisplay: true }; const FRAMEWORK_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.framework.ref_id, - extraProps: { - defaultOptionName: 'framework' // Make translations - } + component: SelectFilter, + getColumn: (row) => row.framework.ref_id, + extraProps: { + defaultOptionName: 'framework' // Make translations + } }; const LANGUAGE_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.locales, - extraProps: { - defaultOptionName: 'language', // Make translations - optionLabels: LOCALE_DISPLAY_MAP - } + component: SelectFilter, + getColumn: (row) => row.locales, + extraProps: { + defaultOptionName: 'language', // Make translations + optionLabels: LOCALE_DISPLAY_MAP + } }; const ASSET_TYPE_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.type, - extraProps: { - defaultOptionName: 'type' // Make translations - }, - alwaysDisplay: true + component: SelectFilter, + getColumn: (row) => row.type, + extraProps: { + defaultOptionName: 'type' // Make translations + }, + alwaysDisplay: true }; const CATEGORY_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.category, - extraProps: { - defaultOptionName: 'category' // Make translations - }, - alwaysDisplay: true + component: SelectFilter, + getColumn: (row) => row.category, + extraProps: { + defaultOptionName: 'category' // Make translations + }, + alwaysDisplay: true }; const CSF_FUNCTION_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => row.csf_function, - extraProps: { - defaultOptionName: 'csfFunction' // Make translations - }, - alwaysDisplay: true + component: SelectFilter, + getColumn: (row) => row.csf_function, + extraProps: { + defaultOptionName: 'csfFunction' // Make translations + }, + alwaysDisplay: true }; const OWNER_FILTER: ListViewFilterConfig = { - component: SelectFilter, - getColumn: (row) => { - const owner = row?.meta?.owner; - return owner && owner.length ? owner.map((o) => o.str) : null; - }, - extraProps: { - defaultOptionName: 'owner' - }, - alwaysDisplay: true + component: SelectFilter, + getColumn: (row) => { + const owner = row?.meta?.owner; + return owner && owner.length ? owner.map((o) => o.str) : null; + }, + extraProps: { + defaultOptionName: 'owner' + }, + alwaysDisplay: true }; /* const HAS_RISK_MATRIX_FILTER: ListViewFilterConfig = { component: CheckboxFilter, @@ -230,301 +228,301 @@ const OWNER_FILTER: ListViewFilterConfig = { }; */ const LIBRARY_TYPE_FILTER = { - component: SelectFilter, - getColumn: (row) => { - const overviewKeys = new Set(row.overview?.map((overviewRow) => overviewRow.split(':')[0])); - const libraryDatatypeSet = new Set([ - 'framework', - 'risk_matrix', - 'threats', - 'requirement_mapping_set', - 'reference_controls' - ]); - const datatypes = [...libraryDatatypeSet].filter((datatype) => overviewKeys.has(datatype)); - return datatypes; - }, - extraProps: { - defaultOptionName: 'objectType' - }, - alwaysDisplay: true + component: SelectFilter, + getColumn: (row) => { + const overviewKeys = new Set(row.overview?.map((overviewRow) => overviewRow.split(':')[0])); + const libraryDatatypeSet = new Set([ + 'framework', + 'risk_matrix', + 'threats', + 'requirement_mapping_set', + 'reference_controls' + ]); + const datatypes = [...libraryDatatypeSet].filter((datatype) => overviewKeys.has(datatype)); + return datatypes; + }, + extraProps: { + defaultOptionName: 'objectType' + }, + alwaysDisplay: true }; export const listViewFields: ListViewFieldsConfig = { - folders: { - head: ['name', 'description', 'parentDomain'], - body: ['name', 'description', 'parent_folder'] - }, - projects: { - head: ['ref_id', 'name', 'description', 'domain'], - body: ['ref_id', 'name', 'description', 'folder'], - filters: { - folder: DOMAIN_FILTER, - lc_status: PROJECT_STATUS_FILTER - } - }, - 'filtering-labels': { - head: ['label'], - body: ['label'] - }, - 'risk-matrices': { - head: ['name', 'description', 'provider', 'domain'], - body: ['name', 'description', 'provider', 'folder'], - meta: ['id', 'urn'], - filters: { - folder: DOMAIN_FILTER - } - }, - vulnerabilities: { - head: ['ref_id', 'name', 'description', 'applied_controls', 'folder', 'labels'], - body: ['ref_id', 'name', 'description', 'applied_controls', 'folder', 'filtering_labels'], - filters: { - folder: DOMAIN_FILTER, - filtering_labels: LABELS_FILTER - } - }, - 'risk-assessments': { - head: ['ref_id', 'name', 'riskMatrix', 'description', 'riskScenarios', 'project'], - body: ['ref_id', 'str', 'risk_matrix', 'description', 'risk_scenarios_count', 'project'], - filters: { - folder: { ...DOMAIN_FILTER_FROM_PROJECT, alwaysDisplay: true }, - project: PROJECT_FILTER, - status: { ...STATUS_FILTER, alwaysDisplay: true } - } - }, - threats: { - head: ['ref_id', 'name', 'description', 'provider', 'domain'], - body: ['ref_id', 'name', 'description', 'provider', 'folder'], - meta: ['id', 'urn'], - filters: { - folder: DOMAIN_FILTER, - provider: PROVIDER_FILTER - } - }, - 'risk-scenarios': { - head: [ - 'ref_id', - 'treatment', - 'name', - 'threats', - 'riskAssessment', - 'existingAppliedControls', - 'currentLevel', - 'extraAppliedControls', - 'residualLevel' - ], - body: [ - 'ref_id', - 'treatment', - 'name', - 'threats', - 'risk_assessment', - 'existing_applied_controls', - 'current_level', - 'applied_controls', - 'residual_level' - ], - filters: { - folder: { ...DOMAIN_FILTER_FROM_PROJECT, alwaysDisplay: true }, - project: { ...PROJECT_FILTER, alwaysDisplay: true }, - treatment: { ...TREATMENT_FILTER, alwaysDisplay: true }, - risk_assessment: RISK_ASSESSMENT_FILTER, - threats: THREAT_FILTER, - assets: ASSET_FILTER - } - }, - 'risk-acceptances': { - head: ['name', 'description', 'riskScenarios'], - body: ['name', 'description', 'risk_scenarios'], - filters: { - folder: DOMAIN_FILTER, - state: STATE_FILTER, - approver: APPROVER_FILTER - } - }, - 'applied-controls': { - head: [ - 'ref_id', - 'name', - 'priority', - 'status', - 'category', - 'csfFunction', - 'eta', - 'owner', - 'domain', - 'referenceControl' - ], - body: [ - 'ref_id', - 'name', - 'priority', - 'status', - 'category', - 'csf_function', - 'eta', - 'owner', - 'folder', - 'reference_control' - ], - filters: { - folder: DOMAIN_FILTER, - status: STATUS_FILTER, - category: CATEGORY_FILTER, - csf_function: CSF_FUNCTION_FILTER, - owner: OWNER_FILTER, - priority: PRIORITY_FILTER - } - }, - policies: { - head: ['name', 'description', 'csfFunction', 'eta', 'owner', 'domain', 'referenceControl'], - body: ['name', 'description', 'csf_function', 'eta', 'owner', 'folder', 'reference_control'], - filters: { - folder: DOMAIN_FILTER, - status: STATUS_FILTER, - csf_function: CSF_FUNCTION_FILTER, - owner: OWNER_FILTER, - priority: PRIORITY_FILTER - } - }, - 'reference-controls': { - head: ['ref_id', 'name', 'description', 'category', 'csfFunction', 'provider', 'domain'], - body: ['ref_id', 'name', 'description', 'category', 'csf_function', 'provider', 'folder'], - meta: ['id', 'urn'], - filters: { - folder: { ...DOMAIN_FILTER, alwaysDisplay: true }, - category: CATEGORY_FILTER, - provider: PROVIDER_FILTER, - csf_function: CSF_FUNCTION_FILTER - } - }, - assets: { - head: [ - 'name', - 'type', - 'description', - 'securityObjectives', - 'disasterRecoveryObjectives', - 'owner', - 'domain' - ], - body: [ - 'name', - 'type', - 'description', - 'security_objectives', - 'disaster_recovery_objectives', - 'owner', - 'folder' - ], - filters: { - folder: DOMAIN_FILTER, - type: ASSET_TYPE_FILTER - } - }, - users: { - head: ['email', 'firstName', 'lastName'], - body: ['email', 'first_name', 'last_name'] - }, - 'user-groups': { - head: ['name'], - body: ['localization_dict'], - meta: ['id', 'builtin'] - }, - roles: { - head: ['name', 'description'], - body: ['name', 'description'] - }, - 'role-assignments': { - head: ['user', 'userGroup', 'role', 'perimeter'], - body: ['user', 'user_group', 'role', 'perimeter_folders'] - }, - frameworks: { - head: ['name', 'description', 'provider', 'complianceAssessments', 'domain'], - body: ['name', 'description', 'provider', 'compliance_assessments', 'folder'], - meta: ['id', 'urn'], - filters: { - folder: DOMAIN_FILTER, - provider: PROVIDER_FILTER - } - }, - 'compliance-assessments': { - head: ['ref_id', 'name', 'framework', 'description', 'project'], - body: ['ref_id', 'name', 'framework', 'description', 'project'], - filters: { - folder: { ...DOMAIN_FILTER_FROM_PROJECT, alwaysDisplay: true }, // alwaysDisplay shoudln't be mandatory here something is wrong - project: PROJECT_FILTER, - framework: FRAMEWORK_FILTER, - status: STATUS_FILTER - } - }, - 'requirement-assessments': { - head: ['name', 'description', 'complianceAssessment'], - body: ['name', 'description', 'compliance_assessment'], - breadcrumb_link_disabled: true - }, - evidences: { - head: ['name', 'file', 'size', 'description'], - body: ['name', 'attachment', 'size', 'description'], - filters: { - folder: { ...DOMAIN_FILTER, alwaysDisplay: true } // This filter should also be displayed even without alwaysDisplay - } - }, - requirements: { - head: ['ref_id', 'name', 'description', 'framework'], - body: ['ref_id', 'name', 'description', 'framework'], - meta: ['id', 'urn'] - }, - libraries: { - head: ['provider', 'name', 'description', 'language', 'overview'], - body: ['provider', 'name', 'description', 'locales', 'overview'] - }, - 'stored-libraries': { - head: ['provider', 'name', 'description', 'language', 'overview'], - body: ['provider', 'name', 'description', 'locales', 'overview'], - filters: { - locales: LANGUAGE_FILTER, - provider: PROVIDER_FILTER, - objectType: LIBRARY_TYPE_FILTER - } - }, - 'loaded-libraries': { - head: ['provider', 'name', 'description', 'language', 'overview'], - body: ['provider', 'name', 'description', 'locales', 'overview'], - filters: { - locales: LANGUAGE_FILTER, - provider: PROVIDER_FILTER, - objectType: LIBRARY_TYPE_FILTER - } - }, - 'sso-settings': { - head: ['name', 'provider', 'providerId'], - body: ['name', 'provider', 'provider_id'] - }, - 'requirement-mapping-sets': { - head: ['sourceFramework', 'targetFramework'], - body: ['source_framework', 'target_framework'] - }, - entities: { - head: ['name', 'description', 'domain', 'ownedFolders'], - body: ['name', 'description', 'folder', 'owned_folders'], - filters: { - folder: DOMAIN_FILTER - } - }, - 'entity-assessments': { - head: ['name', 'description', 'project', 'entity'], - body: ['name', 'description', 'project', 'entity'], - filters: { - project: PROJECT_FILTER, - status: STATUS_FILTER - } - }, - solutions: { - head: ['name', 'description', 'providerEntity', 'recipientEntity', 'criticality'], - body: ['name', 'description', 'provider_entity', 'recipient_entity', 'criticality'] - }, - representatives: { - head: ['email', 'entity', 'role'], - body: ['email', 'entity', 'role'] - } + folders: { + head: ['name', 'description', 'parentDomain'], + body: ['name', 'description', 'parent_folder'] + }, + projects: { + head: ['ref_id', 'name', 'description', 'domain'], + body: ['ref_id', 'name', 'description', 'folder'], + filters: { + folder: DOMAIN_FILTER, + lc_status: PROJECT_STATUS_FILTER + } + }, + 'filtering-labels': { + head: ['label'], + body: ['label'] + }, + 'risk-matrices': { + head: ['name', 'description', 'provider', 'domain'], + body: ['name', 'description', 'provider', 'folder'], + meta: ['id', 'urn'], + filters: { + folder: DOMAIN_FILTER + } + }, + vulnerabilities: { + head: ['ref_id', 'name', 'description', 'applied_controls', 'folder', 'labels'], + body: ['ref_id', 'name', 'description', 'applied_controls', 'folder', 'filtering_labels'], + filters: { + folder: DOMAIN_FILTER, + filtering_labels: LABELS_FILTER + } + }, + 'risk-assessments': { + head: ['ref_id', 'name', 'riskMatrix', 'description', 'riskScenarios', 'project'], + body: ['ref_id', 'str', 'risk_matrix', 'description', 'risk_scenarios_count', 'project'], + filters: { + folder: { ...DOMAIN_FILTER_FROM_PROJECT, alwaysDisplay: true }, + project: PROJECT_FILTER, + status: { ...STATUS_FILTER, alwaysDisplay: true } + } + }, + threats: { + head: ['ref_id', 'name', 'description', 'provider', 'domain'], + body: ['ref_id', 'name', 'description', 'provider', 'folder'], + meta: ['id', 'urn'], + filters: { + folder: DOMAIN_FILTER, + provider: PROVIDER_FILTER + } + }, + 'risk-scenarios': { + head: [ + 'ref_id', + 'treatment', + 'name', + 'threats', + 'riskAssessment', + 'existingAppliedControls', + 'currentLevel', + 'extraAppliedControls', + 'residualLevel' + ], + body: [ + 'ref_id', + 'treatment', + 'name', + 'threats', + 'risk_assessment', + 'existing_applied_controls', + 'current_level', + 'applied_controls', + 'residual_level' + ], + filters: { + folder: { ...DOMAIN_FILTER_FROM_PROJECT, alwaysDisplay: true }, + project: { ...PROJECT_FILTER, alwaysDisplay: true }, + treatment: { ...TREATMENT_FILTER, alwaysDisplay: true }, + risk_assessment: RISK_ASSESSMENT_FILTER, + threats: THREAT_FILTER, + assets: ASSET_FILTER + } + }, + 'risk-acceptances': { + head: ['name', 'description', 'riskScenarios'], + body: ['name', 'description', 'risk_scenarios'], + filters: { + folder: DOMAIN_FILTER, + state: STATE_FILTER, + approver: APPROVER_FILTER + } + }, + 'applied-controls': { + head: [ + 'ref_id', + 'name', + 'priority', + 'status', + 'category', + 'csfFunction', + 'eta', + 'owner', + 'domain', + 'referenceControl' + ], + body: [ + 'ref_id', + 'name', + 'priority', + 'status', + 'category', + 'csf_function', + 'eta', + 'owner', + 'folder', + 'reference_control' + ], + filters: { + folder: DOMAIN_FILTER, + status: STATUS_FILTER, + category: CATEGORY_FILTER, + csf_function: CSF_FUNCTION_FILTER, + owner: OWNER_FILTER, + priority: PRIORITY_FILTER + } + }, + policies: { + head: ['name', 'description', 'csfFunction', 'eta', 'owner', 'domain', 'referenceControl'], + body: ['name', 'description', 'csf_function', 'eta', 'owner', 'folder', 'reference_control'], + filters: { + folder: DOMAIN_FILTER, + status: STATUS_FILTER, + csf_function: CSF_FUNCTION_FILTER, + owner: OWNER_FILTER, + priority: PRIORITY_FILTER + } + }, + 'reference-controls': { + head: ['ref_id', 'name', 'description', 'category', 'csfFunction', 'provider', 'domain'], + body: ['ref_id', 'name', 'description', 'category', 'csf_function', 'provider', 'folder'], + meta: ['id', 'urn'], + filters: { + folder: { ...DOMAIN_FILTER, alwaysDisplay: true }, + category: CATEGORY_FILTER, + provider: PROVIDER_FILTER, + csf_function: CSF_FUNCTION_FILTER + } + }, + assets: { + head: [ + 'name', + 'type', + 'description', + 'securityObjectives', + 'disasterRecoveryObjectives', + 'owner', + 'domain' + ], + body: [ + 'name', + 'type', + 'description', + 'security_objectives', + 'disaster_recovery_objectives', + 'owner', + 'folder' + ], + filters: { + folder: DOMAIN_FILTER, + type: ASSET_TYPE_FILTER + } + }, + users: { + head: ['email', 'firstName', 'lastName'], + body: ['email', 'first_name', 'last_name'] + }, + 'user-groups': { + head: ['name'], + body: ['localization_dict'], + meta: ['id', 'builtin'] + }, + roles: { + head: ['name', 'description'], + body: ['name', 'description'] + }, + 'role-assignments': { + head: ['user', 'userGroup', 'role', 'perimeter'], + body: ['user', 'user_group', 'role', 'perimeter_folders'] + }, + frameworks: { + head: ['name', 'description', 'provider', 'complianceAssessments', 'domain'], + body: ['name', 'description', 'provider', 'compliance_assessments', 'folder'], + meta: ['id', 'urn'], + filters: { + folder: DOMAIN_FILTER, + provider: PROVIDER_FILTER + } + }, + 'compliance-assessments': { + head: ['ref_id', 'name', 'framework', 'description', 'project'], + body: ['ref_id', 'name', 'framework', 'description', 'project'], + filters: { + folder: { ...DOMAIN_FILTER_FROM_PROJECT, alwaysDisplay: true }, // alwaysDisplay shoudln't be mandatory here something is wrong + project: PROJECT_FILTER, + framework: FRAMEWORK_FILTER, + status: STATUS_FILTER + } + }, + 'requirement-assessments': { + head: ['name', 'description', 'complianceAssessment'], + body: ['name', 'description', 'compliance_assessment'], + breadcrumb_link_disabled: true + }, + evidences: { + head: ['name', 'file', 'size', 'description'], + body: ['name', 'attachment', 'size', 'description'], + filters: { + folder: { ...DOMAIN_FILTER, alwaysDisplay: true } // This filter should also be displayed even without alwaysDisplay + } + }, + requirements: { + head: ['ref_id', 'name', 'description', 'framework'], + body: ['ref_id', 'name', 'description', 'framework'], + meta: ['id', 'urn'] + }, + libraries: { + head: ['provider', 'name', 'description', 'language', 'overview'], + body: ['provider', 'name', 'description', 'locales', 'overview'] + }, + 'stored-libraries': { + head: ['provider', 'name', 'description', 'language', 'overview'], + body: ['provider', 'name', 'description', 'locales', 'overview'], + filters: { + locales: LANGUAGE_FILTER, + provider: PROVIDER_FILTER, + objectType: LIBRARY_TYPE_FILTER + } + }, + 'loaded-libraries': { + head: ['provider', 'name', 'description', 'language', 'overview'], + body: ['provider', 'name', 'description', 'locales', 'overview'], + filters: { + locales: LANGUAGE_FILTER, + provider: PROVIDER_FILTER, + objectType: LIBRARY_TYPE_FILTER + } + }, + 'sso-settings': { + head: ['name', 'provider', 'providerId'], + body: ['name', 'provider', 'provider_id'] + }, + 'requirement-mapping-sets': { + head: ['sourceFramework', 'targetFramework'], + body: ['source_framework', 'target_framework'] + }, + entities: { + head: ['name', 'description', 'domain', 'ownedFolders'], + body: ['name', 'description', 'folder', 'owned_folders'], + filters: { + folder: DOMAIN_FILTER + } + }, + 'entity-assessments': { + head: ['name', 'description', 'project', 'entity'], + body: ['name', 'description', 'project', 'entity'], + filters: { + project: PROJECT_FILTER, + status: STATUS_FILTER + } + }, + solutions: { + head: ['name', 'description', 'providerEntity', 'recipientEntity', 'criticality'], + body: ['name', 'description', 'provider_entity', 'recipient_entity', 'criticality'] + }, + representatives: { + head: ['email', 'entity', 'role'], + body: ['email', 'entity', 'role'] + } }; From 5c968465d5ee785626fb165ac9d615d6087ecd75 Mon Sep 17 00:00:00 2001 From: Abderrahmane Smimite Date: Thu, 28 Nov 2024 21:29:34 +0100 Subject: [PATCH 078/107] remove redundant information (badges) - will be treated differently --- .../components/ModelTable/ModelTable.svelte | 19 ------------------- frontend/src/lib/utils/table.ts | 16 ++++++++-------- 2 files changed, 8 insertions(+), 27 deletions(-) diff --git a/frontend/src/lib/components/ModelTable/ModelTable.svelte b/frontend/src/lib/components/ModelTable/ModelTable.svelte index 4b4867594..0f1eeb0bf 100644 --- a/frontend/src/lib/components/ModelTable/ModelTable.svelte +++ b/frontend/src/lib/components/ModelTable/ModelTable.svelte @@ -307,25 +307,6 @@ {#if key !== 'meta'} {@const component = field_component_map[key]} - {#if taggedKeys.has(key)} - {@const _tagList = tagMap[key]} - {@const tagList = Object.keys(_tagList.keys).map((key) => ({ - key: { [key]: _tagList.keys[key] } - }))} - {#each tagList as tag} - {@const tagKey = tag.key && Object.keys(tag.key)[0]} - {@const tagValue = meta[tagKey]} - {@const tagData = tag.key?.[tagKey]?.[tagValue]} - {#if tagData && tags} - {@const { text, cssClasses } = tagData} - - - {safeTranslate(text)} - - - {/if} - {/each} - {/if} {#if component} {:else} diff --git a/frontend/src/lib/utils/table.ts b/frontend/src/lib/utils/table.ts index a46a2ffab..11f00fd00 100644 --- a/frontend/src/lib/utils/table.ts +++ b/frontend/src/lib/utils/table.ts @@ -301,25 +301,25 @@ export const listViewFields: ListViewFieldsConfig = { 'risk-scenarios': { head: [ 'ref_id', - 'treatment', - 'name', 'threats', - 'riskAssessment', + 'name', 'existingAppliedControls', 'currentLevel', 'extraAppliedControls', - 'residualLevel' + 'residualLevel', + 'treatment', + 'riskAssessment' ], body: [ 'ref_id', - 'treatment', - 'name', 'threats', - 'risk_assessment', + 'name', 'existing_applied_controls', 'current_level', 'applied_controls', - 'residual_level' + 'residual_level', + 'treatment', + 'risk_assessment' ], filters: { folder: { ...DOMAIN_FILTER_FROM_PROJECT, alwaysDisplay: true }, From fcdfb72776a0433f5d94d6fe398b7c8bf02e9e1c Mon Sep 17 00:00:00 2001 From: Abderrahmane Smimite Date: Thu, 28 Nov 2024 22:07:27 +0100 Subject: [PATCH 079/107] add folder on ac on my assigments --- .../src/routes/(app)/(internal)/my-assignments/+page.svelte | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/routes/(app)/(internal)/my-assignments/+page.svelte b/frontend/src/routes/(app)/(internal)/my-assignments/+page.svelte index a349eb08c..4143bec25 100644 --- a/frontend/src/routes/(app)/(internal)/my-assignments/+page.svelte +++ b/frontend/src/routes/(app)/(internal)/my-assignments/+page.svelte @@ -32,6 +32,7 @@ {m.csfFunction()} {m.status()} {m.eta()} + {m.folder()} @@ -52,6 +53,7 @@ {formatDateOrDateTime(ac.eta, languageTag()) ?? '-'} + {ac.folder.str} {/each} From ba970a04b2f4129887527d56ed7358c30f782698 Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Fri, 29 Nov 2024 10:27:06 +0100 Subject: [PATCH 080/107] Fallback to 0 if there are no viewable audits This avoids `StatisticsError: mean requires at least one data point` --- backend/core/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/core/helpers.py b/backend/core/helpers.py index 6a3ec34b2..b53c312e7 100644 --- a/backend/core/helpers.py +++ b/backend/core/helpers.py @@ -893,7 +893,7 @@ def viewable_items(model): viewable_controls = viewable_items(AppliedControl) controls_count = viewable_controls.count() progress_avg = math.ceil( - mean([x.progress() for x in viewable_items(ComplianceAssessment)]) + mean([x.progress() for x in viewable_items(ComplianceAssessment)] or [0]) ) data = { From 44e245751cafefb1be6addc14f635536e8a44c1c Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Fri, 29 Nov 2024 10:30:21 +0100 Subject: [PATCH 081/107] UX: Change Card container element to anchor This makes the whole card clickable instead of only the text inside. --- frontend/src/lib/components/DataViz/Card.svelte | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/frontend/src/lib/components/DataViz/Card.svelte b/frontend/src/lib/components/DataViz/Card.svelte index ad465bb7c..277b88ed8 100644 --- a/frontend/src/lib/components/DataViz/Card.svelte +++ b/frontend/src/lib/components/DataViz/Card.svelte @@ -1,7 +1,7 @@ -
          @@ -20,10 +21,10 @@ {section}
          - {count} +

          {count}

          {label}
          -
          +