From 8cd90e07dcc8594ac58fb0e2d6b7a519c52840ed Mon Sep 17 00:00:00 2001 From: Chandrapal Badshah Date: Tue, 2 Sep 2025 15:45:48 +0530 Subject: [PATCH 1/6] chore(ui): eslint nextjs files (#8627) Co-authored-by: Chandrapal Badshah <12944530+Chan9390@users.noreply.github.com> --- ui/types/integrations.ts | 60 +++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/ui/types/integrations.ts b/ui/types/integrations.ts index a95174cb45..033a57f4b4 100644 --- a/ui/types/integrations.ts +++ b/ui/types/integrations.ts @@ -55,14 +55,16 @@ const validateAwsCredentialsCreate = ( if (!data.aws_access_key_id) { ctx.addIssue({ code: z.ZodIssueCode.custom, - message: "AWS Access Key ID is required when using access and secret key", + message: + "AWS Access Key ID is required when using access and secret key", path: ["aws_access_key_id"], }); } if (!data.aws_secret_access_key) { ctx.addIssue({ code: z.ZodIssueCode.custom, - message: "AWS Secret Access Key is required when using access and secret key", + message: + "AWS Secret Access Key is required when using access and secret key", path: ["aws_secret_access_key"], }); } @@ -78,7 +80,8 @@ const validateAwsCredentialsEdit = (data: any, ctx: z.RefinementCtx) => { if (hasAccessKey && !hasSecretKey) { ctx.addIssue({ code: z.ZodIssueCode.custom, - message: "AWS Secret Access Key is required when providing Access Key ID", + message: + "AWS Secret Access Key is required when providing Access Key ID", path: ["aws_secret_access_key"], }); } @@ -86,7 +89,8 @@ const validateAwsCredentialsEdit = (data: any, ctx: z.RefinementCtx) => { if (hasSecretKey && !hasAccessKey) { ctx.addIssue({ code: z.ZodIssueCode.custom, - message: "AWS Access Key ID is required when providing Secret Access Key", + message: + "AWS Access Key ID is required when providing Secret Access Key", path: ["aws_access_key_id"], }); } @@ -99,8 +103,10 @@ const validateIamRole = ( ctx: z.RefinementCtx, checkShowSection: boolean = true, ) => { - const shouldValidate = checkShowSection ? data.show_role_section === true : true; - + const shouldValidate = checkShowSection + ? data.show_role_section === true + : true; + if (shouldValidate && data.role_arn) { if (data.role_arn.trim() === "") { ctx.addIssue({ @@ -160,9 +166,14 @@ export const s3IntegrationFormSchema = baseS3IntegrationSchema export const editS3IntegrationFormSchema = baseS3IntegrationSchema .extend({ bucket_name: z.string().min(1, "Bucket name is required").optional(), - output_directory: z.string().min(1, "Output directory is required").optional(), + output_directory: z + .string() + .min(1, "Output directory is required") + .optional(), providers: z.array(z.string()).optional(), - credentials_type: z.enum(["aws-sdk-default", "access-secret-key"]).optional(), + credentials_type: z + .enum(["aws-sdk-default", "access-secret-key"]) + .optional(), }) .superRefine((data, ctx) => { validateAwsCredentialsEdit(data, ctx); @@ -201,18 +212,21 @@ export const securityHubIntegrationFormSchema = baseSecurityHubIntegrationSchema } }); -export const editSecurityHubIntegrationFormSchema = baseSecurityHubIntegrationSchema - .extend({ - provider_id: z.string().optional(), - send_only_fails: z.boolean().optional(), - archive_previous_findings: z.boolean().optional(), - use_custom_credentials: z.boolean().optional(), - credentials_type: z.enum(["aws-sdk-default", "access-secret-key"]).optional(), - }) - .superRefine((data, ctx) => { - if (data.use_custom_credentials !== false) { - validateAwsCredentialsEdit(data, ctx); - } - // Always validate role if role_arn is provided - validateIamRole(data, ctx, false); - }); \ No newline at end of file +export const editSecurityHubIntegrationFormSchema = + baseSecurityHubIntegrationSchema + .extend({ + provider_id: z.string().optional(), + send_only_fails: z.boolean().optional(), + archive_previous_findings: z.boolean().optional(), + use_custom_credentials: z.boolean().optional(), + credentials_type: z + .enum(["aws-sdk-default", "access-secret-key"]) + .optional(), + }) + .superRefine((data, ctx) => { + if (data.use_custom_credentials !== false) { + validateAwsCredentialsEdit(data, ctx); + } + // Always validate role if role_arn is provided + validateIamRole(data, ctx, false); + }); From 230a085c76714b155856a03048769774a9bfcccd Mon Sep 17 00:00:00 2001 From: sumit-tft <70506234+sumit-tft@users.noreply.github.com> Date: Tue, 2 Sep 2025 16:03:58 +0530 Subject: [PATCH 2/6] fix(ui): display NoProvidersAdded when no cloud providers are configured (#8626) --- ui/CHANGELOG.md | 3 +++ ui/app/(prowler)/scans/page.tsx | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/CHANGELOG.md b/ui/CHANGELOG.md index 0ec9900066..a6ecb2d728 100644 --- a/ui/CHANGELOG.md +++ b/ui/CHANGELOG.md @@ -12,6 +12,9 @@ All notable changes to the **Prowler UI** are documented in this file. - Markdown rendering in finding details page [(#8604)](https://github.com/prowler-cloud/prowler/pull/8604) +### 🐞 Fixed +- Scan page shows NoProvidersAdded when no providers [(#8626)](https://github.com/prowler-cloud/prowler/pull/8626) + ## [1.11.0] (Prowler v5.11.0) ### 🚀 Added diff --git a/ui/app/(prowler)/scans/page.tsx b/ui/app/(prowler)/scans/page.tsx index 68ce8b8e29..623836673c 100644 --- a/ui/app/(prowler)/scans/page.tsx +++ b/ui/app/(prowler)/scans/page.tsx @@ -48,7 +48,8 @@ export default async function Scans({ connected: provider.attributes.connection.connected, })) || []; - const thereIsNoProviders = !providersData?.data; + const thereIsNoProviders = + !providersData?.data || providersData.data.length === 0; const thereIsNoProvidersConnected = providersData?.data?.every( (provider: ProviderProps) => !provider.attributes.connection.connected, From 3ded224a4bc987bb7d870d71d5dedc0f9d605d3b Mon Sep 17 00:00:00 2001 From: Alejandro Bailo <59607668+alejandrobailo@users.noreply.github.com> Date: Tue, 2 Sep 2025 12:35:06 +0200 Subject: [PATCH 3/6] fix: new errors detected through the app (#8629) --- ui/actions/compliances/compliances.ts | 4 ++-- ui/app/(prowler)/compliance/page.tsx | 28 ++++++++++++++++----------- ui/app/(prowler)/lighthouse/page.tsx | 7 +++++++ 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/ui/actions/compliances/compliances.ts b/ui/actions/compliances/compliances.ts index dff6bb5bb0..c29b771984 100644 --- a/ui/actions/compliances/compliances.ts +++ b/ui/actions/compliances/compliances.ts @@ -48,8 +48,8 @@ export const getComplianceOverviewMetadataInfo = async ({ if (sort) url.searchParams.append("sort", sort); Object.entries(filters).forEach(([key, value]) => { - // Define filters to exclude - if (key !== "filter[search]") { + // Define filters to exclude and check for valid values + if (key !== "filter[search]" && value && String(value).trim() !== "") { url.searchParams.append(key, String(value)); } }); diff --git a/ui/app/(prowler)/compliance/page.tsx b/ui/app/(prowler)/compliance/page.tsx index 5a1e7fcca9..e9cd26ac23 100644 --- a/ui/app/(prowler)/compliance/page.tsx +++ b/ui/app/(prowler)/compliance/page.tsx @@ -91,12 +91,14 @@ export default async function Compliance({ } : undefined; - const metadataInfoData = await getComplianceOverviewMetadataInfo({ - query, - filters: { - "filter[scan_id]": selectedScanId, - }, - }); + const metadataInfoData = selectedScanId + ? await getComplianceOverviewMetadataInfo({ + query, + filters: { + "filter[scan_id]": selectedScanId, + }, + }) + : { data: { attributes: { regions: [] } } }; const uniqueRegions = metadataInfoData?.data?.attributes?.regions || []; @@ -140,11 +142,15 @@ const SSRComplianceGrid = async ({ // Extract query from filters const query = (filters["filter[search]"] as string) || ""; - const compliancesData = await getCompliancesOverview({ - scanId, - region: regionFilter, - query, - }); + // Only fetch compliance data if we have a valid scanId + const compliancesData = + scanId && scanId.trim() !== "" + ? await getCompliancesOverview({ + scanId, + region: regionFilter, + query, + }) + : { data: [], errors: [] }; const type = compliancesData?.data?.type; diff --git a/ui/app/(prowler)/lighthouse/page.tsx b/ui/app/(prowler)/lighthouse/page.tsx index 96e5ecbfb7..1bf8551f74 100644 --- a/ui/app/(prowler)/lighthouse/page.tsx +++ b/ui/app/(prowler)/lighthouse/page.tsx @@ -1,3 +1,5 @@ +import { redirect } from "next/navigation"; + import { getLighthouseConfig } from "@/actions/lighthouse/lighthouse"; import { LighthouseIcon } from "@/components/icons/Icons"; import { Chat } from "@/components/lighthouse"; @@ -7,6 +9,11 @@ export default async function AIChatbot() { const lighthouseConfig = await getLighthouseConfig(); const hasConfig = !!lighthouseConfig; + + if (!hasConfig) { + return redirect("/lighthouse/config"); + } + const isActive = lighthouseConfig.is_active ?? false; return ( From fdf45aac51ffd103fa8717bd40dc73e148baf175 Mon Sep 17 00:00:00 2001 From: Pepe Fagoaga Date: Tue, 2 Sep 2025 17:30:40 +0200 Subject: [PATCH 4/6] fix(img): prowler architecture (#8635) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a66868db3b..2fb1b94cf4 100644 --- a/README.md +++ b/README.md @@ -273,7 +273,7 @@ python prowler-cli.py -v - **Prowler API**: A backend service, developed with Django REST Framework, responsible for running Prowler scans and storing the generated results. - **Prowler SDK**: A Python SDK designed to extend the functionality of the Prowler CLI for advanced capabilities. -![Prowler App Architecture](docs/img/prowler-app-architecture.png) +![Prowler App Architecture](docs/products/img/prowler-app-architecture.png) ## Prowler CLI From c9ed7773d23b0f69c0e121cbcc3343e871de2688 Mon Sep 17 00:00:00 2001 From: Hugo Pereira Brito <101209179+HugoPBrito@users.noreply.github.com> Date: Tue, 2 Sep 2025 17:42:21 +0200 Subject: [PATCH 5/6] feat(models): add `AdditionalUrls` field to check metadata (#8590) --- prowler/CHANGELOG.md | 7 + prowler/lib/check/models.py | 22 +- tests/lib/check/models_test.py | 391 +++++++++++++++++++++++++++++++++ 3 files changed, 417 insertions(+), 3 deletions(-) diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index fa05248663..484f4cf41c 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -34,6 +34,13 @@ All notable changes to the **Prowler SDK** are documented in this file. --- +## [v5.12.0] (Prowler UNRELEASED) + +### Added +- `AdditionalUrls` field in CheckMetadata [(#8590)](https://github.com/prowler-cloud/prowler/pull/8590) + +--- + ## [v5.11.0] (Prowler v5.11.0) ### Added diff --git a/prowler/lib/check/models.py b/prowler/lib/check/models.py index de38162cbc..547dc1d94b 100644 --- a/prowler/lib/check/models.py +++ b/prowler/lib/check/models.py @@ -7,7 +7,7 @@ from dataclasses import asdict, dataclass, is_dataclass from enum import Enum from typing import Any, Dict, Optional, Set -from pydantic.v1 import BaseModel, ValidationError, validator +from pydantic.v1 import BaseModel, Field, ValidationError, validator from prowler.config.config import Provider from prowler.lib.check.compliance_models import Compliance @@ -85,6 +85,7 @@ class CheckMetadata(BaseModel): Risk (str): The risk associated with the check. RelatedUrl (str): The URL related to the check. Remediation (Remediation): The remediation steps for the check. + AdditionalUrls (list[str]): Additional URLs related to the check. Defaults to an empty list. Categories (list[str]): The categories of the check. DependsOn (list[str]): The dependencies of the check. RelatedTo (list[str]): The related checks. @@ -97,13 +98,14 @@ class CheckMetadata(BaseModel): valid_severity(severity): Validator function to validate the severity of the check. valid_cli_command(remediation): Validator function to validate the CLI command is not an URL. valid_resource_type(resource_type): Validator function to validate the resource type is not empty. + validate_additional_urls(additional_urls): Validator function to ensure AdditionalUrls contains no duplicates. """ Provider: str CheckID: str CheckTitle: str CheckType: list[str] - CheckAliases: list[str] = [] + CheckAliases: list[str] = Field(default_factory=list) ServiceName: str SubServiceName: str ResourceIdTemplate: str @@ -113,13 +115,14 @@ class CheckMetadata(BaseModel): Risk: str RelatedUrl: str Remediation: Remediation + AdditionalUrls: list[str] = Field(default_factory=list) Categories: list[str] DependsOn: list[str] RelatedTo: list[str] Notes: str # We set the compliance to None to # store the compliance later if supplied - Compliance: Optional[list[Any]] = [] + Compliance: Optional[list[Any]] = Field(default_factory=list) @validator("Categories", each_item=True, pre=True, always=True) def valid_category(value): @@ -178,6 +181,19 @@ class CheckMetadata(BaseModel): return check_id + @validator("AdditionalUrls", pre=True, always=True) + def validate_additional_urls(cls, additional_urls): + if not isinstance(additional_urls, list): + raise ValueError("AdditionalUrls must be a list") + + if any(not url or not url.strip() for url in additional_urls): + raise ValueError("AdditionalUrls cannot contain empty items") + + if len(additional_urls) != len(set(additional_urls)): + raise ValueError("AdditionalUrls cannot contain duplicate items") + + return additional_urls + @staticmethod def get_bulk(provider: str) -> dict[str, "CheckMetadata"]: """ diff --git a/tests/lib/check/models_test.py b/tests/lib/check/models_test.py index 83b294f171..341d696ee3 100644 --- a/tests/lib/check/models_test.py +++ b/tests/lib/check/models_test.py @@ -1,5 +1,8 @@ from unittest import mock +import pytest +from pydantic.v1 import ValidationError + from prowler.lib.check.models import CheckMetadata from tests.lib.check.compliance_check_test import custom_compliance_metadata @@ -325,3 +328,391 @@ class TestCheckMetada: result = CheckMetadata.list(bulk_checks_metadata=bulk_metadata) assert result == set() + + def test_additional_urls_valid_empty_list(self): + """Test AdditionalUrls with valid empty list (default)""" + metadata = CheckMetadata( + Provider="aws", + CheckID="test_check", + CheckTitle="Test Check", + CheckType=["type1"], + ServiceName="test", + SubServiceName="subservice1", + ResourceIdTemplate="template1", + Severity="high", + ResourceType="resource1", + Description="Description 1", + Risk="risk1", + RelatedUrl="url1", + Remediation={ + "Code": { + "CLI": "cli1", + "NativeIaC": "native1", + "Other": "other1", + "Terraform": "terraform1", + }, + "Recommendation": {"Text": "text1", "Url": "url1"}, + }, + Categories=["categoryone"], + DependsOn=["dependency1"], + RelatedTo=["related1"], + Notes="notes1", + AdditionalUrls=[], + Compliance=[], + ) + assert metadata.AdditionalUrls == [] + + def test_additional_urls_valid_with_urls(self): + """Test AdditionalUrls with valid URLs""" + valid_urls = [ + "https://example.com/doc1", + "https://example.com/doc2", + "https://aws.amazon.com/docs", + ] + metadata = CheckMetadata( + Provider="aws", + CheckID="test_check", + CheckTitle="Test Check", + CheckType=["type1"], + ServiceName="test", + SubServiceName="subservice1", + ResourceIdTemplate="template1", + Severity="high", + ResourceType="resource1", + Description="Description 1", + Risk="risk1", + RelatedUrl="url1", + Remediation={ + "Code": { + "CLI": "cli1", + "NativeIaC": "native1", + "Other": "other1", + "Terraform": "terraform1", + }, + "Recommendation": {"Text": "text1", "Url": "url1"}, + }, + Categories=["categoryone"], + DependsOn=["dependency1"], + RelatedTo=["related1"], + Notes="notes1", + AdditionalUrls=valid_urls, + Compliance=[], + ) + assert metadata.AdditionalUrls == valid_urls + + def test_additional_urls_invalid_not_list(self): + """Test AdditionalUrls with non-list value""" + with pytest.raises(ValidationError) as exc_info: + CheckMetadata( + Provider="aws", + CheckID="test_check", + CheckTitle="Test Check", + CheckType=["type1"], + ServiceName="test", + SubServiceName="subservice1", + ResourceIdTemplate="template1", + Severity="high", + ResourceType="resource1", + Description="Description 1", + Risk="risk1", + RelatedUrl="url1", + Remediation={ + "Code": { + "CLI": "cli1", + "NativeIaC": "native1", + "Other": "other1", + "Terraform": "terraform1", + }, + "Recommendation": {"Text": "text1", "Url": "url1"}, + }, + Categories=["categoryone"], + DependsOn=["dependency1"], + RelatedTo=["related1"], + Notes="notes1", + AdditionalUrls="not_a_list", + Compliance=[], + ) + assert "AdditionalUrls must be a list" in str(exc_info.value) + + def test_additional_urls_invalid_empty_items(self): + """Test AdditionalUrls with empty string items""" + with pytest.raises(ValidationError) as exc_info: + CheckMetadata( + Provider="aws", + CheckID="test_check", + CheckTitle="Test Check", + CheckType=["type1"], + ServiceName="test", + SubServiceName="subservice1", + ResourceIdTemplate="template1", + Severity="high", + ResourceType="resource1", + Description="Description 1", + Risk="risk1", + RelatedUrl="url1", + Remediation={ + "Code": { + "CLI": "cli1", + "NativeIaC": "native1", + "Other": "other1", + "Terraform": "terraform1", + }, + "Recommendation": {"Text": "text1", "Url": "url1"}, + }, + Categories=["categoryone"], + DependsOn=["dependency1"], + RelatedTo=["related1"], + Notes="notes1", + AdditionalUrls=["https://example.com", "", "https://example2.com"], + Compliance=[], + ) + assert "AdditionalUrls cannot contain empty items" in str(exc_info.value) + + def test_additional_urls_invalid_whitespace_items(self): + """Test AdditionalUrls with whitespace-only items""" + with pytest.raises(ValidationError) as exc_info: + CheckMetadata( + Provider="aws", + CheckID="test_check", + CheckTitle="Test Check", + CheckType=["type1"], + ServiceName="test", + SubServiceName="subservice1", + ResourceIdTemplate="template1", + Severity="high", + ResourceType="resource1", + Description="Description 1", + Risk="risk1", + RelatedUrl="url1", + Remediation={ + "Code": { + "CLI": "cli1", + "NativeIaC": "native1", + "Other": "other1", + "Terraform": "terraform1", + }, + "Recommendation": {"Text": "text1", "Url": "url1"}, + }, + Categories=["categoryone"], + DependsOn=["dependency1"], + RelatedTo=["related1"], + Notes="notes1", + AdditionalUrls=["https://example.com", " ", "https://example2.com"], + Compliance=[], + ) + assert "AdditionalUrls cannot contain empty items" in str(exc_info.value) + + def test_additional_urls_invalid_duplicates(self): + """Test AdditionalUrls with duplicate items""" + with pytest.raises(ValidationError) as exc_info: + CheckMetadata( + Provider="aws", + CheckID="test_check", + CheckTitle="Test Check", + CheckType=["type1"], + ServiceName="test", + SubServiceName="subservice1", + ResourceIdTemplate="template1", + Severity="high", + ResourceType="resource1", + Description="Description 1", + Risk="risk1", + RelatedUrl="url1", + Remediation={ + "Code": { + "CLI": "cli1", + "NativeIaC": "native1", + "Other": "other1", + "Terraform": "terraform1", + }, + "Recommendation": {"Text": "text1", "Url": "url1"}, + }, + Categories=["categoryone"], + DependsOn=["dependency1"], + RelatedTo=["related1"], + Notes="notes1", + AdditionalUrls=[ + "https://example.com", + "https://example2.com", + "https://example.com", + ], + Compliance=[], + ) + assert "AdditionalUrls cannot contain duplicate items" in str(exc_info.value) + + def test_fields_with_explicit_empty_values(self): + """Test that RelatedUrl and AdditionalUrls can be set to explicit empty values""" + metadata = CheckMetadata( + Provider="aws", + CheckID="test_check_empty_fields", + CheckTitle="Test Check with Empty Fields", + CheckType=["type1"], + ServiceName="test", + SubServiceName="subservice1", + ResourceIdTemplate="template1", + Severity="high", + ResourceType="resource1", + Description="Description 1", + Risk="risk1", + RelatedUrl="", # Explicit empty string + Remediation={ + "Code": { + "CLI": "cli1", + "NativeIaC": "native1", + "Other": "other1", + "Terraform": "terraform1", + }, + "Recommendation": {"Text": "text1", "Url": "url1"}, + }, + Categories=["categoryone"], + DependsOn=["dependency1"], + RelatedTo=["related1"], + Notes="notes1", + AdditionalUrls=[], # Explicit empty list + Compliance=[], + ) + + # Assert that the fields are set to empty values + assert metadata.RelatedUrl == "" + assert metadata.AdditionalUrls == [] + + def test_fields_default_values(self): + """Test that RelatedUrl and AdditionalUrls use proper defaults when not provided""" + metadata = CheckMetadata( + Provider="aws", + CheckID="test_check_defaults", + CheckTitle="Test Check with Default Fields", + CheckType=["type1"], + ServiceName="test", + SubServiceName="subservice1", + ResourceIdTemplate="template1", + Severity="high", + ResourceType="resource1", + Description="Description 1", + Risk="risk1", + RelatedUrl="", + Remediation={ + "Code": { + "CLI": "cli1", + "NativeIaC": "native1", + "Other": "other1", + "Terraform": "terraform1", + }, + "Recommendation": {"Text": "text1", "Url": "url1"}, + }, + Categories=["categoryone"], + DependsOn=["dependency1"], + RelatedTo=["related1"], + Notes="notes1", + # AdditionalUrls not provided - should default to empty list via default_factory + Compliance=[], + ) + + # Assert that the fields use their default values + assert metadata.RelatedUrl == "" # Should default to empty string + assert metadata.AdditionalUrls == [] # Should default to empty list + + def test_related_url_none_fails(self): + """Test that setting RelatedUrl to None raises a ValidationError""" + with pytest.raises(ValidationError) as exc_info: + CheckMetadata( + Provider="aws", + CheckID="test_check_none_related_url", + CheckTitle="Test Check with None RelatedUrl", + CheckType=["type1"], + ServiceName="test", + SubServiceName="subservice1", + ResourceIdTemplate="template1", + Severity="high", + ResourceType="resource1", + Description="Description 1", + Risk="risk1", + RelatedUrl=None, # This should fail + Remediation={ + "Code": { + "CLI": "cli1", + "NativeIaC": "native1", + "Other": "other1", + "Terraform": "terraform1", + }, + "Recommendation": {"Text": "text1", "Url": "url1"}, + }, + Categories=["categoryone"], + DependsOn=["dependency1"], + RelatedTo=["related1"], + Notes="notes1", + AdditionalUrls=[], + Compliance=[], + ) + # Should contain a validation error for RelatedUrl + assert "RelatedUrl" in str(exc_info.value) + + def test_additional_urls_none_fails(self): + """Test that setting AdditionalUrls to None raises a ValidationError""" + with pytest.raises(ValidationError) as exc_info: + CheckMetadata( + Provider="aws", + CheckID="test_check_none_additional_urls", + CheckTitle="Test Check with None AdditionalUrls", + CheckType=["type1"], + ServiceName="test", + SubServiceName="subservice1", + ResourceIdTemplate="template1", + Severity="high", + ResourceType="resource1", + Description="Description 1", + Risk="risk1", + RelatedUrl="https://example.com", + Remediation={ + "Code": { + "CLI": "cli1", + "NativeIaC": "native1", + "Other": "other1", + "Terraform": "terraform1", + }, + "Recommendation": {"Text": "text1", "Url": "url1"}, + }, + Categories=["categoryone"], + DependsOn=["dependency1"], + RelatedTo=["related1"], + Notes="notes1", + AdditionalUrls=None, # This should fail + Compliance=[], + ) + # Should contain the validation error we set in the validator + assert "AdditionalUrls must be a list" in str(exc_info.value) + + def test_additional_urls_invalid_type_fails(self): + """Test that setting AdditionalUrls to non-list value raises a ValidationError""" + with pytest.raises(ValidationError) as exc_info: + CheckMetadata( + Provider="aws", + CheckID="test_check_invalid_additional_urls", + CheckTitle="Test Check with Invalid AdditionalUrls", + CheckType=["type1"], + ServiceName="test", + SubServiceName="subservice1", + ResourceIdTemplate="template1", + Severity="high", + ResourceType="resource1", + Description="Description 1", + Risk="risk1", + RelatedUrl="https://example.com", + Remediation={ + "Code": { + "CLI": "cli1", + "NativeIaC": "native1", + "Other": "other1", + "Terraform": "terraform1", + }, + "Recommendation": {"Text": "text1", "Url": "url1"}, + }, + Categories=["categoryone"], + DependsOn=["dependency1"], + RelatedTo=["related1"], + Notes="notes1", + AdditionalUrls="not_a_list", # This should fail + Compliance=[], + ) + # Should contain the validation error we set in the validator + assert "AdditionalUrls must be a list" in str(exc_info.value) From 987121051ba3b0c18c757ed20033c5e3f6907b3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Arroba?= <19954079+cesararroba@users.noreply.github.com> Date: Tue, 2 Sep 2025 18:03:42 +0200 Subject: [PATCH 6/6] chore(sdk): comment push readme to dockerhub steps (#8628) --- .../sdk-build-lint-push-containers.yml | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/sdk-build-lint-push-containers.yml b/.github/workflows/sdk-build-lint-push-containers.yml index e17241eb46..591b07dc50 100644 --- a/.github/workflows/sdk-build-lint-push-containers.yml +++ b/.github/workflows/sdk-build-lint-push-containers.yml @@ -157,21 +157,21 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max - - name: Push README to Docker Hub (toniblyx) - uses: peter-evans/dockerhub-description@432a30c9e07499fd01da9f8a49f0faf9e0ca5b77 # v4.0.2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - repository: ${{ env.DOCKER_HUB_REPOSITORY }}/${{ env.IMAGE_NAME }} - readme-filepath: ./README.md - - - name: Push README to Docker Hub (prowlercloud) - uses: peter-evans/dockerhub-description@432a30c9e07499fd01da9f8a49f0faf9e0ca5b77 # v4.0.2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - repository: ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }} - readme-filepath: ./README.md +# - name: Push README to Docker Hub (toniblyx) +# uses: peter-evans/dockerhub-description@432a30c9e07499fd01da9f8a49f0faf9e0ca5b77 # v4.0.2 +# with: +# username: ${{ secrets.DOCKERHUB_USERNAME }} +# password: ${{ secrets.DOCKERHUB_TOKEN }} +# repository: ${{ env.DOCKER_HUB_REPOSITORY }}/${{ env.IMAGE_NAME }} +# readme-filepath: ./README.md +# +# - name: Push README to Docker Hub (prowlercloud) +# uses: peter-evans/dockerhub-description@432a30c9e07499fd01da9f8a49f0faf9e0ca5b77 # v4.0.2 +# with: +# username: ${{ secrets.DOCKERHUB_USERNAME }} +# password: ${{ secrets.DOCKERHUB_TOKEN }} +# repository: ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }} +# readme-filepath: ./README.md dispatch-action: needs: container-build-push