diff --git a/backend/core/helpers.py b/backend/core/helpers.py index 6a8b6988a..a01f0521e 100644 --- a/backend/core/helpers.py +++ b/backend/core/helpers.py @@ -803,6 +803,7 @@ def compile_risk_assessment_for_composer(user, risk_assessment_list: list): v = {"value": count, "itemStyle": {"color": STATUS_COLOR_MAP[st[0]]}} values.append(v) labels.append(st[1]) + local_lables = [camel_case(str(l)) for l in labels] risk_assessment_objects = list() @@ -843,6 +844,6 @@ def compile_risk_assessment_for_composer(user, risk_assessment_list: list): "untreated_h_vh": untreated_h_vh, "accepted": accepted, }, - "security_measure_status": {"labels": labels, "values": values}, + "security_measure_status": {"localLables":local_lables, "labels": labels, "values": values}, "colors": get_risk_color_ordered_list(user, risk_assessment_list), } diff --git a/backend/core/models.py b/backend/core/models.py index 175ad1ecf..a44996839 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -655,6 +655,9 @@ def get_linked_requirements_count(self): class PolicyManager(models.Manager): + def get_queryset(self): + return super().get_queryset().filter(category="policy") + def create(self, *args, **kwargs): kwargs["category"] = "policy" # Ensure category is always "policy" return super().create(*args, **kwargs) diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 62b32b7f1..0be0465df 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -35,7 +35,7 @@ "governance": "Governance", "risk": "Risk", "compliance": "Compliance", - "organization": "Organisation", + "organization": "Organization", "extra": "Extra", "analytics": "Analytics", "calendar": "Calendar", @@ -224,6 +224,7 @@ "infosFound": "info{s} found", "remediationPlan": "Remediation plan", "treatmentPlan": "Treatment plan", + "plan": "Plan", "asPDF": "as PDF", "asCSV": "as CSV", "draft": "Draft", @@ -234,13 +235,10 @@ "riskLevel": "Risk level", "cancel": "Cancel", "save": "Save", - "currentAssessment": "Current assessment", - "scope": "Scope", "assetsImpactedByTheRiskScenario": "Assets impacted by the risk scenario", "ecistingMeasures": "Existing measures", "theExistingSecurityMeasuresToManageThisRisk": "The existing security measures to manage this risk", "currentRiskLevelGivenCurrentMeasures": "The current risk level given the current security measures", - "targetAssessment": "Target assessment", "riskLevelWhenAllExtraMeasuresDone": "The risk level when all extra measures are done", "myUserGroups": "My user groups", "changePasswordText": "You can change your password here. You'll need to log in with your new password after this operation", @@ -346,5 +344,31 @@ "copyright": "Copyright", "uploadYourLibrary": "Upload your own library", "libraryFileInYaml": "Library file in YAML format", - "rid": "RID" + "rid": "RID", + "scope": "Scope", + "auditor": "Auditor", + "lastUpdate": "Last update", + "riskScenarioAssetHelpText": "Assets impacted by this risk scenario", + "riskScenarioMeasureHelpText": "The existing security measures to manage this risk", + "currentAssessment": "Current assessment", + "targetAssessment": "Target assessment", + "currentRiskLevel": "Current risk level", + "residualRiskLevel": "Residual risk level", + "currentRiskLevelHelpText": "The risk level given the current measures", + "residualRiskLevelHelpText": "The risk level when all the extra measures are done", + "yourSelection": "Your selection", + "composerHint": "Hint: you can bookmark this page for future usage", + "composerTitle": "Here is the overview for the selected risk assessment", + "composerTitlePlural": "Here is the overview for the {number} selected risk assessments", + "statusOfAssociatedMeasures": "Status of associated measures", + "forTheSelectedScope": "For the selected scope, you have", + "untreatedRiskScenarios": "{count} untreated risk scenario{s}", + "acceptedRiskScenarios": "{count} accepted risk scenario{s}", + "reviewNeeded": "Review needed", + "ok": "Ok", + "inconsistenciesFoundComposer": "Found {count} inconsistenc{plural}. For more details, check", + "current": "Current", + "residual": "Residual", + "jumpToRiskAssessment": "Jump to risk assessment", + "additionalMeasures": "Additional measures" } diff --git a/frontend/messages/fr.json b/frontend/messages/fr.json index 9c5f200a8..13778fd5c 100644 --- a/frontend/messages/fr.json +++ b/frontend/messages/fr.json @@ -224,6 +224,7 @@ "infosFound": "info{s} trouvée{s}", "remediationPlan": "Plan de remédiation", "treatmentPlan": "Plan de traitement", + "plan": "Plan", "asPDF": "en PDF", "asCSV": "en CSV", "draft": "Brouillon", @@ -345,5 +346,31 @@ "dependencies": "Dépendances", "copyright": "Droits d'auteur", "uploadYourLibrary": "Téléchargez votre propre bibliothèque", - "libraryFileInYaml": "Fichier de librairie en format YAML" + "libraryFileInYaml": "Fichier de librairie en format YAML", + "scope": "Périmètre", + "auditor": "Auditeur", + "lastUpdate": "Dernière mise à jour", + "riskScenarioAssetHelpText": "Biens sensibles impactés par ce scénario de risque", + "riskScenarioMeasureHelpText": "Les mesures de sécurité existantes pour gérer ce risque", + "currentAssessment": "Évaluation actuelle", + "targetAssessment": "Évaluation cible", + "currentRiskLevel": "Niveau de risque courrant", + "residualRiskLevel": "Niveau de risque résiduel", + "currentRiskLevelHelpText": "Le niveau de risque compte tenu des mesures actuelles", + "residualRiskLevelHelpText": "Le niveau de risque lorsque toutes les mesures supplémentaires sont prises", + "yourSelection": "Votre sélection", + "composerHint": "Astuce : vous pouvez ajouter cette page à vos favoris pour une utilisation future", + "composerTitle": "Voici l’aperçu de l’évaluation de risque sélectionnée", + "composerTitlePlural": "Voici l'aperçu des {number} évaluations de risques sélectionnées", + "statusOfAssociatedMeasures": "Statut des mesures associées", + "forTheSelectedScope": "Pour le périmètre sélectionné, vous avez", + "untreatedRiskScenarios": "{count} scénario{s} de risque non traité{s}", + "acceptedRiskScenarios": "{count} scénario{s} de risque accepté{s}", + "reviewNeeded": "Révision nécessaire", + "ok": "Ok", + "inconsistenciesFoundComposer": "Il y a {count} incohérence{s}. Pour plus de détail, vérifiez", + "current": "Courrant", + "residual": "Résiduel", + "jumpToRiskAssessment": "Passer à l'évaluation des risques", + "additionalMeasures": "Mesures supplémentaires" } diff --git a/frontend/src/app.postcss b/frontend/src/app.postcss index 047c1affa..a8bf3e1cc 100644 --- a/frontend/src/app.postcss +++ b/frontend/src/app.postcss @@ -6,7 +6,7 @@ /* (all other styles here...) */ html, body { - @apply h-full overflow-scroll; + @apply h-full overflow-auto; } .capitalize-first:first-letter { @apply capitalize; diff --git a/frontend/src/lib/components/Breadcrumbs/Breadcrumbs.svelte b/frontend/src/lib/components/Breadcrumbs/Breadcrumbs.svelte index fadc08ae0..a8bab7086 100644 --- a/frontend/src/lib/components/Breadcrumbs/Breadcrumbs.svelte +++ b/frontend/src/lib/components/Breadcrumbs/Breadcrumbs.svelte @@ -29,15 +29,17 @@ crumbs = tokens.map((t) => { tokenPath += '/' + t; if (t === $breadcrumbObject.id) { - if ($breadcrumbObject.name) title = $breadcrumbObject.name; - else title = $breadcrumbObject.email; + if ($breadcrumbObject.name) t = $breadcrumbObject.name; + else t = $breadcrumbObject.email; } else if (t === 'folders') { t = 'domains'; } - t = t.replace(/-/g, ' '); - t = capitalizeSecondWord(t); + else{ + t = t.replace(/-/g, ' '); + t = capitalizeSecondWord(t); + } return { - label: $page.data.label || title || t, + label: $page.data.label || t, href: Object.keys(listViewFields).includes(tokens[0]) ? tokenPath : null }; }); diff --git a/frontend/src/lib/utils/locales.ts b/frontend/src/lib/utils/locales.ts index 7a152d616..30bfa75a3 100644 --- a/frontend/src/lib/utils/locales.ts +++ b/frontend/src/lib/utils/locales.ts @@ -259,7 +259,9 @@ export function localItems(languageTag: string): LocalItems { lossOfAccountabilityText: m.lossOfAccountabilityText({ languageTag: languageTag }), lossOfAccountabilityChoice1: m.lossOfAccountabilityChoice1({ languageTag: languageTag }), lossOfAccountabilityChoice2: m.lossOfAccountabilityChoice2({ languageTag: languageTag }), - lossOfAccountabilityChoice3: m.lossOfAccountabilityChoice3({ languageTag: languageTag }) + lossOfAccountabilityChoice3: m.lossOfAccountabilityChoice3({ languageTag: languageTag }), + composer: m.composer({ languageTag: languageTag }), + plan: m.plan({ languageTag: languageTag }), }; return LOCAL_ITEMS; } diff --git a/frontend/src/routes/(app)/analytics/composer/+page.svelte b/frontend/src/routes/(app)/analytics/composer/+page.svelte index cb3c19395..16bf41172 100644 --- a/frontend/src/routes/(app)/analytics/composer/+page.svelte +++ b/frontend/src/routes/(app)/analytics/composer/+page.svelte @@ -1,36 +1,47 @@
-
Your selection
+
{m.yourSelection()}
- Hint: you can bookmark this page for future usage + {m.composerHint()}
- Here is the overview for the {data.risk_assessment_objects.length <= 1 - ? 'selected risk_assessment' - : `${data.risk_assessment_objects.length} selected risk assessments`}: + {data.risk_assessment_objects.length <= 1 + ? m.composerTitle() + : m.composerTitlePlural({ number: data.risk_assessment_objects.length})}:
-
Current risk level per risk scenario
+
{m.currentRiskLevelPerScenario()}
object.color)} /> @@ -38,7 +49,7 @@
-
Status of associated measures
+
{m.statusOfAssociatedMeasures()}
-
Residual risk level per risk scenario
+
{m.residualRiskLevelPerScenario()}
 For the selected scope, you have:{m.forTheSelectedScope()}:
  • - {data.counters.untreated} untreated risk scenario{data.counters.untreated > 1 - ? 's' - : ''} + {m.untreatedRiskScenarios({ + count: data.counters.untreated, + s: data.counters.untreated > 1 ? 's' : '' + })}
      {#each data.riskscenarios.untreated as scenario}
    • {scenario.name}
    • @@ -79,8 +91,10 @@
  • - and - {data.counters.accepted} risk scenario{data.counters.accepted > 1 ? 's' : ''} accepted + {m.acceptedRiskScenarios({ + count: data.counters.accepted, + s: data.counters.accepted > 1 ? 's' : '' + })}
      {#each data.riskscenarios.accepted as scenario}
    • {scenario.name}
    • @@ -116,9 +130,9 @@
      {#if item.risk_assessment.quality_check.count > 0} - Review needed + {m.reviewNeeded()} {:else} - Ok + {m.ok()} {/if}
@@ -127,23 +141,23 @@
{#if item.risk_assessment.quality_check.count > 0}➡️ Found - {item.risk_assessment.quality_check.count} inconsistenc{item - .risk_assessment.quality_check.count > 1 - ? 'es' - : 'y'} that you need to check (use + >{m.inconsistenciesFoundComposer({ + count: item.risk_assessment.quality_check.count, + s: item.risk_assessment.quality_check.count > 1 ? 's' : '', + plural: item.risk_assessment.quality_check.count > 1 ? 'ies' : 'y' + })} x-rays for more information). + >. {/if}
- + + {#each item.synth_table as lvl} @@ -159,7 +173,7 @@ Jump to full risk risk_assessment {m.jumpToRiskAssessment()} diff --git a/frontend/src/routes/(app)/risk-assessments/[id=uuid]/plan/+page.server.ts b/frontend/src/routes/(app)/risk-assessments/[id=uuid]/plan/+page.server.ts index ac6a98ab6..fb1b469bd 100644 --- a/frontend/src/routes/(app)/risk-assessments/[id=uuid]/plan/+page.server.ts +++ b/frontend/src/routes/(app)/risk-assessments/[id=uuid]/plan/+page.server.ts @@ -1,5 +1,4 @@ import { BASE_API_URL } from '$lib/utils/constants'; -import type { UUID } from 'crypto'; import type { PageServerLoad } from './$types'; @@ -9,11 +8,12 @@ export const load = (async ({ fetch, params }) => { const res = await fetch(endpoint); const risk_assessment = await res.json(); - const folder = await fetch(`${BASE_API_URL}/folders/${risk_assessment.project.id.folder}/`).then( + const project = await fetch(`${BASE_API_URL}/projects/${risk_assessment.project.id}/`).then( + (res) => res.json() + ); + const folder = await fetch(`${BASE_API_URL}/folders/${project.folder.id}/`).then( (res) => res.json() ); - risk_assessment.folder = folder; - return { URLModel, risk_assessment }; }) satisfies PageServerLoad; diff --git a/frontend/src/routes/(app)/risk-assessments/[id=uuid]/plan/+page.svelte b/frontend/src/routes/(app)/risk-assessments/[id=uuid]/plan/+page.svelte index a09f84752..96711969b 100644 --- a/frontend/src/routes/(app)/risk-assessments/[id=uuid]/plan/+page.svelte +++ b/frontend/src/routes/(app)/risk-assessments/[id=uuid]/plan/+page.svelte @@ -1,5 +1,6 @@ -

Associated risk scenarios:

+

{m.associatedRiskScenarios()}:

- CurrentResidual{m.current()}{m.residual()}
@@ -69,27 +75,27 @@ {#if scenario.existing_measures} - + - + {/if} {#if scenario.security_measures.length > 0} - + - - - - - - - - + + + + + + + + {#each scenario.security_measures as measure, index} 0)} {/if} diff --git a/frontend/src/routes/(app)/risk-scenarios/[id=uuid]/edit/+page.svelte b/frontend/src/routes/(app)/risk-scenarios/[id=uuid]/edit/+page.svelte index c02915c22..3ca45d39e 100644 --- a/frontend/src/routes/(app)/risk-scenarios/[id=uuid]/edit/+page.svelte +++ b/frontend/src/routes/(app)/risk-scenarios/[id=uuid]/edit/+page.svelte @@ -147,7 +147,7 @@ {form} options={getOptions({ objects: data.foreignKeys['risk_assessment'] })} field="risk_assessment" - label={m.riskAssessments()} + label={m.riskAssessment()} />

{m.version()}

@@ -159,7 +159,7 @@

{m.status()}

-

{m.updatedAt()}

+

{m.lastUpdate()}

{new Date(data.scenario.updated_at).toLocaleString()}

@@ -193,7 +193,7 @@ options={getOptions({ objects: data.foreignKeys['assets'] })} field="assets" label={m.assets()} - helpText={m.assetsImpactedByTheRiskScenario()} + helpText={m.riskScenarioAssetHelpText()} />
@@ -206,8 +206,8 @@
Existing measures: {m.existingMeasures()}:
lorem ipsum {scenario.existing_measures}
Additional measures: {m.additionalMeasures()}:
#NameDescriptionTypeSecurity functionETAEffortLinkStatus{m.name()}{m.description()}{m.type()}{m.securityFunction()}{m.eta()}{m.effort()}{m.link()}{m.status()}
- No associated measure + {m.noSecurityMeasureYet()}