diff --git a/.github/workflows/api-pull-request.yml b/.github/workflows/api-pull-request.yml index ed98dafbecf..01167bfb92d 100644 --- a/.github/workflows/api-pull-request.yml +++ b/.github/workflows/api-pull-request.yml @@ -89,7 +89,7 @@ jobs: if: steps.are-non-ignored-files-changed.outputs.any_changed == 'true' run: | python -m pip install --upgrade pip - pipx install poetry + pipx install poetry==1.8.5 - name: Set up Python ${{ matrix.python-version }} if: steps.are-non-ignored-files-changed.outputs.any_changed == 'true' @@ -114,7 +114,7 @@ jobs: working-directory: ./api if: steps.are-non-ignored-files-changed.outputs.any_changed == 'true' run: | - poetry lock --check + poetry check --lock - name: Lint with ruff working-directory: ./api diff --git a/.github/workflows/sdk-build-lint-push-containers.yml b/.github/workflows/sdk-build-lint-push-containers.yml index 98460b32144..45364302557 100644 --- a/.github/workflows/sdk-build-lint-push-containers.yml +++ b/.github/workflows/sdk-build-lint-push-containers.yml @@ -68,7 +68,7 @@ jobs: - name: Install Poetry run: | - pipx install poetry + pipx install poetry==1.8.5 pipx inject poetry poetry-bumpversion - name: Get Prowler version diff --git a/.github/workflows/sdk-pull-request.yml b/.github/workflows/sdk-pull-request.yml index 1f7f03bfa87..ae0669b278d 100644 --- a/.github/workflows/sdk-pull-request.yml +++ b/.github/workflows/sdk-pull-request.yml @@ -43,7 +43,7 @@ jobs: if: steps.are-non-ignored-files-changed.outputs.any_changed == 'true' run: | python -m pip install --upgrade pip - pipx install poetry + pipx install poetry==1.8.5 - name: Set up Python ${{ matrix.python-version }} if: steps.are-non-ignored-files-changed.outputs.any_changed == 'true' @@ -66,7 +66,7 @@ jobs: - name: Poetry check if: steps.are-non-ignored-files-changed.outputs.any_changed == 'true' run: | - poetry lock --check + poetry check --lock - name: Lint with flake8 if: steps.are-non-ignored-files-changed.outputs.any_changed == 'true' diff --git a/.github/workflows/sdk-pypi-release.yml b/.github/workflows/sdk-pypi-release.yml index 1fb960e295a..af44300179d 100644 --- a/.github/workflows/sdk-pypi-release.yml +++ b/.github/workflows/sdk-pypi-release.yml @@ -68,7 +68,7 @@ jobs: - name: Install dependencies run: | - pipx install poetry + pipx install poetry==1.8.5 - name: Setup Python uses: actions/setup-python@v5 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2fcb4d4c6f5..45f34d6748b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -90,7 +90,7 @@ repos: - id: bandit name: bandit description: "Bandit is a tool for finding common security issues in Python code" - entry: bash -c 'bandit -q -lll -x '*_test.py,./contrib/' -r .' + entry: bash -c 'bandit -q -lll -x '*_test.py,./contrib/,./.venv/' -r .' language: system files: '.*\.py' @@ -103,7 +103,6 @@ repos: - id: vulture name: vulture description: "Vulture finds unused code in Python programs." - entry: bash -c 'vulture --exclude "contrib" --min-confidence 100 .' - exclude: 'api/src/backend/' + entry: bash -c 'vulture --exclude "contrib,.venv,api/src/backend/api/tests/,api/src/backend/conftest.py,api/src/backend/tasks/tests/" --min-confidence 100 .' language: system files: '.*\.py' diff --git a/poetry.lock b/poetry.lock index 7c125931cde..4a8bd9c5b48 100644 --- a/poetry.lock +++ b/poetry.lock @@ -399,13 +399,13 @@ isodate = ">=0.6.1,<1.0.0" [[package]] name = "azure-mgmt-compute" -version = "33.0.0" +version = "33.1.0" description = "Microsoft Azure Compute Management Client Library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "azure-mgmt-compute-33.0.0.tar.gz", hash = "sha256:a3cc0fe4f09c8e1d3523c1bfb92620dfe263a0a893b0ac13a33d7057e9ddddd2"}, - {file = "azure_mgmt_compute-33.0.0-py3-none-any.whl", hash = "sha256:155f8d78a1fdedcea1725fd12b85b2d87fbcb6b53f8e77451c644f45701e3bcf"}, + {file = "azure_mgmt_compute-33.1.0-py3-none-any.whl", hash = "sha256:9b119a1b7f621aee951074ef110b16d545d7b45c9cfb303becf04210bd772c91"}, + {file = "azure_mgmt_compute-33.1.0.tar.gz", hash = "sha256:f5a5e18a5a7a0354562bbfa589b5db4aaf1e8ac3a194a2a910db55900d2535e9"}, ] [package.dependencies] @@ -513,13 +513,13 @@ isodate = ">=0.6.1,<1.0.0" [[package]] name = "azure-mgmt-network" -version = "28.0.0" +version = "28.1.0" description = "Microsoft Azure Network Management Client Library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "azure_mgmt_network-28.0.0-py3-none-any.whl", hash = "sha256:2ee23c1f2ba75752187bd7f4c3e94ad172282cbf8153694feadc7886ef88493c"}, - {file = "azure_mgmt_network-28.0.0.tar.gz", hash = "sha256:40356d348ef4838324f19a41cd80340b4f8dd4ac2f0a18a4cbd5cc95ef2974f3"}, + {file = "azure_mgmt_network-28.1.0-py3-none-any.whl", hash = "sha256:8ddb0e9ec8f10c9c152d60fc945908d113e4591f397ea3e40b92290ec2b01658"}, + {file = "azure_mgmt_network-28.1.0.tar.gz", hash = "sha256:8c84bffb5ec75c6e0244e58ecf07c00d5fc421d616b0cb369c6fe585af33cf87"}, ] [package.dependencies] @@ -775,17 +775,17 @@ files = [ [[package]] name = "boto3" -version = "1.35.87" +version = "1.35.93" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.35.87-py3-none-any.whl", hash = "sha256:588ab05e2771c50fca5c242be14e7a25200ffd3dd95c45950ce40993473864c7"}, - {file = "boto3-1.35.87.tar.gz", hash = "sha256:341c58602889078a4a25dc4331b832b5b600a33acd73471d2532c6f01b16fbb4"}, + {file = "boto3-1.35.93-py3-none-any.whl", hash = "sha256:7de2c44c960e486f3c57e5203ea6393c6c4f0914c5f81c789ceb8b5d2ba5d1c5"}, + {file = "boto3-1.35.93.tar.gz", hash = "sha256:2446e819cf4e295833474cdcf2c92bc82718ce537e9ee1f17f7e3d237f60e69b"}, ] [package.dependencies] -botocore = ">=1.35.87,<1.36.0" +botocore = ">=1.35.93,<1.36.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -794,13 +794,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.35.88" +version = "1.35.94" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.35.88-py3-none-any.whl", hash = "sha256:e60cc3fbe8d7a10f70e7e852d76be2b29f23ead418a5899d366ea32b1eacb5a5"}, - {file = "botocore-1.35.88.tar.gz", hash = "sha256:58dcd9a464c354b8c6c25261d8de830d175d9739eae568bf0c52e57116fb03c6"}, + {file = "botocore-1.35.94-py3-none-any.whl", hash = "sha256:d784d944865d8279c79d2301fc09ac28b5221d4e7328fb4e23c642c253b9932c"}, + {file = "botocore-1.35.94.tar.gz", hash = "sha256:2b3309b356541faa4d88bb957dcac1d8004aa44953c0b7d4521a6cc5d3d5d6ba"}, ] [package.dependencies] @@ -1719,13 +1719,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-api-python-client" -version = "2.156.0" +version = "2.157.0" description = "Google API Client Library for Python" optional = false python-versions = ">=3.7" files = [ - {file = "google_api_python_client-2.156.0-py2.py3-none-any.whl", hash = "sha256:6352185c505e1f311f11b0b96c1b636dcb0fec82cd04b80ac5a671ac4dcab339"}, - {file = "google_api_python_client-2.156.0.tar.gz", hash = "sha256:b809c111ded61716a9c1c7936e6899053f13bae3defcdfda904bd2ca68065b9c"}, + {file = "google_api_python_client-2.157.0-py2.py3-none-any.whl", hash = "sha256:0b0231db106324c659bf8b85f390391c00da57a60ebc4271e33def7aac198c75"}, + {file = "google_api_python_client-2.157.0.tar.gz", hash = "sha256:2ee342d0967ad1cedec43ccd7699671d94bff151e1f06833ea81303f9a6d86fd"}, ] [package.dependencies] @@ -2403,13 +2403,13 @@ files = [ [[package]] name = "microsoft-kiota-abstractions" -version = "1.6.7" +version = "1.6.8" description = "Core abstractions for kiota generated libraries in Python" optional = false python-versions = "<4.0,>=3.8" files = [ - {file = "microsoft_kiota_abstractions-1.6.7-py3-none-any.whl", hash = "sha256:18205ad1b1acb0ad650da312a73a194fdaa5f6b4d8f681eb0bc14b5dd6dba355"}, - {file = "microsoft_kiota_abstractions-1.6.7.tar.gz", hash = "sha256:4bb3d1ffea196b1df8b18ec01cad8c6884a9175b35ca6f12beba38baabefc5bc"}, + {file = "microsoft_kiota_abstractions-1.6.8-py3-none-any.whl", hash = "sha256:12819dee24d5aaa31e99683d938f65e50cbc446de087df244cd26c3326ec4e15"}, + {file = "microsoft_kiota_abstractions-1.6.8.tar.gz", hash = "sha256:7070affabfa7182841646a0c8491cbb240af366aff2b9132f0caa45c4837dd78"}, ] [package.dependencies] @@ -2769,13 +2769,13 @@ dev = ["bumpver", "isort", "mypy", "pylint", "pytest", "yapf"] [[package]] name = "msgraph-sdk" -version = "1.15.0" +version = "1.16.0" description = "The Microsoft Graph Python SDK" optional = false python-versions = ">=3.8" files = [ - {file = "msgraph_sdk-1.15.0-py3-none-any.whl", hash = "sha256:85332db7ee19eb3d65a2493de83994ce3f5e4d9a084b3643ff6dea797cda81a7"}, - {file = "msgraph_sdk-1.15.0.tar.gz", hash = "sha256:c920e72cc9de2218f9f9f71682db22ea544d9b440a5f088892bfca686c546b91"}, + {file = "msgraph_sdk-1.16.0-py3-none-any.whl", hash = "sha256:1dd26ece74c43167818e2ff58b062180233ce7187ad2a061057af1195395c56c"}, + {file = "msgraph_sdk-1.16.0.tar.gz", hash = "sha256:980d19617d8d8b20545ef77fa5629fef768ce4ea1f2d1a124c5a9dd88d77940c"}, ] [package.dependencies] @@ -5199,4 +5199,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "b940aac16507f448e7a9988bb158c37b6bb395ba70c9aac92f830b212a191f6c" +content-hash = "cfea7de01550ecd55503d1114471cac7fa9a1fb0f793b1f812f8ef57f5d00750" diff --git a/prowler/config/config.yaml b/prowler/config/config.yaml index 775e44cbac5..cceb5499484 100644 --- a/prowler/config/config.yaml +++ b/prowler/config/config.yaml @@ -73,6 +73,10 @@ aws: # aws.cloudwatch_log_group_retention_policy_specific_days_enabled --> by default is 365 days log_group_retention_days: 365 + # AWS CloudFormation Configuration + # cloudformation_stack_cdktoolkit_bootstrap_version --> by default is 21 + recommended_cdk_bootstrap_version: 21 + # AWS AppStream Session Configuration # aws.appstream_fleet_session_idle_disconnect_timeout max_idle_disconnect_timeout_in_seconds: 600 # 10 Minutes diff --git a/prowler/providers/aws/aws_regions_by_service.json b/prowler/providers/aws/aws_regions_by_service.json index 0d83533adf9..927b9a1bd0f 100644 --- a/prowler/providers/aws/aws_regions_by_service.json +++ b/prowler/providers/aws/aws_regions_by_service.json @@ -15,6 +15,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -97,6 +98,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -335,6 +337,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -455,6 +458,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -499,6 +503,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -825,6 +830,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -869,6 +875,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -994,6 +1001,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -1038,6 +1046,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -1093,6 +1102,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -1224,6 +1234,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -1782,6 +1793,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -1826,6 +1838,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -1957,6 +1970,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -2024,6 +2038,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -2582,6 +2597,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -2911,6 +2927,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -3101,6 +3118,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -3160,6 +3178,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -3204,6 +3223,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -3365,6 +3385,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -3409,6 +3430,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -3453,6 +3475,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -3497,6 +3520,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -3541,6 +3565,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -3595,6 +3620,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -3683,6 +3709,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -3727,6 +3754,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -3782,6 +3810,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -3879,6 +3908,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -3923,6 +3953,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -3967,6 +3998,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -4108,6 +4140,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -4152,6 +4185,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -4196,6 +4230,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -4257,6 +4292,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -4592,6 +4628,7 @@ "ap-southeast-2", "ap-southeast-3", "ap-southeast-4", + "ap-southeast-5", "ca-central-1", "ca-west-1", "eu-central-1", @@ -4635,6 +4672,7 @@ "ap-southeast-2", "ap-southeast-3", "ap-southeast-4", + "ap-southeast-5", "ca-central-1", "ca-west-1", "eu-central-1", @@ -4677,6 +4715,7 @@ "ap-southeast-1", "ap-southeast-2", "ap-southeast-3", + "ap-southeast-5", "ca-central-1", "ca-west-1", "eu-central-1", @@ -4720,6 +4759,7 @@ "ap-southeast-2", "ap-southeast-3", "ap-southeast-4", + "ap-southeast-5", "ca-central-1", "ca-west-1", "eu-central-1", @@ -5034,6 +5074,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -5164,6 +5205,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -5844,6 +5886,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -5961,6 +6004,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -6049,6 +6093,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -6384,6 +6429,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -6655,6 +6701,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -7518,6 +7565,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -7594,6 +7642,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -7732,6 +7781,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -7929,6 +7979,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -8144,6 +8195,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -8316,6 +8368,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -8360,6 +8413,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -8404,6 +8458,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -8479,6 +8534,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -8711,6 +8767,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -8755,6 +8812,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -8897,6 +8955,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -8938,6 +8997,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -9040,6 +9100,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -9118,6 +9179,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -9407,6 +9469,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -9541,6 +9604,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -9709,6 +9773,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -9835,6 +9900,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -10148,6 +10214,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -10208,6 +10275,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -10252,6 +10320,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -10447,6 +10516,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -10535,6 +10605,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -10579,6 +10650,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -10634,6 +10706,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -10678,6 +10751,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -10930,6 +11004,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -11000,6 +11075,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -11164,6 +11240,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -11244,6 +11321,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", @@ -11591,6 +11669,7 @@ "ap-southeast-3", "ap-southeast-4", "ap-southeast-5", + "ap-southeast-7", "ca-central-1", "ca-west-1", "eu-central-1", diff --git a/prowler/providers/aws/services/cloudformation/cloudformation_stack_cdktoolkit_bootstrap_version/__init__.py b/prowler/providers/aws/services/cloudformation/cloudformation_stack_cdktoolkit_bootstrap_version/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/prowler/providers/aws/services/cloudformation/cloudformation_stack_cdktoolkit_bootstrap_version/cloudformation_stack_cdktoolkit_bootstrap_version.metadata.json b/prowler/providers/aws/services/cloudformation/cloudformation_stack_cdktoolkit_bootstrap_version/cloudformation_stack_cdktoolkit_bootstrap_version.metadata.json new file mode 100644 index 00000000000..494daa33372 --- /dev/null +++ b/prowler/providers/aws/services/cloudformation/cloudformation_stack_cdktoolkit_bootstrap_version/cloudformation_stack_cdktoolkit_bootstrap_version.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "aws", + "CheckID": "cloudformation_stack_cdktoolkit_bootstrap_version", + "CheckTitle": "Ensure that CDKToolkit stacks have a Bootstrap version of 21 or higher to mitigate security risks.", + "CheckType": [], + "ServiceName": "cloudformation", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:cloudformation:region:account-id:stack/resource-id", + "Severity": "high", + "ResourceType": "AwsCloudFormationStack", + "Description": "Ensure that CDKToolkit stacks have a Bootstrap version of 21 or higher to mitigate security risks.", + "Risk": "Using outdated CDKToolkit Bootstrap versions can expose accounts to risks such as bucket takeover or privilege escalation.", + "RelatedUrl": "https://docs.aws.amazon.com/cdk/latest/guide/bootstrapping.html", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Update the CDKToolkit stack Bootstrap version to 21 or later by running the cdk bootstrap command with the latest CDK version.", + "Url": "https://docs.aws.amazon.com/cdk/latest/guide/bootstrapping.html" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/cloudformation/cloudformation_stack_cdktoolkit_bootstrap_version/cloudformation_stack_cdktoolkit_bootstrap_version.py b/prowler/providers/aws/services/cloudformation/cloudformation_stack_cdktoolkit_bootstrap_version/cloudformation_stack_cdktoolkit_bootstrap_version.py new file mode 100644 index 00000000000..4306e3e593b --- /dev/null +++ b/prowler/providers/aws/services/cloudformation/cloudformation_stack_cdktoolkit_bootstrap_version/cloudformation_stack_cdktoolkit_bootstrap_version.py @@ -0,0 +1,39 @@ +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.cloudformation.cloudformation_client import ( + cloudformation_client, +) + + +class cloudformation_stack_cdktoolkit_bootstrap_version(Check): + """Check if a CDKToolkit CloudFormation Stack has a Bootstrap version less than recommended""" + + def execute(self): + """Execute the cloudformation_stack_cdktoolkit_bootstrap_version check""" + findings = [] + recommended_cdk_bootstrap_version = cloudformation_client.audit_config.get( + "recommended_cdk_bootstrap_version", 21 + ) + for stack in cloudformation_client.stacks: + # Only check stacks named CDKToolkit + if stack.name == "CDKToolkit": + bootstrap_version = None + if stack.outputs: + for output in stack.outputs: + if output.startswith("BootstrapVersion:"): + bootstrap_version = int(output.split(":")[1]) + break + if bootstrap_version: + report = Check_Report_AWS(self.metadata()) + report.region = stack.region + report.resource_id = stack.name + report.resource_arn = stack.arn + report.resource_tags = stack.tags + report.status = "PASS" + report.status_extended = f"CloudFormation Stack CDKToolkit has a Bootstrap version {bootstrap_version}, which meets the recommended version." + if bootstrap_version < recommended_cdk_bootstrap_version: + report.status = "FAIL" + report.status_extended = f"CloudFormation Stack CDKToolkit has a Bootstrap version {bootstrap_version}, which is less than the recommended version {recommended_cdk_bootstrap_version}." + + findings.append(report) + + return findings diff --git a/prowler/providers/aws/services/cloudformation/cloudformation_stack_outputs_find_secrets/cloudformation_stack_outputs_find_secrets.py b/prowler/providers/aws/services/cloudformation/cloudformation_stack_outputs_find_secrets/cloudformation_stack_outputs_find_secrets.py index 7c40f568a4a..2bcaa2ac30c 100644 --- a/prowler/providers/aws/services/cloudformation/cloudformation_stack_outputs_find_secrets/cloudformation_stack_outputs_find_secrets.py +++ b/prowler/providers/aws/services/cloudformation/cloudformation_stack_outputs_find_secrets/cloudformation_stack_outputs_find_secrets.py @@ -21,7 +21,9 @@ def execute(self): report.resource_arn = stack.arn report.resource_tags = stack.tags report.status = "PASS" - report.status_extended = f"No secrets found in Stack {stack.name} Outputs." + report.status_extended = ( + f"No secrets found in CloudFormation Stack {stack.name} Outputs." + ) if stack.outputs: data = "" # Store the CloudFormation Stack Outputs into a file @@ -40,11 +42,13 @@ def execute(self): ] ) report.status = "FAIL" - report.status_extended = f"Potential secret found in Stack {stack.name} Outputs -> {secrets_string}." + report.status_extended = f"Potential secret found in CloudFormation Stack {stack.name} Outputs -> {secrets_string}." else: report.status = "PASS" - report.status_extended = f"CloudFormation {stack.name} has no Outputs." + report.status_extended = ( + f"CloudFormation Stack {stack.name} has no Outputs." + ) findings.append(report) diff --git a/prowler/providers/aws/services/cloudformation/cloudformation_stacks_termination_protection_enabled/cloudformation_stacks_termination_protection_enabled.py b/prowler/providers/aws/services/cloudformation/cloudformation_stacks_termination_protection_enabled/cloudformation_stacks_termination_protection_enabled.py index 84244f561ac..af44d330386 100644 --- a/prowler/providers/aws/services/cloudformation/cloudformation_stacks_termination_protection_enabled/cloudformation_stacks_termination_protection_enabled.py +++ b/prowler/providers/aws/services/cloudformation/cloudformation_stacks_termination_protection_enabled/cloudformation_stacks_termination_protection_enabled.py @@ -20,10 +20,10 @@ def execute(self): if stack.enable_termination_protection: report.status = "PASS" - report.status_extended = f"CloudFormation {stack.name} has termination protection enabled." + report.status_extended = f"CloudFormation Stack {stack.name} has termination protection enabled." else: report.status = "FAIL" - report.status_extended = f"CloudFormation {stack.name} has termination protection disabled." + report.status_extended = f"CloudFormation Stack {stack.name} has termination protection disabled." findings.append(report) return findings diff --git a/pyproject.toml b/pyproject.toml index 85c07c7fa4e..8a3980a9edd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,13 +32,13 @@ azure-identity = "1.19.0" azure-keyvault-keys = "4.10.0" azure-mgmt-applicationinsights = "4.0.0" azure-mgmt-authorization = "4.0.0" -azure-mgmt-compute = "33.0.0" +azure-mgmt-compute = "33.1.0" azure-mgmt-containerregistry = "10.3.0" azure-mgmt-containerservice = "33.0.0" azure-mgmt-cosmosdb = "9.7.0" azure-mgmt-keyvault = "10.3.1" azure-mgmt-monitor = "6.0.2" -azure-mgmt-network = "28.0.0" +azure-mgmt-network = "28.1.0" azure-mgmt-rdbms = "10.1.0" azure-mgmt-resource = "23.2.0" azure-mgmt-search = "9.1.0" @@ -48,19 +48,19 @@ azure-mgmt-storage = "21.2.1" azure-mgmt-subscription = "3.1.1" azure-mgmt-web = "7.3.1" azure-storage-blob = "12.24.0" -boto3 = "1.35.87" -botocore = "1.35.88" +boto3 = "1.35.93" +botocore = "1.35.94" colorama = "0.4.6" cryptography = "43.0.1" dash = "2.18.2" dash-bootstrap-components = "1.6.0" detect-secrets = "1.5.0" -google-api-python-client = "2.156.0" +google-api-python-client = "2.157.0" google-auth-httplib2 = ">=0.1,<0.3" jsonschema = "4.23.0" kubernetes = "31.0.0" -microsoft-kiota-abstractions = "1.6.7" -msgraph-sdk = "1.15.0" +microsoft-kiota-abstractions = "1.6.8" +msgraph-sdk = "1.16.0" numpy = "2.0.2" pandas = "2.2.3" py-ocsf-models = "0.2.0" diff --git a/tests/providers/aws/aws_provider_test.py b/tests/providers/aws/aws_provider_test.py index 0901c915b7d..74f8e7e2928 100644 --- a/tests/providers/aws/aws_provider_test.py +++ b/tests/providers/aws/aws_provider_test.py @@ -352,7 +352,6 @@ def test_aws_provider_organizations_delegated_administrator(self): @mock_aws def test_aws_provider_organizations_none_organizations_metadata(self): - aws_provider = AwsProvider() assert isinstance(aws_provider.organizations_metadata, AWSOrganizationsInfo) @@ -444,7 +443,6 @@ def test_aws_provider_session_with_mfa(self): totp="111111", ), ): - aws_provider = AwsProvider(mfa=mfa) assert aws_provider.type == "aws" @@ -678,7 +676,6 @@ def test_aws_provider_mutelist(self): @mock_aws def test_aws_provider_mutelist_none(self): - with patch( "prowler.providers.aws.aws_provider.get_default_mute_file_path", return_value=None, @@ -828,7 +825,6 @@ def test_generate_regional_clients_all_enabled_regions(self): @mock_aws def test_generate_regional_clients_with_enabled_regions(self): - aws_provider = AwsProvider() enabled_regions = [AWS_REGION_EU_WEST_1] aws_provider._enabled_regions = enabled_regions @@ -982,7 +978,6 @@ def test_get_available_aws_service_regions_with_us_east_1_audited(self): @mock_aws def test_get_available_aws_service_regions_with_all_regions_audited(self): - aws_provider = AwsProvider() with patch( @@ -1218,25 +1213,27 @@ def test_test_connection_with_env_credentials(self, monkeypatch): assert connection.error is None def test_test_connection_without_credentials(self): - with mock.patch("boto3.Session.get_credentials", return_value=None), mock.patch( - "botocore.session.Session.get_scoped_config", return_value={} - ), mock.patch( - "botocore.credentials.EnvProvider.load", return_value=None - ), mock.patch( - "botocore.credentials.SharedCredentialProvider.load", return_value=None - ), mock.patch( - "botocore.credentials.InstanceMetadataProvider.load", return_value=None - ), mock.patch.dict( - "os.environ", - { - "AWS_ACCESS_KEY_ID": "", - "AWS_SECRET_ACCESS_KEY": "", - "AWS_SESSION_TOKEN": "", - "AWS_PROFILE": "", - }, - clear=True, + with ( + mock.patch("boto3.Session.get_credentials", return_value=None), + mock.patch("botocore.session.Session.get_scoped_config", return_value={}), + mock.patch("botocore.credentials.EnvProvider.load", return_value=None), + mock.patch( + "botocore.credentials.SharedCredentialProvider.load", return_value=None + ), + mock.patch( + "botocore.credentials.InstanceMetadataProvider.load", return_value=None + ), + mock.patch.dict( + "os.environ", + { + "AWS_ACCESS_KEY_ID": "", + "AWS_SECRET_ACCESS_KEY": "", + "AWS_SESSION_TOKEN": "", + "AWS_PROFILE": "", + }, + clear=True, + ), ): - with raises(AWSNoCredentialsError) as exception: AwsProvider.test_connection( profile=None @@ -1494,7 +1491,6 @@ def test_create_sts_session_china(self): new=mock_recover_checks_from_aws_provider_elb_service, ) def test_get_checks_from_input_arn_elb(self): - expected_checks = [ "elb_insecure_ssl_ciphers", "elb_internet_facing", @@ -1515,7 +1511,6 @@ def test_get_checks_from_input_arn_elb(self): new=mock_recover_checks_from_aws_provider_efs_service, ) def test_get_checks_from_input_arn_efs(self): - expected_checks = [ "efs_encryption_at_rest_enabled", "efs_have_backup_enabled", @@ -1556,7 +1551,6 @@ def test_get_checks_from_input_arn_lambda(self): new=mock_recover_checks_from_aws_provider_iam_service, ) def test_get_checks_from_input_arn_iam(self): - expected_checks = [ "iam_check_saml_providers_sts", "iam_customer_attached_policy_no_administrative_privileges", @@ -1578,7 +1572,6 @@ def test_get_checks_from_input_arn_iam(self): new=mock_recover_checks_from_aws_provider_s3_service, ) def test_get_checks_from_input_arn_s3(self): - expected_checks = [ "s3_account_level_public_access_blocks", "s3_bucket_acl_prohibited", @@ -1719,13 +1712,13 @@ def test_get_regions_from_audit_resources_without_regions(self): assert not recovered_regions def test_get_regions_all_count(self): - assert len(AwsProvider.get_regions(partition=None)) == 34 + assert len(AwsProvider.get_regions(partition=None)) == 35 def test_get_regions_cn_count(self): assert len(AwsProvider.get_regions("aws-cn")) == 2 def test_get_regions_aws_count(self): - assert len(AwsProvider.get_regions(partition="aws")) == 30 + assert len(AwsProvider.get_regions(partition="aws")) == 31 def test_get_all_regions(self): with patch( @@ -1899,7 +1892,6 @@ def test_get_aws_region_for_sts_input_regions_ireland_and_virgninia(self): @mock_aws def test_set_session_config_default(self): - aws_provider = AwsProvider() session_config = aws_provider.set_session_config(None) @@ -1908,7 +1900,6 @@ def test_set_session_config_default(self): @mock_aws def test_set_session_config_10_max_attempts(self): - aws_provider = AwsProvider() session_config = aws_provider.set_session_config(10) @@ -1921,7 +1912,6 @@ def test_set_session_config_10_max_attempts(self): new=mock_recover_checks_from_aws_provider_ec2_service, ) def test_get_checks_to_execute_by_audit_resources(self): - aws_provider = AwsProvider() aws_provider._audit_resources = [ f"arn:aws:ec2:us-west-2:{AWS_ACCOUNT_NUMBER}:network-acl/acl-1" @@ -1939,7 +1929,6 @@ def test_update_provider_config_aws(self): "prowler.providers.common.provider.Provider.get_global_provider", return_value=aws_provider, ): - assert { "shodan_api_key": "TEST-API-KEY" } == Provider.update_provider_config( @@ -1955,7 +1944,6 @@ def test_update_provider_config_aws_not_present(self): "prowler.providers.common.provider.Provider.get_global_provider", return_value=aws_provider, ): - assert {"shodan_api_key": "DEFAULT-KEY"} == Provider.update_provider_config( aws_provider.audit_config, "not_found", "not_value" ) diff --git a/tests/providers/aws/services/cloudformation/cloudformation_stack_cdktoolkit_bootstrap_version/cloudformation_stack_cdktoolkit_bootstrap_version_test.py b/tests/providers/aws/services/cloudformation/cloudformation_stack_cdktoolkit_bootstrap_version/cloudformation_stack_cdktoolkit_bootstrap_version_test.py new file mode 100644 index 00000000000..8ba9aabb784 --- /dev/null +++ b/tests/providers/aws/services/cloudformation/cloudformation_stack_cdktoolkit_bootstrap_version/cloudformation_stack_cdktoolkit_bootstrap_version_test.py @@ -0,0 +1,106 @@ +from unittest import mock + +from prowler.providers.aws.services.cloudformation.cloudformation_service import Stack + +# Mock Test Region +AWS_REGION = "eu-west-1" + + +class Test_cloudformation_stack_cdktoolkit_bootstrap_version: + def test_no_stacks(self): + cloudformation_client = mock.MagicMock + cloudformation_client.stacks = [] + cloudformation_client.audit_config = {"recommended_cdk_bootstrap_version": 21} + with mock.patch( + "prowler.providers.aws.services.cloudformation.cloudformation_service.CloudFormation", + new=cloudformation_client, + ), mock.patch( + "prowler.providers.aws.services.cloudformation.cloudformation_client.cloudformation_client", + new=cloudformation_client, + ): + from prowler.providers.aws.services.cloudformation.cloudformation_stack_cdktoolkit_bootstrap_version.cloudformation_stack_cdktoolkit_bootstrap_version import ( + cloudformation_stack_cdktoolkit_bootstrap_version, + ) + + check = cloudformation_stack_cdktoolkit_bootstrap_version() + result = check.execute() + + assert len(result) == 0 + + def test_stack_with_valid_bootstrap_version(self): + cloudformation_client = mock.MagicMock + cloudformation_client.stacks = [ + Stack( + arn="arn:aws:cloudformation:eu-west-1:123456789012:stack/CDKToolkit/1234abcd", + name="CDKToolkit", + outputs=["BootstrapVersion:21"], + region=AWS_REGION, + ) + ] + cloudformation_client.audit_config = {"recommended_cdk_bootstrap_version": 21} + + with mock.patch( + "prowler.providers.aws.services.cloudformation.cloudformation_service.CloudFormation", + new=cloudformation_client, + ), mock.patch( + "prowler.providers.aws.services.cloudformation.cloudformation_client.cloudformation_client", + new=cloudformation_client, + ): + from prowler.providers.aws.services.cloudformation.cloudformation_stack_cdktoolkit_bootstrap_version.cloudformation_stack_cdktoolkit_bootstrap_version import ( + cloudformation_stack_cdktoolkit_bootstrap_version, + ) + + check = cloudformation_stack_cdktoolkit_bootstrap_version() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "CloudFormation Stack CDKToolkit has a Bootstrap version 21, which meets the recommended version." + ) + assert result[0].resource_id == "CDKToolkit" + assert ( + result[0].resource_arn + == "arn:aws:cloudformation:eu-west-1:123456789012:stack/CDKToolkit/1234abcd" + ) + assert result[0].region == AWS_REGION + + def test_stack_with_invalid_bootstrap_version(self): + cloudformation_client = mock.MagicMock + cloudformation_client.stacks = [ + Stack( + arn="arn:aws:cloudformation:eu-west-1:123456789012:stack/CDKToolkit/1234abcd", + name="CDKToolkit", + outputs=["BootstrapVersion:20"], + region=AWS_REGION, + ) + ] + cloudformation_client.audit_config = {"recommended_cdk_bootstrap_version": 21} + + with mock.patch( + "prowler.providers.aws.services.cloudformation.cloudformation_service.CloudFormation", + new=cloudformation_client, + ), mock.patch( + "prowler.providers.aws.services.cloudformation.cloudformation_client.cloudformation_client", + new=cloudformation_client, + ): + from prowler.providers.aws.services.cloudformation.cloudformation_stack_cdktoolkit_bootstrap_version.cloudformation_stack_cdktoolkit_bootstrap_version import ( + cloudformation_stack_cdktoolkit_bootstrap_version, + ) + + check = cloudformation_stack_cdktoolkit_bootstrap_version() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "CloudFormation Stack CDKToolkit has a Bootstrap version 20, which is less than the recommended version 21." + ) + assert result[0].resource_id == "CDKToolkit" + assert ( + result[0].resource_arn + == "arn:aws:cloudformation:eu-west-1:123456789012:stack/CDKToolkit/1234abcd" + ) + assert result[0].region == AWS_REGION diff --git a/tests/providers/aws/services/cloudformation/cloudformation_stack_outputs_find_secrets/cloudformation_stack_outputs_find_secrets_test.py b/tests/providers/aws/services/cloudformation/cloudformation_stack_outputs_find_secrets/cloudformation_stack_outputs_find_secrets_test.py index 6d73a78e22e..c985c838f8a 100644 --- a/tests/providers/aws/services/cloudformation/cloudformation_stack_outputs_find_secrets/cloudformation_stack_outputs_find_secrets_test.py +++ b/tests/providers/aws/services/cloudformation/cloudformation_stack_outputs_find_secrets/cloudformation_stack_outputs_find_secrets_test.py @@ -10,9 +10,13 @@ class Test_cloudformation_stack_outputs_find_secrets: def test_no_stacks(self): cloudformation_client = mock.MagicMock cloudformation_client.stacks = [] + cloudformation_client.audit_config = {"secrets_ignore_patterns": []} with mock.patch( "prowler.providers.aws.services.cloudformation.cloudformation_service.CloudFormation", new=cloudformation_client, + ), mock.patch( + "prowler.providers.aws.services.cloudformation.cloudformation_client.cloudformation_client", + new=cloudformation_client, ): # Test Check from prowler.providers.aws.services.cloudformation.cloudformation_stack_outputs_find_secrets.cloudformation_stack_outputs_find_secrets import ( @@ -36,9 +40,14 @@ def test_stack_secret_in_outputs(self): ) ] + cloudformation_client.audit_config = {"secrets_ignore_patterns": []} + with mock.patch( "prowler.providers.aws.services.cloudformation.cloudformation_service.CloudFormation", cloudformation_client, + ), mock.patch( + "prowler.providers.aws.services.cloudformation.cloudformation_client.cloudformation_client", + new=cloudformation_client, ): from prowler.providers.aws.services.cloudformation.cloudformation_stack_outputs_find_secrets.cloudformation_stack_outputs_find_secrets import ( cloudformation_stack_outputs_find_secrets, @@ -51,7 +60,7 @@ def test_stack_secret_in_outputs(self): assert result[0].status == "FAIL" assert ( result[0].status_extended - == f"Potential secret found in Stack {stack_name} Outputs -> Secret Keyword in Output 1." + == f"Potential secret found in CloudFormation Stack {stack_name} Outputs -> Secret Keyword in Output 1." ) assert result[0].resource_id == "Test-Stack" assert ( @@ -75,9 +84,14 @@ def test_stack_secret_in_outputs_false_case(self): ) ] + cloudformation_client.audit_config = {"secrets_ignore_patterns": []} + with mock.patch( "prowler.providers.aws.services.cloudformation.cloudformation_service.CloudFormation", cloudformation_client, + ), mock.patch( + "prowler.providers.aws.services.cloudformation.cloudformation_client.cloudformation_client", + new=cloudformation_client, ): from prowler.providers.aws.services.cloudformation.cloudformation_stack_outputs_find_secrets.cloudformation_stack_outputs_find_secrets import ( cloudformation_stack_outputs_find_secrets, @@ -90,7 +104,7 @@ def test_stack_secret_in_outputs_false_case(self): assert result[0].status == "PASS" assert ( result[0].status_extended - == f"No secrets found in Stack {stack_name} Outputs." + == f"No secrets found in CloudFormation Stack {stack_name} Outputs." ) assert result[0].resource_id == "Test-Stack" assert ( @@ -112,9 +126,14 @@ def test_stack_no_secret_in_outputs(self): ) ] + cloudformation_client.audit_config = {"secrets_ignore_patterns": []} + with mock.patch( "prowler.providers.aws.services.cloudformation.cloudformation_service.CloudFormation", cloudformation_client, + ), mock.patch( + "prowler.providers.aws.services.cloudformation.cloudformation_client.cloudformation_client", + new=cloudformation_client, ): from prowler.providers.aws.services.cloudformation.cloudformation_stack_outputs_find_secrets.cloudformation_stack_outputs_find_secrets import ( cloudformation_stack_outputs_find_secrets, @@ -127,7 +146,7 @@ def test_stack_no_secret_in_outputs(self): assert result[0].status == "PASS" assert ( result[0].status_extended - == f"No secrets found in Stack {stack_name} Outputs." + == f"No secrets found in CloudFormation Stack {stack_name} Outputs." ) assert result[0].resource_id == "Test-Stack" assert ( @@ -149,9 +168,14 @@ def test_stack_no_outputs(self): ) ] + cloudformation_client.audit_config = {"secrets_ignore_patterns": []} + with mock.patch( "prowler.providers.aws.services.cloudformation.cloudformation_service.CloudFormation", cloudformation_client, + ), mock.patch( + "prowler.providers.aws.services.cloudformation.cloudformation_client.cloudformation_client", + new=cloudformation_client, ): from prowler.providers.aws.services.cloudformation.cloudformation_stack_outputs_find_secrets.cloudformation_stack_outputs_find_secrets import ( cloudformation_stack_outputs_find_secrets, @@ -164,7 +188,7 @@ def test_stack_no_outputs(self): assert result[0].status == "PASS" assert ( result[0].status_extended - == f"CloudFormation {stack_name} has no Outputs." + == f"CloudFormation Stack {stack_name} has no Outputs." ) assert result[0].resource_id == "Test-Stack" assert ( diff --git a/tests/providers/aws/services/cloudformation/cloudformation_stacks_termination_protection_enabled/cloudformation_stacks_termination_protection_enabled_test.py b/tests/providers/aws/services/cloudformation/cloudformation_stacks_termination_protection_enabled/cloudformation_stacks_termination_protection_enabled_test.py index 9bde9f5173b..5940c7bf22b 100644 --- a/tests/providers/aws/services/cloudformation/cloudformation_stacks_termination_protection_enabled/cloudformation_stacks_termination_protection_enabled_test.py +++ b/tests/providers/aws/services/cloudformation/cloudformation_stacks_termination_protection_enabled/cloudformation_stacks_termination_protection_enabled_test.py @@ -13,6 +13,9 @@ def test_no_stacks(self): with mock.patch( "prowler.providers.aws.services.cloudformation.cloudformation_service.CloudFormation", new=cloudformation_client, + ), mock.patch( + "prowler.providers.aws.services.cloudformation.cloudformation_client.cloudformation_client", + new=cloudformation_client, ): # Test Check from prowler.providers.aws.services.cloudformation.cloudformation_stacks_termination_protection_enabled.cloudformation_stacks_termination_protection_enabled import ( @@ -40,6 +43,9 @@ def test_stack_termination_protection_enabled(self): with mock.patch( "prowler.providers.aws.services.cloudformation.cloudformation_service.CloudFormation", cloudformation_client, + ), mock.patch( + "prowler.providers.aws.services.cloudformation.cloudformation_client.cloudformation_client", + new=cloudformation_client, ): from prowler.providers.aws.services.cloudformation.cloudformation_stacks_termination_protection_enabled.cloudformation_stacks_termination_protection_enabled import ( cloudformation_stacks_termination_protection_enabled, @@ -52,7 +58,7 @@ def test_stack_termination_protection_enabled(self): assert result[0].status == "PASS" assert ( result[0].status_extended - == f"CloudFormation {stack_name} has termination protection enabled." + == f"CloudFormation Stack {stack_name} has termination protection enabled." ) assert result[0].resource_id == "Test-Stack" assert ( @@ -78,6 +84,9 @@ def test_stack_termination_protection_disabled(self): with mock.patch( "prowler.providers.aws.services.cloudformation.cloudformation_service.CloudFormation", cloudformation_client, + ), mock.patch( + "prowler.providers.aws.services.cloudformation.cloudformation_client.cloudformation_client", + new=cloudformation_client, ): from prowler.providers.aws.services.cloudformation.cloudformation_stacks_termination_protection_enabled.cloudformation_stacks_termination_protection_enabled import ( cloudformation_stacks_termination_protection_enabled, @@ -90,7 +99,7 @@ def test_stack_termination_protection_disabled(self): assert result[0].status == "FAIL" assert ( result[0].status_extended - == f"CloudFormation {stack_name} has termination protection disabled." + == f"CloudFormation Stack {stack_name} has termination protection disabled." ) assert result[0].resource_id == "Test-Stack" assert ( diff --git a/ui/app/(prowler)/compliance/page.tsx b/ui/app/(prowler)/compliance/page.tsx index 558caeaf9ba..108433a7c70 100644 --- a/ui/app/(prowler)/compliance/page.tsx +++ b/ui/app/(prowler)/compliance/page.tsx @@ -40,6 +40,7 @@ export default async function Compliance({ progress: scan.attributes.progress, })) || []; } catch (error) { + // eslint-disable-next-line no-console console.error("Error fetching scans data:", error); } @@ -72,6 +73,7 @@ export default async function Compliance({ ) : []; } catch (error) { + // eslint-disable-next-line no-console console.error("Error fetching compliance data:", error); } @@ -104,6 +106,7 @@ const SSRComplianceGrid = async ({ region: regionFilter, }); } catch (error) { + // eslint-disable-next-line no-console console.error("Error fetching compliances overview:", error); return (
diff --git a/ui/app/(prowler)/providers/(set-up-provider)/update-credentials/page.tsx b/ui/app/(prowler)/providers/(set-up-provider)/update-credentials/page.tsx index c3476099ab5..0123ce73f2d 100644 --- a/ui/app/(prowler)/providers/(set-up-provider)/update-credentials/page.tsx +++ b/ui/app/(prowler)/providers/(set-up-provider)/update-credentials/page.tsx @@ -1,3 +1,4 @@ +import { Alert, cn } from "@nextui-org/react"; import React from "react"; import { @@ -14,7 +15,39 @@ export default function UpdateCredentialsPage({ searchParams }: Props) { return ( <> {searchParams.type === "aws" && !searchParams.via && ( - + <> +
+

+ If the provider was set up with static credentials, updates must + use static credentials. If it was set up with a role, updates must + use a role. +

+ + + To update provider credentials,{" "} + + you must use the same type that was originally configured. + {" "} + + } + /> +

+ To switch from static credentials to a role (or vice versa), you + need to delete the provider and set it up again. +

+ +
+ )} {((searchParams.type === "aws" && searchParams.via === "credentials") || diff --git a/ui/app/(prowler)/scans/page.tsx b/ui/app/(prowler)/scans/page.tsx index f90c90a9fdc..5b45789214d 100644 --- a/ui/app/(prowler)/scans/page.tsx +++ b/ui/app/(prowler)/scans/page.tsx @@ -8,6 +8,7 @@ import { ButtonRefreshData, NoProvidersAdded, NoProvidersConnected, + ScanWarningBar, } from "@/components/scans"; import { LaunchScanWorkflow } from "@/components/scans/launch-workflow"; import { SkeletonTableScans } from "@/components/scans/table"; @@ -72,9 +73,10 @@ export default async function Scans({ )} -
+ +
{ className="w-full" size="sm" selectedKeys={selectedKeys} - onSelectionChange={(keys) => applyRegionFilter(Array.from(keys))} + onSelectionChange={(keys) => + applyRegionFilter(Array.from(keys) as string[]) + } > {regions.map((region) => ( {region.label} diff --git a/ui/components/findings/table/data-table-row-actions.tsx b/ui/components/findings/table/data-table-row-actions.tsx index 8e15c942a03..a552030a920 100644 --- a/ui/components/findings/table/data-table-row-actions.tsx +++ b/ui/components/findings/table/data-table-row-actions.tsx @@ -76,7 +76,7 @@ export function DataTableRowActions({ description="Allows you to send the finding to Jira" textValue="Send to Jira" startContent={} - // onClick={() => setIsEditOpen(true)} + // onPress={() => setIsEditOpen(true)} > {findingId} Send to Jira @@ -86,7 +86,7 @@ export function DataTableRowActions({ description="Allows you to send the finding to Slack" textValue="Send to Slack" startContent={} - // onClick={() => setIsEditOpen(true)} + // onPress={() => setIsEditOpen(true)} > Send to Slack diff --git a/ui/components/invitations/table/data-table-row-actions.tsx b/ui/components/invitations/table/data-table-row-actions.tsx index 6a30365575b..a244443b90a 100644 --- a/ui/components/invitations/table/data-table-row-actions.tsx +++ b/ui/components/invitations/table/data-table-row-actions.tsx @@ -15,6 +15,7 @@ import { } from "@nextui-org/shared-icons"; import { Row } from "@tanstack/react-table"; import clsx from "clsx"; +import { useRouter } from "next/navigation"; import { useState } from "react"; import { VerticalDotsIcon } from "@/components/icons"; @@ -33,6 +34,7 @@ export function DataTableRowActions({ row, roles, }: DataTableRowActionsProps) { + const router = useRouter(); const [isEditOpen, setIsEditOpen] = useState(false); const [isDeleteOpen, setIsDeleteOpen] = useState(false); const invitationId = (row.original as { id: string }).id; @@ -82,11 +84,13 @@ export function DataTableRowActions({ > } + onPress={() => + router.push(`/invitations/check-details?id=${invitationId}`) + } > Check Details @@ -96,7 +100,7 @@ export function DataTableRowActions({ description="Allows you to edit the invitation" textValue="Edit Invitation" startContent={} - onClick={() => setIsEditOpen(true)} + onPress={() => setIsEditOpen(true)} > Edit Invitation @@ -113,7 +117,7 @@ export function DataTableRowActions({ className={clsx(iconClasses, "!text-danger")} /> } - onClick={() => setIsDeleteOpen(true)} + onPress={() => setIsDeleteOpen(true)} > Revoke Invitation diff --git a/ui/components/manage-groups/table/data-table-row-actions.tsx b/ui/components/manage-groups/table/data-table-row-actions.tsx index 00452446a72..88bfb32dad1 100644 --- a/ui/components/manage-groups/table/data-table-row-actions.tsx +++ b/ui/components/manage-groups/table/data-table-row-actions.tsx @@ -69,7 +69,7 @@ export function DataTableRowActions({ description="Allows you to edit the provider group" textValue="Edit Provider Group" startContent={} - onClick={() => router.push(`/manage-groups?groupId=${groupId}`)} + onPress={() => router.push(`/manage-groups?groupId=${groupId}`)} > Edit Provider Group @@ -86,7 +86,7 @@ export function DataTableRowActions({ className={clsx(iconClasses, "!text-danger")} /> } - onClick={() => setIsDeleteOpen(true)} + onPress={() => setIsDeleteOpen(true)} > Delete Provider Group diff --git a/ui/components/providers/table/data-table-row-actions.tsx b/ui/components/providers/table/data-table-row-actions.tsx index 7cc95cada5f..3a7cfc37f1b 100644 --- a/ui/components/providers/table/data-table-row-actions.tsx +++ b/ui/components/providers/table/data-table-row-actions.tsx @@ -15,6 +15,7 @@ import { } from "@nextui-org/shared-icons"; import { Row } from "@tanstack/react-table"; import clsx from "clsx"; +import { useRouter } from "next/navigation"; import { useState } from "react"; import { checkConnectionProvider } from "@/actions/providers/providers"; @@ -33,6 +34,7 @@ const iconClasses = export function DataTableRowActions({ row, }: DataTableRowActionsProps) { + const router = useRouter(); const [isEditOpen, setIsEditOpen] = useState(false); const [isDeleteOpen, setIsDeleteOpen] = useState(false); const providerId = (row.original as { id: string }).id; @@ -87,11 +89,15 @@ export function DataTableRowActions({ > } + onPress={() => + router.push( + `/providers/update-credentials?type=${providerType}&id=${providerId}${providerSecretId ? `&secretId=${providerSecretId}` : ""}`, + ) + } > Update Credentials @@ -100,7 +106,7 @@ export function DataTableRowActions({ description="Check the connection to the provider" textValue="Check Connection" startContent={} - onClick={handleTestConnection} + onPress={handleTestConnection} > Test Connection @@ -109,7 +115,7 @@ export function DataTableRowActions({ description="Allows you to edit the provider" textValue="Edit Provider" startContent={} - onClick={() => setIsEditOpen(true)} + onPress={() => setIsEditOpen(true)} > Edit Provider Alias @@ -126,7 +132,7 @@ export function DataTableRowActions({ className={clsx(iconClasses, "!text-danger")} /> } - onClick={() => setIsDeleteOpen(true)} + onPress={() => setIsDeleteOpen(true)} > Delete Provider diff --git a/ui/components/roles/table/data-table-row-actions.tsx b/ui/components/roles/table/data-table-row-actions.tsx index ff9d98884bd..abf674838d7 100644 --- a/ui/components/roles/table/data-table-row-actions.tsx +++ b/ui/components/roles/table/data-table-row-actions.tsx @@ -14,6 +14,7 @@ import { } from "@nextui-org/shared-icons"; import { Row } from "@tanstack/react-table"; import clsx from "clsx"; +import { useRouter } from "next/navigation"; import { useState } from "react"; import { VerticalDotsIcon } from "@/components/icons"; @@ -29,6 +30,7 @@ const iconClasses = export function DataTableRowActions({ row, }: DataTableRowActionsProps) { + const router = useRouter(); const [isDeleteOpen, setIsDeleteOpen] = useState(false); const roleId = (row.original as { id: string }).id; return ( @@ -59,11 +61,11 @@ export function DataTableRowActions({ > } + onPress={() => router.push(`/roles/edit?roleId=${roleId}`)} > Edit Role @@ -80,7 +82,7 @@ export function DataTableRowActions({ className={clsx(iconClasses, "!text-danger")} /> } - onClick={() => setIsDeleteOpen(true)} + onPress={() => setIsDeleteOpen(true)} > Delete Role diff --git a/ui/components/scans/index.ts b/ui/components/scans/index.ts index aa42edfe3c3..98157857ab0 100644 --- a/ui/components/scans/index.ts +++ b/ui/components/scans/index.ts @@ -2,3 +2,4 @@ export * from "./button-refresh-data"; export * from "./link-to-findings-from-scan"; export * from "./no-providers-added"; export * from "./no-providers-connected"; +export * from "./scan-warning-bar"; diff --git a/ui/components/scans/scan-warning-bar.tsx b/ui/components/scans/scan-warning-bar.tsx new file mode 100644 index 00000000000..64997879ad2 --- /dev/null +++ b/ui/components/scans/scan-warning-bar.tsx @@ -0,0 +1,19 @@ +import { Alert, cn } from "@nextui-org/react"; + +export const ScanWarningBar = () => { + return ( + + ); +}; diff --git a/ui/components/scans/table/scans/data-table-row-actions.tsx b/ui/components/scans/table/scans/data-table-row-actions.tsx index da35eb2f9b9..7a010239868 100644 --- a/ui/components/scans/table/scans/data-table-row-actions.tsx +++ b/ui/components/scans/table/scans/data-table-row-actions.tsx @@ -69,7 +69,7 @@ export function DataTableRowActions({ description="Allows you to edit the scan name" textValue="Edit Scan Name" startContent={} - onClick={() => setIsEditOpen(true)} + onPress={() => setIsEditOpen(true)} > Edit scan name diff --git a/ui/components/ui/custom/custom-radio.tsx b/ui/components/ui/custom/custom-radio.tsx index a1248209b19..ec77e2b1f7c 100644 --- a/ui/components/ui/custom/custom-radio.tsx +++ b/ui/components/ui/custom/custom-radio.tsx @@ -1,9 +1,9 @@ -import { UseRadioProps } from "@nextui-org/radio/dist/use-radio"; import { cn, useRadio, VisuallyHidden } from "@nextui-org/react"; import React from "react"; -interface CustomRadioProps extends UseRadioProps { +interface CustomRadioProps { description?: string; + value?: string; children?: React.ReactNode; } @@ -18,7 +18,7 @@ export const CustomRadio: React.FC = (props) => { getLabelProps, getLabelWrapperProps, getControlProps, - } = useRadio(props); + } = useRadio({ ...props, value: props.value || "" }); return ( {