diff --git a/backend/library/management/commands/storelibraries.py b/backend/library/management/commands/storelibraries.py index 4ac126240..617c74877 100644 --- a/backend/library/management/commands/storelibraries.py +++ b/backend/library/management/commands/storelibraries.py @@ -1,12 +1,14 @@ from pathlib import Path -import structlog +import structlog, signal from ciso_assistant.settings import LIBRARIES_PATH from core.models import StoredLibrary from django.core.management.base import BaseCommand logger = structlog.getLogger(__name__) +signal.signal(signal.SIGINT, signal.SIG_DFL) + class Command(BaseCommand): help = "Store libraries in the database" diff --git a/frontend/src/lib/components/DetailView/DetailView.svelte b/frontend/src/lib/components/DetailView/DetailView.svelte index 4abb82876..49bb037e7 100644 --- a/frontend/src/lib/components/DetailView/DetailView.svelte +++ b/frontend/src/lib/components/DetailView/DetailView.svelte @@ -3,6 +3,7 @@ import { page } from '$app/stores'; import ConfirmModal from '$lib/components/Modals/ConfirmModal.svelte'; import CreateModal from '$lib/components/Modals/CreateModal.svelte'; + import MissingConstraintsModal from '$lib/components/Modals/MissingConstraintsModal.svelte'; import ModelTable from '$lib/components/ModelTable/ModelTable.svelte'; import type { ModalComponent, @@ -17,6 +18,7 @@ import { URL_MODEL_MAP } from '$lib/utils/crud'; import { isURL } from '$lib/utils/helpers'; import { toCamelCase, capitalizeFirstLetter } from '$lib/utils/locales.js'; + import { checkConstraints } from '$lib/utils/crud'; import { languageTag } from '$paraglide/runtime.js'; import * as m from '$paraglide/messages.js'; import { ISO_8601_REGEX } from '$lib/utils/constants'; @@ -69,7 +71,7 @@ } function modalCreateForm(model: Record): void { - const modalComponent: ModalComponent = { + let modalComponent: ModalComponent = { ref: CreateModal, props: { form: model.createForm, @@ -77,12 +79,24 @@ debug: false } }; - const modal: ModalSettings = { + let modal: ModalSettings = { type: 'component', component: modalComponent, // Data title: safeTranslate('add' + capitalizeFirstLetter(model.info.localName)) }; + if (checkConstraints(model.createForm.constraints, model.foreignKeys).length > 0) { + modalComponent = { + ref: MissingConstraintsModal + }; + modal = { + type: 'component', + component: modalComponent, + title: m.warning(), + body: safeTranslate('add' + capitalizeFirstLetter(model.info.localName)).toLowerCase(), + value: checkConstraints(model.createForm.constraints, model.foreignKeys) + }; + } modalStore.trigger(modal); } diff --git a/frontend/src/lib/utils/crud.ts b/frontend/src/lib/utils/crud.ts index 69f532efe..5bf91c559 100644 --- a/frontend/src/lib/utils/crud.ts +++ b/frontend/src/lib/utils/crud.ts @@ -17,7 +17,7 @@ type GetOptionsParams = { selfSelect?: boolean; }; -export function checkConstraints(constraints: { [key: string]: any }, foreignKeys: any) { +export function checkConstraints(constraints: { [key: string]: any }, foreignKeys: any): string[] { const emptyConstraintsList = []; for (const [key, constraint] of Object.entries(constraints)) { if (constraint.required && foreignKeys[key]) diff --git a/frontend/src/lib/utils/helpers.ts b/frontend/src/lib/utils/helpers.ts index 367b679f1..63a830557 100644 --- a/frontend/src/lib/utils/helpers.ts +++ b/frontend/src/lib/utils/helpers.ts @@ -60,7 +60,7 @@ export function formatScoreValue(value: number, max_score: number, fullDonut = f } export function getSecureRedirect(url: any): string { - const SECURE_REDIRECT_URL_REGEX = /^\/(?!.*\/\/)[^\s]*$/; + const SECURE_REDIRECT_URL_REGEX = /^\/\w+/; return typeof url === 'string' && SECURE_REDIRECT_URL_REGEX.test(url) ? url : ''; } diff --git a/frontend/src/routes/(app)/(internal)/requirement-assessments/[id=uuid]/edit/+page.server.ts b/frontend/src/routes/(app)/(internal)/requirement-assessments/[id=uuid]/edit/+page.server.ts index a6f9d3e3f..599538c38 100644 --- a/frontend/src/routes/(app)/(internal)/requirement-assessments/[id=uuid]/edit/+page.server.ts +++ b/frontend/src/routes/(app)/(internal)/requirement-assessments/[id=uuid]/edit/+page.server.ts @@ -10,7 +10,7 @@ import { tableSourceMapper, type TableSource } from '@skeletonlabs/skeleton'; import type { Actions } from '@sveltejs/kit'; import { fail, redirect } from '@sveltejs/kit'; import { setFlash } from 'sveltekit-flash-message/server'; -import { setError, superValidate } from 'sveltekit-superforms'; +import { superValidate } from 'sveltekit-superforms'; import { zod } from 'sveltekit-superforms/adapters'; import type { PageServerLoad } from './$types'; import { z } from 'zod'; @@ -43,6 +43,7 @@ export const load = (async ({ fetch, params }) => { } const schema = modelSchema(URLModel); + object.evidences = object.evidences.map((evidence) => evidence.id); const form = await superValidate(object, zod(schema), { errors: true }); const foreignKeys: Record = {}; @@ -116,24 +117,27 @@ export const load = (async ({ fetch, params }) => { const tables: Record = {}; - for (const key of ['applied-controls', 'evidences'] as urlModel[]) { - const keyEndpoint = `${BASE_API_URL}/${key}/?requirement_assessments=${params.id}`; - const response = await fetch(keyEndpoint); - if (response.ok) { - const data = await response.json().then((data) => data.results); + await Promise.all( + ['applied-controls', 'evidences'].map(async (key) => { + const keyEndpoint = `${BASE_API_URL}/${key}/?requirement_assessments=${params.id}`; + const response = await fetch(keyEndpoint); - const bodyData = tableSourceMapper(data, listViewFields[key].body); + if (response.ok) { + const data = await response.json().then((data) => data.results); - const table: TableSource = { - head: listViewFields[key].head, - body: bodyData, - meta: data - }; - tables[key] = table; - } else { - console.error(`Failed to fetch data for ${key}: ${response.statusText}`); - } - } + const bodyData = tableSourceMapper(data, listViewFields[key].body); + + const table: TableSource = { + head: listViewFields[key].head, + body: bodyData, + meta: data + }; + tables[key] = table; + } else { + console.error(`Failed to fetch data for ${key}: ${response.statusText}`); + } + }) + ); const measureForeignKeys: Record = {}; diff --git a/frontend/src/routes/(app)/(third-party)/evidences/[id=uuid]/attachment/+server.ts b/frontend/src/routes/(app)/(third-party)/evidences/[id=uuid]/attachment/+server.ts index ed87e123c..9ac07bd10 100644 --- a/frontend/src/routes/(app)/(third-party)/evidences/[id=uuid]/attachment/+server.ts +++ b/frontend/src/routes/(app)/(third-party)/evidences/[id=uuid]/attachment/+server.ts @@ -29,9 +29,13 @@ export const GET: RequestHandler = async ({ fetch, setHeaders, params }) => { } const reader = attachmentResponse.body.getReader(); + let readerTerminated = false; const stream = new ReadableStream({ start(controller) { function push() { + if (readerTerminated) { + return; + } reader.read().then(({ done, value }) => { if (done) { controller.close(); @@ -42,6 +46,9 @@ export const GET: RequestHandler = async ({ fetch, setHeaders, params }) => { }); } push(); + }, + cancel() { + readerTerminated = true; } });