From eb1c9032fe3d6e1a4b8bef8e729c33a7736f15ee Mon Sep 17 00:00:00 2001 From: Niklas B Date: Thu, 23 Jan 2025 13:14:15 +0100 Subject: [PATCH 1/4] Fix project name length validation Fixes #27 The solution was generated using github copilot workspace. --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/accso/SecureCheckPlus/issues/27?shareId=XXXX-XXXX-XXXX-XXXX). --- backend/webserver/serializer/project_serializer.py | 3 ++- backend/webserver/views/project_views.py | 3 +++ .../src/components/NavBar/ProjectCreationContent.tsx | 12 +++++++++++- frontend/src/queries/project-requests.tsx | 5 ++++- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/backend/webserver/serializer/project_serializer.py b/backend/webserver/serializer/project_serializer.py index cab81e4..07498d3 100644 --- a/backend/webserver/serializer/project_serializer.py +++ b/backend/webserver/serializer/project_serializer.py @@ -1,6 +1,7 @@ from datetime import datetime from rest_framework import serializers +from rest_framework.validators import MaxLengthValidator from analyzer.models import Project from utilities.constants import Status, Threshold @@ -12,7 +13,7 @@ class Meta: fields = ["projectId", "projectName", "updated", "deploymentThreshold"] projectId = serializers.CharField(source="project_id", read_only=True) - projectName = serializers.CharField(source="project_name", allow_blank=True) + projectName = serializers.CharField(source="project_name", allow_blank=True, validators=[MaxLengthValidator(255)]) updated = serializers.DateTimeField(default=datetime.now(), read_only=True) deploymentThreshold = serializers.ChoiceField(source="deployment_threshold", choices=Threshold.names) diff --git a/backend/webserver/views/project_views.py b/backend/webserver/views/project_views.py index 351199b..ab1c22e 100644 --- a/backend/webserver/views/project_views.py +++ b/backend/webserver/views/project_views.py @@ -158,6 +158,9 @@ def post(self, request, project_id): else: raise InvalidValueError(project_id) + if len(request.data.get("projectName", "")) > 255: + raise InvalidValueError("Project name exceeds the maximum length of 255 characters") + return Response(f"Creation of {project_id} successful!") except AlreadyExists as ae: diff --git a/frontend/src/components/NavBar/ProjectCreationContent.tsx b/frontend/src/components/NavBar/ProjectCreationContent.tsx index 5e0867c..e61df8f 100644 --- a/frontend/src/components/NavBar/ProjectCreationContent.tsx +++ b/frontend/src/components/NavBar/ProjectCreationContent.tsx @@ -74,6 +74,16 @@ const CreationContent: React.FunctionComponent = (dialogCont } }, [projectId]) + useEffect(() => { + if (projectName.length > 255) { + setInvalid(true); + setHelperText(localization.dialog.projectNameHelperToLong); + } else { + setInvalid(false); + setHelperText(""); + } + }, [projectName]); + return( @@ -120,4 +130,4 @@ const buttonContainerStyle = { justifyContent: "center" } -export default CreationContent \ No newline at end of file +export default CreationContent diff --git a/frontend/src/queries/project-requests.tsx b/frontend/src/queries/project-requests.tsx index 4c1be3f..f6e77af 100644 --- a/frontend/src/queries/project-requests.tsx +++ b/frontend/src/queries/project-requests.tsx @@ -27,6 +27,9 @@ export function updateProject(projectId: string, projectData: {}): AxiosPromise } export function createProject(projectId: string, projectData: {}): AxiosPromise { + if (projectData.projectName && projectData.projectName.length > 255) { + return Promise.reject(new Error("Project name exceeds the maximum length of 255 characters")); + } return apiClient.post(urlAddress.api.createProject(projectId), projectData) } @@ -45,4 +48,4 @@ export function updateProjectCves(projectId: string): AxiosPromise { export function deleteProjects(projectIds: string[]): AxiosPromise { let data = {projectIds: projectIds} return apiClient.post(urlAddress.api.deleteProjects, data) -} \ No newline at end of file +} From df06cab83e731653fed11b0fd94b954d736c38a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20B=C3=BCchel?= Date: Fri, 24 Jan 2025 17:28:04 +0100 Subject: [PATCH 2/4] fix: fix project name length validation --- .../serializer/project_serializer.py | 3 +- .../NavBar/ProjectCreationContent.tsx | 54 +++++++++++-------- frontend/src/queries/project-requests.tsx | 5 +- scripts/run-backend.bash | 2 +- 4 files changed, 38 insertions(+), 26 deletions(-) diff --git a/backend/webserver/serializer/project_serializer.py b/backend/webserver/serializer/project_serializer.py index 07498d3..1b630e2 100644 --- a/backend/webserver/serializer/project_serializer.py +++ b/backend/webserver/serializer/project_serializer.py @@ -1,7 +1,6 @@ from datetime import datetime from rest_framework import serializers -from rest_framework.validators import MaxLengthValidator from analyzer.models import Project from utilities.constants import Status, Threshold @@ -13,7 +12,7 @@ class Meta: fields = ["projectId", "projectName", "updated", "deploymentThreshold"] projectId = serializers.CharField(source="project_id", read_only=True) - projectName = serializers.CharField(source="project_name", allow_blank=True, validators=[MaxLengthValidator(255)]) + projectName = serializers.CharField(source="project_name", allow_blank=True, max_length=255) updated = serializers.DateTimeField(default=datetime.now(), read_only=True) deploymentThreshold = serializers.ChoiceField(source="deployment_threshold", choices=Threshold.names) diff --git a/frontend/src/components/NavBar/ProjectCreationContent.tsx b/frontend/src/components/NavBar/ProjectCreationContent.tsx index e61df8f..44da961 100644 --- a/frontend/src/components/NavBar/ProjectCreationContent.tsx +++ b/frontend/src/components/NavBar/ProjectCreationContent.tsx @@ -13,12 +13,15 @@ import {useNotification} from "../../context/NotificationContext"; const CreationContent: React.FunctionComponent = (dialogContentProps: DialogContentProps) => { const notification = useNotification(); const {data} = useQuery("allProjectsFlat", getAllProjectsFlat) - const [isInvalid, setInvalid] = useState(false); + const [isIdInvalid, setIdInvalid] = useState(true); + const [isNameInvalid, setNameInvalid] = useState(false); + const [isProjectIdTouched, setProjectIdTouched] = useState(false); const [projectId, setProjectId] = useState(""); const [projectName, setProjectName] = useState(""); const [threshold, setThreshold] = useState("HIGH"); const queryClient = useQueryClient() - const [helperText, setHelperText] = useState("") + const [projectIdHelperText, setProjectIdHelperText] = useState("") + const [projectNameHelperText, setProjectNameHelperText] = useState("") const handleSave = useMutation(() => createProject(projectId, { projectName: projectName, deploymentThreshold: threshold @@ -52,35 +55,38 @@ const CreationContent: React.FunctionComponent = (dialogCont } useEffect(() => { + if (!isProjectIdTouched) { + return; + } if (projectId.length < 1){ - setInvalid(true); - setHelperText(localization.dialog.projectIdHelperNotEmpty) + setIdInvalid(true); + setProjectIdHelperText(localization.dialog.projectIdHelperNotEmpty) }else if (projectId.includes(" ")){ - setInvalid(true); - setHelperText(localization.dialog.projectIdHelperNoSpaces) + setIdInvalid(true); + setProjectIdHelperText(localization.dialog.projectIdHelperNoSpaces) }else if (projectId.length > 20){ - setInvalid(true); - setHelperText(localization.dialog.projectIdHelperToLong) + setIdInvalid(true); + setProjectIdHelperText(localization.dialog.projectIdHelperToLong) }else { if (data?.data !== undefined) { if (allProjectIds.includes(projectId.toLowerCase())) { - setInvalid(true); - setHelperText(localization.dialog.projectIdHelperIdAlreadyUsed) + setIdInvalid(true); + setProjectIdHelperText(localization.dialog.projectIdHelperIdAlreadyUsed) } else { - setInvalid(false); - setHelperText("") + setIdInvalid(false); + setProjectIdHelperText("") } } } - }, [projectId]) + }, [projectId, isProjectIdTouched, allProjectIds]) useEffect(() => { if (projectName.length > 255) { - setInvalid(true); - setHelperText(localization.dialog.projectNameHelperToLong); + setNameInvalid(true); + setProjectNameHelperText(localization.dialog.projectNameHelperToLong); } else { - setInvalid(false); - setHelperText(""); + setNameInvalid(false); + setProjectNameHelperText("Optional"); } }, [projectName]); @@ -88,16 +94,20 @@ const CreationContent: React.FunctionComponent = (dialogCont ) => setProjectId(e.target.value)} + onChange={(e: ChangeEvent) => { + setProjectId(e.target.value) + setProjectIdTouched(true) + }} /> = (dialogCont diff --git a/frontend/src/queries/project-requests.tsx b/frontend/src/queries/project-requests.tsx index f6e77af..b08677c 100644 --- a/frontend/src/queries/project-requests.tsx +++ b/frontend/src/queries/project-requests.tsx @@ -26,7 +26,10 @@ export function updateProject(projectId: string, projectData: {}): AxiosPromise return apiClient.put(urlAddress.api.project(projectId), projectData) } -export function createProject(projectId: string, projectData: {}): AxiosPromise { +export function createProject(projectId: string, projectData: { + projectName: string, + deploymentThreshold: string +}): AxiosPromise { if (projectData.projectName && projectData.projectName.length > 255) { return Promise.reject(new Error("Project name exceeds the maximum length of 255 characters")); } diff --git a/scripts/run-backend.bash b/scripts/run-backend.bash index c72bdca..ee24fc3 100755 --- a/scripts/run-backend.bash +++ b/scripts/run-backend.bash @@ -31,7 +31,7 @@ python manage.py createcachetable rate_limit python manage.py migrate python manage.py collectstatic --no-input -if [[ ${IS_DEV} == "true" ]] ; then +if [[ ${IS_DEV} == "True" ]] ; then python manage.py runserver else gunicorn securecheckplus.wsgi:application --bind 0.0.0.0:8000 --workers=2 --threads=2 --log-level INFO From 59a221eaa131c84ed1334075d86dab208a085854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20B=C3=BCchel?= Date: Tue, 28 Jan 2025 12:03:07 +0100 Subject: [PATCH 3/4] fix: adjusted project name length from 255 to 25 to match the backend model --- frontend/src/components/NavBar/ProjectCreationContent.tsx | 2 +- frontend/src/queries/project-requests.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/NavBar/ProjectCreationContent.tsx b/frontend/src/components/NavBar/ProjectCreationContent.tsx index 44da961..3e4e27c 100644 --- a/frontend/src/components/NavBar/ProjectCreationContent.tsx +++ b/frontend/src/components/NavBar/ProjectCreationContent.tsx @@ -81,7 +81,7 @@ const CreationContent: React.FunctionComponent = (dialogCont }, [projectId, isProjectIdTouched, allProjectIds]) useEffect(() => { - if (projectName.length > 255) { + if (projectName.length > 25) { setNameInvalid(true); setProjectNameHelperText(localization.dialog.projectNameHelperToLong); } else { diff --git a/frontend/src/queries/project-requests.tsx b/frontend/src/queries/project-requests.tsx index b08677c..de9b8d3 100644 --- a/frontend/src/queries/project-requests.tsx +++ b/frontend/src/queries/project-requests.tsx @@ -30,8 +30,8 @@ export function createProject(projectId: string, projectData: { projectName: string, deploymentThreshold: string }): AxiosPromise { - if (projectData.projectName && projectData.projectName.length > 255) { - return Promise.reject(new Error("Project name exceeds the maximum length of 255 characters")); + if (projectData.projectName && projectData.projectName.length > 25) { + return Promise.reject(new Error("Project name exceeds the maximum length of 25 characters")); } return apiClient.post(urlAddress.api.createProject(projectId), projectData) } From fee5e305635b6e40a4456c73db9ae1b57e12d118 Mon Sep 17 00:00:00 2001 From: Nils Kreiner Date: Wed, 19 Feb 2025 12:31:17 +0100 Subject: [PATCH 4/4] fixed validation --- backend/webserver/serializer/project_serializer.py | 2 +- backend/webserver/views/project_views.py | 4 ++-- frontend/src/components/NavBar/ProjectCreationContent.tsx | 4 ++-- frontend/src/queries/project-requests.tsx | 4 ++-- frontend/src/utilities/localization.tsx | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/webserver/serializer/project_serializer.py b/backend/webserver/serializer/project_serializer.py index 1b630e2..cab81e4 100644 --- a/backend/webserver/serializer/project_serializer.py +++ b/backend/webserver/serializer/project_serializer.py @@ -12,7 +12,7 @@ class Meta: fields = ["projectId", "projectName", "updated", "deploymentThreshold"] projectId = serializers.CharField(source="project_id", read_only=True) - projectName = serializers.CharField(source="project_name", allow_blank=True, max_length=255) + projectName = serializers.CharField(source="project_name", allow_blank=True) updated = serializers.DateTimeField(default=datetime.now(), read_only=True) deploymentThreshold = serializers.ChoiceField(source="deployment_threshold", choices=Threshold.names) diff --git a/backend/webserver/views/project_views.py b/backend/webserver/views/project_views.py index ab1c22e..683885d 100644 --- a/backend/webserver/views/project_views.py +++ b/backend/webserver/views/project_views.py @@ -158,8 +158,8 @@ def post(self, request, project_id): else: raise InvalidValueError(project_id) - if len(request.data.get("projectName", "")) > 255: - raise InvalidValueError("Project name exceeds the maximum length of 255 characters") + if len(request.data.get("projectName", "")) > 50: + raise InvalidValueError("Project name exceeds the maximum length of 50 characters") return Response(f"Creation of {project_id} successful!") diff --git a/frontend/src/components/NavBar/ProjectCreationContent.tsx b/frontend/src/components/NavBar/ProjectCreationContent.tsx index 3e4e27c..c4a261e 100644 --- a/frontend/src/components/NavBar/ProjectCreationContent.tsx +++ b/frontend/src/components/NavBar/ProjectCreationContent.tsx @@ -64,7 +64,7 @@ const CreationContent: React.FunctionComponent = (dialogCont }else if (projectId.includes(" ")){ setIdInvalid(true); setProjectIdHelperText(localization.dialog.projectIdHelperNoSpaces) - }else if (projectId.length > 20){ + }else if (projectId.length > 50){ setIdInvalid(true); setProjectIdHelperText(localization.dialog.projectIdHelperToLong) }else { @@ -81,7 +81,7 @@ const CreationContent: React.FunctionComponent = (dialogCont }, [projectId, isProjectIdTouched, allProjectIds]) useEffect(() => { - if (projectName.length > 25) { + if (projectName.length > 50) { setNameInvalid(true); setProjectNameHelperText(localization.dialog.projectNameHelperToLong); } else { diff --git a/frontend/src/queries/project-requests.tsx b/frontend/src/queries/project-requests.tsx index de9b8d3..52ca4b5 100644 --- a/frontend/src/queries/project-requests.tsx +++ b/frontend/src/queries/project-requests.tsx @@ -30,8 +30,8 @@ export function createProject(projectId: string, projectData: { projectName: string, deploymentThreshold: string }): AxiosPromise { - if (projectData.projectName && projectData.projectName.length > 25) { - return Promise.reject(new Error("Project name exceeds the maximum length of 25 characters")); + if (projectData.projectName && projectData.projectName.length > 50) { + return Promise.reject(new Error("Project name exceeds the maximum length of 50 characters")); } return apiClient.post(urlAddress.api.createProject(projectId), projectData) } diff --git a/frontend/src/utilities/localization.tsx b/frontend/src/utilities/localization.tsx index cec5165..b180308 100644 --- a/frontend/src/utilities/localization.tsx +++ b/frontend/src/utilities/localization.tsx @@ -283,7 +283,7 @@ const language = { projectId: "Projekt ID", projectIdHelperNotEmpty: "Projekt ID darf nicht leer sein.", projectIdHelperNoSpaces: "Projekt ID darf keine Leerzeichen beinhalten.", - projectIdHelperToLong: "Projekt ID ist länger als 20 Zeichen lang.", + projectIdHelperToLong: "Projekt ID ist länger als 50 Zeichen lang.", projectIdHelperIdAlreadyUsed: "Projekt ID bereits vergeben.", projectName: "Projektname", projectNameHelperToLong: "Projektname zu lang.", @@ -596,7 +596,7 @@ const language = { projectId: "Project ID", projectIdHelperNotEmpty: "Project ID mustn't be empty.", projectIdHelperNoSpaces: "Project ID mustn't contain spaces.", - projectIdHelperToLong: "Project ID is longer than 20 characters.", + projectIdHelperToLong: "Project ID is longer than 50 characters.", projectIdHelperIdAlreadyUsed: "Project ID already in use.", projectName: "Project name", projectNameHelperToLong: "Project name to long.",