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/migrations/0039_remove_project_internal_reference_and_more.py b/backend/core/migrations/0039_remove_project_internal_reference_and_more.py new file mode 100644 index 000000000..58dad0828 --- /dev/null +++ b/backend/core/migrations/0039_remove_project_internal_reference_and_more.py @@ -0,0 +1,43 @@ +# Generated by Django 5.1.1 on 2024-11-19 10:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0038_asset_disaster_recovery_objectives_and_more"), + ] + + operations = [ + migrations.RenameField( + 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" + ), + ), + 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/models.py b/backend/core/models.py index 2421f0d07..f288d7843 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -1169,8 +1169,9 @@ class Project(NameDescriptionMixin, FolderMixin): ("eol", _("EndOfLife")), ("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") ) lc_status = models.CharField( max_length=20, @@ -1541,6 +1542,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, @@ -1803,6 +1807,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") @@ -2394,6 +2401,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 846345235..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", "internal_reference", "description"] + search_fields = ["name", "ref_id", "description"] @action(detail=False, name="Get status choices") def lc_status(self, request): @@ -1382,6 +1382,7 @@ class FolderViewSet(BaseModelViewSet): model = Folder filterset_class = FolderFilter + search_fields = ["ref_id"] def perform_create(self, serializer): """ @@ -1812,7 +1813,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/backend/iam/models.py b/backend/iam/models.py index 61f4c2404..48e800fa8 100644 --- a/backend/iam/models.py +++ b/backend/iam/models.py @@ -80,6 +80,7 @@ class ContentType(models.TextChoices): content_type = models.CharField( max_length=2, choices=ContentType.choices, default=ContentType.DOMAIN ) + parent_folder = models.ForeignKey( "self", null=True, 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": { diff --git a/enterprise/frontend/src/lib/components/Forms/ModelForm/FolderForm.svelte b/enterprise/frontend/src/lib/components/Forms/ModelForm/FolderForm.svelte index f9123d39a..46ca9c739 100644 --- a/enterprise/frontend/src/lib/components/Forms/ModelForm/FolderForm.svelte +++ b/enterprise/frontend/src/lib/components/Forms/ModelForm/FolderForm.svelte @@ -12,6 +12,7 @@ export let initialData: Record = {}; + + {#if schema.shape.category} = new Set(); + + }); export const FolderSchema = baseNamedObject({ + ref_id: z.string().optional().nullable(), parent_folder: z.string().optional() }); 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') }); @@ -87,6 +88,7 @@ export const RiskAssessmentSchema = baseNamedObject({ version: z.string().optional().default('0.1'), project: z.string(), status: z.string().optional().nullable(), + ref_id: z.string().optional().nullable(), risk_matrix: z.string(), eta: z.union([z.literal('').transform(() => null), z.string().date()]).nullish(), due_date: z.union([z.literal('').transform(() => null), z.string().date()]).nullish(), @@ -122,6 +124,7 @@ export const RiskScenarioSchema = baseNamedObject({ }); export const AppliedControlSchema = baseNamedObject({ + ref_id: z.string().optional().nullable(), category: z.string().optional().nullable(), csf_function: z.string().optional().nullable(), priority: z.number().optional().nullable(), @@ -238,6 +241,7 @@ export const SetPasswordSchema = z.object({ export const ComplianceAssessmentSchema = baseNamedObject({ version: z.string().optional().default('0.1'), + ref_id: z.string().optional().nullable(), project: z.string(), status: z.string().optional().nullable(), selected_implementation_groups: z.array(z.string().optional()).optional(), diff --git a/frontend/src/lib/utils/table.ts b/frontend/src/lib/utils/table.ts index a2f181e1d..879876d13 100644 --- a/frontend/src/lib/utils/table.ts +++ b/frontend/src/lib/utils/table.ts @@ -275,8 +275,8 @@ export const listViewFields: ListViewFieldsConfig = { body: ['name', 'description', 'parent_folder'] }, projects: { - head: ['name', 'description', 'domain'], - body: ['name', 'description', 'folder'], + head: ['ref_id', 'name', 'description', 'domain'], + body: ['ref_id', 'name', 'description', 'folder'], filters: { folder: DOMAIN_FILTER, lc_status: PROJECT_STATUS_FILTER @@ -303,8 +303,8 @@ export const listViewFields: ListViewFieldsConfig = { } }, 'risk-assessments': { - head: ['name', 'riskMatrix', 'description', 'riskScenarios', 'project'], - body: ['str', 'risk_matrix', 'description', 'risk_scenarios_count', 'project'], + head: ['ref_id', 'name', 'riskMatrix', 'description', 'riskScenarios', 'project'], + body: ['ref_id', 'str', 'risk_matrix', 'description', 'risk_scenarios_count', 'project'], filters: { folder: { ...DOMAIN_FILTER_FROM_META_PROJECT, alwaysDisplay: true }, project: PROJECT_FILTER, @@ -359,6 +359,7 @@ export const listViewFields: ListViewFieldsConfig = { }, 'applied-controls': { head: [ + 'ref_id', 'name', 'description', 'category', @@ -369,6 +370,7 @@ export const listViewFields: ListViewFieldsConfig = { 'referenceControl' ], body: [ + 'ref_id', 'name', 'description', 'category', @@ -460,8 +462,8 @@ export const listViewFields: ListViewFieldsConfig = { } }, 'compliance-assessments': { - head: ['name', 'framework', 'description', 'project'], - body: ['name', 'framework', 'description', 'project'], + head: ['ref_id', 'name', 'framework', 'description', 'project'], + body: ['ref_id', 'name', 'framework', 'description', 'project'], filters: { folder: { ...DOMAIN_FILTER_FROM_META_PROJECT, alwaysDisplay: true }, // alwaysDisplay shoudln't be mandatory here something is wrong project: PROJECT_FILTER, diff --git a/frontend/src/lib/utils/types.ts b/frontend/src/lib/utils/types.ts index 055edfec3..8d11ac1d0 100644 --- a/frontend/src/lib/utils/types.ts +++ b/frontend/src/lib/utils/types.ts @@ -99,7 +99,7 @@ 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..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, - internal_reference: 'Test internal reference', + ref_id: 'R.1234', lc_status: 'Production' }); 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')) diff --git a/frontend/tests/utils/test-utils.ts b/frontend/tests/utils/test-utils.ts index 2f75a780c..2e54bbdcf 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: 'R.1234', lc_status: 'Production' }, editParams: { name: '', description: '', - internal_reference: '', + ref_id: '', lc_status: 'End of life' } },