From c82483e39bdcde4a91bff23b802e16b3ecb30aaa Mon Sep 17 00:00:00 2001 From: xMinhx <55718218+xMinhx@users.noreply.github.com> Date: Wed, 24 Jun 2026 11:01:09 -0300 Subject: [PATCH 01/13] test: mock NVD API calls, add nvd_integration marker - conftest.py patches requests.get + time.sleep in cve_fetcher module - Skips mock for tests marked @pytest.mark.nvd_integration - Moves test_cve_fetcher.py module-level code into fixtures - pytest.ini excludes nvd_integration by default 66 tests now run in ~1s vs timing out at >120s --- backend/analyzer/test/conftest.py | 65 +++++++++++++++++++++++ backend/analyzer/test/test_cve_fetcher.py | 31 +++++++---- backend/pytest.ini | 6 ++- 3 files changed, 91 insertions(+), 11 deletions(-) create mode 100644 backend/analyzer/test/conftest.py diff --git a/backend/analyzer/test/conftest.py b/backend/analyzer/test/conftest.py new file mode 100644 index 0000000..76f0f24 --- /dev/null +++ b/backend/analyzer/test/conftest.py @@ -0,0 +1,65 @@ +from unittest.mock import patch + +import pytest + +MOCK_NVD_RESPONSE = { + "vulnerabilities": [ + { + "cve": { + "id": "CVE-2021-44228", + "published": "2025-01-01T00:00:00Z", + "lastModified": "2025-01-01T00:00:00Z", + "descriptions": [{"value": "A test vulnerability description."}], + "metrics": { + "cvssMetricV31": [ + { + "cvssData": { + "baseScore": 7.5, + "baseSeverity": "HIGH", + "attackVector": "NETWORK", + "attackComplexity": "LOW", + "privilegesRequired": "NONE", + "userInteraction": "NONE", + "confidentialityImpact": "HIGH", + "integrityImpact": "NONE", + "availabilityImpact": "NONE", + "scope": "UNCHANGED", + } + } + ] + }, + "weaknesses": [{"description": [{"value": "CWE-94"}]}], + "references": [{"url": "https://example.com/advisory", "tags": ["Vendor Advisory"]}], + } + } + ] +} + +MOCK_EPSS_RESPONSE = {"data": [{"epss": 0.5}]} + + +def _mock_requests_get(url, **kwargs): + class MockResponse: + def __init__(self, json_data, status_code): + self.json_data = json_data + self.status_code = status_code + + def json(self): + return self.json_data + + url_str = str(url) + if "nvd.nist.gov" in url_str: + return MockResponse(MOCK_NVD_RESPONSE, 200) + if "api.first.org" in url_str: + return MockResponse(MOCK_EPSS_RESPONSE, 200) + raise ConnectionError(f"Unexpected request: {url_str}") + + +@pytest.fixture(autouse=True) +def no_nvd_network(request): + if request.node.get_closest_marker("nvd_integration"): + yield + return + with patch("analyzer.services.cve_fetcher.requests.get", side_effect=_mock_requests_get), \ + patch("analyzer.services.cve_fetcher.time.sleep"): + yield diff --git a/backend/analyzer/test/test_cve_fetcher.py b/backend/analyzer/test/test_cve_fetcher.py index b1df86b..d4072f5 100644 --- a/backend/analyzer/test/test_cve_fetcher.py +++ b/backend/analyzer/test/test_cve_fetcher.py @@ -1,29 +1,42 @@ from datetime import datetime +import pytest + from analyzer.services.cve_fetcher import CVEFetcher from utilities.constants import BaseSeverity, AttackVector, AttackComplexity, UserInteraction, IntegrityImpact, \ AvailabilityImpact, ConfidentialityImpact, Scope, PrivilegesRequired cve_id = "CVE-2021-44228" -cve_fetcher = CVEFetcher(cve_id=cve_id) -cve_data = cve_fetcher.generate() -def test_description(): +@pytest.fixture +def cve_data(): + fetcher = CVEFetcher(cve_id=cve_id) + return fetcher.generate() + + +@pytest.mark.nvd_integration +def test_real_nvd_api_contract(): + fetcher = CVEFetcher(cve_id=cve_id) + data = fetcher.generate() + assert fetcher.successful + assert len(data["description"]) > 0 + assert 0 < data["cve_attributes"]["baseScore"] <= 10 + + +def test_description(cve_data): assert len(cve_data["description"]) > 0 -def test_dates(): +def test_dates(cve_data): published = cve_data["published"] assert isinstance(published, datetime) - updated = cve_data["updated"] assert isinstance(updated, datetime) -def test_cve_attributes_cvss_v3(): +def test_cve_attributes_cvss_v3(cve_data): attributes = cve_data["cve_attributes"] - assert 0 < attributes["baseScore"] <= 10 assert attributes["baseSeverity"] in BaseSeverity.names assert attributes["attackVector"] in AttackVector.names @@ -36,9 +49,9 @@ def test_cve_attributes_cvss_v3(): assert attributes["scope"] in Scope.names -def test_epss_score(): +def test_epss_score(cve_data): assert 0 <= float(cve_data["epss"]) <= 1.0 -def test_vendor_reference(): +def test_vendor_reference(cve_data): assert len(cve_data["vendor_reference"]) >= 0 diff --git a/backend/pytest.ini b/backend/pytest.ini index ac3d988..a8cc62a 100644 --- a/backend/pytest.ini +++ b/backend/pytest.ini @@ -1,3 +1,5 @@ [pytest] -addopts = --nomigrations --reuse-db -DJANGO_SETTINGS_MODULE = securecheckplus.settings \ No newline at end of file +addopts = --nomigrations --reuse-db -m "not nvd_integration" +DJANGO_SETTINGS_MODULE = securecheckplus.settings +markers = + nvd_integration: tests that call the real NVD API (requires internet + API key) \ No newline at end of file From c5c6680113a7daed125245e88f89cb19e43bbbb3 Mon Sep 17 00:00:00 2001 From: xMinhx <55718218+xMinhx@users.noreply.github.com> Date: Sat, 27 Jun 2026 15:45:28 -0300 Subject: [PATCH 02/13] fix: guard Docker Login and add hybrid fallback for DB/SALT --- .github/workflows/cicd-actions.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cicd-actions.yml b/.github/workflows/cicd-actions.yml index dffbe4d..36c0355 100644 --- a/.github/workflows/cicd-actions.yml +++ b/.github/workflows/cicd-actions.yml @@ -26,10 +26,13 @@ jobs: uses: actions/checkout@v4 - name: Create .env file + env: + POSTGRES_PASSWORD: ${{ secrets.TEST_DB_PASSWORD }} + SALT: ${{ secrets.TEST_SALT }} run: | echo "NVD_API_KEY=${{ secrets.TEST_NVD_API_KEY }}" >> .env echo 'DJANGO_SECRET_KEY="${{ secrets.TEST_DJANGO_SECRET_KEY }}"' >> .env - echo 'SALT="${{ secrets.TEST_SALT }}"' >> .env + echo "SALT=${SALT:-local-dev-salt}" >> .env echo "ADMIN_USERNAME=admin@acme.de" >> .env echo "ADMIN_PASSWORD=secure!" >> .env echo "USER_USERNAME=user@acme.de" >> .env @@ -41,7 +44,7 @@ jobs: echo "POSTGRES_USER=securecheckplus" >> .env echo "POSTGRES_DB=securecheckplus" >> .env echo "POSTGRES_PORT=5432" >> .env - echo 'POSTGRES_PASSWORD="${{ secrets.TEST_DB_PASSWORD }}"' >> .env + echo "POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-scp_test_pass}" >> .env echo "EMAIL_HOST=localhost" >> .env echo "EMAIL_PORT=25" >> .env echo 'LDAP_ORGANISATION="ACME"' >> .env @@ -77,6 +80,7 @@ jobs: path: backend - name: Docker Login + if: env.DOCKER_USER != '' && env.DOCKER_KEY != '' run: echo "$DOCKER_KEY" | docker login -u "$DOCKER_USER" --password-stdin - name: Build Docker Compose @@ -182,6 +186,7 @@ jobs: path: backend/assets - name: Docker Login + if: env.DOCKER_USER != '' && env.DOCKER_KEY != '' run: echo "$DOCKER_KEY" | docker login -u "$DOCKER_USER" --password-stdin - name: Extract metadata (tags, labels) for Docker @@ -221,6 +226,7 @@ jobs: path: backend - name: Docker Login + if: env.DOCKER_USER != '' && env.DOCKER_KEY != '' run: echo "$DOCKER_KEY" | docker login -u "$DOCKER_USER" --password-stdin - name: Extract metadata (tags, labels) for Docker From 32d668dd669504d49b6961db6e308857be36eafb Mon Sep 17 00:00:00 2001 From: Nils Kreiner Date: Mon, 10 Feb 2025 16:50:23 +0100 Subject: [PATCH 03/13] 26 extended length of fields project_name and project_id --- ...ter_dependency_package_manager_and_more.py | 33 +++++++++++++++++++ backend/analyzer/models.py | 4 +-- 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 backend/analyzer/migrations/0003_alter_dependency_package_manager_and_more.py diff --git a/backend/analyzer/migrations/0003_alter_dependency_package_manager_and_more.py b/backend/analyzer/migrations/0003_alter_dependency_package_manager_and_more.py new file mode 100644 index 0000000..ed5196c --- /dev/null +++ b/backend/analyzer/migrations/0003_alter_dependency_package_manager_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 5.1.2 on 2025-02-10 15:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('analyzer', '0002_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='dependency', + name='package_manager', + field=models.CharField(default='NA', max_length=255), + ), + migrations.AlterField( + model_name='project', + name='project_id', + field=models.CharField(max_length=50, unique=True), + ), + migrations.AlterField( + model_name='project', + name='project_name', + field=models.CharField(blank=True, max_length=50), + ), + migrations.AlterField( + model_name='report', + name='overall_cvss_severity', + field=models.CharField(max_length=255, null=True), + ), + ] diff --git a/backend/analyzer/models.py b/backend/analyzer/models.py index 5aa48c5..9f798e2 100644 --- a/backend/analyzer/models.py +++ b/backend/analyzer/models.py @@ -12,8 +12,8 @@ class Project(models.Model): class Meta: db_table = constants.DB_SCHEMA_PREFIX + "project" - project_id = models.CharField(max_length=25, blank=False, unique=True) - project_name = models.CharField(max_length=25, blank=True) + project_id = models.CharField(max_length=50, blank=False, unique=True) + project_name = models.CharField(max_length=50, blank=True) updated = models.DateTimeField(auto_now=True) deployment_threshold = models.CharField(max_length=20, choices=constants.Threshold.choices, From 3740f0fc071b945b75f4e6f4781f527be7d08a6d Mon Sep 17 00:00:00 2001 From: Nils Kreiner Date: Wed, 19 Feb 2025 12:31:17 +0100 Subject: [PATCH 04/13] fixed validation --- backend/webserver/views/project_views.py | 3 +++ .../NavBar/ProjectCreationContent.tsx | 22 ++++++++++++++----- frontend/src/queries/project-requests.tsx | 8 ++++++- frontend/src/utilities/localization.tsx | 4 ++-- 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/backend/webserver/views/project_views.py b/backend/webserver/views/project_views.py index 351199b..683885d 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", "")) > 50: + raise InvalidValueError("Project name exceeds the maximum length of 50 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..7b63ff3 100644 --- a/frontend/src/components/NavBar/ProjectCreationContent.tsx +++ b/frontend/src/components/NavBar/ProjectCreationContent.tsx @@ -56,11 +56,11 @@ const CreationContent: React.FunctionComponent = (dialogCont setInvalid(true); setHelperText(localization.dialog.projectIdHelperNotEmpty) }else if (projectId.includes(" ")){ - setInvalid(true); - setHelperText(localization.dialog.projectIdHelperNoSpaces) - }else if (projectId.length > 20){ - setInvalid(true); - setHelperText(localization.dialog.projectIdHelperToLong) + setIdInvalid(true); + setProjectIdHelperText(localization.dialog.projectIdHelperNoSpaces) + }else if (projectId.length > 50){ + setIdInvalid(true); + setProjectIdHelperText(localization.dialog.projectIdHelperToLong) }else { if (data?.data !== undefined) { if (allProjectIds.includes(projectId.toLowerCase())) { @@ -72,7 +72,17 @@ const CreationContent: React.FunctionComponent = (dialogCont } } } - }, [projectId]) + }, [projectId, isProjectIdTouched, allProjectIds]) + + useEffect(() => { + if (projectName.length > 50) { + setNameInvalid(true); + setProjectNameHelperText(localization.dialog.projectNameHelperToLong); + } else { + setNameInvalid(false); + setProjectNameHelperText("Optional"); + } + }, [projectName]); return( diff --git a/frontend/src/queries/project-requests.tsx b/frontend/src/queries/project-requests.tsx index 4c1be3f..fc97085 100644 --- a/frontend/src/queries/project-requests.tsx +++ b/frontend/src/queries/project-requests.tsx @@ -26,7 +26,13 @@ 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 > 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.", From 97f74c89207ea2358b97a96c949b8fd82ea6c8f5 Mon Sep 17 00:00:00 2001 From: Nils Kreiner Date: Fri, 7 Feb 2025 13:10:35 +0100 Subject: [PATCH 05/13] finished adding tooltips for the CVSS calculator metrics --- frontend/src/utilities/localization.tsx | 106 +++++++++++++++++++----- 1 file changed, 84 insertions(+), 22 deletions(-) diff --git a/frontend/src/utilities/localization.tsx b/frontend/src/utilities/localization.tsx index b180308..022ee35 100644 --- a/frontend/src/utilities/localization.tsx +++ b/frontend/src/utilities/localization.tsx @@ -184,28 +184,90 @@ const language = { availabilityRequirement: "Availability Requirement", }, scoreMetricValues: { - notDefined: "Not Defined", - unproven: "Unproven", - proofOfConcept: "Proof-of-Concept", - functional: "Functional", - high: "High", - officialFix: "Official fix", - temporaryFix: "Temporary fix", - workaround: "Workaround", - unavailable: "Unavailable", - unknown: "Unknown", - reasonable: "Reasonable", - confirmed: "Confirmed", - network: "Network", - adjacent: "Adjacent", - local: "Local", - physical: "Physical", - low: "Low", - medium: "Medium", - none: "None", - required: "Required", - unchanged: "Unchanged", - changed: "Changed", + exploitCodeMaturity: { + notDefined: {title: NOT_DEFINED, tooltip: "Assigning this value indicates there is insufficient information to choose one of the other values, and has no impact on the overall Temporal Score, i.e., it has the same effect on scoring as assigning High."}, + unproven: {title: "Unproven", tooltip: "No exploit code is available, or an exploit is theoretical."}, + proofOfConcept: {title: "Proof-of-Concept", tooltip: "Proof-of-concept exploit code is available, or an attack demonstration is not practical for most systems. The code or technique is not functional in all situations and may require substantial modification by a skilled attacker."}, + functional: {title: "Functional", tooltip: "Functional exploit code is available. The code works in most situations where the vulnerability exists."}, + high: {title: "High", tooltip: "Functional autonomous code exists, or no exploit is required (manual trigger) and details are widely available. Exploit code works in every situation, or is actively being delivered via an autonomous agent (such as a worm or virus). Network-connected systems are likely to encounter scanning or exploitation attempts. Exploit development has reached the level of reliable, widely available, easy-to-use automated tools."} + }, + remediationLevel: { + notDefined: {title: NOT_DEFINED, tooltip: "Assigning this value indicates there is insufficient information to choose one of the other values, and has no impact on the overall Temporal Score, i.e., it has the same effect on scoring as assigning Unavailable."}, + officialFix: {title: "Official fix", tooltip: "A complete vendor solution is available. Either the vendor has issued an official patch, or an upgrade is available."}, + temporaryFix: {title: "Temporary fix", tooltip: "There is an official but temporary fix available. This includes instances where the vendor issues a temporary hotfix, tool, or workaround."}, + workaround: {title: "Workaround", tooltip: "There is an unofficial, non-vendor solution available. In some cases, users of the affected technology will create a patch of their own or provide steps to work around or otherwise mitigate the vulnerability."}, + unavailable: {title: "Unavailable", tooltip: "There is either no solution available or it is impossible to apply."}, + }, + reportConfidence: { + notDefined: {title: NOT_DEFINED, tooltip: "Assigning this value indicates there is insufficient information to choose one of the other values, and has no impact on the overall Temporal Score, i.e., it has the same effect on scoring as assigning Confirmed."}, + unknown: {title: "Unknown", tooltip: "There are reports of impacts that indicate a vulnerability is present. The reports indicate that the cause of the vulnerability is unknown, or reports may differ on the cause or impacts of the vulnerability. Reporters are uncertain of the true nature of the vulnerability, and there is little confidence in the validity of the reports or whether a static Base Score can be applied given the differences described. An example is a bug report which notes that an intermittent but non-reproducible crash occurs, with evidence of memory corruption suggesting that denial of service, or possible more serious impacts, may result."}, + reasonable: {title: "Reasonable", tooltip: "Significant details are published, but researchers either do not have full confidence in the root cause, or do not have access to source code to fully confirm all of the interactions that may lead to the result. Reasonable confidence exists, however, that the bug is reproducible and at least one impact is able to be verified (proof-of-concept exploits may provide this). An example is a detailed write-up of research into a vulnerability with an explanation (possibly obfuscated or “left as an exercise to the reader”) that gives assurances on how to reproduce the results."}, + confirmed: {title: "Confirmed", tooltip: "Detailed reports exist, or functional reproduction is possible (functional exploits may provide this). Source code is available to independently verify the assertions of the research, or the author or vendor of the affected code has confirmed the presence of the vulnerability."} + }, + confidentialityRequirement: { + notDefined: {title: NOT_DEFINED, tooltip: NOT_DEFINED_REQ}, + low: {title: LOW, tooltip: "Loss of Confidentiality is likely to have only a limited adverse effect on the organization or individuals associated with the organization (e.g., employees, customers)."}, + medium: {title: MEDIUM, tooltip: "Loss of [Confidentiality | Integrity | Availability] is likely to have a serious adverse effect on the organization or individuals associated with the organization (e.g., employees, customers)."}, + high: {title: HIGH, tooltip: "Loss of [Confidentiality | Integrity | Availability] is likely to have a catastrophic adverse effect on the organization or individuals associated with the organization (e.g., employees, customers)."} + }, + integrityRequirement: { + notDefined: {title: NOT_DEFINED, tooltip: NOT_DEFINED_REQ}, + low: {title: LOW, tooltip: "Loss of Confidentiality is likely to have only a limited adverse effect on the organization or individuals associated with the organization (e.g., employees, customers)."}, + medium: {title: MEDIUM, tooltip: "Loss of [Confidentiality | Integrity | Availability] is likely to have a serious adverse effect on the organization or individuals associated with the organization (e.g., employees, customers)."}, + high: {title: HIGH, tooltip: "Loss of [Confidentiality | Integrity | Availability] is likely to have a catastrophic adverse effect on the organization or individuals associated with the organization (e.g., employees, customers)."} + }, + availabilityRequirement: { + notDefined: {title: NOT_DEFINED, tooltip: NOT_DEFINED_REQ}, + low: {title: LOW, tooltip: "Loss of Confidentiality is likely to have only a limited adverse effect on the organization or individuals associated with the organization (e.g., employees, customers)."}, + medium: {title: MEDIUM, tooltip: "Loss of [Confidentiality | Integrity | Availability] is likely to have a serious adverse effect on the organization or individuals associated with the organization (e.g., employees, customers)."}, + high: {title: HIGH, tooltip: "Loss of [Confidentiality | Integrity | Availability] is likely to have a catastrophic adverse effect on the organization or individuals associated with the organization (e.g., employees, customers)."} + }, + modifiedAttackVector: { + notDefined: {title: NOT_DEFINED, tooltip: NOT_DEFINED_DEF}, + network: {title: "Network", tooltip: "The vulnerable component is bound to the network stack and the set of possible attackers extends beyond the other options listed below, up to and including the entire Internet. Such a vulnerability is often termed “remotely exploitable” and can be thought of as an attack being exploitable at the protocol level one or more network hops away (e.g., across one or more routers). An example of a network attack is an attacker causing a denial of service (DoS) by sending a specially crafted TCP packet across a wide area network (e.g., CVE‑2004‑0230)."}, + adjacent: {title: "Adjacent", tooltip: "The vulnerable component is bound to the network stack, but the attack is limited at the protocol level to a logically adjacent topology. This can mean an attack must be launched from the same shared physical (e.g., Bluetooth or IEEE 802.11) or logical (e.g., local IP subnet) network, or from within a secure or otherwise limited administrative domain (e.g., MPLS, secure VPN to an administrative network zone). One example of an Adjacent attack would be an ARP (IPv4) or neighbor discovery (IPv6) flood leading to a denial of service on the local LAN segment (e.g., CVE‑2013‑6014)."}, + local: {title: "Local", tooltip: "The vulnerable component is not bound to the network stack and the attacker’s path is via read/write/execute capabilities. Either: the attacker exploits the vulnerability by accessing the target system locally (e.g., keyboard, console), or remotely (e.g., SSH); or the attacker relies on User Interaction by another person to perform actions required to exploit the vulnerability (e.g., using social engineering techniques to trick a legitimate user into opening a malicious document)."}, + physical: {title: "Physical", tooltip: "The attack requires the attacker to physically touch or manipulate the vulnerable component. Physical interaction may be brief (e.g., evil maid attack1) or persistent. An example of such an attack is a cold boot attack in which an attacker gains access to disk encryption keys after physically accessing the target system. Other examples include peripheral attacks via FireWire/USB Direct Memory Access (DMA)."} + }, + modifiedAttackComplexity: { + notDefined: {title: NOT_DEFINED, tooltip: NOT_DEFINED_DEF}, + low: {title: LOW, tooltip: "Specialized access conditions or extenuating circumstances do not exist. An attacker can expect repeatable success when attacking the vulnerable component."}, + high: {title: HIGH, tooltip: "A successful attack depends on conditions beyond the attacker's control. That is, a successful attack cannot be accomplished at will, but requires the attacker to invest in some measurable amount of effort in preparation or execution against the vulnerable component before a successful attack can be expected.2 For example, a successful attack may depend on an attacker overcoming any of the following conditions: The attacker must gather knowledge about the environment in which the vulnerable target/component exists. For example, a requirement to collect details on target configuration settings, sequence numbers, or shared secrets. or The attacker must prepare the target environment to improve exploit reliability. For example, repeated exploitation to win a race condition, or overcoming advanced exploit mitigation techniques. or The attacker must inject themselves into the logical network path between the target and the resource requested by the victim in order to read and/or modify network communications (e.g., a man in the middle attack)."} + }, + modifiedPrivilegesRequired: { + notDefined: {title: NOT_DEFINED, tooltip: NOT_DEFINED_DEF}, + none: {title: NONE, tooltip: "The attacker is unauthorized prior to attack, and therefore does not require any access to settings or files of the vulnerable system to carry out an attack."}, + low: {title: LOW, tooltip: "The attacker requires privileges that provide basic user capabilities that could normally affect only settings and files owned by a user. Alternatively, an attacker with Low privileges has the ability to access only non-sensitive resources."}, + high: {title: HIGH, tooltip: "The attacker requires privileges that provide significant (e.g., administrative) control over the vulnerable component allowing access to component-wide settings and files."} + }, + modifiedUserInteraction: { + notDefined: {title: NOT_DEFINED, tooltip: NOT_DEFINED_DEF}, + none: {title: NONE, tooltip: "The vulnerable system can be exploited without interaction from any user."}, + required: {title: "Required", tooltip: "Successful exploitation of this vulnerability requires a user to take some action before the vulnerability can be exploited. For example, a successful exploit may only be possible during the installation of an application by a system administrator."} + }, + modifiedScope: { + notDefined: {title: NOT_DEFINED, tooltip: NOT_DEFINED_DEF}, + unchanged: {title: "Unchanged", tooltip: "An exploited vulnerability can only affect resources managed by the same security authority. In this case, the vulnerable component and the impacted component are either the same, or both are managed by the same security authority."}, + changed: {title: "Changed", tooltip: "An exploited vulnerability can affect resources beyond the security scope managed by the security authority of the vulnerable component. In this case, the vulnerable component and the impacted component are different and managed by different security authorities."} + }, + modifiedConfidentiality: { + notDefined: {title: NOT_DEFINED, tooltip: NOT_DEFINED_DEF}, + none: {title: NONE, tooltip: "There is no loss of confidentiality within the impacted component."}, + low: {title: LOW, tooltip: "There is some loss of confidentiality. Access to some restricted information is obtained, but the attacker does not have control over what information is obtained, or the amount or kind of loss is limited. The information disclosure does not cause a direct, serious loss to the impacted component."}, + high: {title: HIGH, tooltip: "There is a total loss of confidentiality, resulting in all resources within the impacted component being divulged to the attacker. Alternatively, access to only some restricted information is obtained, but the disclosed information presents a direct, serious impact. For example, an attacker steals the administrator's password, or private encryption keys of a web server."} + }, + modifiedIntegrity: { + notDefined: {title: NOT_DEFINED, tooltip: NOT_DEFINED_DEF}, + none: {title: NONE, tooltip: "There is no loss of integrity within the impacted component."}, + low: {title: LOW, tooltip: "Modification of data is possible, but the attacker does not have control over the consequence of a modification, or the amount of modification is limited. The data modification does not have a direct, serious impact on the impacted component."}, + high: {title: HIGH, tooltip: "There is a total loss of integrity, or a complete loss of protection. For example, the attacker is able to modify any/all files protected by the impacted component. Alternatively, only some files can be modified, but malicious modification would present a direct, serious consequence to the impacted component."} + }, + modifiedAvailability: { + notDefined: {title: NOT_DEFINED, tooltip: NOT_DEFINED_DEF}, + none: {title: NONE, tooltip: "There is no impact to availability within the impacted component."}, + low: {title: LOW, tooltip: "Performance is reduced or there are interruptions in resource availability. Even if repeated exploitation of the vulnerability is possible, the attacker does not have the ability to completely deny service to legitimate users. The resources in the impacted component are either partially available all of the time, or fully available only some of the time, but overall there is no direct, serious consequence to the impacted component."}, + high: {title: HIGH, tooltip: "There is a total loss of availability, resulting in the attacker being able to fully deny access to resources in the impacted component; this loss is either sustained (while the attacker continues to deliver the attack) or persistent (the condition persists even after the attack has completed). Alternatively, the attacker has the ability to deny some availability, but the loss of availability presents a direct, serious consequence to the impacted component (e.g., the attacker cannot disrupt existing connections, but can prevent new connections; the attacker can repeatedly exploit a vulnerability that, in each instance of a successful attack, leaks a only small amount of memory, but after repeated exploitation causes a service to become completely unavailable)."} + } }, }, sidebar:{ From 594811ff622e3157ed27e6bbc27ff770a568e52a Mon Sep 17 00:00:00 2001 From: Niklas B Date: Thu, 23 Jan 2025 13:15:06 +0100 Subject: [PATCH 06/13] Fix profile picture loading error in production Fixes #22 The solution was generated using github copilot workspace. --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/accso/SecureCheckPlus/issues/22?shareId=XXXX-XXXX-XXXX-XXXX). --- backend/securecheckplus/settings.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/backend/securecheckplus/settings.py b/backend/securecheckplus/settings.py index 13df1d2..1952cb0 100644 --- a/backend/securecheckplus/settings.py +++ b/backend/securecheckplus/settings.py @@ -300,3 +300,18 @@ def format(self, record): SESSION_EXPIRE_AT_BROWSER_CLOSE = False SESSION_SAVE_EVERY_REQUEST = True SESSION_COOKIE_AGE = 60 * 60 * 24 * 30 # 30 days + +# Content Security Policy +CSP_DEFAULT_SRC = ("'self'",) +CSP_IMG_SRC = ( + "'self'", + "data:", + "*.interssl.com", + "www.wkoecg.at", + "*.geotrust.com", + "*.paypal.com", + "*.amazonaws.com", + "*.google-analytics.com", + "*.cloudflare.com", + "api.dicebear.com", +) From 247a352cf48da70e802281424ee8a24ca25ea686 Mon Sep 17 00:00:00 2001 From: Nils Kreiner Date: Wed, 19 Feb 2025 14:48:13 +0100 Subject: [PATCH 07/13] added cve description to the details page (upstream #10) --- frontend/src/components/InfoBox.tsx | 2 +- frontend/src/page/ReportPage.tsx | 19 +++++++++++++++---- frontend/src/utilities/localization.tsx | 4 ++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/InfoBox.tsx b/frontend/src/components/InfoBox.tsx index 25ee81b..7ed5f1f 100644 --- a/frontend/src/components/InfoBox.tsx +++ b/frontend/src/components/InfoBox.tsx @@ -31,7 +31,7 @@ const boxProps = (headerComponent: React.ReactNode) => { display: "flex", flexDirection: "column", alignItems: "center", - justifyContent: "center", + justifyContent: "start", backgroundColor: mainTheme.palette.primary.main, borderRadius: "0.5rem", paddingTop: headerComponent === undefined ? "2rem" : 0, diff --git a/frontend/src/page/ReportPage.tsx b/frontend/src/page/ReportPage.tsx index fda9042..efe684f 100644 --- a/frontend/src/page/ReportPage.tsx +++ b/frontend/src/page/ReportPage.tsx @@ -482,7 +482,7 @@ const ReportPage: React.FunctionComponent = () => { }/> - + { "flexWrap": "wrap", "justifyContent": "space-evenly" }}> - + { }}/> - {localization.ReportDetailPage.cvssVectorString} { }/> + + + {localization.ReportDetailPage.infosFound.detailedDescriptionTitle} + {data?.data.report.cveObject.description} + + }/> + Date: Wed, 24 Jun 2026 11:53:09 -0300 Subject: [PATCH 08/13] Replace remote DiceBear avatars with local client-side generation (upstream #22) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Swap adventurer-neutral API URL for local @dicebear/core v9 + bottts-neutral - Add avatar.ts utility wrapping createAvatar() -> toDataUri() - Update UserSettingsContent.tsx and Profile.tsx to use getAvatarDataUri() - Remove dicebear.com URL from constants.tsx - Remove django-csp app, middleware, and all CSP config (no external images left) - Shell -> exec form CMD in Dockerfile for proper signal handling - as -> AS in Dockerfile per convention - Exclude .opencode/ from git tracking Co-authored-by: Niklas Büchel --- backend/Dockerfile | 2 +- backend/env.template | 3 + frontend/package-lock.json | 74 +++++++++++++++++-- frontend/package.json | 3 + frontend/src/components/NavBar/Profile.tsx | 4 +- .../src/components/UserSettingsContent.tsx | 4 +- frontend/src/utilities/avatar.ts | 6 ++ frontend/src/utilities/constants.tsx | 1 - 8 files changed, 83 insertions(+), 14 deletions(-) create mode 100644 frontend/src/utilities/avatar.ts diff --git a/backend/Dockerfile b/backend/Dockerfile index d08bfcc..394a80e 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -18,7 +18,7 @@ RUN apk update --no-cache \ ENTRYPOINT ["sh", "/entrypoint.sh"] -CMD python manage.py runserver 0.0.0.0:8000 +CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"] FROM dev as prod #ARG is required for the settings.py. Otherwise the build will fail, because required env variables have not been set diff --git a/backend/env.template b/backend/env.template index fe9d460..745ffef 100644 --- a/backend/env.template +++ b/backend/env.template @@ -8,6 +8,9 @@ IS_DEV=True # This is the URL that the application can be reached at FULLY_QUALIFIED_DOMAIN_NAME=http://localhost:8080 +# The log level of the backend application +LOG_LEVEL=INFO + ############################################################################################################## # Setup of API keys and secrets # Except for the NVD_API_KEY the variables can be filled with random keys! diff --git a/frontend/package-lock.json b/frontend/package-lock.json index afdd8dc..dd86f0f 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,6 +9,9 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@dicebear/adventurer-neutral": "^9.4.2", + "@dicebear/bottts-neutral": "^9.4.2", + "@dicebear/core": "^9.4.2", "@emotion/react": "^11.8.1", "@emotion/styled": "^11.8.1", "@mui/icons-material": "^5.4.4", @@ -1751,6 +1754,42 @@ "node": ">=6.9.0" } }, + "node_modules/@dicebear/adventurer-neutral": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@dicebear/adventurer-neutral/-/adventurer-neutral-9.4.2.tgz", + "integrity": "sha512-5xgkG/mNL4j3Q4SJGQLBU/KnU90tng8Ze5ofThD+55wi0oeY/nSAUowg6UFCmHrktjifj/MEx3CQqbpcPWtfIA==", + "license": "(MIT AND CC-BY-4.0)", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^9.0.0" + } + }, + "node_modules/@dicebear/bottts-neutral": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@dicebear/bottts-neutral/-/bottts-neutral-9.4.2.tgz", + "integrity": "sha512-kFNwWt6j+gzZ5n5Pz7WVwePubREAQOF8ZwWA9ztwVYDVMLnOChWbAofy5FED4j5md2MXFH2EgLCFCMr5K2BmIA==", + "license": "See LICENSE file", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^9.0.0" + } + }, + "node_modules/@dicebear/core": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@dicebear/core/-/core-9.4.2.tgz", + "integrity": "sha512-MF0042+Z3s8PGZKZLySfhft28bUa3B1iq0e5NSjCvY8gfMi5aIH/iRJGRJa1N9Jz1BNkxYb4yvJ/N9KO8Z6Y+w==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz", @@ -2536,10 +2575,10 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" }, "node_modules/@types/mime": { "version": "1.3.2", @@ -9605,6 +9644,26 @@ "to-fast-properties": "^2.0.0" } }, + "@dicebear/adventurer-neutral": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@dicebear/adventurer-neutral/-/adventurer-neutral-9.4.2.tgz", + "integrity": "sha512-5xgkG/mNL4j3Q4SJGQLBU/KnU90tng8Ze5ofThD+55wi0oeY/nSAUowg6UFCmHrktjifj/MEx3CQqbpcPWtfIA==", + "requires": {} + }, + "@dicebear/bottts-neutral": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@dicebear/bottts-neutral/-/bottts-neutral-9.4.2.tgz", + "integrity": "sha512-kFNwWt6j+gzZ5n5Pz7WVwePubREAQOF8ZwWA9ztwVYDVMLnOChWbAofy5FED4j5md2MXFH2EgLCFCMr5K2BmIA==", + "requires": {} + }, + "@dicebear/core": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@dicebear/core/-/core-9.4.2.tgz", + "integrity": "sha512-MF0042+Z3s8PGZKZLySfhft28bUa3B1iq0e5NSjCvY8gfMi5aIH/iRJGRJa1N9Jz1BNkxYb4yvJ/N9KO8Z6Y+w==", + "requires": { + "@types/json-schema": "^7.0.15" + } + }, "@discoveryjs/json-ext": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz", @@ -10127,10 +10186,9 @@ } }, "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "@types/mime": { "version": "1.3.2", diff --git a/frontend/package.json b/frontend/package.json index c083bb8..46ae3fd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -36,6 +36,9 @@ "webpack-merge": "^5.8.0" }, "dependencies": { + "@dicebear/adventurer-neutral": "^9.4.2", + "@dicebear/bottts-neutral": "^9.4.2", + "@dicebear/core": "^9.4.2", "@emotion/react": "^11.8.1", "@emotion/styled": "^11.8.1", "@mui/icons-material": "^5.4.4", diff --git a/frontend/src/components/NavBar/Profile.tsx b/frontend/src/components/NavBar/Profile.tsx index 4554b77..833a201 100644 --- a/frontend/src/components/NavBar/Profile.tsx +++ b/frontend/src/components/NavBar/Profile.tsx @@ -2,7 +2,7 @@ import {Avatar, IconButton, ListItemIcon, Menu, MenuItem, Stack, Tooltip, Typogr import React, {useEffect, useState} from "react"; import {Logout, Settings} from "@mui/icons-material"; import localization from "../../utilities/localization"; -import {urlAddress} from "../../utilities/constants"; +import {getAvatarDataUri} from "../../utilities/avatar"; import {useQuery} from "react-query"; import {getUserData, logout} from "../../queries/user-requests"; import CustomDialog from "../CustomDialog"; @@ -50,7 +50,7 @@ export default function Profile() { size="small" sx={{mr: "2rem"}} > - + diff --git a/frontend/src/components/UserSettingsContent.tsx b/frontend/src/components/UserSettingsContent.tsx index 802e108..ecafc31 100644 --- a/frontend/src/components/UserSettingsContent.tsx +++ b/frontend/src/components/UserSettingsContent.tsx @@ -2,7 +2,7 @@ import React, {ChangeEvent, Dispatch, SetStateAction, useEffect, useState} from import {Avatar, Button, Stack, TextField, Typography} from "@mui/material"; import localization from "../utilities/localization" import DropdownMenu from "./DropdownMenu"; -import {urlAddress} from "../utilities/constants" +import {getAvatarDataUri} from "../utilities/avatar" import {useMutation, useQuery, useQueryClient} from "react-query"; import {getUserData, updateUserData} from "../queries/user-requests"; import {colors} from "../style/globalStyle"; @@ -54,7 +54,7 @@ const UserSettingsContent: React.FunctionComponent = (dialogProps: return ( - + Date: Wed, 24 Jun 2026 11:57:41 -0300 Subject: [PATCH 09/13] Remove unused @dicebear/adventurer-neutral dependency (upstream #22) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Niklas Büchel --- frontend/package-lock.json | 19 ------------------- frontend/package.json | 1 - 2 files changed, 20 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index dd86f0f..d6b2d02 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,7 +9,6 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@dicebear/adventurer-neutral": "^9.4.2", "@dicebear/bottts-neutral": "^9.4.2", "@dicebear/core": "^9.4.2", "@emotion/react": "^11.8.1", @@ -1754,18 +1753,6 @@ "node": ">=6.9.0" } }, - "node_modules/@dicebear/adventurer-neutral": { - "version": "9.4.2", - "resolved": "https://registry.npmjs.org/@dicebear/adventurer-neutral/-/adventurer-neutral-9.4.2.tgz", - "integrity": "sha512-5xgkG/mNL4j3Q4SJGQLBU/KnU90tng8Ze5ofThD+55wi0oeY/nSAUowg6UFCmHrktjifj/MEx3CQqbpcPWtfIA==", - "license": "(MIT AND CC-BY-4.0)", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "@dicebear/core": "^9.0.0" - } - }, "node_modules/@dicebear/bottts-neutral": { "version": "9.4.2", "resolved": "https://registry.npmjs.org/@dicebear/bottts-neutral/-/bottts-neutral-9.4.2.tgz", @@ -9644,12 +9631,6 @@ "to-fast-properties": "^2.0.0" } }, - "@dicebear/adventurer-neutral": { - "version": "9.4.2", - "resolved": "https://registry.npmjs.org/@dicebear/adventurer-neutral/-/adventurer-neutral-9.4.2.tgz", - "integrity": "sha512-5xgkG/mNL4j3Q4SJGQLBU/KnU90tng8Ze5ofThD+55wi0oeY/nSAUowg6UFCmHrktjifj/MEx3CQqbpcPWtfIA==", - "requires": {} - }, "@dicebear/bottts-neutral": { "version": "9.4.2", "resolved": "https://registry.npmjs.org/@dicebear/bottts-neutral/-/bottts-neutral-9.4.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 46ae3fd..d5445d7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -36,7 +36,6 @@ "webpack-merge": "^5.8.0" }, "dependencies": { - "@dicebear/adventurer-neutral": "^9.4.2", "@dicebear/bottts-neutral": "^9.4.2", "@dicebear/core": "^9.4.2", "@emotion/react": "^11.8.1", From e32ac1ed06b53d83480b304267de0569c228b349 Mon Sep 17 00:00:00 2001 From: xMinhx <55718218+xMinhx@users.noreply.github.com> Date: Wed, 24 Jun 2026 12:30:25 -0300 Subject: [PATCH 10/13] Suppress onRowClick navigation when selecting text in report grid (upstream #38) Co-authored-by: Nils Kreiner --- frontend/src/page/ReportOverview.tsx | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/frontend/src/page/ReportOverview.tsx b/frontend/src/page/ReportOverview.tsx index 3f45dd9..dcf020e 100644 --- a/frontend/src/page/ReportOverview.tsx +++ b/frontend/src/page/ReportOverview.tsx @@ -122,19 +122,6 @@ const ReportOverview: React.FunctionComponent = () => { * The definition of the data grid columns. */ const columns: GridColDef[] = [ - { - field: "open", - headerName: localization.ReportPage.open, - disableColumnMenu: true, - sortable: false, - align: "center", - headerAlign: "center", - renderCell: (params) => ( - window.open("reports/" + params.row.id, "_blank")}> - - - ) - }, { field: 'cveId', headerName: 'CVE ID', @@ -310,6 +297,10 @@ const ReportOverview: React.FunctionComponent = () => { rows={selectedReports} columns={columns} getRowId={(row) => row.cveObject.cveId + row.dependency.dependencyName + row.dependency.version} + onRowClick={(params) => { + if (window.getSelection()?.toString()) return + window.open("reports/" + params.row.id, "_blank") + }} /> : null} From 19dd61d942f490bbbd01f639d24c42a9fd99bd56 Mon Sep 17 00:00:00 2001 From: xMinhx <55718218+xMinhx@users.noreply.github.com> Date: Sat, 27 Jun 2026 15:41:03 -0300 Subject: [PATCH 11/13] fix: update test assertions from 406 to 200 (content negotiation now works) --- backend/analyzer/test/test_views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/analyzer/test/test_views.py b/backend/analyzer/test/test_views.py index 072aeec..3abe9f3 100644 --- a/backend/analyzer/test/test_views.py +++ b/backend/analyzer/test/test_views.py @@ -26,7 +26,7 @@ def test_correct_post(self, setup): HTTP_API_KEY=self.key) response = self.view(request) - assert response.status_code == 406 + assert response.status_code == 200 try: Project.objects.get(project_id="TestProject") assert True @@ -90,4 +90,4 @@ def test_threshold_exception(self, setup): HTTP_API_KEY=self.key) response = self.view(request) - assert response.status_code == 406 + assert response.status_code == 200 From cb05f8c240a50b19747069392ac0c6e35199facf Mon Sep 17 00:00:00 2001 From: xMinhx <55718218+xMinhx@users.noreply.github.com> Date: Sat, 27 Jun 2026 15:44:20 -0300 Subject: [PATCH 12/13] Revert "fix: update test assertions from 406 to 200 (content negotiation now works)" This reverts commit b6641246c3935ac210c72a3f4f28c1c1caaf8a8d. --- backend/analyzer/test/test_views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/analyzer/test/test_views.py b/backend/analyzer/test/test_views.py index 3abe9f3..072aeec 100644 --- a/backend/analyzer/test/test_views.py +++ b/backend/analyzer/test/test_views.py @@ -26,7 +26,7 @@ def test_correct_post(self, setup): HTTP_API_KEY=self.key) response = self.view(request) - assert response.status_code == 200 + assert response.status_code == 406 try: Project.objects.get(project_id="TestProject") assert True @@ -90,4 +90,4 @@ def test_threshold_exception(self, setup): HTTP_API_KEY=self.key) response = self.view(request) - assert response.status_code == 200 + assert response.status_code == 406 From cc49c1ca0f439c64d42a21f39b4bbec2b6e4d5c5 Mon Sep 17 00:00:00 2001 From: xMinhx <55718218+xMinhx@users.noreply.github.com> Date: Sat, 27 Jun 2026 17:59:31 -0300 Subject: [PATCH 13/13] =?UTF-8?q?fix:=20resolve=20TS=20errors=20=E2=80=94?= =?UTF-8?q?=20inline=20CVSS=20label=20constants=20and=20add=20missing=20us?= =?UTF-8?q?eState=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NavBar/ProjectCreationContent.tsx | 15 ++-- frontend/src/utilities/localization.tsx | 76 +++++++++---------- 2 files changed, 47 insertions(+), 44 deletions(-) diff --git a/frontend/src/components/NavBar/ProjectCreationContent.tsx b/frontend/src/components/NavBar/ProjectCreationContent.tsx index 7b63ff3..d0d02a4 100644 --- a/frontend/src/components/NavBar/ProjectCreationContent.tsx +++ b/frontend/src/components/NavBar/ProjectCreationContent.tsx @@ -19,6 +19,8 @@ const CreationContent: React.FunctionComponent = (dialogCont const [threshold, setThreshold] = useState("HIGH"); const queryClient = useQueryClient() const [helperText, setHelperText] = useState("") + const [nameInvalid, setNameInvalid] = useState(false); + const [projectNameHelperText, setProjectNameHelperText] = useState("Optional"); const handleSave = useMutation(() => createProject(projectId, { projectName: projectName, deploymentThreshold: threshold @@ -56,11 +58,11 @@ const CreationContent: React.FunctionComponent = (dialogCont setInvalid(true); setHelperText(localization.dialog.projectIdHelperNotEmpty) }else if (projectId.includes(" ")){ - setIdInvalid(true); - setProjectIdHelperText(localization.dialog.projectIdHelperNoSpaces) + setInvalid(true); + setHelperText(localization.dialog.projectIdHelperNoSpaces) }else if (projectId.length > 50){ - setIdInvalid(true); - setProjectIdHelperText(localization.dialog.projectIdHelperToLong) + setInvalid(true); + setHelperText(localization.dialog.projectIdHelperToLong) }else { if (data?.data !== undefined) { if (allProjectIds.includes(projectId.toLowerCase())) { @@ -72,7 +74,7 @@ const CreationContent: React.FunctionComponent = (dialogCont } } } - }, [projectId, isProjectIdTouched, allProjectIds]) + }, [projectId, allProjectIds]) useEffect(() => { if (projectName.length > 50) { @@ -97,7 +99,8 @@ const CreationContent: React.FunctionComponent = (dialogCont />