diff --git a/.github/workflows/api-code-quality.yml b/.github/workflows/api-code-quality.yml index 68928833a2..4e15f54dbf 100644 --- a/.github/workflows/api-code-quality.yml +++ b/.github/workflows/api-code-quality.yml @@ -50,7 +50,7 @@ jobs: - name: Check for API changes id: check-changes - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | api/** diff --git a/.github/workflows/api-container-build-push.yml b/.github/workflows/api-container-build-push.yml index 0cd3b82071..b045bb0322 100644 --- a/.github/workflows/api-container-build-push.yml +++ b/.github/workflows/api-container-build-push.yml @@ -137,18 +137,18 @@ jobs: sed -i "s|prowler-cloud/prowler.git@master|prowler-cloud/prowler.git@${LATEST_SHA}|" api/pyproject.toml - name: Login to DockerHub - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - name: Build and push API container for ${{ matrix.arch }} id: container-push if: github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'workflow_dispatch' - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2 + uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 with: context: ${{ env.WORKING_DIRECTORY }} push: true @@ -178,7 +178,7 @@ jobs: auth.docker.io:443 production.cloudflare.docker.com:443 - name: Login to DockerHub - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} diff --git a/.github/workflows/api-container-checks.yml b/.github/workflows/api-container-checks.yml index 14c163e89a..5192d1bd07 100644 --- a/.github/workflows/api-container-checks.yml +++ b/.github/workflows/api-container-checks.yml @@ -42,7 +42,7 @@ jobs: - name: Check if Dockerfile changed id: dockerfile-changed - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: api/Dockerfile @@ -104,7 +104,7 @@ jobs: - name: Check for API changes id: check-changes - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: api/** files_ignore: | @@ -115,11 +115,11 @@ jobs: - name: Set up Docker Buildx if: steps.check-changes.outputs.any_changed == 'true' - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - name: Build container for ${{ matrix.arch }} if: steps.check-changes.outputs.any_changed == 'true' - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2 + uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 with: context: ${{ env.API_WORKING_DIR }} push: false diff --git a/.github/workflows/api-security.yml b/.github/workflows/api-security.yml index 9cd6b14623..505c24f57a 100644 --- a/.github/workflows/api-security.yml +++ b/.github/workflows/api-security.yml @@ -53,7 +53,7 @@ jobs: - name: Check for API changes id: check-changes - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | api/** diff --git a/.github/workflows/api-tests.yml b/.github/workflows/api-tests.yml index ce00b03108..21c4f03965 100644 --- a/.github/workflows/api-tests.yml +++ b/.github/workflows/api-tests.yml @@ -99,7 +99,7 @@ jobs: - name: Check for API changes id: check-changes - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | api/** diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index feb8c7c9bf..3563db7154 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -46,7 +46,7 @@ jobs: - name: Backport PR if: steps.label_check.outputs.label_check == 'success' - uses: sorenlouv/backport-github-action@516854e7c9f962b9939085c9a92ea28411d1ae90 # v10.2.0 + uses: sorenlouv/backport-github-action@9460b7102fea25466026ce806c9ebf873ac48721 # v11.0.0 with: github_token: ${{ secrets.PROWLER_BOT_ACCESS_TOKEN }} auto_backport_label_prefix: ${{ env.BACKPORT_LABEL_PREFIX }} diff --git a/.github/workflows/ci-zizmor.yml b/.github/workflows/ci-zizmor.yml index 08db92d7b6..e79971bdf4 100644 --- a/.github/workflows/ci-zizmor.yml +++ b/.github/workflows/ci-zizmor.yml @@ -54,7 +54,7 @@ jobs: run: echo "files=$(find .github -name '*.yml' -o -name '*.yaml' | grep -v '\.lock\.yml$' | sort | tr '\n' ' ')" >> "$GITHUB_OUTPUT" - name: Run zizmor - uses: zizmorcore/zizmor-action@0dce2577a4760a2749d8cfb7a84b7d5585ebcb7d # v0.5.0 + uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2 with: inputs: ${{ steps.collect.outputs.files }} token: ${{ github.token }} diff --git a/.github/workflows/helm-chart-checks.yml b/.github/workflows/helm-chart-checks.yml index a0a24c2516..3cc857abd7 100644 --- a/.github/workflows/helm-chart-checks.yml +++ b/.github/workflows/helm-chart-checks.yml @@ -36,12 +36,12 @@ jobs: egress-policy: audit - name: Checkout repository - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Set up Helm - uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4.3.1 + uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0 - name: Update chart dependencies run: helm dependency update ${{ env.CHART_PATH }} diff --git a/.github/workflows/helm-chart-release.yml b/.github/workflows/helm-chart-release.yml index c0c5773bdb..e947f8af82 100644 --- a/.github/workflows/helm-chart-release.yml +++ b/.github/workflows/helm-chart-release.yml @@ -29,12 +29,12 @@ jobs: egress-policy: audit - name: Checkout repository - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Set up Helm - uses: azure/setup-helm@b9e51907a09c216f16ebe8536097933489208112 # v4.3.0 + uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0 - name: Set appVersion from release tag run: | diff --git a/.github/workflows/issue-triage.lock.yml b/.github/workflows/issue-triage.lock.yml index fcc6f1e363..b694e8ecef 100644 --- a/.github/workflows/issue-triage.lock.yml +++ b/.github/workflows/issue-triage.lock.yml @@ -772,7 +772,7 @@ jobs: SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload Safe Outputs if: always() - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: safe-output path: ${{ env.GH_AW_SAFE_OUTPUTS }} @@ -793,13 +793,13 @@ jobs: await main(); - name: Upload sanitized agent output if: always() && env.GH_AW_AGENT_OUTPUT - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: agent-output path: ${{ env.GH_AW_AGENT_OUTPUT }} if-no-files-found: warn - name: Upload engine output files - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: agent_outputs path: | @@ -839,7 +839,7 @@ jobs: - name: Upload agent artifacts if: always() continue-on-error: true - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: agent-artifacts path: | @@ -880,7 +880,7 @@ jobs: destination: /opt/gh-aw/actions - name: Download agent output artifact continue-on-error: true - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: agent-output path: /tmp/gh-aw/safeoutputs/ @@ -992,13 +992,13 @@ jobs: destination: /opt/gh-aw/actions - name: Download agent artifacts continue-on-error: true - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: agent-artifacts path: /tmp/gh-aw/threat-detection/ - name: Download agent output artifact continue-on-error: true - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: agent-output path: /tmp/gh-aw/threat-detection/ @@ -1071,7 +1071,7 @@ jobs: await main(); - name: Upload threat detection log if: always() - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: threat-detection.log path: /tmp/gh-aw/threat-detection/detection.log @@ -1174,7 +1174,7 @@ jobs: destination: /opt/gh-aw/actions - name: Download agent output artifact continue-on-error: true - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: agent-output path: /tmp/gh-aw/safeoutputs/ diff --git a/.github/workflows/mcp-container-build-push.yml b/.github/workflows/mcp-container-build-push.yml index 3f59a455db..27d59d7bc1 100644 --- a/.github/workflows/mcp-container-build-push.yml +++ b/.github/workflows/mcp-container-build-push.yml @@ -123,18 +123,18 @@ jobs: persist-credentials: false - name: Login to DockerHub - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - name: Build and push MCP container for ${{ matrix.arch }} id: container-push if: github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'workflow_dispatch' - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2 + uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 with: context: ${{ env.WORKING_DIRECTORY }} push: true @@ -173,7 +173,7 @@ jobs: release-assets.githubusercontent.com:443 - name: Login to DockerHub - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} diff --git a/.github/workflows/mcp-container-checks.yml b/.github/workflows/mcp-container-checks.yml index f8fbfca12f..665153f1f1 100644 --- a/.github/workflows/mcp-container-checks.yml +++ b/.github/workflows/mcp-container-checks.yml @@ -42,7 +42,7 @@ jobs: - name: Check if Dockerfile changed id: dockerfile-changed - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: mcp_server/Dockerfile @@ -96,7 +96,7 @@ jobs: - name: Check for MCP changes id: check-changes - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: mcp_server/** files_ignore: | @@ -105,11 +105,11 @@ jobs: - name: Set up Docker Buildx if: steps.check-changes.outputs.any_changed == 'true' - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - name: Build MCP container for ${{ matrix.arch }} if: steps.check-changes.outputs.any_changed == 'true' - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2 + uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 with: context: ${{ env.MCP_WORKING_DIR }} push: false diff --git a/.github/workflows/pr-check-changelog.yml b/.github/workflows/pr-check-changelog.yml index c729875843..c021e079cc 100644 --- a/.github/workflows/pr-check-changelog.yml +++ b/.github/workflows/pr-check-changelog.yml @@ -45,7 +45,7 @@ jobs: - name: Get changed files id: changed-files - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | api/** diff --git a/.github/workflows/pr-check-compliance-mapping.yml b/.github/workflows/pr-check-compliance-mapping.yml index dcf61602ba..939e17d0b4 100644 --- a/.github/workflows/pr-check-compliance-mapping.yml +++ b/.github/workflows/pr-check-compliance-mapping.yml @@ -43,7 +43,7 @@ jobs: - name: Get changed files id: changed-files - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | prowler/providers/**/services/**/*.metadata.json diff --git a/.github/workflows/pr-conflict-checker.yml b/.github/workflows/pr-conflict-checker.yml index 330a038fa0..e53a34ea23 100644 --- a/.github/workflows/pr-conflict-checker.yml +++ b/.github/workflows/pr-conflict-checker.yml @@ -39,7 +39,7 @@ jobs: - name: Get changed files id: changed-files - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: '**' diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index f0aee83174..c4163aa397 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -380,7 +380,7 @@ jobs: no-changelog - name: Create draft release - uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 + uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1 with: tag_name: ${{ env.PROWLER_VERSION }} name: Prowler ${{ env.PROWLER_VERSION }} diff --git a/.github/workflows/sdk-code-quality.yml b/.github/workflows/sdk-code-quality.yml index b854e9ddeb..08f8001120 100644 --- a/.github/workflows/sdk-code-quality.yml +++ b/.github/workflows/sdk-code-quality.yml @@ -46,7 +46,7 @@ jobs: - name: Check for SDK changes id: check-changes - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: ./** files_ignore: | diff --git a/.github/workflows/sdk-container-build-push.yml b/.github/workflows/sdk-container-build-push.yml index 4a0563b89f..ebe595bbbc 100644 --- a/.github/workflows/sdk-container-build-push.yml +++ b/.github/workflows/sdk-container-build-push.yml @@ -197,13 +197,13 @@ jobs: persist-credentials: false - name: Login to DockerHub - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to Public ECR - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: registry: public.ecr.aws username: ${{ secrets.PUBLIC_ECR_AWS_ACCESS_KEY_ID }} @@ -212,12 +212,12 @@ jobs: AWS_REGION: ${{ env.AWS_REGION }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - name: Build and push SDK container for ${{ matrix.arch }} id: container-push if: github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'workflow_dispatch' - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2 + uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 with: context: . file: ${{ env.DOCKERFILE_PATH }} @@ -252,13 +252,13 @@ jobs: - name: Login to DockerHub - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to Public ECR - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: registry: public.ecr.aws username: ${{ secrets.PUBLIC_ECR_AWS_ACCESS_KEY_ID }} @@ -295,7 +295,7 @@ jobs: # Push to toniblyx/prowler only for current version (latest/stable/release tags) - name: Login to DockerHub (toniblyx) if: needs.setup.outputs.latest_tag == 'latest' - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: username: ${{ secrets.TONIBLYX_DOCKERHUB_USERNAME }} password: ${{ secrets.TONIBLYX_DOCKERHUB_PASSWORD }} @@ -320,7 +320,7 @@ jobs: # Re-login as prowlercloud for cleanup of intermediate tags - name: Login to DockerHub (prowlercloud) if: always() - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} diff --git a/.github/workflows/sdk-container-checks.yml b/.github/workflows/sdk-container-checks.yml index dacf160a86..19d11647c4 100644 --- a/.github/workflows/sdk-container-checks.yml +++ b/.github/workflows/sdk-container-checks.yml @@ -41,7 +41,7 @@ jobs: - name: Check if Dockerfile changed id: dockerfile-changed - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: Dockerfile @@ -102,7 +102,7 @@ jobs: - name: Check for SDK changes id: check-changes - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: ./** files_ignore: | @@ -127,11 +127,11 @@ jobs: - name: Set up Docker Buildx if: steps.check-changes.outputs.any_changed == 'true' - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - name: Build SDK container for ${{ matrix.arch }} if: steps.check-changes.outputs.any_changed == 'true' - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2 + uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 with: context: . push: false diff --git a/.github/workflows/sdk-security.yml b/.github/workflows/sdk-security.yml index 2229568b3f..f4924ab030 100644 --- a/.github/workflows/sdk-security.yml +++ b/.github/workflows/sdk-security.yml @@ -44,7 +44,7 @@ jobs: - name: Check for SDK changes id: check-changes - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: ./** diff --git a/.github/workflows/sdk-tests.yml b/.github/workflows/sdk-tests.yml index b649db4f5a..7b9fc9d6ef 100644 --- a/.github/workflows/sdk-tests.yml +++ b/.github/workflows/sdk-tests.yml @@ -67,7 +67,7 @@ jobs: - name: Check for SDK changes id: check-changes - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: ./** files_ignore: | @@ -109,7 +109,7 @@ jobs: - name: Check if AWS files changed if: steps.check-changes.outputs.any_changed == 'true' id: changed-aws - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | ./prowler/**/aws/** @@ -239,7 +239,7 @@ jobs: - name: Check if Azure files changed if: steps.check-changes.outputs.any_changed == 'true' id: changed-azure - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | ./prowler/**/azure/** @@ -263,7 +263,7 @@ jobs: - name: Check if GCP files changed if: steps.check-changes.outputs.any_changed == 'true' id: changed-gcp - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | ./prowler/**/gcp/** @@ -287,7 +287,7 @@ jobs: - name: Check if Kubernetes files changed if: steps.check-changes.outputs.any_changed == 'true' id: changed-kubernetes - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | ./prowler/**/kubernetes/** @@ -311,7 +311,7 @@ jobs: - name: Check if GitHub files changed if: steps.check-changes.outputs.any_changed == 'true' id: changed-github - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | ./prowler/**/github/** @@ -335,7 +335,7 @@ jobs: - name: Check if NHN files changed if: steps.check-changes.outputs.any_changed == 'true' id: changed-nhn - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | ./prowler/**/nhn/** @@ -359,7 +359,7 @@ jobs: - name: Check if M365 files changed if: steps.check-changes.outputs.any_changed == 'true' id: changed-m365 - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | ./prowler/**/m365/** @@ -383,7 +383,7 @@ jobs: - name: Check if IaC files changed if: steps.check-changes.outputs.any_changed == 'true' id: changed-iac - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | ./prowler/**/iac/** @@ -407,7 +407,7 @@ jobs: - name: Check if MongoDB Atlas files changed if: steps.check-changes.outputs.any_changed == 'true' id: changed-mongodbatlas - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | ./prowler/**/mongodbatlas/** @@ -431,7 +431,7 @@ jobs: - name: Check if OCI files changed if: steps.check-changes.outputs.any_changed == 'true' id: changed-oraclecloud - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | ./prowler/**/oraclecloud/** @@ -455,7 +455,7 @@ jobs: - name: Check if OpenStack files changed if: steps.check-changes.outputs.any_changed == 'true' id: changed-openstack - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | ./prowler/**/openstack/** @@ -479,7 +479,7 @@ jobs: - name: Check if Google Workspace files changed if: steps.check-changes.outputs.any_changed == 'true' id: changed-googleworkspace - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | ./prowler/**/googleworkspace/** @@ -503,7 +503,7 @@ jobs: - name: Check if Vercel files changed if: steps.check-changes.outputs.any_changed == 'true' id: changed-vercel - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | ./prowler/**/vercel/** @@ -527,7 +527,7 @@ jobs: - name: Check if Lib files changed if: steps.check-changes.outputs.any_changed == 'true' id: changed-lib - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | ./prowler/lib/** @@ -551,7 +551,7 @@ jobs: - name: Check if Config files changed if: steps.check-changes.outputs.any_changed == 'true' id: changed-config - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | ./prowler/config/** diff --git a/.github/workflows/test-impact-analysis.yml b/.github/workflows/test-impact-analysis.yml index f03f99d0fc..34a1536cbc 100644 --- a/.github/workflows/test-impact-analysis.yml +++ b/.github/workflows/test-impact-analysis.yml @@ -66,7 +66,7 @@ jobs: - name: Get changed files id: changed-files - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 - name: Setup Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 diff --git a/.github/workflows/ui-container-build-push.yml b/.github/workflows/ui-container-build-push.yml index c8886a5ae9..4b73540b9b 100644 --- a/.github/workflows/ui-container-build-push.yml +++ b/.github/workflows/ui-container-build-push.yml @@ -127,18 +127,18 @@ jobs: persist-credentials: false - name: Login to DockerHub - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - name: Build and push UI container for ${{ matrix.arch }} id: container-push if: github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'workflow_dispatch' - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2 + uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 with: context: ${{ env.WORKING_DIRECTORY }} build-args: | @@ -172,7 +172,7 @@ jobs: production.cloudflare.docker.com:443 - name: Login to DockerHub - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} diff --git a/.github/workflows/ui-container-checks.yml b/.github/workflows/ui-container-checks.yml index 10a910ace4..56c9dd13f4 100644 --- a/.github/workflows/ui-container-checks.yml +++ b/.github/workflows/ui-container-checks.yml @@ -42,7 +42,7 @@ jobs: - name: Check if Dockerfile changed id: dockerfile-changed - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: ui/Dockerfile @@ -98,7 +98,7 @@ jobs: - name: Check for UI changes id: check-changes - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: ui/** files_ignore: | @@ -108,11 +108,11 @@ jobs: - name: Set up Docker Buildx if: steps.check-changes.outputs.any_changed == 'true' - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - name: Build UI container for ${{ matrix.arch }} if: steps.check-changes.outputs.any_changed == 'true' - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2 + uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 with: context: ${{ env.UI_WORKING_DIR }} target: prod diff --git a/.github/workflows/ui-e2e-tests-v2.yml b/.github/workflows/ui-e2e-tests-v2.yml index 53533474fa..a83073b16d 100644 --- a/.github/workflows/ui-e2e-tests-v2.yml +++ b/.github/workflows/ui-e2e-tests-v2.yml @@ -158,12 +158,12 @@ jobs: ' - name: Setup Node.js - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: '24.13.0' - name: Setup pnpm - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0 with: package_json_file: ui/package.json run_install: false @@ -172,7 +172,7 @@ jobs: run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - name: Setup pnpm and Next.js cache - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: path: | ${{ env.STORE_PATH }} @@ -192,7 +192,7 @@ jobs: run: pnpm run build - name: Cache Playwright browsers - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: playwright-cache with: path: ~/.cache/ms-playwright @@ -259,7 +259,7 @@ jobs: fi - name: Upload test reports - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 if: failure() with: name: playwright-report diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index 57cb149523..631af22615 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -49,7 +49,7 @@ jobs: - name: Check for UI changes id: check-changes - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | ui/** @@ -62,7 +62,7 @@ jobs: - name: Get changed source files for targeted tests id: changed-source if: steps.check-changes.outputs.any_changed == 'true' - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | ui/**/*.ts @@ -78,7 +78,7 @@ jobs: - name: Check for critical path changes (run all tests) id: critical-changes if: steps.check-changes.outputs.any_changed == 'true' - uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5 with: files: | ui/lib/** @@ -90,13 +90,13 @@ jobs: - name: Setup Node.js ${{ env.NODE_VERSION }} if: steps.check-changes.outputs.any_changed == 'true' - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: ${{ env.NODE_VERSION }} - name: Setup pnpm if: steps.check-changes.outputs.any_changed == 'true' - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 + uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0 with: package_json_file: ui/package.json run_install: false @@ -108,7 +108,7 @@ jobs: - name: Setup pnpm and Next.js cache if: steps.check-changes.outputs.any_changed == 'true' - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: path: | ${{ env.STORE_PATH }} diff --git a/README.md b/README.md index 90003e7055..79b336bfbd 100644 --- a/README.md +++ b/README.md @@ -317,7 +317,10 @@ python prowler-cli.py -v - **Prowler SDK**: A Python SDK designed to extend the functionality of the Prowler CLI for advanced capabilities. - **Prowler MCP Server**: A Model Context Protocol server that provides AI tools for Lighthouse, the AI-powered security assistant. This is a critical dependency for Lighthouse functionality. -![Prowler App Architecture](docs/products/img/prowler-app-architecture.png) +![Prowler App Architecture](docs/images/products/prowler-app-architecture.png) + + + ## Prowler CLI diff --git a/api/CHANGELOG.md b/api/CHANGELOG.md index 4704532d22..0fbb745d3c 100644 --- a/api/CHANGELOG.md +++ b/api/CHANGELOG.md @@ -10,14 +10,19 @@ All notable changes to the **Prowler API** are documented in this file. - Filter RBAC role lookup by `tenant_id` to prevent cross-tenant privilege leak [(#10491)](https://github.com/prowler-cloud/prowler/pull/10491) - `VALKEY_SCHEME`, `VALKEY_USERNAME`, and `VALKEY_PASSWORD` environment variables to configure Celery broker TLS/auth connection details for Valkey/ElastiCache [(#10420)](https://github.com/prowler-cloud/prowler/pull/10420) - `Vercel` provider support [(#10190)](https://github.com/prowler-cloud/prowler/pull/10190) +- Finding groups list and latest endpoints support `sort=delta`, ordering by `new_count` then `changed_count` so groups with the most new findings rank highest [(#10606)](https://github.com/prowler-cloud/prowler/pull/10606) +- Finding group resources endpoints (`/finding-groups/{check_id}/resources` and `/finding-groups/latest/{check_id}/resources`) now expose `finding_id` per row, pointing to the most recent matching Finding for each resource. UUIDv7 ordering guarantees `Max(finding__id)` resolves to the latest snapshot [(#10630)](https://github.com/prowler-cloud/prowler/pull/10630) +- Handle CIS and CISA SCuBA compliance framework from google workspace [(#10629)](https://github.com/prowler-cloud/prowler/pull/10629) ### 🔄 Changed +- Finding groups list/latest/resources now expose `status` ∈ `{FAIL, PASS, MANUAL}` and `muted: bool` as orthogonal fields. The aggregated `status` reflects the underlying check outcome regardless of mute state, and `muted=true` signals that every finding in the group/resource is muted. New `manual_count` is exposed alongside `pass_count`/`fail_count`, plus `pass_muted_count`/`fail_muted_count`/`manual_muted_count` siblings so clients can isolate the muted half of each status. The `new_*`/`changed_*` deltas are now broken down by status and mute state via 12 new counters (`new_fail_count`, `new_fail_muted_count`, `new_pass_count`, `new_pass_muted_count`, `new_manual_count`, `new_manual_muted_count` and the matching `changed_*` set). New `filter[muted]=true|false` and `sort=status` (FAIL > PASS > MANUAL) / `sort=muted` are supported. `filter[status]=MUTED` is no longer accepted [(#10630)](https://github.com/prowler-cloud/prowler/pull/10630) - Attack Paths: Periodic cleanup of stale scans with dead-worker detection via Celery inspect, marking orphaned `EXECUTING` scans as `FAILED` and recovering `graph_data_ready` [(#10387)](https://github.com/prowler-cloud/prowler/pull/10387) - Attack Paths: Replace `_provider_id` property with `_Provider_{uuid}` label for provider isolation, add regex-based label injection for custom queries [(#10402)](https://github.com/prowler-cloud/prowler/pull/10402) ### 🐞 Fixed +- `reaggregate_all_finding_group_summaries_task` now refreshes finding group daily summaries for every `(provider, day)` combination instead of only the latest scan per provider, matching the unbounded scope of `mute_historical_findings_task`. Mute rule operations no longer leave older daily summaries drifting from the underlying muted findings [(#10630)](https://github.com/prowler-cloud/prowler/pull/10630) - Finding groups list/latest now apply computed status/severity filters and finding-level prefilters (delta, region, service, category, resource group, scan, resource type), plus `check_title` support for sort/filter consistency [(#10428)](https://github.com/prowler-cloud/prowler/pull/10428) - Populate compliance data inside `check_metadata` for findings, which was always returned as `null` [(#10449)](https://github.com/prowler-cloud/prowler/pull/10449) - 403 error for admin users listing tenants due to roles query not using the admin database connection [(#10460)](https://github.com/prowler-cloud/prowler/pull/10460) @@ -35,6 +40,7 @@ All notable changes to the **Prowler API** are documented in this file. - Pin all unpinned dependencies to exact versions to prevent supply chain attacks and ensure reproducible builds [(#10469)](https://github.com/prowler-cloud/prowler/pull/10469) - `authlib` bumped from 1.6.6 to 1.6.9 to fix CVE-2026-28802 (JWT `alg: none` validation bypass) [(#10579)](https://github.com/prowler-cloud/prowler/pull/10579) +- `aiohttp` bumped from 3.13.3 to 3.13.5 to fix CVE-2026-34520 (the C parser accepted null bytes and control characters in response headers) [(#10538)](https://github.com/prowler-cloud/prowler/pull/10538) --- diff --git a/api/poetry.lock b/api/poetry.lock index 95f7cce24e..24fca80a26 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -103,132 +103,132 @@ files = [ [[package]] name = "aiohttp" -version = "3.13.3" +version = "3.13.5" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "aiohttp-3.13.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a372fd5afd301b3a89582817fdcdb6c34124787c70dbcc616f259013e7eef7"}, - {file = "aiohttp-3.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:147e422fd1223005c22b4fe080f5d93ced44460f5f9c105406b753612b587821"}, - {file = "aiohttp-3.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859bd3f2156e81dd01432f5849fc73e2243d4a487c4fd26609b1299534ee1845"}, - {file = "aiohttp-3.13.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dca68018bf48c251ba17c72ed479f4dafe9dbd5a73707ad8d28a38d11f3d42af"}, - {file = "aiohttp-3.13.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fee0c6bc7db1de362252affec009707a17478a00ec69f797d23ca256e36d5940"}, - {file = "aiohttp-3.13.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c048058117fd649334d81b4b526e94bde3ccaddb20463a815ced6ecbb7d11160"}, - {file = "aiohttp-3.13.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:215a685b6fbbfcf71dfe96e3eba7a6f58f10da1dfdf4889c7dd856abe430dca7"}, - {file = "aiohttp-3.13.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2c184bb1fe2cbd2cefba613e9db29a5ab559323f994b6737e370d3da0ac455"}, - {file = "aiohttp-3.13.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75ca857eba4e20ce9f546cd59c7007b33906a4cd48f2ff6ccf1ccfc3b646f279"}, - {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81e97251d9298386c2b7dbeb490d3d1badbdc69107fb8c9299dd04eb39bddc0e"}, - {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0e2d366af265797506f0283487223146af57815b388623f0357ef7eac9b209d"}, - {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4e239d501f73d6db1522599e14b9b321a7e3b1de66ce33d53a765d975e9f4808"}, - {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0db318f7a6f065d84cb1e02662c526294450b314a02bd9e2a8e67f0d8564ce40"}, - {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bfc1cc2fe31a6026a8a88e4ecfb98d7f6b1fec150cfd708adbfd1d2f42257c29"}, - {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af71fff7bac6bb7508956696dce8f6eec2bbb045eceb40343944b1ae62b5ef11"}, - {file = "aiohttp-3.13.3-cp310-cp310-win32.whl", hash = "sha256:37da61e244d1749798c151421602884db5270faf479cf0ef03af0ff68954c9dd"}, - {file = "aiohttp-3.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:7e63f210bc1b57ef699035f2b4b6d9ce096b5914414a49b0997c839b2bd2223c"}, - {file = "aiohttp-3.13.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b6073099fb654e0a068ae678b10feff95c5cae95bbfcbfa7af669d361a8aa6b"}, - {file = "aiohttp-3.13.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cb93e166e6c28716c8c6aeb5f99dfb6d5ccf482d29fe9bf9a794110e6d0ab64"}, - {file = "aiohttp-3.13.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28e027cf2f6b641693a09f631759b4d9ce9165099d2b5d92af9bd4e197690eea"}, - {file = "aiohttp-3.13.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b61b7169ababd7802f9568ed96142616a9118dd2be0d1866e920e77ec8fa92a"}, - {file = "aiohttp-3.13.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:80dd4c21b0f6237676449c6baaa1039abae86b91636b6c91a7f8e61c87f89540"}, - {file = "aiohttp-3.13.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65d2ccb7eabee90ce0503c17716fc77226be026dcc3e65cce859a30db715025b"}, - {file = "aiohttp-3.13.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b179331a481cb5529fca8b432d8d3c7001cb217513c94cd72d668d1248688a3"}, - {file = "aiohttp-3.13.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d4c940f02f49483b18b079d1c27ab948721852b281f8b015c058100e9421dd1"}, - {file = "aiohttp-3.13.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9444f105664c4ce47a2a7171a2418bce5b7bae45fb610f4e2c36045d85911d3"}, - {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:694976222c711d1d00ba131904beb60534f93966562f64440d0c9d41b8cdb440"}, - {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f33ed1a2bf1997a36661874b017f5c4b760f41266341af36febaf271d179f6d7"}, - {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e636b3c5f61da31a92bf0d91da83e58fdfa96f178ba682f11d24f31944cdd28c"}, - {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5d2d94f1f5fcbe40838ac51a6ab5704a6f9ea42e72ceda48de5e6b898521da51"}, - {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2be0e9ccf23e8a94f6f0650ce06042cefc6ac703d0d7ab6c7a917289f2539ad4"}, - {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9af5e68ee47d6534d36791bbe9b646d2a7c7deb6fc24d7943628edfbb3581f29"}, - {file = "aiohttp-3.13.3-cp311-cp311-win32.whl", hash = "sha256:a2212ad43c0833a873d0fb3c63fa1bacedd4cf6af2fee62bf4b739ceec3ab239"}, - {file = "aiohttp-3.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:642f752c3eb117b105acbd87e2c143de710987e09860d674e068c4c2c441034f"}, - {file = "aiohttp-3.13.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c"}, - {file = "aiohttp-3.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168"}, - {file = "aiohttp-3.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d"}, - {file = "aiohttp-3.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29"}, - {file = "aiohttp-3.13.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3"}, - {file = "aiohttp-3.13.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d"}, - {file = "aiohttp-3.13.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463"}, - {file = "aiohttp-3.13.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc"}, - {file = "aiohttp-3.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf"}, - {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033"}, - {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f"}, - {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679"}, - {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423"}, - {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce"}, - {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a"}, - {file = "aiohttp-3.13.3-cp312-cp312-win32.whl", hash = "sha256:b04be762396457bef43f3597c991e192ee7da460a4953d7e647ee4b1c28e7046"}, - {file = "aiohttp-3.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:e3531d63d3bdfa7e3ac5e9b27b2dd7ec9df3206a98e0b3445fa906f233264c57"}, - {file = "aiohttp-3.13.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5dff64413671b0d3e7d5918ea490bdccb97a4ad29b3f311ed423200b2203e01c"}, - {file = "aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:87b9aab6d6ed88235aa2970294f496ff1a1f9adcd724d800e9b952395a80ffd9"}, - {file = "aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:425c126c0dc43861e22cb1c14ba4c8e45d09516d0a3ae0a3f7494b79f5f233a3"}, - {file = "aiohttp-3.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f9120f7093c2a32d9647abcaf21e6ad275b4fbec5b55969f978b1a97c7c86bf"}, - {file = "aiohttp-3.13.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:697753042d57f4bf7122cab985bf15d0cef23c770864580f5af4f52023a56bd6"}, - {file = "aiohttp-3.13.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6de499a1a44e7de70735d0b39f67c8f25eb3d91eb3103be99ca0fa882cdd987d"}, - {file = "aiohttp-3.13.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:37239e9f9a7ea9ac5bf6b92b0260b01f8a22281996da609206a84df860bc1261"}, - {file = "aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f76c1e3fe7d7c8afad7ed193f89a292e1999608170dcc9751a7462a87dfd5bc0"}, - {file = "aiohttp-3.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fc290605db2a917f6e81b0e1e0796469871f5af381ce15c604a3c5c7e51cb730"}, - {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4021b51936308aeea0367b8f006dc999ca02bc118a0cc78c303f50a2ff6afb91"}, - {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:49a03727c1bba9a97d3e93c9f93ca03a57300f484b6e935463099841261195d3"}, - {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3d9908a48eb7416dc1f4524e69f1d32e5d90e3981e4e37eb0aa1cd18f9cfa2a4"}, - {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2712039939ec963c237286113c68dbad80a82a4281543f3abf766d9d73228998"}, - {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7bfdc049127717581866fa4708791220970ce291c23e28ccf3922c700740fdc0"}, - {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8057c98e0c8472d8846b9c79f56766bcc57e3e8ac7bfd510482332366c56c591"}, - {file = "aiohttp-3.13.3-cp313-cp313-win32.whl", hash = "sha256:1449ceddcdbcf2e0446957863af03ebaaa03f94c090f945411b61269e2cb5daf"}, - {file = "aiohttp-3.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:693781c45a4033d31d4187d2436f5ac701e7bbfe5df40d917736108c1cc7436e"}, - {file = "aiohttp-3.13.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:ea37047c6b367fd4bd632bff8077449b8fa034b69e812a18e0132a00fae6e808"}, - {file = "aiohttp-3.13.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6fc0e2337d1a4c3e6acafda6a78a39d4c14caea625124817420abceed36e2415"}, - {file = "aiohttp-3.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c685f2d80bb67ca8c3837823ad76196b3694b0159d232206d1e461d3d434666f"}, - {file = "aiohttp-3.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e377758516d262bde50c2584fc6c578af272559c409eecbdd2bae1601184d6"}, - {file = "aiohttp-3.13.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:34749271508078b261c4abb1767d42b8d0c0cc9449c73a4df494777dc55f0687"}, - {file = "aiohttp-3.13.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82611aeec80eb144416956ec85b6ca45a64d76429c1ed46ae1b5f86c6e0c9a26"}, - {file = "aiohttp-3.13.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2fff83cfc93f18f215896e3a190e8e5cb413ce01553901aca925176e7568963a"}, - {file = "aiohttp-3.13.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbe7d4cecacb439e2e2a8a1a7b935c25b812af7a5fd26503a66dadf428e79ec1"}, - {file = "aiohttp-3.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b928f30fe49574253644b1ca44b1b8adbd903aa0da4b9054a6c20fc7f4092a25"}, - {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b5e8fe4de30df199155baaf64f2fcd604f4c678ed20910db8e2c66dc4b11603"}, - {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:8542f41a62bcc58fc7f11cf7c90e0ec324ce44950003feb70640fc2a9092c32a"}, - {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5e1d8c8b8f1d91cd08d8f4a3c2b067bfca6ec043d3ff36de0f3a715feeedf926"}, - {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:90455115e5da1c3c51ab619ac57f877da8fd6d73c05aacd125c5ae9819582aba"}, - {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:042e9e0bcb5fba81886c8b4fbb9a09d6b8a00245fd8d88e4d989c1f96c74164c"}, - {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2eb752b102b12a76ca02dff751a801f028b4ffbbc478840b473597fc91a9ed43"}, - {file = "aiohttp-3.13.3-cp314-cp314-win32.whl", hash = "sha256:b556c85915d8efaed322bf1bdae9486aa0f3f764195a0fb6ee962e5c71ef5ce1"}, - {file = "aiohttp-3.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9bf9f7a65e7aa20dd764151fb3d616c81088f91f8df39c3893a536e279b4b984"}, - {file = "aiohttp-3.13.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:05861afbbec40650d8a07ea324367cb93e9e8cc7762e04dd4405df99fa65159c"}, - {file = "aiohttp-3.13.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2fc82186fadc4a8316768d61f3722c230e2c1dcab4200d52d2ebdf2482e47592"}, - {file = "aiohttp-3.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0add0900ff220d1d5c5ebbf99ed88b0c1bbf87aa7e4262300ed1376a6b13414f"}, - {file = "aiohttp-3.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:568f416a4072fbfae453dcf9a99194bbb8bdeab718e08ee13dfa2ba0e4bebf29"}, - {file = "aiohttp-3.13.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:add1da70de90a2569c5e15249ff76a631ccacfe198375eead4aadf3b8dc849dc"}, - {file = "aiohttp-3.13.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b47b7ba335d2e9b1239fa571131a87e2d8ec96b333e68b2a305e7a98b0bae2"}, - {file = "aiohttp-3.13.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4dce1c718e38081c8f35f323209d4c1df7d4db4bab1b5c88a6b4d12b74587"}, - {file = "aiohttp-3.13.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34bac00a67a812570d4a460447e1e9e06fae622946955f939051e7cc895cfab8"}, - {file = "aiohttp-3.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19884d2ee70b06d9204b2727a7b9f983d0c684c650254679e716b0b77920632"}, - {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ca7f2bb6ba8348a3614c7918cc4bb73268c5ac2a207576b7afea19d3d9f64"}, - {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b0d95340658b9d2f11d9697f59b3814a9d3bb4b7a7c20b131df4bcef464037c0"}, - {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1e53262fd202e4b40b70c3aff944a8155059beedc8a89bba9dc1f9ef06a1b56"}, - {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d60ac9663f44168038586cab2157e122e46bdef09e9368b37f2d82d354c23f72"}, - {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:90751b8eed69435bac9ff4e3d2f6b3af1f57e37ecb0fbeee59c0174c9e2d41df"}, - {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fc353029f176fd2b3ec6cfc71be166aba1936fe5d73dd1992ce289ca6647a9aa"}, - {file = "aiohttp-3.13.3-cp314-cp314t-win32.whl", hash = "sha256:2e41b18a58da1e474a057b3d35248d8320029f61d70a37629535b16a0c8f3767"}, - {file = "aiohttp-3.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344"}, - {file = "aiohttp-3.13.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:31a83ea4aead760dfcb6962efb1d861db48c34379f2ff72db9ddddd4cda9ea2e"}, - {file = "aiohttp-3.13.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:988a8c5e317544fdf0d39871559e67b6341065b87fceac641108c2096d5506b7"}, - {file = "aiohttp-3.13.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b174f267b5cfb9a7dba9ee6859cecd234e9a681841eb85068059bc867fb8f02"}, - {file = "aiohttp-3.13.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:947c26539750deeaee933b000fb6517cc770bbd064bad6033f1cff4803881e43"}, - {file = "aiohttp-3.13.3-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9ebf57d09e131f5323464bd347135a88622d1c0976e88ce15b670e7ad57e4bd6"}, - {file = "aiohttp-3.13.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4ae5b5a0e1926e504c81c5b84353e7a5516d8778fbbff00429fe7b05bb25cbce"}, - {file = "aiohttp-3.13.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2ba0eea45eb5cc3172dbfc497c066f19c41bac70963ea1a67d51fc92e4cf9a80"}, - {file = "aiohttp-3.13.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bae5c2ed2eae26cc382020edad80d01f36cb8e746da40b292e68fec40421dc6a"}, - {file = "aiohttp-3.13.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8a60e60746623925eab7d25823329941aee7242d559baa119ca2b253c88a7bd6"}, - {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e50a2e1404f063427c9d027378472316201a2290959a295169bcf25992d04558"}, - {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:9a9dc347e5a3dc7dfdbc1f82da0ef29e388ddb2ed281bfce9dd8248a313e62b7"}, - {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b46020d11d23fe16551466c77823df9cc2f2c1e63cc965daf67fa5eec6ca1877"}, - {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:69c56fbc1993fa17043e24a546959c0178fe2b5782405ad4559e6c13975c15e3"}, - {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b99281b0704c103d4e11e72a76f1b543d4946fea7dd10767e7e1b5f00d4e5704"}, - {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:40c5e40ecc29ba010656c18052b877a1c28f84344825efa106705e835c28530f"}, - {file = "aiohttp-3.13.3-cp39-cp39-win32.whl", hash = "sha256:56339a36b9f1fc708260c76c87e593e2afb30d26de9ae1eb445b5e051b98a7a1"}, - {file = "aiohttp-3.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:c6b8568a3bb5819a0ad087f16d40e5a3fb6099f39ea1d5625a3edc1e923fc538"}, - {file = "aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88"}, + {file = "aiohttp-3.13.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:02222e7e233295f40e011c1b00e3b0bd451f22cf853a0304c3595633ee47da4b"}, + {file = "aiohttp-3.13.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bace460460ed20614fa6bc8cb09966c0b8517b8c58ad8046828c6078d25333b5"}, + {file = "aiohttp-3.13.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f546a4dc1e6a5edbb9fd1fd6ad18134550e096a5a43f4ad74acfbd834fc6670"}, + {file = "aiohttp-3.13.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c86969d012e51b8e415a8c6ce96f7857d6a87d6207303ab02d5d11ef0cad2274"}, + {file = "aiohttp-3.13.5-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b6f6cd1560c5fa427e3b6074bb24d2c64e225afbb7165008903bd42e4e33e28a"}, + {file = "aiohttp-3.13.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:636bc362f0c5bbc7372bc3ae49737f9e3030dbce469f0f422c8f38079780363d"}, + {file = "aiohttp-3.13.5-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6a7cbeb06d1070f1d14895eeeed4dac5913b22d7b456f2eb969f11f4b3993796"}, + {file = "aiohttp-3.13.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca9ef7517fd7874a1a08970ae88f497bf5c984610caa0bf40bd7e8450852b95"}, + {file = "aiohttp-3.13.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:019a67772e034a0e6b9b17c13d0a8fe56ad9fb150fc724b7f3ffd3724288d9e5"}, + {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f34ecee82858e41dd217734f0c41a532bd066bcaab636ad830f03a30b2a96f2a"}, + {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4eac02d9af4813ee289cd63a361576da36dba57f5a1ab36377bc2600db0cbb73"}, + {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4beac52e9fe46d6abf98b0176a88154b742e878fdf209d2248e99fcdf73cd297"}, + {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:c180f480207a9b2475f2b8d8bd7204e47aec952d084b2a2be58a782ffcf96074"}, + {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2837fb92951564d6339cedae4a7231692aa9f73cbc4fb2e04263b96844e03b4e"}, + {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9010032a0b9710f58012a1e9c222528763d860ba2ee1422c03473eab47703e7"}, + {file = "aiohttp-3.13.5-cp310-cp310-win32.whl", hash = "sha256:7c4b6668b2b2b9027f209ddf647f2a4407784b5d88b8be4efcc72036f365baf9"}, + {file = "aiohttp-3.13.5-cp310-cp310-win_amd64.whl", hash = "sha256:cd3db5927bf9167d5a6157ddb2f036f6b6b0ad001ac82355d43e97a4bde76d76"}, + {file = "aiohttp-3.13.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ab7229b6f9b5c1ba4910d6c41a9eb11f543eadb3f384df1b4c293f4e73d44d6"}, + {file = "aiohttp-3.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f14c50708bb156b3a3ca7230b3d820199d56a48e3af76fa21c2d6087190fe3d"}, + {file = "aiohttp-3.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7d2f8616f0ff60bd332022279011776c3ac0faa0f1b463f7bb12326fbc97a1c"}, + {file = "aiohttp-3.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2567b72e1ffc3ab25510db43f355b29eeada56c0a622e58dcdb19530eb0a3cb"}, + {file = "aiohttp-3.13.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fb0540c854ac9c0c5ad495908fdfd3e332d553ec731698c0e29b1877ba0d2ec6"}, + {file = "aiohttp-3.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9883051c6972f58bfc4ebb2116345ee2aa151178e99c3f2b2bbe2af712abd13"}, + {file = "aiohttp-3.13.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2294172ce08a82fb7c7273485895de1fa1186cc8294cfeb6aef4af42ad261174"}, + {file = "aiohttp-3.13.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a807cabd5115fb55af198b98178997a5e0e57dead43eb74a93d9c07d6d4a7dc"}, + {file = "aiohttp-3.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa6d0d932e0f39c02b80744273cd5c388a2d9bc07760a03164f229c8e02662f6"}, + {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60869c7ac4aaabe7110f26499f3e6e5696eae98144735b12a9c3d9eae2b51a49"}, + {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:26d2f8546f1dfa75efa50c3488215a903c0168d253b75fba4210f57ab77a0fb8"}, + {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1162a1492032c82f14271e831c8f4b49f2b6078f4f5fc74de2c912fa225d51d"}, + {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8b14eb3262fad0dc2f89c1a43b13727e709504972186ff6a99a3ecaa77102b6c"}, + {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ca9ac61ac6db4eb6c2a0cd1d0f7e1357647b638ccc92f7e9d8d133e71ed3c6ac"}, + {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7996023b2ed59489ae4762256c8516df9820f751cf2c5da8ed2fb20ee50abab3"}, + {file = "aiohttp-3.13.5-cp311-cp311-win32.whl", hash = "sha256:77dfa48c9f8013271011e51c00f8ada19851f013cde2c48fca1ba5e0caf5bb06"}, + {file = "aiohttp-3.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:d3a4834f221061624b8887090637db9ad4f61752001eae37d56c52fddade2dc8"}, + {file = "aiohttp-3.13.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:023ecba036ddd840b0b19bf195bfae970083fd7024ce1ac22e9bba90464620e9"}, + {file = "aiohttp-3.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15c933ad7920b7d9a20de151efcd05a6e38302cbf0e10c9b2acb9a42210a2416"}, + {file = "aiohttp-3.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab2899f9fa2f9f741896ebb6fa07c4c883bfa5c7f2ddd8cf2aafa86fa981b2d2"}, + {file = "aiohttp-3.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60eaa2d440cd4707696b52e40ed3e2b0f73f65be07fd0ef23b6b539c9c0b0b4"}, + {file = "aiohttp-3.13.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b3bdd3292283295774ab585160c4004f4f2f203946997f49aac032c84649e9"}, + {file = "aiohttp-3.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2b2355dc094e5f7d45a7bb262fe7207aa0460b37a0d87027dcf21b5d890e7d5"}, + {file = "aiohttp-3.13.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b38765950832f7d728297689ad78f5f2cf79ff82487131c4d26fe6ceecdc5f8e"}, + {file = "aiohttp-3.13.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b18f31b80d5a33661e08c89e202edabf1986e9b49c42b4504371daeaa11b47c1"}, + {file = "aiohttp-3.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33add2463dde55c4f2d9635c6ab33ce154e5ecf322bd26d09af95c5f81cfa286"}, + {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:327cc432fdf1356fb4fbc6fe833ad4e9f6aacb71a8acaa5f1855e4b25910e4a9"}, + {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7c35b0bf0b48a70b4cb4fc5d7bed9b932532728e124874355de1a0af8ec4bc88"}, + {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:df23d57718f24badef8656c49743e11a89fd6f5358fa8a7b96e728fda2abf7d3"}, + {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:02e048037a6501a5ec1f6fc9736135aec6eb8a004ce48838cb951c515f32c80b"}, + {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31cebae8b26f8a615d2b546fee45d5ffb76852ae6450e2a03f42c9102260d6fe"}, + {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:888e78eb5ca55a615d285c3c09a7a91b42e9dd6fc699b166ebd5dee87c9ccf14"}, + {file = "aiohttp-3.13.5-cp312-cp312-win32.whl", hash = "sha256:8bd3ec6376e68a41f9f95f5ed170e2fcf22d4eb27a1f8cb361d0508f6e0557f3"}, + {file = "aiohttp-3.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:110e448e02c729bcebb18c60b9214a87ba33bac4a9fa5e9a5f139938b56c6cb1"}, + {file = "aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61"}, + {file = "aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832"}, + {file = "aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9"}, + {file = "aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090"}, + {file = "aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b"}, + {file = "aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a"}, + {file = "aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8"}, + {file = "aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665"}, + {file = "aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540"}, + {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb"}, + {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46"}, + {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8"}, + {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d"}, + {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6"}, + {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c"}, + {file = "aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc"}, + {file = "aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83"}, + {file = "aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c"}, + {file = "aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be"}, + {file = "aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25"}, + {file = "aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56"}, + {file = "aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2"}, + {file = "aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a"}, + {file = "aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be"}, + {file = "aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b"}, + {file = "aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94"}, + {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d"}, + {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7"}, + {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772"}, + {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5"}, + {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1"}, + {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b"}, + {file = "aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3"}, + {file = "aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162"}, + {file = "aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a"}, + {file = "aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254"}, + {file = "aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36"}, + {file = "aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f"}, + {file = "aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800"}, + {file = "aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf"}, + {file = "aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b"}, + {file = "aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a"}, + {file = "aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8"}, + {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be"}, + {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b"}, + {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6"}, + {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037"}, + {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500"}, + {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9"}, + {file = "aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8"}, + {file = "aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9"}, + {file = "aiohttp-3.13.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:347542f0ea3f95b2a955ee6656461fa1c776e401ac50ebce055a6c38454a0adf"}, + {file = "aiohttp-3.13.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:178c7b5e62b454c2bc790786e6058c3cc968613b4419251b478c153a4aec32b1"}, + {file = "aiohttp-3.13.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af545c2cffdb0967a96b6249e6f5f7b0d92cdfd267f9d5238d5b9ca63e8edb10"}, + {file = "aiohttp-3.13.5-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:206b7b3ef96e4ce211754f0cd003feb28b7d81f0ad26b8d077a5d5161436067f"}, + {file = "aiohttp-3.13.5-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ee5e86776273de1795947d17bddd6bb19e0365fd2af4289c0d2c5454b6b1d36b"}, + {file = "aiohttp-3.13.5-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:95d14ca7abefde230f7639ec136ade282655431fd5db03c343b19dda72dd1643"}, + {file = "aiohttp-3.13.5-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:912d4b6af530ddb1338a66229dac3a25ff11d4448be3ec3d6340583995f56031"}, + {file = "aiohttp-3.13.5-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e999f0c88a458c836d5fb521814e92ed2172c649200336a6df514987c1488258"}, + {file = "aiohttp-3.13.5-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39380e12bd1f2fdab4285b6e055ad48efbaed5c836433b142ed4f5b9be71036a"}, + {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9efcc0f11d850cefcafdd9275b9576ad3bfb539bed96807663b32ad99c4d4b88"}, + {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:147b4f501d0292077f29d5268c16bb7c864a1f054d7001c4c1812c0421ea1ed0"}, + {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d147004fede1b12f6013a6dbb2a26a986a671a03c6ea740ddc76500e5f1c399f"}, + {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:9277145d36a01653863899c665243871434694bcc3431922c3b35c978061bdb8"}, + {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4e704c52438f66fdd89588346183d898bb42167cf88f8b7ff1c0f9fc957c348f"}, + {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a8a4d3427e8de1312ddf309cc482186466c79895b3a139fed3259fc01dfa9a5b"}, + {file = "aiohttp-3.13.5-cp39-cp39-win32.whl", hash = "sha256:6f497a6876aa4b1a102b04996ce4c1170c7040d83faa9387dd921c16e30d5c83"}, + {file = "aiohttp-3.13.5-cp39-cp39-win_amd64.whl", hash = "sha256:cb979826071c0986a5f08333a36104153478ce6018c58cba7f9caddaf63d5d67"}, + {file = "aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1"}, ] [package.dependencies] diff --git a/api/src/backend/api/filters.py b/api/src/backend/api/filters.py index a496a0bf67..fa31289964 100644 --- a/api/src/backend/api/filters.py +++ b/api/src/backend/api/filters.py @@ -1115,13 +1115,14 @@ class FindingGroupAggregatedComputedFilter(FilterSet): STATUS_CHOICES = ( ("FAIL", "Fail"), ("PASS", "Pass"), - ("MUTED", "Muted"), + ("MANUAL", "Manual"), ) status = ChoiceFilter(method="filter_status", choices=STATUS_CHOICES) status__in = CharInFilter(method="filter_status_in", lookup_expr="in") severity = ChoiceFilter(method="filter_severity", choices=SeverityChoices) severity__in = CharInFilter(method="filter_severity_in", lookup_expr="in") + muted = BooleanFilter(field_name="muted") include_muted = BooleanFilter(method="filter_include_muted") def filter_status(self, queryset, name, value): @@ -1198,7 +1199,7 @@ class FindingGroupAggregatedComputedFilter(FilterSet): if value is True: return queryset # include_muted=false: exclude fully-muted groups - return queryset.exclude(fail_count=0, pass_count=0, muted_count__gt=0) + return queryset.exclude(muted=True) class ProviderSecretFilter(FilterSet): diff --git a/api/src/backend/api/migrations/0088_finding_group_status_muted_fields.py b/api/src/backend/api/migrations/0088_finding_group_status_muted_fields.py new file mode 100644 index 0000000000..ff3b981435 --- /dev/null +++ b/api/src/backend/api/migrations/0088_finding_group_status_muted_fields.py @@ -0,0 +1,95 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("api", "0087_vercel_provider"), + ] + + operations = [ + migrations.AddField( + model_name="findinggroupdailysummary", + name="manual_count", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="findinggroupdailysummary", + name="pass_muted_count", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="findinggroupdailysummary", + name="fail_muted_count", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="findinggroupdailysummary", + name="manual_muted_count", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="findinggroupdailysummary", + name="muted", + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name="findinggroupdailysummary", + name="new_fail_count", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="findinggroupdailysummary", + name="new_fail_muted_count", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="findinggroupdailysummary", + name="new_pass_count", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="findinggroupdailysummary", + name="new_pass_muted_count", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="findinggroupdailysummary", + name="new_manual_count", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="findinggroupdailysummary", + name="new_manual_muted_count", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="findinggroupdailysummary", + name="changed_fail_count", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="findinggroupdailysummary", + name="changed_fail_muted_count", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="findinggroupdailysummary", + name="changed_pass_count", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="findinggroupdailysummary", + name="changed_pass_muted_count", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="findinggroupdailysummary", + name="changed_manual_count", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="findinggroupdailysummary", + name="changed_manual_muted_count", + field=models.IntegerField(default=0), + ), + ] diff --git a/api/src/backend/api/migrations/0089_backfill_finding_group_status_muted.py b/api/src/backend/api/migrations/0089_backfill_finding_group_status_muted.py new file mode 100644 index 0000000000..501fcf3cb4 --- /dev/null +++ b/api/src/backend/api/migrations/0089_backfill_finding_group_status_muted.py @@ -0,0 +1,31 @@ +from django.db import migrations +from tasks.tasks import backfill_finding_group_summaries_task + +from api.db_router import MainRouter +from api.rls import Tenant + + +def trigger_backfill_task(apps, schema_editor): + """ + Re-dispatch the finding-group backfill task for every tenant so the new + `manual_count` and `muted` columns added in 0088 get populated from the + last 10 days of completed scans. + + The aggregator (`aggregate_finding_group_summaries`) recomputes every + column on each call, so it back-populates the new fields without touching + the existing ones beyond a normal upsert. + """ + tenant_ids = Tenant.objects.using(MainRouter.admin_db).values_list("id", flat=True) + + for tenant_id in tenant_ids: + backfill_finding_group_summaries_task.delay(tenant_id=str(tenant_id), days=10) + + +class Migration(migrations.Migration): + dependencies = [ + ("api", "0088_finding_group_status_muted_fields"), + ] + + operations = [ + migrations.RunPython(trigger_backfill_task, migrations.RunPython.noop), + ] diff --git a/api/src/backend/api/models.py b/api/src/backend/api/models.py index 5e0880be08..7f3131fe7b 100644 --- a/api/src/backend/api/models.py +++ b/api/src/backend/api/models.py @@ -1748,15 +1748,45 @@ class FindingGroupDailySummary(RowLevelSecurityProtectedModel): # Severity stored as integer for MAX aggregation (5=critical, 4=high, etc.) severity_order = models.SmallIntegerField(default=1) - # Finding counts + # Finding counts (inclusive of muted findings; use the `muted` flag to + # tell whether the group has any actionable findings). pass_count = models.IntegerField(default=0) fail_count = models.IntegerField(default=0) + manual_count = models.IntegerField(default=0) muted_count = models.IntegerField(default=0) - # Delta counts + # Status counts restricted to muted findings, so clients can isolate the + # muted half of each status (e.g. `pass_count - pass_muted_count` gives the + # actionable PASS findings). + pass_muted_count = models.IntegerField(default=0) + fail_muted_count = models.IntegerField(default=0) + manual_muted_count = models.IntegerField(default=0) + + # Whether every finding for this (provider, check, day) is muted. + muted = models.BooleanField(default=False) + + # Delta counts (non-muted, kept for convenience and as a "total" view). new_count = models.IntegerField(default=0) changed_count = models.IntegerField(default=0) + # Delta breakdown by (status, muted) so clients can answer questions like + # "how many new failing findings appeared in this scan?" without scanning + # the underlying findings table. Mirrors the existing pass/fail/manual + # naming, with `_muted_count` siblings tracking the muted half of each + # bucket explicitly. + new_fail_count = models.IntegerField(default=0) + new_fail_muted_count = models.IntegerField(default=0) + new_pass_count = models.IntegerField(default=0) + new_pass_muted_count = models.IntegerField(default=0) + new_manual_count = models.IntegerField(default=0) + new_manual_muted_count = models.IntegerField(default=0) + changed_fail_count = models.IntegerField(default=0) + changed_fail_muted_count = models.IntegerField(default=0) + changed_pass_count = models.IntegerField(default=0) + changed_pass_muted_count = models.IntegerField(default=0) + changed_manual_count = models.IntegerField(default=0) + changed_manual_muted_count = models.IntegerField(default=0) + # Resource counts resources_fail = models.IntegerField(default=0) resources_total = models.IntegerField(default=0) diff --git a/api/src/backend/api/tests/test_views.py b/api/src/backend/api/tests/test_views.py index d30e786e5f..7457f20f4d 100644 --- a/api/src/backend/api/tests/test_views.py +++ b/api/src/backend/api/tests/test_views.py @@ -15445,10 +15445,16 @@ class TestFindingGroupViewSet: # iam_password_policy has only PASS findings assert data[0]["attributes"]["status"] == "PASS" - def test_finding_groups_status_muted_all( + def test_finding_groups_fully_muted_group_reflects_underlying_status( self, authenticated_client, finding_groups_fixture ): - """Test that MUTED status returned when all findings are muted.""" + """A fully-muted group still surfaces its underlying status (no MUTED). + + rds_encryption has 2 muted FAIL findings, so the group must report + status=FAIL (the orthogonal `muted` boolean signals it isn't actionable). + The status×muted breakdown lets clients answer 'how many failing + findings are muted in this group'. + """ response = authenticated_client.get( reverse("finding-group-list"), {"filter[inserted_at]": TODAY, "filter[check_id]": "rds_encryption"}, @@ -15456,8 +15462,21 @@ class TestFindingGroupViewSet: assert response.status_code == status.HTTP_200_OK data = response.json()["data"] assert len(data) == 1 - # rds_encryption has all muted findings - assert data[0]["attributes"]["status"] == "MUTED" + attrs = data[0]["attributes"] + assert attrs["status"] == "FAIL" + assert attrs["muted"] is True + assert attrs["fail_count"] == 2 + assert attrs["fail_muted_count"] == 2 + assert attrs["pass_muted_count"] == 0 + assert attrs["manual_muted_count"] == 0 + assert attrs["muted_count"] == 2 + # Sanity: the per-status muted counts must add up to muted_count. + assert ( + attrs["pass_muted_count"] + + attrs["fail_muted_count"] + + attrs["manual_muted_count"] + == attrs["muted_count"] + ) def test_finding_groups_status_filter( self, authenticated_client, finding_groups_fixture @@ -15949,7 +15968,7 @@ class TestFindingGroupViewSet: "extra_filters", [ {}, - {"filter[muted]": "include"}, + {"filter[delta]": "new"}, ], ids=["summary_path", "finding_level_path"], ) @@ -15967,7 +15986,8 @@ class TestFindingGroupViewSet: Parametrized to cover both aggregation paths: - summary_path: default, uses _CheckTitleToCheckIdMixin on summaries - - finding_level_path: filter[muted]=include forces CommonFindingFilters + - finding_level_path: filter[delta]=new forces _aggregate_findings via + CommonFindingFilters (delta is finding-level, not summary-level) """ params = { "filter[inserted_at]": TODAY, @@ -16839,6 +16859,39 @@ class TestFindingGroupViewSet: data = response.json()["data"] assert len(data) > 0 + @pytest.mark.parametrize( + "endpoint_name", ["finding-group-list", "finding-group-latest"] + ) + def test_finding_groups_sort_by_delta( + self, + authenticated_client, + finding_groups_fixture, + endpoint_name, + ): + """Sort by delta orders by new_count then changed_count (lexicographic).""" + params = {"sort": "-delta"} + if endpoint_name == "finding-group-list": + params["filter[inserted_at]"] = TODAY + + response = authenticated_client.get(reverse(endpoint_name), params) + assert response.status_code == status.HTTP_200_OK + data = response.json()["data"] + assert len(data) > 0 + + def delta_key(item): + attrs = item["attributes"] + return (attrs.get("new_count", 0), attrs.get("changed_count", 0)) + + desc_keys = [delta_key(item) for item in data] + assert desc_keys == sorted(desc_keys, reverse=True) + + # Ascending order produces the inverse arrangement + params["sort"] = "delta" + response = authenticated_client.get(reverse(endpoint_name), params) + assert response.status_code == status.HTTP_200_OK + asc_keys = [delta_key(item) for item in response.json()["data"]] + assert asc_keys == sorted(asc_keys) + def test_finding_groups_latest_ignores_date_filters( self, authenticated_client, finding_groups_fixture ): @@ -16852,3 +16905,287 @@ class TestFindingGroupViewSet: data = response.json()["data"] # Should still return data, not filtered by the old date assert len(data) == 5 + + def test_finding_groups_status_choices_no_muted( + self, authenticated_client, finding_groups_fixture + ): + """Every returned group must have status ∈ {FAIL, PASS, MANUAL}.""" + response = authenticated_client.get( + reverse("finding-group-list"), + {"filter[inserted_at]": TODAY}, + ) + assert response.status_code == status.HTTP_200_OK + statuses = {item["attributes"]["status"] for item in response.json()["data"]} + assert statuses, "fixture should produce at least one group" + assert statuses <= {"FAIL", "PASS", "MANUAL"} + assert "MUTED" not in statuses + + def test_finding_groups_serializer_exposes_muted_and_manual_count( + self, authenticated_client, finding_groups_fixture + ): + """The /finding-groups payload must expose `muted`, `manual_count` and + the per-status muted siblings (`pass_muted_count`/`fail_muted_count`/ + `manual_muted_count`).""" + response = authenticated_client.get( + reverse("finding-group-list"), + {"filter[inserted_at]": TODAY, "filter[check_id]": "iam_password_policy"}, + ) + assert response.status_code == status.HTTP_200_OK + attrs = response.json()["data"][0]["attributes"] + assert "muted" in attrs and isinstance(attrs["muted"], bool) + assert "manual_count" in attrs and isinstance(attrs["manual_count"], int) + assert attrs["muted"] is False # iam_password_policy has only non-muted PASS + assert attrs["manual_count"] == 0 + assert attrs["pass_muted_count"] == 0 + assert attrs["fail_muted_count"] == 0 + assert attrs["manual_muted_count"] == 0 + + @pytest.mark.parametrize( + "endpoint_name", ["finding-group-list", "finding-group-latest"] + ) + def test_finding_groups_filter_status_muted_is_rejected( + self, authenticated_client, finding_groups_fixture, endpoint_name + ): + """`filter[status]=MUTED` is no longer a valid status value.""" + params = {"filter[status]": "MUTED"} + if endpoint_name == "finding-group-list": + params["filter[inserted_at]"] = TODAY + + response = authenticated_client.get(reverse(endpoint_name), params) + assert response.status_code == status.HTTP_400_BAD_REQUEST + + @pytest.mark.parametrize( + "endpoint_name", ["finding-group-list", "finding-group-latest"] + ) + def test_finding_groups_filter_muted_true( + self, authenticated_client, finding_groups_fixture, endpoint_name + ): + """`filter[muted]=true` returns only fully-muted groups.""" + params = {"filter[muted]": "true"} + if endpoint_name == "finding-group-list": + params["filter[inserted_at]"] = TODAY + + response = authenticated_client.get(reverse(endpoint_name), params) + assert response.status_code == status.HTTP_200_OK + data = response.json()["data"] + check_ids = {item["id"] for item in data} + # Only rds_encryption is fully muted in the fixture + assert check_ids == {"rds_encryption"} + assert all(item["attributes"]["muted"] is True for item in data) + + @pytest.mark.parametrize( + "endpoint_name", ["finding-group-list", "finding-group-latest"] + ) + def test_finding_groups_filter_muted_false( + self, authenticated_client, finding_groups_fixture, endpoint_name + ): + """`filter[muted]=false` returns only groups with actionable findings.""" + params = {"filter[muted]": "false"} + if endpoint_name == "finding-group-list": + params["filter[inserted_at]"] = TODAY + + response = authenticated_client.get(reverse(endpoint_name), params) + assert response.status_code == status.HTTP_200_OK + data = response.json()["data"] + check_ids = {item["id"] for item in data} + assert "rds_encryption" not in check_ids + assert check_ids == { + "s3_bucket_public_access", + "ec2_instance_public_ip", + "iam_password_policy", + "cloudtrail_enabled", + } + assert all(item["attributes"]["muted"] is False for item in data) + + @pytest.mark.parametrize( + "endpoint_name", ["finding-group-list", "finding-group-latest"] + ) + def test_finding_groups_sort_by_status( + self, authenticated_client, finding_groups_fixture, endpoint_name + ): + """sort=status orders by aggregated status (FAIL > PASS > MANUAL).""" + priority = {"FAIL": 3, "PASS": 2, "MANUAL": 1} + params = {"sort": "-status"} + if endpoint_name == "finding-group-list": + params["filter[inserted_at]"] = TODAY + + response = authenticated_client.get(reverse(endpoint_name), params) + assert response.status_code == status.HTTP_200_OK + data = response.json()["data"] + assert data, "fixture should produce groups" + + desc_keys = [priority[item["attributes"]["status"]] for item in data] + assert desc_keys == sorted(desc_keys, reverse=True) + + params["sort"] = "status" + response = authenticated_client.get(reverse(endpoint_name), params) + assert response.status_code == status.HTTP_200_OK + asc_keys = [ + priority[item["attributes"]["status"]] for item in response.json()["data"] + ] + assert asc_keys == sorted(asc_keys) + + @pytest.mark.parametrize( + "endpoint_name", ["finding-group-list", "finding-group-latest"] + ) + def test_finding_groups_sort_by_muted( + self, authenticated_client, finding_groups_fixture, endpoint_name + ): + """sort=muted orders by the boolean muted attribute.""" + # Need include_muted=true so the fully-muted group is part of the result + params = {"sort": "-muted", "filter[include_muted]": "true"} + if endpoint_name == "finding-group-list": + params["filter[inserted_at]"] = TODAY + + response = authenticated_client.get(reverse(endpoint_name), params) + assert response.status_code == status.HTTP_200_OK + data = response.json()["data"] + assert data, "fixture should produce groups" + + muted_values = [item["attributes"]["muted"] for item in data] + # Descending boolean: True (1) before False (0) + assert muted_values == sorted(muted_values, reverse=True) + + @pytest.mark.parametrize( + "endpoint_name", ["finding-group-list", "finding-group-latest"] + ) + def test_finding_groups_delta_status_breakdown( + self, authenticated_client, finding_groups_fixture, endpoint_name + ): + """`new_*` and `changed_*` counters split by status and mute state. + + s3_bucket_public_access has 1 new FAIL and 1 changed FAIL (both + non-muted) so the breakdown must reflect exactly that and the totals + must equal the sum of the buckets. + """ + params = {"filter[check_id]": "s3_bucket_public_access"} + if endpoint_name == "finding-group-list": + params["filter[inserted_at]"] = TODAY + + response = authenticated_client.get(reverse(endpoint_name), params) + assert response.status_code == status.HTTP_200_OK + data = response.json()["data"] + assert len(data) == 1 + attrs = data[0]["attributes"] + + assert attrs["new_fail_count"] == 1 + assert attrs["new_fail_muted_count"] == 0 + assert attrs["new_pass_count"] == 0 + assert attrs["new_pass_muted_count"] == 0 + assert attrs["new_manual_count"] == 0 + assert attrs["new_manual_muted_count"] == 0 + assert attrs["changed_fail_count"] == 1 + assert attrs["changed_fail_muted_count"] == 0 + assert attrs["changed_pass_count"] == 0 + assert attrs["changed_pass_muted_count"] == 0 + assert attrs["changed_manual_count"] == 0 + assert attrs["changed_manual_muted_count"] == 0 + + new_total = ( + attrs["new_fail_count"] + + attrs["new_fail_muted_count"] + + attrs["new_pass_count"] + + attrs["new_pass_muted_count"] + + attrs["new_manual_count"] + + attrs["new_manual_muted_count"] + ) + changed_total = ( + attrs["changed_fail_count"] + + attrs["changed_fail_muted_count"] + + attrs["changed_pass_count"] + + attrs["changed_pass_muted_count"] + + attrs["changed_manual_count"] + + attrs["changed_manual_muted_count"] + ) + # The non-muted variants of the breakdown must sum to the legacy + # totals (new_count/changed_count are stored as non-muted). + assert ( + attrs["new_fail_count"] + + attrs["new_pass_count"] + + attrs["new_manual_count"] + == attrs["new_count"] + ) + assert ( + attrs["changed_fail_count"] + + attrs["changed_pass_count"] + + attrs["changed_manual_count"] + == attrs["changed_count"] + ) + # And the *full* breakdown (including the muted halves) is exposed + # so clients can also count muted-only deltas without losing data. + assert new_total >= attrs["new_count"] + assert changed_total >= attrs["changed_count"] + + def test_finding_groups_resources_serializer_exposes_muted( + self, authenticated_client, finding_groups_fixture + ): + """The /finding-groups//resources payload must expose `muted`.""" + response = authenticated_client.get( + reverse( + "finding-group-resources", + kwargs={"pk": "rds_encryption"}, + ), + {"filter[inserted_at]": TODAY}, + ) + assert response.status_code == status.HTTP_200_OK + data = response.json()["data"] + assert data, "rds_encryption should expose its resources" + for item in data: + attrs = item["attributes"] + assert "muted" in attrs and isinstance(attrs["muted"], bool) + # rds_encryption has all muted findings + assert attrs["muted"] is True + # Status reflects the underlying check outcome (FAIL), not MUTED + assert attrs["status"] == "FAIL" + + def test_finding_groups_resources_exposes_finding_id( + self, authenticated_client, finding_groups_fixture + ): + """The /resources payload exposes the most recent matching finding_id. + + rds_encryption has 2 findings, one per resource. Each resource row must + report the UUID of its corresponding Finding (UUIDv7 ordering means + Max(finding__id) resolves to the latest snapshot in time). + """ + response = authenticated_client.get( + reverse( + "finding-group-resources", + kwargs={"pk": "rds_encryption"}, + ), + {"filter[inserted_at]": TODAY}, + ) + assert response.status_code == status.HTTP_200_OK + data = response.json()["data"] + assert data, "rds_encryption should expose its resources" + + rds_finding_ids = { + str(f.id) for f in finding_groups_fixture if f.check_id == "rds_encryption" + } + assert rds_finding_ids, "fixture sanity" + + for item in data: + attrs = item["attributes"] + assert "finding_id" in attrs + assert attrs["finding_id"] in rds_finding_ids + + def test_finding_groups_latest_resources_exposes_finding_id( + self, authenticated_client, finding_groups_fixture + ): + """The /latest/.../resources payload also exposes finding_id.""" + response = authenticated_client.get( + reverse( + "finding-group-latest_resources", + kwargs={"check_id": "rds_encryption"}, + ), + ) + assert response.status_code == status.HTTP_200_OK + data = response.json()["data"] + assert data, "rds_encryption should expose its resources via /latest" + + rds_finding_ids = { + str(f.id) for f in finding_groups_fixture if f.check_id == "rds_encryption" + } + for item in data: + attrs = item["attributes"] + assert "finding_id" in attrs + assert attrs["finding_id"] in rds_finding_ids diff --git a/api/src/backend/api/v1/serializers.py b/api/src/backend/api/v1/serializers.py index 6af4d01000..52ec47f4f5 100644 --- a/api/src/backend/api/v1/serializers.py +++ b/api/src/backend/api/v1/serializers.py @@ -4185,6 +4185,7 @@ class FindingGroupSerializer(BaseSerializerV1): check_description = serializers.CharField(required=False, allow_null=True) severity = serializers.CharField() status = serializers.CharField() + muted = serializers.BooleanField() impacted_providers = serializers.ListField( child=serializers.CharField(), required=False ) @@ -4192,9 +4193,25 @@ class FindingGroupSerializer(BaseSerializerV1): resources_total = serializers.IntegerField() pass_count = serializers.IntegerField() fail_count = serializers.IntegerField() + manual_count = serializers.IntegerField() + pass_muted_count = serializers.IntegerField() + fail_muted_count = serializers.IntegerField() + manual_muted_count = serializers.IntegerField() muted_count = serializers.IntegerField() new_count = serializers.IntegerField() changed_count = serializers.IntegerField() + new_fail_count = serializers.IntegerField() + new_fail_muted_count = serializers.IntegerField() + new_pass_count = serializers.IntegerField() + new_pass_muted_count = serializers.IntegerField() + new_manual_count = serializers.IntegerField() + new_manual_muted_count = serializers.IntegerField() + changed_fail_count = serializers.IntegerField() + changed_fail_muted_count = serializers.IntegerField() + changed_pass_count = serializers.IntegerField() + changed_pass_muted_count = serializers.IntegerField() + changed_manual_count = serializers.IntegerField() + changed_manual_muted_count = serializers.IntegerField() first_seen_at = serializers.DateTimeField(required=False, allow_null=True) last_seen_at = serializers.DateTimeField(required=False, allow_null=True) failing_since = serializers.DateTimeField(required=False, allow_null=True) @@ -4214,8 +4231,10 @@ class FindingGroupResourceSerializer(BaseSerializerV1): id = serializers.UUIDField(source="resource_id") resource = serializers.SerializerMethodField() provider = serializers.SerializerMethodField() + finding_id = serializers.UUIDField() status = serializers.CharField() severity = serializers.CharField() + muted = serializers.BooleanField() delta = serializers.CharField(required=False, allow_null=True) first_seen_at = serializers.DateTimeField(required=False, allow_null=True) last_seen_at = serializers.DateTimeField(required=False, allow_null=True) diff --git a/api/src/backend/api/v1/views.py b/api/src/backend/api/v1/views.py index 38875ccc2f..23cfe17f2d 100644 --- a/api/src/backend/api/v1/views.py +++ b/api/src/backend/api/v1/views.py @@ -26,10 +26,11 @@ from config.settings.social_login import ( ) from dj_rest_auth.registration.views import SocialLoginView from django.conf import settings as django_settings -from django.contrib.postgres.aggregates import ArrayAgg, StringAgg +from django.contrib.postgres.aggregates import ArrayAgg, BoolAnd, StringAgg from django.contrib.postgres.search import SearchQuery from django.db import transaction from django.db.models import ( + BooleanField, Case, CharField, Count, @@ -7076,9 +7077,29 @@ class FindingGroupViewSet(BaseRLSViewSet): severity_order=Max("severity_order"), pass_count=Sum("pass_count"), fail_count=Sum("fail_count"), + manual_count=Sum("manual_count"), + pass_muted_count=Sum("pass_muted_count"), + fail_muted_count=Sum("fail_muted_count"), + manual_muted_count=Sum("manual_muted_count"), muted_count=Sum("muted_count"), + # The group is muted only if every contributing daily summary is + # itself fully muted. BoolAnd returns False as soon as one row has + # at least one actionable finding. + muted=BoolAnd("muted"), new_count=Sum("new_count"), changed_count=Sum("changed_count"), + new_fail_count=Sum("new_fail_count"), + new_fail_muted_count=Sum("new_fail_muted_count"), + new_pass_count=Sum("new_pass_count"), + new_pass_muted_count=Sum("new_pass_muted_count"), + new_manual_count=Sum("new_manual_count"), + new_manual_muted_count=Sum("new_manual_muted_count"), + changed_fail_count=Sum("changed_fail_count"), + changed_fail_muted_count=Sum("changed_fail_muted_count"), + changed_pass_count=Sum("changed_pass_count"), + changed_pass_muted_count=Sum("changed_pass_muted_count"), + changed_manual_count=Sum("changed_manual_count"), + changed_manual_muted_count=Sum("changed_manual_muted_count"), resources_total=Sum("resources_total"), resources_fail=Sum("resources_fail"), impacted_providers_str=StringAgg( @@ -7104,39 +7125,95 @@ class FindingGroupViewSet(BaseRLSViewSet): output_field=IntegerField(), ) - return queryset.values("check_id").annotate( - severity_order=Max(severity_case), - pass_count=Count("id", filter=Q(status="PASS", muted=False)), - fail_count=Count("id", filter=Q(status="FAIL", muted=False)), - muted_count=Count("id", filter=Q(muted=True)), - new_count=Count("id", filter=Q(delta="new", muted=False)), - changed_count=Count("id", filter=Q(delta="changed", muted=False)), - resources_total=Count("resources__id", distinct=True), - resources_fail=Count( - "resources__id", - distinct=True, - filter=Q(status="FAIL", muted=False), - ), - impacted_providers_str=StringAgg( - Cast("scan__provider__provider", CharField()), - delimiter=",", - distinct=True, - default="", - ), - agg_first_seen_at=Min("first_seen_at"), - agg_last_seen_at=Max("inserted_at"), - agg_failing_since=Min( - "first_seen_at", filter=Q(status="FAIL", muted=False) - ), - check_title=Coalesce( - Max(KeyTextTransform("checktitle", "check_metadata")), - Max(KeyTextTransform("CheckTitle", "check_metadata")), - Max(KeyTextTransform("Checktitle", "check_metadata")), - ), - check_description=Coalesce( - Max(KeyTextTransform("description", "check_metadata")), - Max(KeyTextTransform("Description", "check_metadata")), - ), + # `pass_count`, `fail_count` and `manual_count` count *every* finding + # for the check (muted or not) so the aggregated `status` reflects the + # underlying check outcome regardless of mute state. Whether the group + # is actionable is signalled by the orthogonal `muted` flag below. + return ( + queryset.values("check_id") + .annotate( + severity_order=Max(severity_case), + pass_count=Count("id", filter=Q(status="PASS")), + fail_count=Count("id", filter=Q(status="FAIL")), + manual_count=Count("id", filter=Q(status="MANUAL")), + pass_muted_count=Count("id", filter=Q(status="PASS", muted=True)), + fail_muted_count=Count("id", filter=Q(status="FAIL", muted=True)), + manual_muted_count=Count("id", filter=Q(status="MANUAL", muted=True)), + muted_count=Count("id", filter=Q(muted=True)), + nonmuted_count=Count("id", filter=Q(muted=False)), + new_count=Count("id", filter=Q(delta="new", muted=False)), + changed_count=Count("id", filter=Q(delta="changed", muted=False)), + new_fail_count=Count( + "id", filter=Q(delta="new", status="FAIL", muted=False) + ), + new_fail_muted_count=Count( + "id", filter=Q(delta="new", status="FAIL", muted=True) + ), + new_pass_count=Count( + "id", filter=Q(delta="new", status="PASS", muted=False) + ), + new_pass_muted_count=Count( + "id", filter=Q(delta="new", status="PASS", muted=True) + ), + new_manual_count=Count( + "id", filter=Q(delta="new", status="MANUAL", muted=False) + ), + new_manual_muted_count=Count( + "id", filter=Q(delta="new", status="MANUAL", muted=True) + ), + changed_fail_count=Count( + "id", filter=Q(delta="changed", status="FAIL", muted=False) + ), + changed_fail_muted_count=Count( + "id", filter=Q(delta="changed", status="FAIL", muted=True) + ), + changed_pass_count=Count( + "id", filter=Q(delta="changed", status="PASS", muted=False) + ), + changed_pass_muted_count=Count( + "id", filter=Q(delta="changed", status="PASS", muted=True) + ), + changed_manual_count=Count( + "id", filter=Q(delta="changed", status="MANUAL", muted=False) + ), + changed_manual_muted_count=Count( + "id", filter=Q(delta="changed", status="MANUAL", muted=True) + ), + resources_total=Count("resources__id", distinct=True), + resources_fail=Count( + "resources__id", + distinct=True, + filter=Q(status="FAIL", muted=False), + ), + impacted_providers_str=StringAgg( + Cast("scan__provider__provider", CharField()), + delimiter=",", + distinct=True, + default="", + ), + agg_first_seen_at=Min("first_seen_at"), + agg_last_seen_at=Max("inserted_at"), + agg_failing_since=Min( + "first_seen_at", filter=Q(status="FAIL", muted=False) + ), + check_title=Coalesce( + Max(KeyTextTransform("checktitle", "check_metadata")), + Max(KeyTextTransform("CheckTitle", "check_metadata")), + Max(KeyTextTransform("Checktitle", "check_metadata")), + ), + check_description=Coalesce( + Max(KeyTextTransform("description", "check_metadata")), + Max(KeyTextTransform("Description", "check_metadata")), + ), + ) + .annotate( + # Group is muted only if it has zero non-muted findings. + muted=Case( + When(nonmuted_count=0, then=Value(True)), + default=Value(False), + output_field=BooleanField(), + ), + ) ) def _split_computed_aggregate_filters( @@ -7148,6 +7225,7 @@ class FindingGroupViewSet(BaseRLSViewSet): "status__in", "severity", "severity__in", + "muted", "include_muted", } finding_params = QueryDict(mutable=True) @@ -7179,7 +7257,8 @@ class FindingGroupViewSet(BaseRLSViewSet): Post-process aggregation results to add computed fields. - Converts severity integer back to string - - Computes aggregated status (FAIL > PASS > MUTED) + - Computes aggregated status (FAIL > PASS > MANUAL); the orthogonal + ``muted`` boolean is already on the row from the SQL aggregation - Converts provider string to list """ results = [] @@ -7197,13 +7276,19 @@ class FindingGroupViewSet(BaseRLSViewSet): if "agg_failing_since" in row: row["failing_since"] = row.pop("agg_failing_since") - # Compute aggregated status + # Drop the helper count we use to derive `muted` in the + # finding-level aggregation path. + row.pop("nonmuted_count", None) + + # Compute aggregated status. Counts are inclusive of muted findings, + # so the underlying check outcome surfaces even when the group is + # fully muted. if row.get("fail_count", 0) > 0: row["status"] = "FAIL" elif row.get("pass_count", 0) > 0: row["status"] = "PASS" else: - row["status"] = "MUTED" + row["status"] = "MANUAL" # Convert provider string to list providers_str = row.pop("impacted_providers_str", "") or "" @@ -7219,8 +7304,12 @@ class FindingGroupViewSet(BaseRLSViewSet): "check_id": "check_id", "check_title": "check_title", "severity": "severity_order", + "status": "status_order", + "muted": "muted", + "delta": "delta_order", "fail_count": "fail_count", "pass_count": "pass_count", + "manual_count": "manual_count", "muted_count": "muted_count", "new_count": "new_count", "changed_count": "changed_count", @@ -7275,7 +7364,7 @@ class FindingGroupViewSet(BaseRLSViewSet): return ordering def _apply_aggregated_computed_filters(self, queryset, computed_params: QueryDict): - """Apply computed filters (status/severity) on aggregated finding-group rows.""" + """Apply computed filters (status/severity/muted) on aggregated finding-group rows.""" if not computed_params: return queryset @@ -7284,14 +7373,16 @@ class FindingGroupViewSet(BaseRLSViewSet): aggregated_status=Case( When(fail_count__gt=0, then=Value("FAIL")), When(pass_count__gt=0, then=Value("PASS")), - default=Value("MUTED"), + default=Value("MANUAL"), output_field=CharField(), ) ) - # Exclude fully-muted groups by default unless include_muted is set - if "include_muted" not in computed_params: - queryset = queryset.exclude(fail_count=0, pass_count=0, muted_count__gt=0) + # Exclude fully-muted groups by default unless the caller has opted in + # via either `include_muted` or an explicit `muted` filter (the latter + # gives the caller direct control over the column). + if "include_muted" not in computed_params and "muted" not in computed_params: + queryset = queryset.exclude(muted=True) filterset = FindingGroupAggregatedComputedFilter( computed_params, queryset=queryset @@ -7346,18 +7437,14 @@ class FindingGroupViewSet(BaseRLSViewSet): provider_type=Max("resource__provider__provider"), provider_uid=Max("resource__provider__uid"), provider_alias=Max("resource__provider__alias"), + # status_order considers ALL findings (muted or not) so it + # surfaces FAIL/PASS/MANUAL based on the underlying check + # outcome. Whether the resource is actionable is signalled by + # the orthogonal `muted` flag below. status_order=Max( Case( - When( - finding__status="FAIL", - finding__muted=False, - then=Value(3), - ), - When( - finding__status="PASS", - finding__muted=False, - then=Value(2), - ), + When(finding__status="FAIL", then=Value(3)), + When(finding__status="PASS", then=Value(2)), default=Value(1), output_field=IntegerField(), ) @@ -7389,6 +7476,8 @@ class FindingGroupViewSet(BaseRLSViewSet): ), first_seen_at=Min("finding__first_seen_at"), last_seen_at=Max("finding__inserted_at"), + # True only if every finding for this resource+check is muted. + muted=BoolAnd("finding__muted"), # Max() on muted_reason / check_metadata is safe because # all findings for the same resource+check share identical # values (mute rules and metadata are applied per-check). @@ -7396,6 +7485,12 @@ class FindingGroupViewSet(BaseRLSViewSet): resource_group=Max( KeyTextTransform("resourcegroup", "finding__check_metadata") ), + # Most recent matching Finding for this (resource, check): + # Finding.id is a UUIDv7 (time-ordered in its high 48 bits). + # Cast to text first because PostgreSQL has no built-in + # `max(uuid)` aggregate; on the canonical lowercase form a + # lexicographic Max() still resolves to the latest snapshot. + finding_id=Max(Cast("finding__id", output_field=CharField())), ) .filter(resource_id__isnull=False) ) @@ -7404,8 +7499,8 @@ class FindingGroupViewSet(BaseRLSViewSet): _RESOURCE_SORT_ANNOTATIONS = { "status_order": lambda: Max( Case( - When(finding__status="FAIL", finding__muted=False, then=Value(3)), - When(finding__status="PASS", finding__muted=False, then=Value(2)), + When(finding__status="FAIL", then=Value(3)), + When(finding__status="PASS", then=Value(2)), default=Value(1), output_field=IntegerField(), ) @@ -7479,7 +7574,7 @@ class FindingGroupViewSet(BaseRLSViewSet): elif status_order == 2: status = "PASS" else: - status = "MUTED" + status = "MANUAL" delta_order = row.get("delta_order", 0) if delta_order == 2: @@ -7507,8 +7602,12 @@ class FindingGroupViewSet(BaseRLSViewSet): "delta": delta, "first_seen_at": row["first_seen_at"], "last_seen_at": row["last_seen_at"], + "muted": bool(row.get("muted", False)), "muted_reason": row.get("muted_reason"), "resource_group": row.get("resource_group", ""), + "finding_id": ( + str(row["finding_id"]) if row.get("finding_id") else None + ), } ) @@ -7569,7 +7668,35 @@ class FindingGroupViewSet(BaseRLSViewSet): sort_param, self._FINDING_GROUP_SORT_MAP ) if ordering: - aggregated_queryset = aggregated_queryset.order_by(*ordering) + # status_order is annotated on demand so groups can be sorted by + # their aggregated status (FAIL > PASS > MANUAL), mirroring the + # priority used in _post_process_aggregation. Counts are + # inclusive of muted findings, so the underlying check outcome + # surfaces even for fully muted groups. + if any(field.lstrip("-") == "status_order" for field in ordering): + aggregated_queryset = aggregated_queryset.annotate( + status_order=Case( + When(fail_count__gt=0, then=Value(3)), + When(pass_count__gt=0, then=Value(2)), + default=Value(1), + output_field=IntegerField(), + ) + ) + + # delta_order is a virtual sort field: expand it to a + # lexicographic ordering by (new_count, changed_count) so groups + # with more new findings rank higher, with changed_count as the + # tie-breaker (preserves the "new > changed" priority used by + # the resources endpoint, but driven by the actual counters). + expanded_ordering = [] + for field in ordering: + if field.lstrip("-") == "delta_order": + sign = "-" if field.startswith("-") else "" + expanded_ordering.append(f"{sign}new_count") + expanded_ordering.append(f"{sign}changed_count") + else: + expanded_ordering.append(field) + aggregated_queryset = aggregated_queryset.order_by(*expanded_ordering) else: aggregated_queryset = aggregated_queryset.order_by( "-fail_count", "-severity_order", "check_id" diff --git a/api/src/backend/tasks/jobs/export.py b/api/src/backend/tasks/jobs/export.py index 4b8498f7e7..3be9b81544 100644 --- a/api/src/backend/tasks/jobs/export.py +++ b/api/src/backend/tasks/jobs/export.py @@ -32,9 +32,13 @@ from prowler.lib.outputs.compliance.cis.cis_aws import AWSCIS from prowler.lib.outputs.compliance.cis.cis_azure import AzureCIS from prowler.lib.outputs.compliance.cis.cis_gcp import GCPCIS from prowler.lib.outputs.compliance.cis.cis_github import GithubCIS +from prowler.lib.outputs.compliance.cis.cis_googleworkspace import GoogleWorkspaceCIS from prowler.lib.outputs.compliance.cis.cis_kubernetes import KubernetesCIS from prowler.lib.outputs.compliance.cis.cis_m365 import M365CIS from prowler.lib.outputs.compliance.cis.cis_oraclecloud import OracleCloudCIS +from prowler.lib.outputs.compliance.cisa_scuba.cisa_scuba_googleworkspace import ( + GoogleWorkspaceCISASCuBA, +) from prowler.lib.outputs.compliance.csa.csa_alibabacloud import AlibabaCloudCSA from prowler.lib.outputs.compliance.csa.csa_aws import AWSCSA from prowler.lib.outputs.compliance.csa.csa_azure import AzureCSA @@ -93,7 +97,7 @@ COMPLIANCE_CLASS_MAP = { (lambda name: name.startswith("iso27001_"), AWSISO27001), (lambda name: name.startswith("kisa"), AWSKISAISMSP), (lambda name: name == "prowler_threatscore_aws", ProwlerThreatScoreAWS), - (lambda name: name == "ccc_aws", CCC_AWS), + (lambda name: name.startswith("ccc_"), CCC_AWS), (lambda name: name.startswith("c5_"), AWSC5), (lambda name: name.startswith("csa_"), AWSCSA), ], @@ -102,7 +106,7 @@ COMPLIANCE_CLASS_MAP = { (lambda name: name == "mitre_attack_azure", AzureMitreAttack), (lambda name: name.startswith("ens_"), AzureENS), (lambda name: name.startswith("iso27001_"), AzureISO27001), - (lambda name: name == "ccc_azure", CCC_Azure), + (lambda name: name.startswith("ccc_"), CCC_Azure), (lambda name: name == "prowler_threatscore_azure", ProwlerThreatScoreAzure), (lambda name: name == "c5_azure", AzureC5), (lambda name: name.startswith("csa_"), AzureCSA), @@ -113,7 +117,7 @@ COMPLIANCE_CLASS_MAP = { (lambda name: name.startswith("ens_"), GCPENS), (lambda name: name.startswith("iso27001_"), GCPISO27001), (lambda name: name == "prowler_threatscore_gcp", ProwlerThreatScoreGCP), - (lambda name: name == "ccc_gcp", CCC_GCP), + (lambda name: name.startswith("ccc_"), CCC_GCP), (lambda name: name == "c5_gcp", GCPC5), (lambda name: name.startswith("csa_"), GCPCSA), ], @@ -133,6 +137,10 @@ COMPLIANCE_CLASS_MAP = { "github": [ (lambda name: name.startswith("cis_"), GithubCIS), ], + "googleworkspace": [ + (lambda name: name.startswith("cis_"), GoogleWorkspaceCIS), + (lambda name: name.startswith("cisa_scuba_"), GoogleWorkspaceCISASCuBA), + ], "iac": [ # IaC provider doesn't have specific compliance frameworks yet # Trivy handles its own compliance checks diff --git a/api/src/backend/tasks/jobs/scan.py b/api/src/backend/tasks/jobs/scan.py index 364b12d146..2c73c97f2f 100644 --- a/api/src/backend/tasks/jobs/scan.py +++ b/api/src/backend/tasks/jobs/scan.py @@ -1803,7 +1803,12 @@ def aggregate_finding_group_summaries(tenant_id: str, scan_id: str): output_field=IntegerField(), ) - # Aggregate findings by check_id for this scan + # Aggregate findings by check_id for this scan. + # `pass_count`, `fail_count` and `manual_count` count *every* finding + # in this group, regardless of mute state, so the aggregated `status` + # always reflects the underlying check outcome (FAIL > PASS > MANUAL) + # even when the group is fully muted. The orthogonal `muted` flag is + # what tells whether the group has any actionable (non-muted) findings. aggregated = ( Finding.objects.filter( tenant_id=tenant_id, @@ -1812,11 +1817,52 @@ def aggregate_finding_group_summaries(tenant_id: str, scan_id: str): .values("check_id") .annotate( severity_order=Max(severity_case), - pass_count=Count("id", filter=Q(status="PASS", muted=False)), - fail_count=Count("id", filter=Q(status="FAIL", muted=False)), + pass_count=Count("id", filter=Q(status="PASS")), + fail_count=Count("id", filter=Q(status="FAIL")), + manual_count=Count("id", filter=Q(status="MANUAL")), + pass_muted_count=Count("id", filter=Q(status="PASS", muted=True)), + fail_muted_count=Count("id", filter=Q(status="FAIL", muted=True)), + manual_muted_count=Count("id", filter=Q(status="MANUAL", muted=True)), muted_count=Count("id", filter=Q(muted=True)), + nonmuted_count=Count("id", filter=Q(muted=False)), new_count=Count("id", filter=Q(delta="new", muted=False)), changed_count=Count("id", filter=Q(delta="changed", muted=False)), + new_fail_count=Count( + "id", filter=Q(delta="new", status="FAIL", muted=False) + ), + new_fail_muted_count=Count( + "id", filter=Q(delta="new", status="FAIL", muted=True) + ), + new_pass_count=Count( + "id", filter=Q(delta="new", status="PASS", muted=False) + ), + new_pass_muted_count=Count( + "id", filter=Q(delta="new", status="PASS", muted=True) + ), + new_manual_count=Count( + "id", filter=Q(delta="new", status="MANUAL", muted=False) + ), + new_manual_muted_count=Count( + "id", filter=Q(delta="new", status="MANUAL", muted=True) + ), + changed_fail_count=Count( + "id", filter=Q(delta="changed", status="FAIL", muted=False) + ), + changed_fail_muted_count=Count( + "id", filter=Q(delta="changed", status="FAIL", muted=True) + ), + changed_pass_count=Count( + "id", filter=Q(delta="changed", status="PASS", muted=False) + ), + changed_pass_muted_count=Count( + "id", filter=Q(delta="changed", status="PASS", muted=True) + ), + changed_manual_count=Count( + "id", filter=Q(delta="changed", status="MANUAL", muted=False) + ), + changed_manual_muted_count=Count( + "id", filter=Q(delta="changed", status="MANUAL", muted=True) + ), resources_total=Count("resources__id", distinct=True), resources_fail=Count( "resources__id", @@ -1895,9 +1941,26 @@ def aggregate_finding_group_summaries(tenant_id: str, scan_id: str): severity_order=row["severity_order"] or 1, pass_count=row["pass_count"], fail_count=row["fail_count"], + manual_count=row["manual_count"], + pass_muted_count=row["pass_muted_count"], + fail_muted_count=row["fail_muted_count"], + manual_muted_count=row["manual_muted_count"], muted_count=row["muted_count"], + muted=row["nonmuted_count"] == 0, new_count=row["new_count"], changed_count=row["changed_count"], + new_fail_count=row["new_fail_count"], + new_fail_muted_count=row["new_fail_muted_count"], + new_pass_count=row["new_pass_count"], + new_pass_muted_count=row["new_pass_muted_count"], + new_manual_count=row["new_manual_count"], + new_manual_muted_count=row["new_manual_muted_count"], + changed_fail_count=row["changed_fail_count"], + changed_fail_muted_count=row["changed_fail_muted_count"], + changed_pass_count=row["changed_pass_count"], + changed_pass_muted_count=row["changed_pass_muted_count"], + changed_manual_count=row["changed_manual_count"], + changed_manual_muted_count=row["changed_manual_muted_count"], resources_total=row["resources_total"], resources_fail=row["resources_fail"], first_seen_at=row["agg_first_seen_at"], @@ -1917,9 +1980,26 @@ def aggregate_finding_group_summaries(tenant_id: str, scan_id: str): "severity_order", "pass_count", "fail_count", + "manual_count", + "pass_muted_count", + "fail_muted_count", + "manual_muted_count", "muted_count", + "muted", "new_count", "changed_count", + "new_fail_count", + "new_fail_muted_count", + "new_pass_count", + "new_pass_muted_count", + "new_manual_count", + "new_manual_muted_count", + "changed_fail_count", + "changed_fail_muted_count", + "changed_pass_count", + "changed_pass_muted_count", + "changed_manual_count", + "changed_manual_muted_count", "resources_total", "resources_fail", "first_seen_at", diff --git a/api/src/backend/tasks/tasks.py b/api/src/backend/tasks/tasks.py index fbd0440af8..bbf4d89772 100644 --- a/api/src/backend/tasks/tasks.py +++ b/api/src/backend/tasks/tasks.py @@ -771,26 +771,49 @@ def aggregate_finding_group_summaries_task(tenant_id: str, scan_id: str): ) @set_tenant(keep_tenant=True) def reaggregate_all_finding_group_summaries_task(tenant_id: str): - """Reaggregate finding group summaries for all providers' latest completed scans.""" - latest_scan_ids = list( - Scan.objects.filter(tenant_id=tenant_id, state=StateChoices.COMPLETED) - .order_by("provider_id", "-completed_at", "-inserted_at") - .distinct("provider_id") - .values_list("id", flat=True) + """Reaggregate finding group summaries for every (provider, day) combination. + + Mirrors the unbounded scope of `mute_historical_findings_task`: that task + rewrites every Finding row whose UID matches a mute rule, with no time + limit. To keep the daily summaries consistent with that update, this task + re-runs the aggregator on the latest completed scan of every (provider, + day) pair that exists in the database. Tasks are dispatched in parallel + via a Celery group so the wallclock scales with the worker pool, not with + the number of pairs. + """ + completed_scans = list( + Scan.objects.filter( + tenant_id=tenant_id, + state=StateChoices.COMPLETED, + completed_at__isnull=False, + ) + .order_by("-completed_at") + .values("id", "completed_at", "provider_id") ) - if latest_scan_ids: + + # Keep the latest scan per (provider, day) pair so the daily summary row + # the aggregator writes is the most recent snapshot of that day for that + # provider. Iterating from most recent to oldest means the first scan we + # see for a given key wins. + latest_scans: dict[tuple, str] = {} + for scan in completed_scans: + key = (scan["provider_id"], scan["completed_at"].date()) + if key not in latest_scans: + latest_scans[key] = str(scan["id"]) + + scan_ids = list(latest_scans.values()) + if scan_ids: logger.info( - "Reaggregating finding group summaries for %d scans: %s", - len(latest_scan_ids), - latest_scan_ids, + "Reaggregating finding group summaries for %d scans (provider x day)", + len(scan_ids), ) group( aggregate_finding_group_summaries_task.si( - tenant_id=tenant_id, scan_id=str(scan_id) + tenant_id=tenant_id, scan_id=scan_id ) - for scan_id in latest_scan_ids + for scan_id in scan_ids ).apply_async() - return {"scans_reaggregated": len(latest_scan_ids)} + return {"scans_reaggregated": len(scan_ids)} @shared_task(base=RLSTask, name="lighthouse-connection-check") diff --git a/api/src/backend/tasks/tests/test_tasks.py b/api/src/backend/tasks/tests/test_tasks.py index 8469b7db09..4a4108607e 100644 --- a/api/src/backend/tasks/tests/test_tasks.py +++ b/api/src/backend/tasks/tests/test_tasks.py @@ -1,6 +1,6 @@ import uuid from contextlib import contextmanager -from datetime import datetime, timezone +from datetime import datetime, timedelta, timezone from unittest.mock import MagicMock, patch import openai @@ -2362,35 +2362,96 @@ class TestReaggregateAllFindingGroupSummaries: @patch("tasks.tasks.group") @patch("tasks.tasks.aggregate_finding_group_summaries_task") @patch("tasks.tasks.Scan.objects.filter") - def test_dispatches_subtasks_for_each_provider( + def test_dispatches_subtasks_for_each_provider_per_day( self, mock_scan_filter, mock_agg_task, mock_group ): - scan_id_1 = uuid.uuid4() - scan_id_2 = uuid.uuid4() + provider_id_1 = uuid.uuid4() + provider_id_2 = uuid.uuid4() + scan_id_today_p1 = uuid.uuid4() + scan_id_yesterday_p1 = uuid.uuid4() + scan_id_today_p2 = uuid.uuid4() + today = datetime.now(tz=timezone.utc) + yesterday = today - timedelta(days=1) + mock_group_result = MagicMock() mock_group.side_effect = lambda gen: (list(gen), mock_group_result)[1] - mock_scan_filter.return_value.order_by.return_value.distinct.return_value.values_list.return_value = [ - scan_id_1, - scan_id_2, + mock_scan_filter.return_value.order_by.return_value.values.return_value = [ + { + "id": scan_id_today_p1, + "completed_at": today, + "provider_id": provider_id_1, + }, + { + "id": scan_id_today_p2, + "completed_at": today, + "provider_id": provider_id_2, + }, + { + "id": scan_id_yesterday_p1, + "completed_at": yesterday, + "provider_id": provider_id_1, + }, ] result = reaggregate_all_finding_group_summaries_task(tenant_id=self.tenant_id) - assert result == {"scans_reaggregated": 2} - assert mock_agg_task.si.call_count == 2 + assert result == {"scans_reaggregated": 3} + assert mock_agg_task.si.call_count == 3 mock_agg_task.si.assert_any_call( - tenant_id=self.tenant_id, scan_id=str(scan_id_1) + tenant_id=self.tenant_id, scan_id=str(scan_id_today_p1) ) mock_agg_task.si.assert_any_call( - tenant_id=self.tenant_id, scan_id=str(scan_id_2) + tenant_id=self.tenant_id, scan_id=str(scan_id_today_p2) + ) + mock_agg_task.si.assert_any_call( + tenant_id=self.tenant_id, scan_id=str(scan_id_yesterday_p1) + ) + mock_group_result.apply_async.assert_called_once() + + @patch("tasks.tasks.group") + @patch("tasks.tasks.aggregate_finding_group_summaries_task") + @patch("tasks.tasks.Scan.objects.filter") + def test_dedupes_scans_to_latest_per_provider_per_day( + self, mock_scan_filter, mock_agg_task, mock_group + ): + """When several scans run on the same day for the same provider, only + the latest one is dispatched (matching the daily summary unique key).""" + provider_id = uuid.uuid4() + latest_scan_today = uuid.uuid4() + earlier_scan_today = uuid.uuid4() + today_late = datetime.now(tz=timezone.utc) + today_early = today_late - timedelta(hours=4) + + mock_group_result = MagicMock() + mock_group.side_effect = lambda gen: (list(gen), mock_group_result)[1] + + # Returned ordered by `-completed_at`, so the most recent comes first. + mock_scan_filter.return_value.order_by.return_value.values.return_value = [ + { + "id": latest_scan_today, + "completed_at": today_late, + "provider_id": provider_id, + }, + { + "id": earlier_scan_today, + "completed_at": today_early, + "provider_id": provider_id, + }, + ] + + result = reaggregate_all_finding_group_summaries_task(tenant_id=self.tenant_id) + + assert result == {"scans_reaggregated": 1} + mock_agg_task.si.assert_called_once_with( + tenant_id=self.tenant_id, scan_id=str(latest_scan_today) ) mock_group_result.apply_async.assert_called_once() @patch("tasks.tasks.group") @patch("tasks.tasks.Scan.objects.filter") def test_no_completed_scans_skips_dispatch(self, mock_scan_filter, mock_group): - mock_scan_filter.return_value.order_by.return_value.distinct.return_value.values_list.return_value = [] + mock_scan_filter.return_value.order_by.return_value.values.return_value = [] result = reaggregate_all_finding_group_summaries_task(tenant_id=self.tenant_id) diff --git a/docs/developer-guide/introduction.mdx b/docs/developer-guide/introduction.mdx index 2a4aa3abe1..947c4b4c71 100644 --- a/docs/developer-guide/introduction.mdx +++ b/docs/developer-guide/introduction.mdx @@ -163,6 +163,8 @@ These resources help ensure that AI-assisted contributions maintain consistency All dependencies are listed in the `pyproject.toml` file. +The SDK keeps direct dependencies pinned to exact versions, while `poetry.lock` records the full resolved dependency tree and the artifact hashes for every package. Use `poetry install` from the lock file instead of ad-hoc `pip` installs when you need a reproducible environment. + For proper code documentation, refer to the following and follow the code documentation practices presented there: [Google Python Style Guide - Comments and Docstrings](https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings). diff --git a/docs/developer-guide/provider.mdx b/docs/developer-guide/provider.mdx index 0fb3bc549d..ec3e106150 100644 --- a/docs/developer-guide/provider.mdx +++ b/docs/developer-guide/provider.mdx @@ -750,6 +750,35 @@ def init_parser(self): # More arguments for the provider. ``` +##### Sensitive CLI Arguments + +CLI flags that accept secrets (tokens, passwords, API keys) require special handling to protect credentials from leaking in HTML output and process listings: + +1. **Use `nargs="?"` with `default=None`** so the flag works both with and without an inline value. This allows the provider to fall back to an environment variable when no value is passed. +2. **Add a `SENSITIVE_ARGUMENTS` frozenset** at the top of the `arguments.py` file listing every flag that accepts secret values: + + ```python + SENSITIVE_ARGUMENTS = frozenset({"--your-provider-password", "--your-provider-token"}) + ``` + + Prowler automatically discovers these frozensets and uses them to redact values in HTML output and warn users who pass secrets directly on the command line. + +3. **Document the environment variable** in the `help` text so users know the recommended alternative: + + ```python + _parser.add_argument( + "--your-provider-password", + nargs="?", + default=None, + metavar="PASSWORD", + help="Password for authentication. We recommend using the YOUR_PROVIDER_PASSWORD environment variable instead.", + ) + ``` + + +Do not add new arguments that require passing secrets as CLI values without an environment variable fallback. Prowler CLI warns users when sensitive flags receive explicit values on the command line. + + #### Step 5: Implement Mutelist **Explanation:** diff --git a/docs/docs.json b/docs/docs.json index 2e39c4beac..c76b7174ef 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -98,6 +98,7 @@ ] }, "user-guide/tutorials/prowler-app-rbac", + "user-guide/tutorials/prowler-app-multi-tenant", "user-guide/tutorials/prowler-app-api-keys", "user-guide/tutorials/prowler-app-import-findings", { diff --git a/docs/getting-started/products/img/prowler-app-architecture.png b/docs/getting-started/products/img/prowler-app-architecture.png deleted file mode 100644 index 889bc0da88..0000000000 Binary files a/docs/getting-started/products/img/prowler-app-architecture.png and /dev/null differ diff --git a/docs/getting-started/products/prowler-app.mdx b/docs/getting-started/products/prowler-app.mdx index f21e0222cd..2df6015d06 100644 --- a/docs/getting-started/products/prowler-app.mdx +++ b/docs/getting-started/products/prowler-app.mdx @@ -11,16 +11,19 @@ Prowler App is a web application that simplifies running Prowler. It provides: ## Components -Prowler App consists of three main components: +Prowler App consists of four main components: - **Prowler UI**: User-friendly web interface for running Prowler and viewing results, powered by Next.js - **Prowler API**: Backend API that executes Prowler scans and stores results, built with Django REST Framework - **Prowler SDK**: Python SDK that integrates with Prowler CLI for advanced functionality +- **Prowler MCP Server**: Model Context Protocol server that exposes AI tools for Lighthouse, the AI-powered security assistant. Required dependency for Lighthouse. Supporting infrastructure includes: - **PostgreSQL**: Persistent storage of scan results - **Celery Workers**: Asynchronous execution of Prowler scans +- **Celery Beat (API Scheduler)**: Schedules recurring scans and enqueues jobs on the broker - **Valkey**: In-memory database serving as message broker for Celery workers +- **Neo4j**: Graph database used by the Attack Paths feature to combine cloud inventory with Prowler findings (currently populated by AWS scans) ![Prowler App Architecture](/images/products/prowler-app-architecture.png) diff --git a/docs/images/products/prowler-app-architecture.mmd b/docs/images/products/prowler-app-architecture.mmd new file mode 100644 index 0000000000..bfc687b82e --- /dev/null +++ b/docs/images/products/prowler-app-architecture.mmd @@ -0,0 +1,37 @@ +flowchart TB + user([User / Security Team]) + cli([Prowler CLI]) + + subgraph APP["Prowler App"] + ui["Prowler UI
(Next.js)"] + api["Prowler API
(Django REST Framework)"] + worker["API Worker
(Celery)"] + beat["API Scheduler
(Celery Beat)"] + mcp["Prowler MCP Server
(Lighthouse AI tools)"] + end + + sdk["Prowler SDK
(Python)"] + + subgraph DATA["Data Layer"] + pg[("PostgreSQL")] + valkey[("Valkey / Redis")] + neo4j[("Neo4j")] + end + + providers["Providers"] + + user --> ui + user --> cli + ui -->|REST| api + api --> pg + api --> valkey + beat -->|enqueue jobs| valkey + valkey -->|dispatch| worker + worker --> pg + worker -->|Attack Paths| neo4j + worker -->|invokes| sdk + cli --> sdk + api -. AI tools .-> mcp + mcp -. context .-> api + + sdk --> providers diff --git a/docs/images/products/prowler-app-architecture.png b/docs/images/products/prowler-app-architecture.png index 889bc0da88..19bbfab0ee 100644 Binary files a/docs/images/products/prowler-app-architecture.png and b/docs/images/products/prowler-app-architecture.png differ diff --git a/docs/images/providers/select-vercel-prowler-cloud.png b/docs/images/providers/select-vercel-prowler-cloud.png new file mode 100644 index 0000000000..b332103e1f Binary files /dev/null and b/docs/images/providers/select-vercel-prowler-cloud.png differ diff --git a/docs/images/providers/vercel-launch-scan.png b/docs/images/providers/vercel-launch-scan.png new file mode 100644 index 0000000000..4e7dd36a25 Binary files /dev/null and b/docs/images/providers/vercel-launch-scan.png differ diff --git a/docs/images/providers/vercel-team-id-form.png b/docs/images/providers/vercel-team-id-form.png new file mode 100644 index 0000000000..fad53fe017 Binary files /dev/null and b/docs/images/providers/vercel-team-id-form.png differ diff --git a/docs/images/providers/vercel-token-form.png b/docs/images/providers/vercel-token-form.png new file mode 100644 index 0000000000..991b9ddedc Binary files /dev/null and b/docs/images/providers/vercel-token-form.png differ diff --git a/docs/images/prowler-app/multi-tenant/create-organization-button.png b/docs/images/prowler-app/multi-tenant/create-organization-button.png new file mode 100644 index 0000000000..70675c334f Binary files /dev/null and b/docs/images/prowler-app/multi-tenant/create-organization-button.png differ diff --git a/docs/images/prowler-app/multi-tenant/create-organization-modal.png b/docs/images/prowler-app/multi-tenant/create-organization-modal.png new file mode 100644 index 0000000000..f0f932035c Binary files /dev/null and b/docs/images/prowler-app/multi-tenant/create-organization-modal.png differ diff --git a/docs/images/prowler-app/multi-tenant/delete-active-organization-modal.png b/docs/images/prowler-app/multi-tenant/delete-active-organization-modal.png new file mode 100644 index 0000000000..41f5231753 Binary files /dev/null and b/docs/images/prowler-app/multi-tenant/delete-active-organization-modal.png differ diff --git a/docs/images/prowler-app/multi-tenant/delete-organization-modal.png b/docs/images/prowler-app/multi-tenant/delete-organization-modal.png new file mode 100644 index 0000000000..dd06f937e9 Binary files /dev/null and b/docs/images/prowler-app/multi-tenant/delete-organization-modal.png differ diff --git a/docs/images/prowler-app/multi-tenant/edit-organization-modal.png b/docs/images/prowler-app/multi-tenant/edit-organization-modal.png new file mode 100644 index 0000000000..e0d28c727d Binary files /dev/null and b/docs/images/prowler-app/multi-tenant/edit-organization-modal.png differ diff --git a/docs/images/prowler-app/multi-tenant/organizations-card.png b/docs/images/prowler-app/multi-tenant/organizations-card.png new file mode 100644 index 0000000000..10ddb4b15b Binary files /dev/null and b/docs/images/prowler-app/multi-tenant/organizations-card.png differ diff --git a/docs/images/prowler-app/multi-tenant/sign-in-invitation.png b/docs/images/prowler-app/multi-tenant/sign-in-invitation.png new file mode 100644 index 0000000000..418216c09b Binary files /dev/null and b/docs/images/prowler-app/multi-tenant/sign-in-invitation.png differ diff --git a/docs/images/prowler-app/multi-tenant/switch-organization-modal.png b/docs/images/prowler-app/multi-tenant/switch-organization-modal.png new file mode 100644 index 0000000000..b9e2607a75 Binary files /dev/null and b/docs/images/prowler-app/multi-tenant/switch-organization-modal.png differ diff --git a/docs/introduction.mdx b/docs/introduction.mdx index 766f60e3c0..5747eeadb1 100644 --- a/docs/introduction.mdx +++ b/docs/introduction.mdx @@ -21,29 +21,57 @@ ## Supported Providers -The supported providers right now are: +Prowler supports a wide range of providers organized by category: -| Provider | Support | Audit Scope/Entities | Interface | -| -------------------------------------------------------------------------------- | ---------- | ---------------------------- | ------------ | -| [AWS](/user-guide/providers/aws/getting-started-aws) | Official | Accounts | UI, API, CLI | -| [Azure](/user-guide/providers/azure/getting-started-azure) | Official | Subscriptions | UI, API, CLI | -| [Google Cloud](/user-guide/providers/gcp/getting-started-gcp) | Official | Projects | UI, API, CLI | -| [Kubernetes](/user-guide/providers/kubernetes/getting-started-k8s) | Official | Clusters | UI, API, CLI | -| [M365](/user-guide/providers/microsoft365/getting-started-m365) | Official | Tenants | UI, API, CLI | -| [Github](/user-guide/providers/github/getting-started-github) | Official | Organizations / Repositories | UI, API, CLI | -| [Oracle Cloud](/user-guide/providers/oci/getting-started-oci) | Official | Tenancies / Compartments | UI, API, CLI | -| [Alibaba Cloud](/user-guide/providers/alibabacloud/getting-started-alibabacloud) | Official | Accounts | UI, API, CLI | -| [Cloudflare](/user-guide/providers/cloudflare/getting-started-cloudflare) | Official | Accounts | UI, API, CLI | -| [Infra as Code](/user-guide/providers/iac/getting-started-iac) | Official | Repositories | UI, API, CLI | -| [MongoDB Atlas](/user-guide/providers/mongodbatlas/getting-started-mongodbatlas) | Official | Organizations | UI, API, CLI | -| [OpenStack](/user-guide/providers/openstack/getting-started-openstack) | Official | Projects | UI, API, CLI | -| [Vercel](/user-guide/providers/vercel/getting-started-vercel) | Official | Teams / Projects | CLI | -| [LLM](/user-guide/providers/llm/getting-started-llm) | Official | Models | CLI | -| [Image](/user-guide/providers/image/getting-started-image) | Official | Container Images | CLI, API | -| [Google Workspace](/user-guide/providers/googleworkspace/getting-started-googleworkspace) | Official | Domains | CLI | -| **NHN** | Unofficial | Tenants | CLI | +### Cloud Service Providers (Infrastructure) -For more information about the checks and compliance of each provider visit [Prowler Hub](https://hub.prowler.com). +| Provider | Support | Audit Scope/Entities | Interface | +| -------------------------------------------------------------------------------- | ---------- | ------------------------ | ------------ | +| [Alibaba Cloud](/user-guide/providers/alibabacloud/getting-started-alibabacloud) | Official | Accounts | UI, API, CLI | +| [AWS](/user-guide/providers/aws/getting-started-aws) | Official | Accounts | UI, API, CLI | +| [Azure](/user-guide/providers/azure/getting-started-azure) | Official | Subscriptions | UI, API, CLI | +| [Cloudflare](/user-guide/providers/cloudflare/getting-started-cloudflare) | Official | Accounts | UI, API, CLI | +| [Google Cloud](/user-guide/providers/gcp/getting-started-gcp) | Official | Projects | UI, API, CLI | +| **NHN** | Unofficial | Tenants | CLI | +| [OpenStack](/user-guide/providers/openstack/getting-started-openstack) | Official | Projects | UI, API, CLI | +| [Oracle Cloud](/user-guide/providers/oci/getting-started-oci) | Official | Tenancies / Compartments | UI, API, CLI | + +### Infrastructure as Code Providers + +| Provider | Support | Audit Scope/Entities | Interface | +| --------------------------------------------------------------------- | -------- | -------------------- | ------------ | +| [Infra as Code](/user-guide/providers/iac/getting-started-iac) | Official | Repositories | UI, API, CLI | + +### Software as a Service (SaaS) Providers + +| Provider | Support | Audit Scope/Entities | Interface | +| ----------------------------------------------------------------------------------------- | -------- | ---------------------------- | ------------ | +| [GitHub](/user-guide/providers/github/getting-started-github) | Official | Organizations / Repositories | UI, API, CLI | +| [Google Workspace](/user-guide/providers/googleworkspace/getting-started-googleworkspace) | Official | Domains | CLI | +| [LLM](/user-guide/providers/llm/getting-started-llm) | Official | Models | CLI | +| [M365](/user-guide/providers/microsoft365/getting-started-m365) | Official | Tenants | UI, API, CLI | +| [MongoDB Atlas](/user-guide/providers/mongodbatlas/getting-started-mongodbatlas) | Official | Organizations | UI, API, CLI | +| [Vercel](/user-guide/providers/vercel/getting-started-vercel) | Official | Teams / Projects | CLI | + +### Kubernetes + +| Provider | Support | Audit Scope/Entities | Interface | +| -------------------------------------------------------------------------- | -------- | -------------------- | ------------ | +| [Kubernetes](/user-guide/providers/kubernetes/getting-started-k8s) | Official | Clusters | UI, API, CLI | + +### Containers + +| Provider | Support | Audit Scope/Entities | Interface | +| ------------------------------------------------------------------- | -------- | -------------------- | --------- | +| [Image](/user-guide/providers/image/getting-started-image) | Official | Container Images / Registries | CLI, API | + +### Custom Providers (Prowler Cloud Enterprise Only) + +| Provider | Support | Audit Scope/Entities | Interface | +| -------------------- | -------- | -------------------- | --------- | +| VMware/Broadcom VCF | Official | Infrastructure | CLI | + +For more information about the checks and compliance of each provider, visit [Prowler Hub](https://hub.prowler.com). ## Where to go next? diff --git a/docs/user-guide/cli/tutorials/pentesting.mdx b/docs/user-guide/cli/tutorials/pentesting.mdx index f59c9ccc2e..0ad5313611 100644 --- a/docs/user-guide/cli/tutorials/pentesting.mdx +++ b/docs/user-guide/cli/tutorials/pentesting.mdx @@ -66,22 +66,38 @@ prowler --categories internet-exposed ### Shodan -Prowler allows you check if any public IPs in your Cloud environments are exposed in Shodan with the `-N`/`--shodan ` option: +Prowler can check whether any public IPs in cloud environments are exposed in Shodan using the `-N`/`--shodan` option. -For example, you can check if any of your AWS Elastic Compute Cloud (EC2) instances has an elastic IP exposed in Shodan: +#### Using the Environment Variable (Recommended) + +Set the `SHODAN_API_KEY` environment variable to avoid exposing the API key in process listings and shell history: ```console -prowler aws -N/--shodan -c ec2_elastic_ip_shodan +export SHODAN_API_KEY= ``` -Also, you can check if any of your Azure Subscription has an public IP exposed in Shodan: +Then run Prowler with the `--shodan` flag (no value needed): ```console -prowler azure -N/--shodan -c network_public_ip_shodan +prowler aws --shodan -c ec2_elastic_ip_shodan ``` -And finally, you can check if any of your GCP projects has an public IP address exposed in Shodan: - ```console -prowler gcp -N/--shodan -c compute_public_address_shodan +prowler azure --shodan -c network_public_ip_shodan ``` + +```console +prowler gcp --shodan -c compute_public_address_shodan +``` + +#### Using the CLI Flag + +Alternatively, pass the API key directly on the command line: + +```console +prowler aws --shodan -c ec2_elastic_ip_shodan +``` + + +Passing secret values directly on the command line exposes them in process listings and shell history. Prowler CLI displays a warning when this pattern is detected. Use the `SHODAN_API_KEY` environment variable instead. + diff --git a/docs/user-guide/providers/alibabacloud/getting-started-alibabacloud.mdx b/docs/user-guide/providers/alibabacloud/getting-started-alibabacloud.mdx index 0a1d54b079..de8708d867 100644 --- a/docs/user-guide/providers/alibabacloud/getting-started-alibabacloud.mdx +++ b/docs/user-guide/providers/alibabacloud/getting-started-alibabacloud.mdx @@ -37,7 +37,7 @@ Before you begin, make sure you have: ![Get Account ID](/images/providers/alibaba-account-id.png) -### Step 2: Access Prowler Cloud or Prowler App +### Step 2: Access Prowler Cloud 1. Navigate to [Prowler Cloud](https://cloud.prowler.com/) or launch [Prowler App](/user-guide/tutorials/prowler-app) 2. Go to "Configuration" > "Cloud Providers" diff --git a/docs/user-guide/providers/aws/getting-started-aws.mdx b/docs/user-guide/providers/aws/getting-started-aws.mdx index 7d003d9821..2c94700b15 100644 --- a/docs/user-guide/providers/aws/getting-started-aws.mdx +++ b/docs/user-guide/providers/aws/getting-started-aws.mdx @@ -2,7 +2,7 @@ title: 'Getting Started With AWS on Prowler' --- -## Prowler App +## Prowler Cloud @@ -16,7 +16,7 @@ title: 'Getting Started With AWS on Prowler' ![Account ID detail](/images/providers/aws-account-id.png) -### Step 2: Access Prowler Cloud or Prowler App +### Step 2: Access Prowler Cloud 1. Navigate to [Prowler Cloud](https://cloud.prowler.com/) or launch [Prowler App](/user-guide/tutorials/prowler-app) 2. Go to "Configuration" > "Cloud Providers" diff --git a/docs/user-guide/providers/azure/getting-started-azure.mdx b/docs/user-guide/providers/azure/getting-started-azure.mdx index a5299c614b..456c226aab 100644 --- a/docs/user-guide/providers/azure/getting-started-azure.mdx +++ b/docs/user-guide/providers/azure/getting-started-azure.mdx @@ -2,7 +2,7 @@ title: 'Getting Started With Azure on Prowler' --- -## Prowler App +## Prowler Cloud > Walkthrough video onboarding an Azure Subscription using Service Principal. @@ -32,7 +32,7 @@ For detailed instructions on how to create the Service Principal and configure p --- -### Step 2: Access Prowler App +### Step 2: Access Prowler Cloud 1. Navigate to [Prowler Cloud](https://cloud.prowler.com/) or launch [Prowler App](/user-guide/tutorials/prowler-app) 2. Navigate to `Configuration` > `Cloud Providers` @@ -51,7 +51,7 @@ For detailed instructions on how to create the Service Principal and configure p ![Add Subscription ID](/images/providers/add-subscription-id.png) -### Step 3: Add Credentials to Prowler App +### Step 3: Add Credentials to Prowler Cloud For Azure, Prowler App uses a service principal application to authenticate. For more information about the process of creating and adding permissions to a service principal refer to this [section](/user-guide/providers/azure/authentication). When you finish creating and adding the [Entra](/user-guide/providers/azure/create-prowler-service-principal#assigning-proper-permissions) and [Subscription](/user-guide/providers/azure/subscriptions) scope permissions to the service principal, enter the `Tenant ID`, `Client ID` and `Client Secret` of the service principal application. diff --git a/docs/user-guide/providers/gcp/getting-started-gcp.mdx b/docs/user-guide/providers/gcp/getting-started-gcp.mdx index 1cdc587d45..c70250c8a9 100644 --- a/docs/user-guide/providers/gcp/getting-started-gcp.mdx +++ b/docs/user-guide/providers/gcp/getting-started-gcp.mdx @@ -2,7 +2,7 @@ title: 'Getting Started With GCP on Prowler' --- -## Prowler App +## Prowler Cloud ### Step 1: Get the GCP Project ID @@ -11,7 +11,7 @@ title: 'Getting Started With GCP on Prowler' ![Get the Project ID](/images/providers/project-id-console.png) -### Step 2: Access Prowler Cloud or Prowler App +### Step 2: Access Prowler Cloud 1. Navigate to [Prowler Cloud](https://cloud.prowler.com/) or launch [Prowler App](/user-guide/tutorials/prowler-app) 2. Go to "Configuration" > "Cloud Providers" diff --git a/docs/user-guide/providers/googleworkspace/authentication.mdx b/docs/user-guide/providers/googleworkspace/authentication.mdx index d8812fa7b3..b070c443b3 100644 --- a/docs/user-guide/providers/googleworkspace/authentication.mdx +++ b/docs/user-guide/providers/googleworkspace/authentication.mdx @@ -6,17 +6,19 @@ import { VersionBadge } from "/snippets/version-badge.mdx" -Prowler for Google Workspace uses a **Service Account with Domain-Wide Delegation** to authenticate to the Google Workspace Admin SDK. This allows Prowler to read directory data on behalf of a super administrator without requiring an interactive login. +Prowler for Google Workspace uses a **Service Account with Domain-Wide Delegation** to authenticate to the Google Workspace Admin SDK and the Cloud Identity Policy API. This allows Prowler to read directory data and domain-level application policies on behalf of a super administrator without requiring an interactive login. ## Required Open Authorization (OAuth) Scopes -Prowler requests the following read-only OAuth 2.0 scopes from the Google Workspace Admin SDK: +Prowler requests the following read-only OAuth 2.0 scopes: | Scope | Description | |-------|-------------| | `https://www.googleapis.com/auth/admin.directory.user.readonly` | Read access to user accounts and their admin status | | `https://www.googleapis.com/auth/admin.directory.domain.readonly` | Read access to domain information | | `https://www.googleapis.com/auth/admin.directory.customer.readonly` | Read access to customer information (Customer ID) | +| `https://www.googleapis.com/auth/cloud-identity.policies.readonly` | Read access to domain-level application policies (required for Calendar service checks) | +| `https://www.googleapis.com/auth/admin.directory.rolemanagement.readonly` | Read access to admin roles and role assignments | The delegated user must be a **super administrator** in your Google Workspace organization. Using a non-admin account will result in permission errors when accessing the Admin SDK. @@ -30,13 +32,24 @@ If no GCP project exists, create one at [https://console.cloud.google.com](https The project is only used to host the Service Account — it does not need to have any Google Workspace data in it. -### Step 2: Enable the Admin SDK API +### Step 2: Enable Required APIs -1. Navigate to the [Google Cloud Console](https://console.cloud.google.com) -2. Select the target project -3. Navigate to **APIs & Services → Library** -4. Search for **Admin SDK API** -5. Click **Enable** +In the [Google Cloud Console](https://console.cloud.google.com), select the target project and navigate to **APIs & Services → Library**. Search for and enable each of the following APIs: + +| API | Required For | +|-----|--------------| +| **Admin SDK API** | Directory service checks (users, roles, domains) | +| **Cloud Identity API** | Calendar service checks (domain-level sharing and invitation policies) | + +For each API: + +1. Search for the API name in the library +2. Click the API result +3. Click **Enable** + + +Both APIs must be enabled in the same GCP project that hosts the Service Account. Calendar checks will return no findings if the Cloud Identity API is not enabled. + ### Step 3: Create a Service Account @@ -73,7 +86,7 @@ This JSON key grants access to your Google Workspace organization. Never commit 6. In the **OAuth scopes** field, enter the following scopes as a comma-separated list: ``` -https://www.googleapis.com/auth/admin.directory.user.readonly,https://www.googleapis.com/auth/admin.directory.domain.readonly,https://www.googleapis.com/auth/admin.directory.customer.readonly +https://www.googleapis.com/auth/admin.directory.user.readonly,https://www.googleapis.com/auth/admin.directory.domain.readonly,https://www.googleapis.com/auth/admin.directory.customer.readonly,https://www.googleapis.com/auth/cloud-identity.policies.readonly,https://www.googleapis.com/auth/admin.directory.rolemanagement.readonly ``` 7. Click **Authorize** @@ -114,7 +127,7 @@ The delegated user must be provided via the `GOOGLEWORKSPACE_DELEGATED_USER` env - **Use environment variables** — Never hardcode credentials in scripts or commands - **Use a dedicated Service Account** — Create one specifically for Prowler, separate from other integrations -- **Use read-only scopes** — Prowler only requires the three read-only scopes listed above +- **Use read-only scopes** — Prowler only requires the read-only scopes listed above - **Restrict key access** — Set file permissions to `600` on the JSON key file - **Rotate keys regularly** — Delete and regenerate the JSON key periodically - **Use a least-privilege super admin** — Consider using a dedicated super admin account for Prowler's delegated user rather than a personal admin account @@ -151,7 +164,7 @@ python3 -c "import json; json.load(open('/path/to/key.json'))" && echo "Valid JS The Service Account cannot impersonate the delegated user. This usually means Domain-Wide Delegation has not been configured, or the OAuth scopes are incorrect. Verify: - The Service Account Client ID is correctly entered in the Admin Console -- All three required OAuth scopes are included +- All required OAuth scopes are included - The delegated user is a super administrator ### Permission Denied on Admin SDK Calls @@ -159,5 +172,14 @@ The Service Account cannot impersonate the delegated user. This usually means Do If Prowler connects but returns empty results or permission errors for specific API calls: - Confirm Domain-Wide Delegation is fully propagated (wait a few minutes after setup) -- Verify all three scopes are authorized in the Admin Console +- Verify all scopes are authorized in the Admin Console - Ensure the delegated user is an active super administrator + +### Calendar Checks Return No Findings + +If the Directory checks run successfully but the Calendar checks (e.g., `calendar_external_sharing_primary_calendar`) return no findings, the Cloud Identity Policy API is not reachable for this Service Account. Verify: + +- The **Cloud Identity API** is enabled in the GCP project hosting the Service Account (Step 2) +- The scope `https://www.googleapis.com/auth/cloud-identity.policies.readonly` is included in the Domain-Wide Delegation OAuth scopes list in the Admin Console (Step 5) +- The delegated user is a super administrator (the Policy API only returns data to super admins) +- Domain-Wide Delegation has had time to propagate after adding the new scope (a few minutes) diff --git a/docs/user-guide/providers/googleworkspace/getting-started-googleworkspace.mdx b/docs/user-guide/providers/googleworkspace/getting-started-googleworkspace.mdx index 361de533e1..af09ab75c3 100644 --- a/docs/user-guide/providers/googleworkspace/getting-started-googleworkspace.mdx +++ b/docs/user-guide/providers/googleworkspace/getting-started-googleworkspace.mdx @@ -78,7 +78,7 @@ The Service Account JSON is the full content of the key file downloaded when cre ![Check Connection](/images/providers/googleworkspace-check-connection.png) -If the connection test fails, verify that Domain-Wide Delegation is properly configured and that all three OAuth scopes are authorized. It may take a few minutes for delegation changes to propagate. See the [Troubleshooting](/user-guide/providers/googleworkspace/authentication#troubleshooting) section for common errors. +If the connection test fails, verify that Domain-Wide Delegation is properly configured and that all required OAuth scopes are authorized. It may take a few minutes for delegation changes to propagate. See the [Troubleshooting](/user-guide/providers/googleworkspace/authentication#troubleshooting) section for common errors. ### Step 5: Launch the Scan diff --git a/docs/user-guide/providers/iac/getting-started-iac.mdx b/docs/user-guide/providers/iac/getting-started-iac.mdx index 849571b2c8..2aba3cf551 100644 --- a/docs/user-guide/providers/iac/getting-started-iac.mdx +++ b/docs/user-guide/providers/iac/getting-started-iac.mdx @@ -31,7 +31,7 @@ Prowler IaC provider scans the following Infrastructure as Code configurations f - Mutelist logic ([filtering](https://trivy.dev/latest/docs/configuration/filtering/)) is handled by Trivy, not Prowler. - Results are output in the same formats as other Prowler providers (CSV, JSON, HTML, etc.). -## Prowler App +## Prowler Cloud diff --git a/docs/user-guide/providers/kubernetes/getting-started-k8s.mdx b/docs/user-guide/providers/kubernetes/getting-started-k8s.mdx index c4f4822792..aff63b81a3 100644 --- a/docs/user-guide/providers/kubernetes/getting-started-k8s.mdx +++ b/docs/user-guide/providers/kubernetes/getting-started-k8s.mdx @@ -2,7 +2,7 @@ title: 'Getting Started with Kubernetes' --- -## Prowler App +## Prowler Cloud ### Step 1: Access Prowler Cloud/App diff --git a/docs/user-guide/providers/oci/getting-started-oci.mdx b/docs/user-guide/providers/oci/getting-started-oci.mdx index 8affa2f992..459d9ad685 100644 --- a/docs/user-guide/providers/oci/getting-started-oci.mdx +++ b/docs/user-guide/providers/oci/getting-started-oci.mdx @@ -14,7 +14,7 @@ The following steps apply to Prowler Cloud and the self-hosted Prowler App. 3. Generate or locate the API key fingerprint and private key for that user. Follow the [Config File Authentication steps](/user-guide/providers/oci/authentication#config-file-authentication-manual-api-key-setup) to create or rotate the key pair and copy the fingerprint. 4. Note the **Region** identifier to scan (for example, `us-ashburn-1`). -### Step 2: Access Prowler Cloud or Prowler App +### Step 2: Access Prowler Cloud 1. Navigate to [Prowler Cloud](https://cloud.prowler.com/) or launch [Prowler App](/user-guide/tutorials/prowler-app). 2. Go to **Configuration** → **Cloud Providers** and click **Add Cloud Provider**. ![Add OCI Cloud Provider](./images/oci-add-cloud-provider.png) diff --git a/docs/user-guide/providers/vercel/getting-started-vercel.mdx b/docs/user-guide/providers/vercel/getting-started-vercel.mdx index 67d4853d18..ddec26e6dc 100644 --- a/docs/user-guide/providers/vercel/getting-started-vercel.mdx +++ b/docs/user-guide/providers/vercel/getting-started-vercel.mdx @@ -13,9 +13,63 @@ Set up authentication for Vercel with the [Vercel Authentication](/user-guide/pr - Create a Vercel API Token with access to the target team - Identify the Team ID (optional, required to scope the scan to a single team) + + + Onboard Vercel using Prowler Cloud + + + Onboard Vercel using Prowler CLI + + + +## Prowler Cloud + + + +### Step 1: Add the Provider + +1. Go to [Prowler Cloud](https://cloud.prowler.com/) or launch [Prowler App](/user-guide/tutorials/prowler-app). +2. Navigate to "Configuration" > "Cloud Providers". + + ![Cloud Providers Page](/images/prowler-app/cloud-providers-page.png) + +3. Click "Add Cloud Provider". + + ![Add a Cloud Provider](/images/prowler-app/add-cloud-provider.png) + +4. Select "Vercel". + + ![Select Vercel](/images/providers/select-vercel-prowler-cloud.png) + +5. Enter the **Team ID** and an optional alias, then click "Next". + + ![Add Vercel Team ID](/images/providers/vercel-team-id-form.png) + + +The Team ID can be found in the Vercel Dashboard under "Settings" > "General". It follows the format `team_xxxxxxxxxxxxxxxxxxxx`. For detailed instructions, see the [Authentication guide](/user-guide/providers/vercel/authentication). + + +### Step 2: Provide Credentials + +1. Enter the **API Token** created in the Vercel Dashboard. + + ![API Token Form](/images/providers/vercel-token-form.png) + +For the complete token creation workflow, follow the [Authentication guide](/user-guide/providers/vercel/authentication#api-token). + +### Step 3: Launch the Scan + +1. Review the connection summary. +2. Choose the scan schedule: run a single scan or set up daily scans (every 24 hours). +3. Click **Launch Scan** to start auditing Vercel. + + ![Launch Scan](/images/providers/vercel-launch-scan.png) + +--- + ## Prowler CLI - + ### Step 1: Set Up Authentication diff --git a/docs/user-guide/tutorials/prowler-app-multi-tenant.mdx b/docs/user-guide/tutorials/prowler-app-multi-tenant.mdx new file mode 100644 index 0000000000..69577c2f0c --- /dev/null +++ b/docs/user-guide/tutorials/prowler-app-multi-tenant.mdx @@ -0,0 +1,151 @@ +--- +title: 'Managing Organizations (Multi-Tenant)' +--- + +import { VersionBadge } from "/snippets/version-badge.mdx" + + + +Prowler App supports multi-tenancy through **Organizations**, allowing users to belong to multiple isolated environments within a single account. Each organization maintains its own providers, scans, findings, and user memberships, ensuring complete data separation between teams or business units. + +## Key Concepts + +* **Organization (Tenant):** An isolated workspace containing its own providers, scans, findings, roles, and users. Every Prowler account operates within at least one organization. +* **Membership:** The association between a user and an organization, including the membership role (`owner` or `member`). +* **Active Organization:** The organization currently in use for the session. All actions (scans, findings, provider management) apply to the active organization. + + +When a new account is created without an invitation, a default organization is automatically provisioned. Accounts created through an invitation join the inviter's organization instead. + + + +## Viewing Organizations + +To view all organizations associated with an account, navigate to the **Profile** page. The **Organizations** card displays every organization the user belongs to, including the role, name, join date, and whether it is the currently active organization. + +Organizations card in profile page + +## Creating an Organization + +To create a new organization: + +1. Navigate to the **Profile** page. + +2. In the **Organizations** card, click the **Create organization** button. + + Create organization button + +3. Enter a name for the new organization (maximum 100 characters). + + Create organization modal + +4. Click **Create**. The session automatically switches to the newly created organization. + + +Creating an organization requires being authenticated. Any user can create a new organization regardless of their current role. + + + +## Switching Between Organizations + +To switch the active organization: + +1. Navigate to the **Profile** page. + +2. In the **Organizations** card, locate the organization to switch to. + +3. Click the **Switch** button next to the desired organization. + +4. Confirm the switch in the dialog. The page reloads with the new organization's context, and all subsequent actions apply to it. + + Switch organization confirmation modal + + +The currently active organization is indicated by an **Active** badge. Switching updates the session tokens, so the page will reload automatically. + + + +## Editing an Organization Name + +Organization owners with the **Manage Account** permission can rename an organization: + +1. Navigate to the **Profile** page. + +2. In the **Organizations** card, click the **Edit** button next to the organization. + +3. Update the name and save the changes. + + Edit organization name modal + +## Deleting an Organization + +Organization owners with the **Manage Account** permission can delete an organization, provided they belong to at least two organizations (the last remaining organization cannot be deleted). + +### Deleting a Non-Active Organization + +1. Navigate to the **Profile** page. + +2. Click the **Delete** button next to the organization to remove. + +3. Type the organization name to confirm deletion. + + Delete organization confirmation modal + +4. Click **Delete**. The organization and all its associated data (providers, scans, findings) are permanently removed. + +### Deleting the Active Organization + +When deleting the currently active organization, an additional step is required: + +1. Navigate to the **Profile** page. + +2. Click the **Delete** button next to the active organization. + +3. Select which organization to switch to after deletion. + +4. Type the organization name to confirm. + + Delete active organization modal with target selection + +5. Click **Delete**. The session switches to the selected organization, and the deleted organization's data is permanently removed. + + +Deleting an organization is irreversible. All providers, scans, findings, and configuration data within the organization are permanently deleted. Users who belong only to the deleted organization will lose access to Prowler. + + +## Accepting an Invitation to an Organization + +When invited to join an organization, the invited user receives a link to accept the invitation. The flow adapts depending on whether the user already has a Prowler account: + +### Existing Users + +1. Open the invitation link. + +2. If already authenticated, the invitation is accepted automatically and the user is redirected to Prowler App. + +3. If not authenticated, choose **I have an account -- Sign in**, authenticate with existing credentials, and the invitation is accepted upon sign-in. + + Sign in screen after choosing I have an account from invitation + +### New Users + +1. Open the invitation link. + +2. Choose **I'm new -- Create an account**. + +3. Complete the sign-up process. Upon account creation, the invitation is accepted and the user joins the inviter's organization. + + +Invitations expire after 7 days. If an invitation has expired, contact the organization administrator to send a new one. For more details on invitation management, see [Managing Users and Role-Based Access Control (RBAC)](/user-guide/tutorials/prowler-app-rbac#invitations). + + + +## Permissions Reference + +| Action | Required Conditions | +|--------|-------------------| +| View organizations | Any authenticated user | +| Create an organization | Any authenticated user | +| Switch organizations | Any authenticated user | +| Edit organization name | Organization owner with **Manage Account** permission | +| Delete an organization | Organization owner with **Manage Account** permission; must belong to more than one organization | diff --git a/mcp_server/CHANGELOG.md b/mcp_server/CHANGELOG.md index 21aa71dbf2..e94487e257 100644 --- a/mcp_server/CHANGELOG.md +++ b/mcp_server/CHANGELOG.md @@ -8,6 +8,10 @@ All notable changes to the **Prowler MCP Server** are documented in this file. - Resource events tool to get timeline for a resource (who, what, when) [(#10412)](https://github.com/prowler-cloud/prowler/pull/10412) +### 🔄 Changed + +- Pin `httpx` dependency to exact version for reproducible installs [(#10593)](https://github.com/prowler-cloud/prowler/pull/10593) + ### 🔐 Security - `authlib` bumped from 1.6.5 to 1.6.9 to fix CVE-2026-28802 (JWT `alg: none` validation bypass) [(#10579)](https://github.com/prowler-cloud/prowler/pull/10579) diff --git a/mcp_server/pyproject.toml b/mcp_server/pyproject.toml index 4ea4a9859e..2a6885fedb 100644 --- a/mcp_server/pyproject.toml +++ b/mcp_server/pyproject.toml @@ -5,7 +5,7 @@ requires = ["setuptools>=61.0", "wheel"] [project] dependencies = [ "fastmcp==2.14.0", - "httpx>=0.28.0" + "httpx==0.28.1" ] description = "MCP server for Prowler ecosystem" name = "prowler-mcp" diff --git a/mcp_server/uv.lock b/mcp_server/uv.lock index ca1d9482be..bcff18e2d9 100644 --- a/mcp_server/uv.lock +++ b/mcp_server/uv.lock @@ -727,7 +727,7 @@ dependencies = [ [package.metadata] requires-dist = [ { name = "fastmcp", specifier = "==2.14.0" }, - { name = "httpx", specifier = ">=0.28.0" }, + { name = "httpx", specifier = "==0.28.1" }, ] [[package]] diff --git a/poetry.lock b/poetry.lock index 1206608902..c0ffadab6f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "about-time" @@ -38,132 +38,132 @@ files = [ [[package]] name = "aiohttp" -version = "3.13.3" +version = "3.13.5" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "aiohttp-3.13.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a372fd5afd301b3a89582817fdcdb6c34124787c70dbcc616f259013e7eef7"}, - {file = "aiohttp-3.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:147e422fd1223005c22b4fe080f5d93ced44460f5f9c105406b753612b587821"}, - {file = "aiohttp-3.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859bd3f2156e81dd01432f5849fc73e2243d4a487c4fd26609b1299534ee1845"}, - {file = "aiohttp-3.13.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dca68018bf48c251ba17c72ed479f4dafe9dbd5a73707ad8d28a38d11f3d42af"}, - {file = "aiohttp-3.13.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fee0c6bc7db1de362252affec009707a17478a00ec69f797d23ca256e36d5940"}, - {file = "aiohttp-3.13.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c048058117fd649334d81b4b526e94bde3ccaddb20463a815ced6ecbb7d11160"}, - {file = "aiohttp-3.13.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:215a685b6fbbfcf71dfe96e3eba7a6f58f10da1dfdf4889c7dd856abe430dca7"}, - {file = "aiohttp-3.13.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2c184bb1fe2cbd2cefba613e9db29a5ab559323f994b6737e370d3da0ac455"}, - {file = "aiohttp-3.13.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75ca857eba4e20ce9f546cd59c7007b33906a4cd48f2ff6ccf1ccfc3b646f279"}, - {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81e97251d9298386c2b7dbeb490d3d1badbdc69107fb8c9299dd04eb39bddc0e"}, - {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0e2d366af265797506f0283487223146af57815b388623f0357ef7eac9b209d"}, - {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4e239d501f73d6db1522599e14b9b321a7e3b1de66ce33d53a765d975e9f4808"}, - {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0db318f7a6f065d84cb1e02662c526294450b314a02bd9e2a8e67f0d8564ce40"}, - {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bfc1cc2fe31a6026a8a88e4ecfb98d7f6b1fec150cfd708adbfd1d2f42257c29"}, - {file = "aiohttp-3.13.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af71fff7bac6bb7508956696dce8f6eec2bbb045eceb40343944b1ae62b5ef11"}, - {file = "aiohttp-3.13.3-cp310-cp310-win32.whl", hash = "sha256:37da61e244d1749798c151421602884db5270faf479cf0ef03af0ff68954c9dd"}, - {file = "aiohttp-3.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:7e63f210bc1b57ef699035f2b4b6d9ce096b5914414a49b0997c839b2bd2223c"}, - {file = "aiohttp-3.13.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b6073099fb654e0a068ae678b10feff95c5cae95bbfcbfa7af669d361a8aa6b"}, - {file = "aiohttp-3.13.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cb93e166e6c28716c8c6aeb5f99dfb6d5ccf482d29fe9bf9a794110e6d0ab64"}, - {file = "aiohttp-3.13.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28e027cf2f6b641693a09f631759b4d9ce9165099d2b5d92af9bd4e197690eea"}, - {file = "aiohttp-3.13.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b61b7169ababd7802f9568ed96142616a9118dd2be0d1866e920e77ec8fa92a"}, - {file = "aiohttp-3.13.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:80dd4c21b0f6237676449c6baaa1039abae86b91636b6c91a7f8e61c87f89540"}, - {file = "aiohttp-3.13.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65d2ccb7eabee90ce0503c17716fc77226be026dcc3e65cce859a30db715025b"}, - {file = "aiohttp-3.13.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b179331a481cb5529fca8b432d8d3c7001cb217513c94cd72d668d1248688a3"}, - {file = "aiohttp-3.13.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d4c940f02f49483b18b079d1c27ab948721852b281f8b015c058100e9421dd1"}, - {file = "aiohttp-3.13.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9444f105664c4ce47a2a7171a2418bce5b7bae45fb610f4e2c36045d85911d3"}, - {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:694976222c711d1d00ba131904beb60534f93966562f64440d0c9d41b8cdb440"}, - {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f33ed1a2bf1997a36661874b017f5c4b760f41266341af36febaf271d179f6d7"}, - {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e636b3c5f61da31a92bf0d91da83e58fdfa96f178ba682f11d24f31944cdd28c"}, - {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5d2d94f1f5fcbe40838ac51a6ab5704a6f9ea42e72ceda48de5e6b898521da51"}, - {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2be0e9ccf23e8a94f6f0650ce06042cefc6ac703d0d7ab6c7a917289f2539ad4"}, - {file = "aiohttp-3.13.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9af5e68ee47d6534d36791bbe9b646d2a7c7deb6fc24d7943628edfbb3581f29"}, - {file = "aiohttp-3.13.3-cp311-cp311-win32.whl", hash = "sha256:a2212ad43c0833a873d0fb3c63fa1bacedd4cf6af2fee62bf4b739ceec3ab239"}, - {file = "aiohttp-3.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:642f752c3eb117b105acbd87e2c143de710987e09860d674e068c4c2c441034f"}, - {file = "aiohttp-3.13.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c"}, - {file = "aiohttp-3.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168"}, - {file = "aiohttp-3.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d"}, - {file = "aiohttp-3.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29"}, - {file = "aiohttp-3.13.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3"}, - {file = "aiohttp-3.13.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d"}, - {file = "aiohttp-3.13.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463"}, - {file = "aiohttp-3.13.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc"}, - {file = "aiohttp-3.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf"}, - {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033"}, - {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f"}, - {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679"}, - {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423"}, - {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce"}, - {file = "aiohttp-3.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a"}, - {file = "aiohttp-3.13.3-cp312-cp312-win32.whl", hash = "sha256:b04be762396457bef43f3597c991e192ee7da460a4953d7e647ee4b1c28e7046"}, - {file = "aiohttp-3.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:e3531d63d3bdfa7e3ac5e9b27b2dd7ec9df3206a98e0b3445fa906f233264c57"}, - {file = "aiohttp-3.13.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5dff64413671b0d3e7d5918ea490bdccb97a4ad29b3f311ed423200b2203e01c"}, - {file = "aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:87b9aab6d6ed88235aa2970294f496ff1a1f9adcd724d800e9b952395a80ffd9"}, - {file = "aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:425c126c0dc43861e22cb1c14ba4c8e45d09516d0a3ae0a3f7494b79f5f233a3"}, - {file = "aiohttp-3.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f9120f7093c2a32d9647abcaf21e6ad275b4fbec5b55969f978b1a97c7c86bf"}, - {file = "aiohttp-3.13.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:697753042d57f4bf7122cab985bf15d0cef23c770864580f5af4f52023a56bd6"}, - {file = "aiohttp-3.13.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6de499a1a44e7de70735d0b39f67c8f25eb3d91eb3103be99ca0fa882cdd987d"}, - {file = "aiohttp-3.13.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:37239e9f9a7ea9ac5bf6b92b0260b01f8a22281996da609206a84df860bc1261"}, - {file = "aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f76c1e3fe7d7c8afad7ed193f89a292e1999608170dcc9751a7462a87dfd5bc0"}, - {file = "aiohttp-3.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fc290605db2a917f6e81b0e1e0796469871f5af381ce15c604a3c5c7e51cb730"}, - {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4021b51936308aeea0367b8f006dc999ca02bc118a0cc78c303f50a2ff6afb91"}, - {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:49a03727c1bba9a97d3e93c9f93ca03a57300f484b6e935463099841261195d3"}, - {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3d9908a48eb7416dc1f4524e69f1d32e5d90e3981e4e37eb0aa1cd18f9cfa2a4"}, - {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2712039939ec963c237286113c68dbad80a82a4281543f3abf766d9d73228998"}, - {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7bfdc049127717581866fa4708791220970ce291c23e28ccf3922c700740fdc0"}, - {file = "aiohttp-3.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8057c98e0c8472d8846b9c79f56766bcc57e3e8ac7bfd510482332366c56c591"}, - {file = "aiohttp-3.13.3-cp313-cp313-win32.whl", hash = "sha256:1449ceddcdbcf2e0446957863af03ebaaa03f94c090f945411b61269e2cb5daf"}, - {file = "aiohttp-3.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:693781c45a4033d31d4187d2436f5ac701e7bbfe5df40d917736108c1cc7436e"}, - {file = "aiohttp-3.13.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:ea37047c6b367fd4bd632bff8077449b8fa034b69e812a18e0132a00fae6e808"}, - {file = "aiohttp-3.13.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6fc0e2337d1a4c3e6acafda6a78a39d4c14caea625124817420abceed36e2415"}, - {file = "aiohttp-3.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c685f2d80bb67ca8c3837823ad76196b3694b0159d232206d1e461d3d434666f"}, - {file = "aiohttp-3.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e377758516d262bde50c2584fc6c578af272559c409eecbdd2bae1601184d6"}, - {file = "aiohttp-3.13.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:34749271508078b261c4abb1767d42b8d0c0cc9449c73a4df494777dc55f0687"}, - {file = "aiohttp-3.13.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82611aeec80eb144416956ec85b6ca45a64d76429c1ed46ae1b5f86c6e0c9a26"}, - {file = "aiohttp-3.13.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2fff83cfc93f18f215896e3a190e8e5cb413ce01553901aca925176e7568963a"}, - {file = "aiohttp-3.13.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbe7d4cecacb439e2e2a8a1a7b935c25b812af7a5fd26503a66dadf428e79ec1"}, - {file = "aiohttp-3.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b928f30fe49574253644b1ca44b1b8adbd903aa0da4b9054a6c20fc7f4092a25"}, - {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b5e8fe4de30df199155baaf64f2fcd604f4c678ed20910db8e2c66dc4b11603"}, - {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:8542f41a62bcc58fc7f11cf7c90e0ec324ce44950003feb70640fc2a9092c32a"}, - {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5e1d8c8b8f1d91cd08d8f4a3c2b067bfca6ec043d3ff36de0f3a715feeedf926"}, - {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:90455115e5da1c3c51ab619ac57f877da8fd6d73c05aacd125c5ae9819582aba"}, - {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:042e9e0bcb5fba81886c8b4fbb9a09d6b8a00245fd8d88e4d989c1f96c74164c"}, - {file = "aiohttp-3.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2eb752b102b12a76ca02dff751a801f028b4ffbbc478840b473597fc91a9ed43"}, - {file = "aiohttp-3.13.3-cp314-cp314-win32.whl", hash = "sha256:b556c85915d8efaed322bf1bdae9486aa0f3f764195a0fb6ee962e5c71ef5ce1"}, - {file = "aiohttp-3.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9bf9f7a65e7aa20dd764151fb3d616c81088f91f8df39c3893a536e279b4b984"}, - {file = "aiohttp-3.13.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:05861afbbec40650d8a07ea324367cb93e9e8cc7762e04dd4405df99fa65159c"}, - {file = "aiohttp-3.13.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2fc82186fadc4a8316768d61f3722c230e2c1dcab4200d52d2ebdf2482e47592"}, - {file = "aiohttp-3.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0add0900ff220d1d5c5ebbf99ed88b0c1bbf87aa7e4262300ed1376a6b13414f"}, - {file = "aiohttp-3.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:568f416a4072fbfae453dcf9a99194bbb8bdeab718e08ee13dfa2ba0e4bebf29"}, - {file = "aiohttp-3.13.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:add1da70de90a2569c5e15249ff76a631ccacfe198375eead4aadf3b8dc849dc"}, - {file = "aiohttp-3.13.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b47b7ba335d2e9b1239fa571131a87e2d8ec96b333e68b2a305e7a98b0bae2"}, - {file = "aiohttp-3.13.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4dce1c718e38081c8f35f323209d4c1df7d4db4bab1b5c88a6b4d12b74587"}, - {file = "aiohttp-3.13.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34bac00a67a812570d4a460447e1e9e06fae622946955f939051e7cc895cfab8"}, - {file = "aiohttp-3.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19884d2ee70b06d9204b2727a7b9f983d0c684c650254679e716b0b77920632"}, - {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ca7f2bb6ba8348a3614c7918cc4bb73268c5ac2a207576b7afea19d3d9f64"}, - {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b0d95340658b9d2f11d9697f59b3814a9d3bb4b7a7c20b131df4bcef464037c0"}, - {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1e53262fd202e4b40b70c3aff944a8155059beedc8a89bba9dc1f9ef06a1b56"}, - {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d60ac9663f44168038586cab2157e122e46bdef09e9368b37f2d82d354c23f72"}, - {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:90751b8eed69435bac9ff4e3d2f6b3af1f57e37ecb0fbeee59c0174c9e2d41df"}, - {file = "aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fc353029f176fd2b3ec6cfc71be166aba1936fe5d73dd1992ce289ca6647a9aa"}, - {file = "aiohttp-3.13.3-cp314-cp314t-win32.whl", hash = "sha256:2e41b18a58da1e474a057b3d35248d8320029f61d70a37629535b16a0c8f3767"}, - {file = "aiohttp-3.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344"}, - {file = "aiohttp-3.13.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:31a83ea4aead760dfcb6962efb1d861db48c34379f2ff72db9ddddd4cda9ea2e"}, - {file = "aiohttp-3.13.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:988a8c5e317544fdf0d39871559e67b6341065b87fceac641108c2096d5506b7"}, - {file = "aiohttp-3.13.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b174f267b5cfb9a7dba9ee6859cecd234e9a681841eb85068059bc867fb8f02"}, - {file = "aiohttp-3.13.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:947c26539750deeaee933b000fb6517cc770bbd064bad6033f1cff4803881e43"}, - {file = "aiohttp-3.13.3-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9ebf57d09e131f5323464bd347135a88622d1c0976e88ce15b670e7ad57e4bd6"}, - {file = "aiohttp-3.13.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4ae5b5a0e1926e504c81c5b84353e7a5516d8778fbbff00429fe7b05bb25cbce"}, - {file = "aiohttp-3.13.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2ba0eea45eb5cc3172dbfc497c066f19c41bac70963ea1a67d51fc92e4cf9a80"}, - {file = "aiohttp-3.13.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bae5c2ed2eae26cc382020edad80d01f36cb8e746da40b292e68fec40421dc6a"}, - {file = "aiohttp-3.13.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8a60e60746623925eab7d25823329941aee7242d559baa119ca2b253c88a7bd6"}, - {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e50a2e1404f063427c9d027378472316201a2290959a295169bcf25992d04558"}, - {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:9a9dc347e5a3dc7dfdbc1f82da0ef29e388ddb2ed281bfce9dd8248a313e62b7"}, - {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b46020d11d23fe16551466c77823df9cc2f2c1e63cc965daf67fa5eec6ca1877"}, - {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:69c56fbc1993fa17043e24a546959c0178fe2b5782405ad4559e6c13975c15e3"}, - {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b99281b0704c103d4e11e72a76f1b543d4946fea7dd10767e7e1b5f00d4e5704"}, - {file = "aiohttp-3.13.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:40c5e40ecc29ba010656c18052b877a1c28f84344825efa106705e835c28530f"}, - {file = "aiohttp-3.13.3-cp39-cp39-win32.whl", hash = "sha256:56339a36b9f1fc708260c76c87e593e2afb30d26de9ae1eb445b5e051b98a7a1"}, - {file = "aiohttp-3.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:c6b8568a3bb5819a0ad087f16d40e5a3fb6099f39ea1d5625a3edc1e923fc538"}, - {file = "aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88"}, + {file = "aiohttp-3.13.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:02222e7e233295f40e011c1b00e3b0bd451f22cf853a0304c3595633ee47da4b"}, + {file = "aiohttp-3.13.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bace460460ed20614fa6bc8cb09966c0b8517b8c58ad8046828c6078d25333b5"}, + {file = "aiohttp-3.13.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f546a4dc1e6a5edbb9fd1fd6ad18134550e096a5a43f4ad74acfbd834fc6670"}, + {file = "aiohttp-3.13.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c86969d012e51b8e415a8c6ce96f7857d6a87d6207303ab02d5d11ef0cad2274"}, + {file = "aiohttp-3.13.5-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b6f6cd1560c5fa427e3b6074bb24d2c64e225afbb7165008903bd42e4e33e28a"}, + {file = "aiohttp-3.13.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:636bc362f0c5bbc7372bc3ae49737f9e3030dbce469f0f422c8f38079780363d"}, + {file = "aiohttp-3.13.5-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6a7cbeb06d1070f1d14895eeeed4dac5913b22d7b456f2eb969f11f4b3993796"}, + {file = "aiohttp-3.13.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca9ef7517fd7874a1a08970ae88f497bf5c984610caa0bf40bd7e8450852b95"}, + {file = "aiohttp-3.13.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:019a67772e034a0e6b9b17c13d0a8fe56ad9fb150fc724b7f3ffd3724288d9e5"}, + {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f34ecee82858e41dd217734f0c41a532bd066bcaab636ad830f03a30b2a96f2a"}, + {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4eac02d9af4813ee289cd63a361576da36dba57f5a1ab36377bc2600db0cbb73"}, + {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4beac52e9fe46d6abf98b0176a88154b742e878fdf209d2248e99fcdf73cd297"}, + {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:c180f480207a9b2475f2b8d8bd7204e47aec952d084b2a2be58a782ffcf96074"}, + {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2837fb92951564d6339cedae4a7231692aa9f73cbc4fb2e04263b96844e03b4e"}, + {file = "aiohttp-3.13.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9010032a0b9710f58012a1e9c222528763d860ba2ee1422c03473eab47703e7"}, + {file = "aiohttp-3.13.5-cp310-cp310-win32.whl", hash = "sha256:7c4b6668b2b2b9027f209ddf647f2a4407784b5d88b8be4efcc72036f365baf9"}, + {file = "aiohttp-3.13.5-cp310-cp310-win_amd64.whl", hash = "sha256:cd3db5927bf9167d5a6157ddb2f036f6b6b0ad001ac82355d43e97a4bde76d76"}, + {file = "aiohttp-3.13.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ab7229b6f9b5c1ba4910d6c41a9eb11f543eadb3f384df1b4c293f4e73d44d6"}, + {file = "aiohttp-3.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f14c50708bb156b3a3ca7230b3d820199d56a48e3af76fa21c2d6087190fe3d"}, + {file = "aiohttp-3.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7d2f8616f0ff60bd332022279011776c3ac0faa0f1b463f7bb12326fbc97a1c"}, + {file = "aiohttp-3.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2567b72e1ffc3ab25510db43f355b29eeada56c0a622e58dcdb19530eb0a3cb"}, + {file = "aiohttp-3.13.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fb0540c854ac9c0c5ad495908fdfd3e332d553ec731698c0e29b1877ba0d2ec6"}, + {file = "aiohttp-3.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9883051c6972f58bfc4ebb2116345ee2aa151178e99c3f2b2bbe2af712abd13"}, + {file = "aiohttp-3.13.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2294172ce08a82fb7c7273485895de1fa1186cc8294cfeb6aef4af42ad261174"}, + {file = "aiohttp-3.13.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a807cabd5115fb55af198b98178997a5e0e57dead43eb74a93d9c07d6d4a7dc"}, + {file = "aiohttp-3.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa6d0d932e0f39c02b80744273cd5c388a2d9bc07760a03164f229c8e02662f6"}, + {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60869c7ac4aaabe7110f26499f3e6e5696eae98144735b12a9c3d9eae2b51a49"}, + {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:26d2f8546f1dfa75efa50c3488215a903c0168d253b75fba4210f57ab77a0fb8"}, + {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1162a1492032c82f14271e831c8f4b49f2b6078f4f5fc74de2c912fa225d51d"}, + {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8b14eb3262fad0dc2f89c1a43b13727e709504972186ff6a99a3ecaa77102b6c"}, + {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ca9ac61ac6db4eb6c2a0cd1d0f7e1357647b638ccc92f7e9d8d133e71ed3c6ac"}, + {file = "aiohttp-3.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7996023b2ed59489ae4762256c8516df9820f751cf2c5da8ed2fb20ee50abab3"}, + {file = "aiohttp-3.13.5-cp311-cp311-win32.whl", hash = "sha256:77dfa48c9f8013271011e51c00f8ada19851f013cde2c48fca1ba5e0caf5bb06"}, + {file = "aiohttp-3.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:d3a4834f221061624b8887090637db9ad4f61752001eae37d56c52fddade2dc8"}, + {file = "aiohttp-3.13.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:023ecba036ddd840b0b19bf195bfae970083fd7024ce1ac22e9bba90464620e9"}, + {file = "aiohttp-3.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15c933ad7920b7d9a20de151efcd05a6e38302cbf0e10c9b2acb9a42210a2416"}, + {file = "aiohttp-3.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab2899f9fa2f9f741896ebb6fa07c4c883bfa5c7f2ddd8cf2aafa86fa981b2d2"}, + {file = "aiohttp-3.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60eaa2d440cd4707696b52e40ed3e2b0f73f65be07fd0ef23b6b539c9c0b0b4"}, + {file = "aiohttp-3.13.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b3bdd3292283295774ab585160c4004f4f2f203946997f49aac032c84649e9"}, + {file = "aiohttp-3.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2b2355dc094e5f7d45a7bb262fe7207aa0460b37a0d87027dcf21b5d890e7d5"}, + {file = "aiohttp-3.13.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b38765950832f7d728297689ad78f5f2cf79ff82487131c4d26fe6ceecdc5f8e"}, + {file = "aiohttp-3.13.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b18f31b80d5a33661e08c89e202edabf1986e9b49c42b4504371daeaa11b47c1"}, + {file = "aiohttp-3.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33add2463dde55c4f2d9635c6ab33ce154e5ecf322bd26d09af95c5f81cfa286"}, + {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:327cc432fdf1356fb4fbc6fe833ad4e9f6aacb71a8acaa5f1855e4b25910e4a9"}, + {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7c35b0bf0b48a70b4cb4fc5d7bed9b932532728e124874355de1a0af8ec4bc88"}, + {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:df23d57718f24badef8656c49743e11a89fd6f5358fa8a7b96e728fda2abf7d3"}, + {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:02e048037a6501a5ec1f6fc9736135aec6eb8a004ce48838cb951c515f32c80b"}, + {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31cebae8b26f8a615d2b546fee45d5ffb76852ae6450e2a03f42c9102260d6fe"}, + {file = "aiohttp-3.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:888e78eb5ca55a615d285c3c09a7a91b42e9dd6fc699b166ebd5dee87c9ccf14"}, + {file = "aiohttp-3.13.5-cp312-cp312-win32.whl", hash = "sha256:8bd3ec6376e68a41f9f95f5ed170e2fcf22d4eb27a1f8cb361d0508f6e0557f3"}, + {file = "aiohttp-3.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:110e448e02c729bcebb18c60b9214a87ba33bac4a9fa5e9a5f139938b56c6cb1"}, + {file = "aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61"}, + {file = "aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832"}, + {file = "aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9"}, + {file = "aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090"}, + {file = "aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b"}, + {file = "aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a"}, + {file = "aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8"}, + {file = "aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665"}, + {file = "aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540"}, + {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb"}, + {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46"}, + {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8"}, + {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d"}, + {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6"}, + {file = "aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c"}, + {file = "aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc"}, + {file = "aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83"}, + {file = "aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c"}, + {file = "aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be"}, + {file = "aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25"}, + {file = "aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56"}, + {file = "aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2"}, + {file = "aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a"}, + {file = "aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be"}, + {file = "aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b"}, + {file = "aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94"}, + {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d"}, + {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7"}, + {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772"}, + {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5"}, + {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1"}, + {file = "aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b"}, + {file = "aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3"}, + {file = "aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162"}, + {file = "aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a"}, + {file = "aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254"}, + {file = "aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36"}, + {file = "aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f"}, + {file = "aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800"}, + {file = "aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf"}, + {file = "aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b"}, + {file = "aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a"}, + {file = "aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8"}, + {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be"}, + {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b"}, + {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6"}, + {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037"}, + {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500"}, + {file = "aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9"}, + {file = "aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8"}, + {file = "aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9"}, + {file = "aiohttp-3.13.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:347542f0ea3f95b2a955ee6656461fa1c776e401ac50ebce055a6c38454a0adf"}, + {file = "aiohttp-3.13.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:178c7b5e62b454c2bc790786e6058c3cc968613b4419251b478c153a4aec32b1"}, + {file = "aiohttp-3.13.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af545c2cffdb0967a96b6249e6f5f7b0d92cdfd267f9d5238d5b9ca63e8edb10"}, + {file = "aiohttp-3.13.5-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:206b7b3ef96e4ce211754f0cd003feb28b7d81f0ad26b8d077a5d5161436067f"}, + {file = "aiohttp-3.13.5-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ee5e86776273de1795947d17bddd6bb19e0365fd2af4289c0d2c5454b6b1d36b"}, + {file = "aiohttp-3.13.5-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:95d14ca7abefde230f7639ec136ade282655431fd5db03c343b19dda72dd1643"}, + {file = "aiohttp-3.13.5-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:912d4b6af530ddb1338a66229dac3a25ff11d4448be3ec3d6340583995f56031"}, + {file = "aiohttp-3.13.5-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e999f0c88a458c836d5fb521814e92ed2172c649200336a6df514987c1488258"}, + {file = "aiohttp-3.13.5-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39380e12bd1f2fdab4285b6e055ad48efbaed5c836433b142ed4f5b9be71036a"}, + {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9efcc0f11d850cefcafdd9275b9576ad3bfb539bed96807663b32ad99c4d4b88"}, + {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:147b4f501d0292077f29d5268c16bb7c864a1f054d7001c4c1812c0421ea1ed0"}, + {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d147004fede1b12f6013a6dbb2a26a986a671a03c6ea740ddc76500e5f1c399f"}, + {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:9277145d36a01653863899c665243871434694bcc3431922c3b35c978061bdb8"}, + {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4e704c52438f66fdd89588346183d898bb42167cf88f8b7ff1c0f9fc957c348f"}, + {file = "aiohttp-3.13.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a8a4d3427e8de1312ddf309cc482186466c79895b3a139fed3259fc01dfa9a5b"}, + {file = "aiohttp-3.13.5-cp39-cp39-win32.whl", hash = "sha256:6f497a6876aa4b1a102b04996ce4c1170c7040d83faa9387dd921c16e30d5c83"}, + {file = "aiohttp-3.13.5-cp39-cp39-win_amd64.whl", hash = "sha256:cb979826071c0986a5f08333a36104153478ce6018c58cba7f9caddaf63d5d67"}, + {file = "aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1"}, ] [package.dependencies] @@ -808,7 +808,7 @@ description = "Timeout context manager for asyncio programs" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version == \"3.10\"" +markers = "python_version < \"3.11\"" files = [ {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, @@ -2379,7 +2379,7 @@ description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" groups = ["main", "dev"] -markers = "python_version == \"3.10\"" +markers = "python_version < \"3.11\"" files = [ {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, @@ -3938,7 +3938,7 @@ description = "Python package for creating and manipulating graphs and networks" optional = false python-versions = ">=3.10" groups = ["dev"] -markers = "python_version == \"3.10\"" +markers = "python_version < \"3.11\"" files = [ {file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"}, {file = "networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1"}, @@ -6094,7 +6094,7 @@ description = "A lil' TOML parser" optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version == \"3.10\"" +markers = "python_version < \"3.11\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -6743,4 +6743,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">=3.10,<3.13" -content-hash = "91739ee5e383337160f9f08b76944ab4e8629c94084c8a9d115246862557f7c5" +content-hash = "4050d3a95f5bc5448576ca0361fd899b35aa04de28d379cdfd3c2b0db67848ad" diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index 7eb8864498..8c184e868b 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -11,20 +11,26 @@ All notable changes to the **Prowler SDK** are documented in this file. - `glue_etl_jobs_no_secrets_in_arguments` check for plaintext secrets in AWS Glue ETL job arguments [(#10368)](https://github.com/prowler-cloud/prowler/pull/10368) - `awslambda_function_no_dead_letter_queue`, `awslambda_function_using_cross_account_layers`, and `awslambda_function_env_vars_not_encrypted_with_cmk` checks for AWS Lambda [(#10381)](https://github.com/prowler-cloud/prowler/pull/10381) - `entra_conditional_access_policy_mdm_compliant_device_required` check for M365 provider [(#10220)](https://github.com/prowler-cloud/prowler/pull/10220) +- `directory_super_admin_only_admin_roles` check for Google Workspace provider [(#10488)](https://github.com/prowler-cloud/prowler/pull/10488) - `ec2_securitygroup_allow_ingress_from_internet_to_any_port_from_ip` check for AWS provider using `ipaddress.is_global` for accurate public IP detection [(#10335)](https://github.com/prowler-cloud/prowler/pull/10335) - `entra_conditional_access_policy_block_o365_elevated_insider_risk` check for M365 provider [(#10232)](https://github.com/prowler-cloud/prowler/pull/10232) - `--resource-group` and `--list-resource-groups` CLI flags to filter checks by resource group across all providers [(#10479)](https://github.com/prowler-cloud/prowler/pull/10479) - CISA SCuBA Google Workspace Baselines compliance [(#10466)](https://github.com/prowler-cloud/prowler/pull/10466) - CIS Google Workspace Foundations Benchmark v1.3.0 compliance [(#10462)](https://github.com/prowler-cloud/prowler/pull/10462) +- `calendar_external_sharing_primary_calendar`, `calendar_external_sharing_secondary_calendar`, and `calendar_external_invitations_warning` checks for Google Workspace provider using the Cloud Identity Policy API [(#10597)](https://github.com/prowler-cloud/prowler/pull/10597) - `entra_conditional_access_policy_device_registration_mfa_required` check and `entra_intune_enrollment_sign_in_frequency_every_time` enhancement for M365 provider [(#10222)](https://github.com/prowler-cloud/prowler/pull/10222) - `entra_conditional_access_policy_block_elevated_insider_risk` check for M365 provider [(#10234)](https://github.com/prowler-cloud/prowler/pull/10234) - `Vercel` provider support with 30 checks [(#10189)](https://github.com/prowler-cloud/prowler/pull/10189) +- `stepfunctions_statemachine_no_secrets_in_definition` check for hardcoded secrets in AWS Step Functions state machine definitions [(#10570)](https://github.com/prowler-cloud/prowler/pull/10570) +- CCC improvements with the latest checks and new mappings [(#10625)](https://github.com/prowler-cloud/prowler/pull/10625) - Added new Github's Agentic Workflow to review CHANGELOG files ### 🔄 Changed - Added `internet-exposed` category to 13 AWS checks (CloudFront, CodeArtifact, EC2, EFS, RDS, SageMaker, Shield, VPC) [(#10502)](https://github.com/prowler-cloud/prowler/pull/10502) - Minimum Python version from 3.9 to 3.10 and updated classifiers to reflect supported versions (3.10, 3.11, 3.12) [(#10464)](https://github.com/prowler-cloud/prowler/pull/10464) +- Pin direct SDK dependencies to exact versions and rely on `poetry.lock` artifact hashes for reproducible installs [(#10593)](https://github.com/prowler-cloud/prowler/pull/10593) +- Sensitive CLI flags now warn when values are passed directly, recommending environment variables instead [(#10532)](https://github.com/prowler-cloud/prowler/pull/10532) ### 🐞 Fixed @@ -34,6 +40,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - `--list-checks` and `--list-checks-json` now include `threat-detection` category checks in their output [(#10578)](https://github.com/prowler-cloud/prowler/pull/10578) - Missing `__init__.py` in `codebuild_project_uses_allowed_github_organizations` check preventing discovery by `--list-checks` [(#10584)](https://github.com/prowler-cloud/prowler/pull/10584) - Azure Key Vault checks emitting incorrect findings for keys, secrets, and vault logging [(#10332)](https://github.com/prowler-cloud/prowler/pull/10332) +- `is_policy_public` now recognizes `kms:CallerAccount`, `kms:ViaService`, `aws:CalledVia`, `aws:CalledViaFirst`, and `aws:CalledViaLast` as restrictive condition keys, fixing false positives in `kms_key_policy_is_not_public` and other checks that use `is_condition_block_restrictive` [(#10600)](https://github.com/prowler-cloud/prowler/pull/10600) - `_enabled_regions` empty-set bug in `AwsProvider.generate_regional_clients` creating boto3 clients for all 36 AWS regions instead of the audited ones, causing random CI timeouts and slow test runs [(#10598)](https://github.com/prowler-cloud/prowler/pull/10598) - Retrieve only the latest version from a package in AWS CodeArtifact [(#10243)](https://github.com/prowler-cloud/prowler/pull/10243) @@ -42,6 +49,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - Sensitive CLI flag values (tokens, keys, passwords) in HTML output "Parameters used" field now redacted to prevent credential leaks [(#10518)](https://github.com/prowler-cloud/prowler/pull/10518) - `authlib` bumped from 1.6.5 to 1.6.9 to fix CVE-2026-28802 (JWT `alg: none` validation bypass) [(#10579)](https://github.com/prowler-cloud/prowler/pull/10579) - `cryptography` bumped from 44.0.3 to 46.0.6 ([CVE-2026-26007](https://github.com/pyca/cryptography/security/advisories/GHSA-r6ph-v2qm-q3c2), [CVE-2026-34073](https://github.com/pyca/cryptography/security/advisories/GHSA-m959-cc7f-wv43)), `oci` to 2.169.0, and `alibabacloud-tea-openapi` to 0.4.4 [(#10535)](https://github.com/prowler-cloud/prowler/pull/10535) +- `aiohttp` bumped from 3.13.3 to 3.13.5 to fix CVE-2026-34520 (the C parser accepted null bytes and control characters in response headers) [(#10537)](https://github.com/prowler-cloud/prowler/pull/10537) --- diff --git a/prowler/compliance/aws/ccc_aws.json b/prowler/compliance/aws/ccc_aws.json index 1f6da6d5f6..11aa32f4ee 100644 --- a/prowler/compliance/aws/ccc_aws.json +++ b/prowler/compliance/aws/ccc_aws.json @@ -1,10 +1,1835 @@ { "Framework": "CCC", - "Version": "", + "Version": "v2025.10", "Provider": "AWS", "Name": "Common Cloud Controls Catalog (CCC)", "Description": "Common Cloud Controls Catalog (CCC) for AWS", "Requirements": [ + { + "Id": "CCC.Core.CN01.AR01", + "Description": "When a port is exposed for non-SSH network traffic, all traffic MUST include a TLS handshake AND be encrypted using TLS 1.3 or higher.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN01 Encrypt Data for Transmission", + "SubSection": "", + "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Most cloud services enable TLS 1.3 by default. Where it is not already set, ensure that your services are configured or updated accordingly.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-03", + "CEK-04", + "IVS-03", + "IVS-07" + ] + } + ] + } + ], + "Checks": [ + "cloudfront_distributions_https_enabled", + "cloudfront_distributions_origin_traffic_encrypted", + "cloudfront_distributions_using_deprecated_ssl_protocols", + "elb_insecure_ssl_ciphers", + "elb_ssl_listeners", + "elbv2_insecure_ssl_ciphers", + "elbv2_ssl_listeners", + "elbv2_nlb_tls_termination_enabled", + "s3_bucket_secure_transport_policy", + "opensearch_service_domains_https_communications_enforced", + "opensearch_service_domains_node_to_node_encryption_enabled", + "elasticache_redis_cluster_in_transit_encryption_enabled", + "dynamodb_accelerator_cluster_in_transit_encryption_enabled", + "dms_endpoint_ssl_enabled", + "dms_endpoint_redis_in_transit_encryption_enabled", + "kafka_cluster_in_transit_encryption_enabled", + "kafka_connector_in_transit_encryption_enabled", + "redshift_cluster_in_transit_encryption_enabled", + "rds_instance_transport_encrypted", + "transfer_server_in_transit_encryption_enabled", + "glue_database_connections_ssl_enabled", + "sns_subscription_not_using_http_endpoints" + ] + }, + { + "Id": "CCC.Core.CN01.AR02", + "Description": "When a port is exposed for SSH network traffic, all traffic MUST include a SSH handshake AND be encrypted using SSHv2 or higher.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN01 Encrypt Data for Transmission", + "SubSection": "", + "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Any time port 22 is exposed, ensure that it has a properly implemented SSH server with SSHv2 enabled and configured with strong ciphers.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-03", + "CEK-04", + "IVS-03", + "IVS-07" + ] + } + ] + } + ], + "Checks": [ + "ec2_instance_port_ssh_exposed_to_internet", + "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22", + "ec2_securitygroup_allow_ingress_from_internet_to_all_ports", + "ec2_networkacl_allow_ingress_tcp_port_22" + ] + }, + { + "Id": "CCC.Core.CN01.AR03", + "Description": "When the service receives unencrypted traffic, then it MUST either block the request or automatically redirect it to the secure equivalent.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN01 Encrypt Data for Transmission", + "SubSection": "", + "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Review firewall, load balancer, and application configurations to ensure insecure protocols such as HTTP, FTP, and Telnet are not exposed. Where possible, implement automatic redirection to secure protocols such as HTTPS, SFTP, SSH, and regularly scan for protocol drift.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-03", + "CEK-04", + "IVS-03", + "IVS-07" + ] + } + ] + } + ], + "Checks": [ + "cloudfront_distributions_https_enabled", + "cloudfront_distributions_origin_traffic_encrypted", + "opensearch_service_domains_https_communications_enforced", + "transfer_server_in_transit_encryption_enabled", + "s3_bucket_secure_transport_policy", + "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_ftp_20_21", + "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23" + ] + }, + { + "Id": "CCC.Core.CN01.AR07", + "Description": "When a port is exposed, the service MUST ensure that the protocol and service officially assigned to that port number by the IANA Service Name and Transport Protocol Port Number Registry, and no other, is run on that port.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN01 Encrypt Data for Transmission", + "SubSection": "", + "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Reference the IANA Service Name and Transport Protocol Port Number Registry for more information about correct protocol-to-port assignments. Avoid running non-standard services on well-known ports.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-03", + "CEK-04", + "IVS-03", + "IVS-07" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Core.CN01.AR08", + "Description": "When a service transmits data using TLS, mutual TLS (mTLS) MUST be implemented to require both client and server certificate authentication for all connections.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN01 Encrypt Data for Transmission", + "SubSection": "", + "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Configure mTLS for all endpoints that process or transmit sensitive data. Ensure both client and server certificates are validated and managed securely. Regularly review certificate authorities and automate certificate rotation where possible.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-03", + "CEK-04", + "IVS-03", + "IVS-07" + ] + } + ] + } + ], + "Checks": [ + "apigateway_restapi_client_certificate_enabled", + "kafka_cluster_mutual_tls_authentication_enabled" + ] + }, + { + "Id": "CCC.Core.CN13.AR01", + "Description": "When a port is exposed that uses certificate-based encryption, the service MUST only use valid, unexpired certificates issued by a trusted certificate authority.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN13 Minimize Lifetime of Encryption and Authentication Certificates", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption and authentication certificates have a limited lifetime to reduce the risk of compromise and ensure the use of up-to-date security practices.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Track certificate expiration dates and automate certificate renewal where possible. Use certificate management tools to ensure only certificates from trusted authorities are deployed.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH18" + ] + } + ], + "SectionGuidelineMappings": [] + } + ], + "Checks": [ + "acm_certificates_expiration_check", + "acm_certificates_with_secure_key_algorithms", + "acm_certificates_transparency_logs_enabled" + ] + }, + { + "Id": "CCC.Core.CN13.AR02", + "Description": "When a port is exposed that uses certificate-based encryption, the service MUST rotate active certificates within 180 days of issuance.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN13 Minimize Lifetime of Encryption and Authentication Certificates", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption and authentication certificates have a limited lifetime to reduce the risk of compromise and ensure the use of up-to-date security practices.", + "Applicability": [ + "tlp-amber" + ], + "Recommendation": "Track certificate expiration dates and automate certificate renewal where possible. Use certificate management tools to ensure only certificates from trusted authorities are deployed.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH18" + ] + } + ], + "SectionGuidelineMappings": [] + } + ], + "Checks": [ + "acm_certificates_expiration_check" + ] + }, + { + "Id": "CCC.Core.CN13.AR03", + "Description": "When a port is exposed that uses certificate-based encryption, the service MUST rotate active certificates within 90 days of issuance.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN13 Minimize Lifetime of Encryption and Authentication Certificates", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption and authentication certificates have a limited lifetime to reduce the risk of compromise and ensure the use of up-to-date security practices.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "Track certificate expiration dates and automate certificate renewal where possible. Use certificate management tools to ensure only certificates from trusted authorities are deployed.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH18" + ] + } + ], + "SectionGuidelineMappings": [] + } + ], + "Checks": [ + "acm_certificates_expiration_check" + ] + }, + { + "Id": "CCC.Core.CN06.AR01", + "Description": "When the service is running, its region and availability zone MUST be included in a list of explicitly trusted or approved locations within the trust perimeter.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN06 Restrict Deployments to Trust Perimeter", + "SubSection": "", + "SubSectionObjective": "Ensure that the service and its child resources are only deployed on infrastructure in locations that are explicitly included within a defined trust perimeter.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Maintain an up-to-date list of trusted and approved regions based on organizational policies. Validate the service's deployment location is included in this list.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-19" + ] + } + ] + } + ], + "Checks": [ + "organizations_scp_check_deny_regions" + ] + }, + { + "Id": "CCC.Core.CN06.AR02", + "Description": "When a child resource is deployed, its region and availability zone MUST be included in a list of explicitly trusted or approved locations within the trust perimeter.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN06 Restrict Deployments to Trust Perimeter", + "SubSection": "", + "SubSectionObjective": "Ensure that the service and its child resources are only deployed on infrastructure in locations that are explicitly included within a defined trust perimeter.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Maintain an up-to-date list of trusted and approved regions based on organizational policies. Validate that child resources can only be deployed to locations included in this list.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-19" + ] + } + ] + } + ], + "Checks": [ + "organizations_scp_check_deny_regions" + ] + }, + { + "Id": "CCC.Core.CN08.AR01", + "Description": "When data is created or modified, the data MUST have a complete and recoverable duplicate that is stored in a physically separate data center.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN08 Replicate Data to Multiple Locations", + "SubSection": "", + "SubSectionObjective": "Ensure that data is replicated across multiple physical locations to protect against data loss due to hardware failures, natural disasters, or other catastrophic events.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Implement automated data replication processes to ensure that data is consistently duplicated in another region or availability zone. Regularly test data recovery from the replicated location to ensure integrity and availability.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "BCR-08", + "BCR-10", + "BCR-11" + ] + } + ] + } + ], + "Checks": [ + "s3_bucket_cross_region_replication", + "backup_plans_exist", + "backup_vaults_exist", + "dynamodb_table_protected_by_backup_plan", + "rds_cluster_protected_by_backup_plan", + "rds_instance_protected_by_backup_plan", + "rds_cluster_multi_az", + "rds_instance_multi_az", + "efs_multi_az_enabled", + "neptune_cluster_multi_az", + "documentdb_cluster_multi_az_enabled", + "elasticache_redis_cluster_multi_az_enabled" + ] + }, + { + "Id": "CCC.Core.CN08.AR02", + "Description": "When data is replicated into a second location, the service MUST be able to accurately represent the replication locations, replication status, and data synchronization status.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN08 Replicate Data to Multiple Locations", + "SubSection": "", + "SubSectionObjective": "Ensure that data is replicated across multiple physical locations to protect against data loss due to hardware failures, natural disasters, or other catastrophic events.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "BCR-08", + "BCR-10", + "BCR-11" + ] + } + ] + } + ], + "Checks": [ + "s3_bucket_cross_region_replication" + ] + }, + { + "Id": "CCC.Core.CN09.AR01", + "Description": "When the service is operational, its logs and any child resource logs MUST NOT be accessible from the resource they record access to.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN09 Ensure Integrity of Access Logs", + "SubSection": "", + "SubSectionObjective": "Ensure that access logs are always recorded to an external location that cannot be manipulated from the context of the service(s) it contains logs for.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH07", + "CCC.Core.TH09", + "CCC.Core.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-02", + "LOG-04", + "LOG-09" + ] + } + ] + } + ], + "Checks": [ + "cloudtrail_logs_s3_bucket_is_not_publicly_accessible", + "cloudwatch_log_group_not_publicly_accessible", + "cloudtrail_logs_s3_bucket_access_logging_enabled", + "cloudtrail_log_file_validation_enabled", + "cloudtrail_kms_encryption_enabled", + "cloudtrail_bucket_requires_mfa_delete" + ] + }, + { + "Id": "CCC.Core.CN09.AR02", + "Description": "When the service is operational, disabling the logs for the service or its child resources MUST NOT be possible without also disabling the corresponding resource.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN09 Ensure Integrity of Access Logs", + "SubSection": "", + "SubSectionObjective": "Ensure that access logs are always recorded to an external location that cannot be manipulated from the context of the service(s) it contains logs for.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "No normal business operations should disable logs, as this could indicate an attempt to cover up unauthorized access. Ensure that logging mechanisms are tightly integrated with service operations, so that logging cannot be disabled without stopping the service itself.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH07", + "CCC.Core.TH09", + "CCC.Core.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-02", + "LOG-04", + "LOG-09" + ] + } + ] + } + ], + "Checks": [ + "cloudtrail_multi_region_enabled", + "cloudtrail_log_file_validation_enabled", + "cloudtrail_kms_encryption_enabled", + "cloudtrail_cloudwatch_logging_enabled", + "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled" + ] + }, + { + "Id": "CCC.Core.CN09.AR03", + "Description": "When the service is operational, any attempt to redirect logs for the service or its child resources MUST NOT be possible without halting operation of the corresponding resource and publishing corresponding events to monitored channels.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN09 Ensure Integrity of Access Logs", + "SubSection": "", + "SubSectionObjective": "Ensure that access logs are always recorded to an external location that cannot be manipulated from the context of the service(s) it contains logs for.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "No normal business operations should result in the redirection of logs, as this could indicate an attempt to cover up unauthorized access. Ensure that logging configurations are immutable during service operation so that any changes require stopping the service and publishing corresponding events to monitored channels.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH07", + "CCC.Core.TH09", + "CCC.Core.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-02", + "LOG-04", + "LOG-09" + ] + } + ] + } + ], + "Checks": [ + "cloudtrail_log_file_validation_enabled", + "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", + "cloudwatch_changes_to_network_acls_alarm_configured", + "cloudwatch_changes_to_network_gateways_alarm_configured", + "cloudwatch_changes_to_network_route_tables_alarm_configured", + "cloudwatch_changes_to_vpcs_alarm_configured" + ] + }, + { + "Id": "CCC.Core.CN10.AR01", + "Description": "When data is replicated, the service MUST ensure that replication only occurs to destinations that are explicitly included within the defined trust perimeter.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN10 Restrict Data Replication to Trust Perimeter", + "SubSection": "", + "SubSectionObjective": "Ensure that data is only replicated on infrastructure in locations that are explicitly included within a defined trust perimeter.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-10", + "DSP-19" + ] + } + ] + } + ], + "Checks": [ + "s3_bucket_cross_region_replication" + ] + }, + { + "Id": "CCC.Core.CN02.AR01", + "Description": "When data is stored, it MUST be encrypted using the latest industry-standard encryption methods.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN02 Encrypt Data for Storage", + "SubSection": "", + "SubSectionObjective": "Ensure that all data stored is encrypted at rest using strong encryption algorithms.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-03", + "CEK-04", + "UEM-08", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "s3_bucket_default_encryption", + "s3_bucket_kms_encryption", + "ec2_ebs_default_encryption", + "ec2_ebs_volume_encryption", + "ec2_ebs_snapshots_encrypted", + "efs_encryption_at_rest_enabled", + "storagegateway_fileshare_encryption_enabled", + "rds_instance_storage_encrypted", + "rds_cluster_storage_encrypted", + "rds_snapshots_encrypted", + "redshift_cluster_encrypted_at_rest", + "documentdb_cluster_storage_encrypted", + "neptune_cluster_storage_encrypted", + "neptune_cluster_snapshot_encrypted", + "dynamodb_tables_kms_cmk_encryption_enabled", + "dynamodb_accelerator_cluster_encryption_enabled", + "kafka_cluster_encryption_at_rest_uses_cmk", + "kinesis_stream_encrypted_at_rest", + "firehose_stream_encrypted_at_rest", + "sns_topics_kms_encryption_at_rest_enabled", + "sqs_queues_server_side_encryption_enabled", + "opensearch_service_domains_encryption_at_rest_enabled", + "athena_workgroup_encryption", + "glue_data_catalogs_metadata_encryption_enabled", + "glue_data_catalogs_connection_passwords_encryption_enabled", + "glue_etl_jobs_amazon_s3_encryption_enabled", + "backup_vaults_encrypted", + "backup_recovery_point_encrypted", + "cloudtrail_kms_encryption_enabled", + "cloudwatch_log_group_kms_encryption_enabled", + "eks_cluster_kms_cmk_encryption_in_secrets_enabled", + "sagemaker_notebook_instance_encryption_enabled", + "apigateway_restapi_cache_encrypted" + ] + }, + { + "Id": "CCC.Core.CN11.AR01", + "Description": "When encryption keys are used, the service MUST verify that all encryption keys use the latest industry-standard cryptographic algorithms.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN11 Protect Encryption Keys", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH16" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-08", + "CEK-10", + "CEK-12" + ] + } + ] + } + ], + "Checks": [ + "acm_certificates_with_secure_key_algorithms" + ] + }, + { + "Id": "CCC.Core.CN11.AR02", + "Description": "When encryption keys are used, the service MUST rotate active keys within 180 days of issuance.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN11 Protect Encryption Keys", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "Applicability": [ + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH16" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-08", + "CEK-10", + "CEK-12" + ] + } + ] + } + ], + "Checks": [ + "kms_cmk_rotation_enabled" + ] + }, + { + "Id": "CCC.Core.CN11.AR03", + "Description": "When encrypting data, the service MUST verify that customer-managed encryption keys (CMEKs) are used.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN11 Protect Encryption Keys", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH16" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-08", + "CEK-10", + "CEK-12" + ] + } + ] + } + ], + "Checks": [ + "kms_cmk_are_used" + ] + }, + { + "Id": "CCC.Core.CN11.AR04", + "Description": "When encryption keys are accessed, the service MUST verify that access to encryption keys is restricted to authorized personnel and services, following the principle of least privilege.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN11 Protect Encryption Keys", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH16" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-08", + "CEK-10", + "CEK-12" + ] + } + ] + } + ], + "Checks": [ + "iam_inline_policy_no_full_access_to_kms", + "iam_policy_no_full_access_to_kms", + "kms_cmk_not_deleted_unintentionally" + ] + }, + { + "Id": "CCC.Core.CN11.AR05", + "Description": "When encryption keys are used, the service MUST rotate active keys within 365 days of issuance.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN11 Protect Encryption Keys", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "Applicability": [ + "tlp-clear", + "tlp-green" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH16" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-08", + "CEK-10", + "CEK-12" + ] + } + ] + } + ], + "Checks": [ + "kms_cmk_rotation_enabled" + ] + }, + { + "Id": "CCC.Core.CN11.AR06", + "Description": "When encryption keys are used, the service MUST rotate active keys within 90 days of issuance.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN11 Protect Encryption Keys", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH16" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-08", + "CEK-10", + "CEK-12" + ] + } + ] + } + ], + "Checks": [ + "kms_cmk_rotation_enabled" + ] + }, + { + "Id": "CCC.Core.CN14.AR01", + "Description": "When backups are created for disaster recovery purposes, the storage mechanism MUST NOT allow modification or deletion within 30 days of creation.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN14 Maintain Recent Backups", + "SubSection": "", + "SubSectionObjective": "Ensure that all backups used for disaster recovery are recent and subject to a retention policy that limits deletion.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Use immutable storage solutions where possible. Implement backup retention policies that enforce a minimum retention period of 30 days.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [] + } + ], + "Checks": [ + "backup_vaults_exist", + "backup_plans_exist", + "rds_instance_backup_enabled", + "neptune_cluster_backup_enabled", + "documentdb_cluster_backup_enabled" + ] + }, + { + "Id": "CCC.Core.CN14.AR02", + "Description": "When backups are created for disaster recovery purposes, the most recent backup MUST have a creation date within the past 30 days.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN14 Maintain Recent Backups", + "SubSection": "", + "SubSectionObjective": "Ensure that all backups used for disaster recovery are recent and subject to a retention policy that limits deletion.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber" + ], + "Recommendation": "Implement automated backup processes to ensure that backups are created regularly. Monitor backup schedules and verify that the most recent backup creation date is within the last 30 days.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [] + } + ], + "Checks": [ + "backup_vaults_exist", + "backup_plans_exist", + "backup_reportplans_exist", + "backup_recovery_point_encrypted", + "rds_instance_backup_enabled", + "rds_instance_protected_by_backup_plan", + "rds_cluster_protected_by_backup_plan", + "neptune_cluster_backup_enabled", + "documentdb_cluster_backup_enabled", + "dynamodb_table_protected_by_backup_plan", + "efs_have_backup_enabled" + ] + }, + { + "Id": "CCC.Core.CN14.AR03", + "Description": "When backups are created for disaster recovery purposes, the most recent backup MUST have a creation date within the past 14 days.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN14 Maintain Recent Backups", + "SubSection": "", + "SubSectionObjective": "Ensure that all backups used for disaster recovery are recent and subject to a retention policy that limits deletion.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "Implement automated backup processes to ensure that backups are created regularly. Monitor backup schedules and verify that the most recent backup creation date is within the last 14 days.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [] + } + ], + "Checks": [ + "backup_vaults_exist", + "backup_plans_exist", + "rds_instance_backup_enabled", + "neptune_cluster_backup_enabled", + "documentdb_cluster_backup_enabled" + ] + }, + { + "Id": "CCC.Core.CN03.AR01", + "Description": "When an entity attempts to modify the service through a user interface, the authentication process MUST require multiple identifying factors for authentication.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", + "SubSection": "", + "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-14" + ] + } + ] + } + ], + "Checks": [ + "iam_root_mfa_enabled", + "iam_root_hardware_mfa_enabled", + "iam_user_mfa_enabled_console_access", + "iam_user_hardware_mfa_enabled", + "iam_administrator_access_with_mfa", + "cognito_user_pool_mfa_enabled" + ] + }, + { + "Id": "CCC.Core.CN03.AR02", + "Description": "When an entity attempts to modify the service through an API endpoint, the authentication process MUST require a credential such as an API key or token AND originate from within the trust perimeter.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", + "SubSection": "", + "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-14" + ] + } + ] + } + ], + "Checks": [ + "iam_no_root_access_key", + "iam_root_credentials_management_enabled", + "iam_user_no_setup_initial_access_key", + "iam_administrator_access_with_mfa", + "apigateway_restapi_authorizers_enabled", + "apigatewayv2_api_authorizers_enabled", + "apigateway_restapi_public_with_authorizer" + ] + }, + { + "Id": "CCC.Core.CN03.AR03", + "Description": "When an entity attempts to view information on the service through a user interface, the authentication process MUST require multiple identifying factors from the user.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", + "SubSection": "", + "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-14" + ] + } + ] + } + ], + "Checks": [ + "iam_user_mfa_enabled_console_access", + "iam_user_hardware_mfa_enabled", + "iam_root_mfa_enabled", + "iam_root_hardware_mfa_enabled", + "iam_administrator_access_with_mfa", + "cognito_user_pool_mfa_enabled" + ] + }, + { + "Id": "CCC.Core.CN03.AR04", + "Description": "When an entity attempts to view information on the service through an API endpoint, the authentication process MUST require a credential such as an API key or token AND originate from within the trust perimeter.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", + "SubSection": "", + "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-14" + ] + } + ] + } + ], + "Checks": [ + "iam_no_root_access_key", + "iam_user_no_setup_initial_access_key", + "apigateway_restapi_authorizers_enabled", + "apigatewayv2_api_authorizers_enabled", + "apigateway_restapi_public_with_authorizer" + ] + }, + { + "Id": "CCC.Core.CN05.AR01", + "Description": "When an attempt is made to modify data on the service or a child resource, the service MUST block requests from unauthorized entities.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", + "SubSection": "", + "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-01", + "DSP-07", + "DSP-08", + "DSP-10", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "apigateway_restapi_authorizers_enabled", + "apigateway_restapi_public", + "apigateway_restapi_public_with_authorizer", + "apigatewayv2_api_authorizers_enabled", + "awslambda_function_url_public", + "awslambda_function_not_publicly_accessible", + "ec2_securitygroup_allow_ingress_from_internet_to_all_ports", + "s3_bucket_public_access", + "s3_bucket_public_list_acl", + "s3_bucket_public_write_acl", + "s3_bucket_cross_account_access", + "s3_account_level_public_access_blocks", + "iam_policy_no_full_access_to_cloudtrail", + "iam_policy_no_full_access_to_kms", + "iam_inline_policy_no_full_access_to_cloudtrail", + "iam_inline_policy_no_full_access_to_kms", + "iam_role_administratoraccess_policy", + "iam_group_administrator_access_policy", + "iam_user_administrator_access_policy", + "iam_policy_attached_only_to_group_or_roles", + "iam_role_cross_account_readonlyaccess_policy", + "iam_role_cross_service_confused_deputy_prevention" + ] + }, + { + "Id": "CCC.Core.CN05.AR02", + "Description": "When administrative access or configuration change is attempted on the service or a child resource, the service MUST refuse requests from unauthorized entities.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", + "SubSection": "", + "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-01", + "DSP-07", + "DSP-08", + "DSP-10", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "iam_root_mfa_enabled", + "iam_root_hardware_mfa_enabled", + "iam_avoid_root_usage", + "iam_user_mfa_enabled_console_access", + "iam_administrator_access_with_mfa", + "iam_group_administrator_access_policy", + "iam_role_administratoraccess_policy", + "iam_user_administrator_access_policy", + "iam_inline_policy_no_full_access_to_cloudtrail", + "iam_inline_policy_no_full_access_to_kms", + "iam_policy_no_full_access_to_cloudtrail", + "iam_policy_no_full_access_to_kms", + "iam_policy_allows_privilege_escalation", + "iam_inline_policy_allows_privilege_escalation", + "iam_customer_attached_policy_no_administrative_privileges", + "iam_customer_unattached_policy_no_administrative_privileges", + "iam_aws_attached_policy_no_administrative_privileges", + "iam_password_policy_minimum_length_14", + "iam_password_policy_uppercase", + "iam_password_policy_lowercase", + "iam_password_policy_symbol", + "iam_password_policy_number", + "iam_password_policy_expires_passwords_within_90_days_or_less", + "iam_password_policy_reuse_24" + ] + }, + { + "Id": "CCC.Core.CN05.AR03", + "Description": "When administrative access or configuration change is attempted on the service or a child resource in a multi-tenant environment, the service MUST refuse requests across tenant boundaries unless the origin is explicitly included in a pre-approved allowlist.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", + "SubSection": "", + "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-01", + "DSP-07", + "DSP-08", + "DSP-10", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "vpc_endpoint_services_allowed_principals_trust_boundaries", + "vpc_endpoint_connections_trust_boundaries", + "iam_role_cross_service_confused_deputy_prevention", + "iam_role_cross_account_readonlyaccess_policy", + "s3_bucket_cross_account_access", + "eventbridge_bus_cross_account_access", + "eventbridge_schema_registry_cross_account_access" + ] + }, + { + "Id": "CCC.Core.CN05.AR04", + "Description": "When data is requested from outside the trust perimeter, the service MUST refuse requests from unauthorized entities.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", + "SubSection": "", + "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-01", + "DSP-07", + "DSP-08", + "DSP-10", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "accessanalyzer_enabled", + "accessanalyzer_enabled_without_findings", + "vpc_endpoint_connections_trust_boundaries", + "vpc_endpoint_services_allowed_principals_trust_boundaries", + "s3_bucket_cross_account_access", + "s3_bucket_public_access", + "iam_administrator_access_with_mfa", + "iam_inline_policy_no_full_access_to_kms", + "iam_inline_policy_no_full_access_to_cloudtrail", + "iam_policy_no_full_access_to_kms", + "iam_policy_no_full_access_to_cloudtrail", + "iam_policy_attached_only_to_group_or_roles" + ] + }, + { + "Id": "CCC.Core.CN05.AR05", + "Description": "When any request is made from outside the trust perimeter, the service MUST NOT provide any response that may indicate the service exists.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", + "SubSection": "", + "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-01", + "DSP-07", + "DSP-08", + "DSP-10", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "awslambda_function_url_public", + "apigateway_restapi_public", + "s3_bucket_public_access", + "s3_bucket_policy_public_write_access", + "sns_topics_not_publicly_accessible", + "sqs_queues_not_publicly_accessible", + "ec2_securitygroup_allow_ingress_from_internet_to_all_ports", + "ec2_networkacl_allow_ingress_any_port", + "ec2_securitygroup_default_restrict_traffic", + "vpc_endpoint_for_ec2_enabled", + "vpc_endpoint_connections_trust_boundaries", + "vpc_endpoint_services_allowed_principals_trust_boundaries" + ] + }, + { + "Id": "CCC.Core.CN05.AR06", + "Description": "When any request is made to the service or a child resource, the service MUST refuse requests from unauthorized entities.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", + "SubSection": "", + "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-01", + "DSP-07", + "DSP-08", + "DSP-10", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "iam_root_mfa_enabled", + "iam_root_hardware_mfa_enabled", + "iam_no_root_access_key", + "iam_administrator_access_with_mfa", + "iam_user_mfa_enabled_console_access", + "iam_user_hardware_mfa_enabled", + "iam_root_credentials_management_enabled", + "iam_check_saml_providers_sts", + "iam_policy_attached_only_to_group_or_roles", + "iam_role_cross_service_confused_deputy_prevention", + "iam_role_cross_account_readonlyaccess_policy", + "vpc_endpoint_connections_trust_boundaries", + "vpc_endpoint_services_allowed_principals_trust_boundaries" + ] + }, + { + "Id": "CCC.Core.CN04.AR01", + "Description": "When administrative access or configuration change is attempted on the service or a child resource, the service MUST log the client identity, time, and result of the attempt.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements.", + "Section": "CCC.Core.CN04 Log All Access and Changes", + "SubSection": "", + "SubSectionObjective": "Ensure that all access attempts are logged to maintain a detailed audit trail for security and compliance purposes.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-08" + ] + } + ] + } + ], + "Checks": [ + "cloudtrail_multi_region_enabled", + "cloudtrail_multi_region_enabled_logging_management_events", + "cloudtrail_cloudwatch_logging_enabled", + "cloudtrail_log_file_validation_enabled", + "cloudwatch_log_metric_filter_authentication_failures", + "cloudwatch_log_metric_filter_unauthorized_api_calls", + "cloudwatch_log_metric_filter_root_usage", + "cloudwatch_log_metric_filter_sign_in_without_mfa", + "cloudwatch_log_metric_filter_for_s3_bucket_policy_changes", + "cloudwatch_log_metric_filter_policy_changes", + "cloudwatch_log_metric_filter_security_group_changes", + "cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk", + "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", + "cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled", + "cloudwatch_changes_to_network_acls_alarm_configured", + "cloudwatch_changes_to_network_gateways_alarm_configured", + "cloudwatch_changes_to_network_route_tables_alarm_configured", + "cloudwatch_changes_to_vpcs_alarm_configured", + "config_recorder_all_regions_enabled" + ] + }, + { + "Id": "CCC.Core.CN04.AR02", + "Description": "When any attempt is made to modify data on the service or a child resource, the service MUST log the client identity, time, and result of the attempt.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements.", + "Section": "CCC.Core.CN04 Log All Access and Changes", + "SubSection": "", + "SubSectionObjective": "Ensure that all access attempts are logged to maintain a detailed audit trail for security and compliance purposes.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-08" + ] + } + ] + } + ], + "Checks": [ + "cloudtrail_s3_dataevents_write_enabled", + "cloudtrail_multi_region_enabled_logging_management_events", + "cloudtrail_cloudwatch_logging_enabled", + "awslambda_function_invoke_api_operations_cloudtrail_logging_enabled", + "vpc_flow_logs_enabled", + "apigateway_restapi_logging_enabled", + "apigatewayv2_api_access_logging_enabled" + ] + }, + { + "Id": "CCC.Core.CN04.AR03", + "Description": "When any attempt is made to read data on the service or a child resource, the service MUST log the client identity, time, and result of the attempt.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements.", + "Section": "CCC.Core.CN04 Log All Access and Changes", + "SubSection": "", + "SubSectionObjective": "Ensure that all access attempts are logged to maintain a detailed audit trail for security and compliance purposes.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-08" + ] + } + ] + } + ], + "Checks": [ + "cloudtrail_s3_dataevents_read_enabled", + "cloudtrail_insights_exist", + "cloudtrail_cloudwatch_logging_enabled", + "cloudtrail_multi_region_enabled", + "apigateway_restapi_logging_enabled", + "apigatewayv2_api_access_logging_enabled", + "s3_bucket_server_access_logging_enabled" + ] + }, + { + "Id": "CCC.Core.CN07.AR01", + "Description": "When enumeration activities are detected, the service MUST publish an event to a monitored channel which includes the client identity, time, and nature of the activity.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements.", + "Section": "CCC.Core.CN07 Alert on Unusual Enumeration Activity", + "SubSection": "", + "SubSectionObjective": "Ensure that logs and associated alerts are generated when unusual enumeration activity is detected that may indicate reconnaissance activities.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Implement event publication mechanisms and alerts for patterns indicative of enumeration activities, such as repeated access attempts, requests, or liveness probes. Configure alerts to notify security teams of any activities that merit further investigation.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH15" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-05", + "SEF-05" + ] + } + ] + } + ], + "Checks": [ + "cloudtrail_threat_detection_enumeration", + "guardduty_is_enabled", + "guardduty_no_high_severity_findings" + ] + }, + { + "Id": "CCC.Core.CN07.AR02", + "Description": "When enumeration activities are detected, the service MUST log the client identity, time, and nature of the activity.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements.", + "Section": "CCC.Core.CN07 Alert on Unusual Enumeration Activity", + "SubSection": "", + "SubSectionObjective": "Ensure that logs and associated alerts are generated when unusual enumeration activity is detected that may indicate reconnaissance activities.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Implement logging mechanisms to capture details of enumeration activities, including client identity, timestamps, and activity nature. Retain logs according to organizational policies, and occasionally review them for patterns that may indicate reconnaissance activities.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH15" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-05", + "SEF-05" + ] + } + ] + } + ], + "Checks": [ + "cloudtrail_threat_detection_enumeration" + ] + }, { "Id": "CCC.AuditLog.CN01.AR01", "Description": "When the signature validation process is performed, then it MUST detect any modification of data.", @@ -18,13 +1843,13 @@ "Applicability": [ "tlp-red" ], - "Recommendation": "Ensure hash of data is included in digital signature. ", + "Recommendation": "Ensure hash of data is included in digital signature.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH06", - "CCC.TH07" + "CCC.Core.TH06", + "CCC.Core.TH07" ] } ], @@ -61,13 +1886,13 @@ "Applicability": [ "tlp-red" ], - "Recommendation": "Ensure verification process includes a chained hash function. ", + "Recommendation": "Ensure verification process includes a chained hash function.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH06", - "CCC.TH07" + "CCC.Core.TH06", + "CCC.Core.TH07" ] } ], @@ -110,7 +1935,7 @@ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH06" + "CCC.Core.TH06" ] } ], @@ -135,17 +1960,10 @@ "Checks": [ "cloudtrail_multi_region_enabled", "cloudtrail_multi_region_enabled_logging_management_events", - "cloudtrail_insights_exist", - "cloudtrail_log_file_validation_enabled", - "cloudtrail_kms_encryption_enabled", "cloudtrail_cloudwatch_logging_enabled", - "cloudtrail_logs_s3_bucket_is_not_publicly_accessible", - "cloudtrail_logs_s3_bucket_access_logging_enabled", "cloudtrail_s3_dataevents_read_enabled", "cloudtrail_s3_dataevents_write_enabled", - "cloudtrail_threat_detection_enumeration", - "cloudtrail_threat_detection_llm_jacking", - "cloudtrail_threat_detection_privilege_escalation" + "cloudtrail_insights_exist" ] }, { @@ -162,12 +1980,12 @@ "tlp-red", "tlp-amber" ], - "Recommendation": "Ensure alerting is correctly configured ", + "Recommendation": "Ensure alerting is correctly configured", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07" + "CCC.Core.TH07" ] } ], @@ -189,14 +2007,7 @@ } ], "Checks": [ - "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", - "cloudtrail_log_file_validation_enabled", - "cloudtrail_kms_encryption_enabled", - "cloudtrail_cloudwatch_logging_enabled", - "cloudtrail_multi_region_enabled", - "cloudtrail_insights_exist", - "s3_bucket_object_lock", - "cloudtrail_multi_region_enabled_logging_management_events" + "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled" ] }, { @@ -213,12 +2024,12 @@ "tlp-red", "tlp-amber" ], - "Recommendation": "Ensure alerting is correctly configured ", + "Recommendation": "Ensure alerting is correctly configured", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07" + "CCC.Core.TH07" ] } ], @@ -240,17 +2051,7 @@ } ], "Checks": [ - "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", - "cloudwatch_changes_to_network_acls_alarm_configured", - "cloudwatch_changes_to_network_gateways_alarm_configured", - "cloudwatch_changes_to_network_route_tables_alarm_configured", - "cloudwatch_changes_to_vpcs_alarm_configured", - "cloudwatch_log_metric_filter_policy_changes", - "cloudwatch_log_metric_filter_aws_organizations_changes", - "cloudwatch_log_metric_filter_for_s3_bucket_policy_changes", - "cloudwatch_log_metric_filter_security_group_changes", - "cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk", - "cloudwatch_log_metric_filter_unauthorized_api_calls" + "cloudwatch_log_metric_filter_for_s3_bucket_policy_changes" ] }, { @@ -267,13 +2068,13 @@ "tlp-red", "tlp-amber" ], - "Recommendation": "Configure the audit log bucket to enable server access logging. Ensure the target logging bucket is configured for appropriate security, including restricted access and immutability. ", + "Recommendation": "Configure the audit log bucket to enable server access logging. Ensure the target logging bucket is configured for appropriate security, including restricted access and immutability.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01", - "CCC.TH09" + "CCC.Core.TH01", + "CCC.Core.TH09" ] } ], @@ -296,9 +2097,6 @@ ], "Checks": [ "cloudtrail_logs_s3_bucket_access_logging_enabled", - "cloudtrail_logs_s3_bucket_is_not_publicly_accessible", - "cloudtrail_bucket_requires_mfa_delete", - "cloudtrail_kms_encryption_enabled", "s3_bucket_server_access_logging_enabled" ] }, @@ -316,12 +2114,12 @@ "tlp-red", "tlp-amber" ], - "Recommendation": "Configure audit log exporting. ", + "Recommendation": "Configure audit log exporting.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07" + "CCC.Core.TH07" ] } ], @@ -344,16 +2142,10 @@ } ], "Checks": [ - "cloudtrail_kms_encryption_enabled", - "cloudtrail_logs_s3_bucket_is_not_publicly_accessible", - "cloudtrail_logs_s3_bucket_access_logging_enabled", - "cloudtrail_bucket_requires_mfa_delete", + "cloudtrail_multi_region_enabled", "cloudtrail_s3_dataevents_read_enabled", "cloudtrail_s3_dataevents_write_enabled", - "cloudtrail_multi_region_enabled", - "cloudtrail_multi_region_enabled_logging_management_events", - "s3_bucket_cross_region_replication", - "s3_bucket_cross_account_access" + "s3_bucket_cross_region_replication" ] }, { @@ -371,13 +2163,13 @@ "tlp-amber", "tlp-green" ], - "Recommendation": "Configure the audit log bucket's lifecycle rules or object retention settings to enforce the required data retention period. ", + "Recommendation": "Configure the audit log bucket's lifecycle rules or object retention settings to enforce the required data retention period.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH06", - "CCC.TH07" + "CCC.Core.TH06", + "CCC.Core.TH07" ] } ], @@ -417,13 +2209,13 @@ "tlp-amber", "tlp-green" ], - "Recommendation": "Enable MFA Delete (or equivalent multi-factor authentication for delete operations) on the audit log bucket. ", + "Recommendation": "Enable MFA Delete (or equivalent multi-factor authentication for delete operations) on the audit log bucket.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH06", - "CCC.TH07" + "CCC.Core.TH06", + "CCC.Core.TH07" ] } ], @@ -462,12 +2254,12 @@ "tlp-red", "tlp-amber" ], - "Recommendation": "Configure object lock policy. ", + "Recommendation": "Configure object lock policy.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07" + "CCC.Core.TH07" ] } ], @@ -506,12 +2298,12 @@ "tlp-red", "tlp-amber" ], - "Recommendation": "Review field level access controls on audit data. ", + "Recommendation": "Review field level access controls on audit data.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07" + "CCC.Core.TH07" ] } ], @@ -537,11 +2329,8 @@ } ], "Checks": [ - "cloudtrail_kms_encryption_enabled", "cloudtrail_logs_s3_bucket_is_not_publicly_accessible", - "cloudtrail_bucket_requires_mfa_delete", - "cloudwatch_log_group_not_publicly_accessible", - "s3_bucket_public_access" + "cloudwatch_log_group_not_publicly_accessible" ] }, { @@ -559,12 +2348,12 @@ "tlp-amber", "tlp-green" ], - "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted. ", + "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01" + "CCC.Core.TH01" ] } ], @@ -587,7 +2376,7 @@ ], "Checks": [ "cloudtrail_logs_s3_bucket_is_not_publicly_accessible", - "cloudtrail_kms_encryption_enabled", + "s3_bucket_public_access", "s3_bucket_public_list_acl", "s3_bucket_public_write_acl" ] @@ -607,12 +2396,12 @@ "tlp-amber", "tlp-green" ], - "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted. ", + "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01" + "CCC.Core.TH01" ] } ], @@ -635,220 +2424,34 @@ ], "Checks": [ "cloudtrail_logs_s3_bucket_is_not_publicly_accessible", - "s3_bucket_public_write_acl", - "s3_bucket_public_list_acl", "s3_bucket_public_access", + "s3_bucket_public_list_acl", + "s3_bucket_public_write_acl", "s3_bucket_policy_public_write_access" ] }, { - "Id": "CCC.Build.CN01.AR01", - "Description": "Attempt to initiate a build using an unauthorized build agent and verify that the build is rejected.", - "Attributes": [ - { - "FamilyName": "Access Control", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.Build.CN01 Restrict Allowed Build Agents", - "SubSection": "", - "SubSectionObjective": "Ensure that builds are executed only on authorized build agents to maintain control over the build environment and prevent unauthorized code execution.", - "Applicability": [ - "tlp-red", - "tlp-amber" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3", - "AC-6" - ] - } - ] - } - ], - "Checks": [ - "codebuild_project_user_controlled_buildspec", - "codebuild_project_source_repo_url_no_sensitive_credentials", - "codebuild_project_uses_allowed_github_organizations", - "codebuild_project_not_publicly_accessible", - "codebuild_project_logging_enabled", - "codebuild_project_s3_logs_encrypted", - "codebuild_project_no_secrets_in_variables", - "codebuild_project_older_90_days" - ] - }, - { - "Id": "CCC.Build.CN02.AR01", - "Description": "Attempt to trigger a build from an unauthorized external service or repository and verify that the build does not start.", - "Attributes": [ - { - "FamilyName": "Access Control", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.Build.CN02 Restrict Allowed External Services for Build Triggers", - "SubSection": "", - "SubSectionObjective": "Ensure that builds can only be triggered by authorized external services or repositories to prevent unauthorized code execution or tampering.", - "Applicability": [ - "tlp-red", - "tlp-amber" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3", - "AC-6" - ] - } - ] - } - ], - "Checks": [ - "codebuild_project_uses_allowed_github_organizations", - "codebuild_project_user_controlled_buildspec", - "codebuild_project_source_repo_url_no_sensitive_credentials", - "codebuild_project_not_publicly_accessible" - ] - }, - { - "Id": "CCC.Build.CN03.AR01", - "Description": "Attempt to access the build environment from an external network and verify that access is denied.", - "Attributes": [ - { - "FamilyName": "Network Security", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.Build.CN03 Deny External Network Access for Build Environments", - "SubSection": "", - "SubSectionObjective": "Ensure that build environments do not have external network access to prevent unauthorized external access and data exfiltration.", - "Applicability": [ - "tlp-red", - "tlp-amber" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH02", - "CCC.TH05" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-5" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-7", - "SC-5" - ] - } - ] - } - ], - "Checks": [ - "codebuild_project_not_publicly_accessible" - ] - }, - { - "Id": "CCC.CntrReg.CN01.AR01", - "Description": "Attempt to push an artifact with known vulnerabilities to the registry and observe if it is flagged or rejected by the vulnerability scanning process.", - "Attributes": [ - { - "FamilyName": "Risk Management", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CntrReg.CN01 Implement Vulnerability Scanning for Artifacts", - "SubSection": "", - "SubSectionObjective": "Ensure that container images and artifacts stored in the container registry are scanned for vulnerabilities to identify and remediate security issues before deployment.", - "Applicability": [ - "tlp-red", - "tlp-amber" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.CntrReg.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "ID.RA-1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "RA-5", - "SI-5" - ] - } - ] - } - ], - "Checks": [ - "ecr_registry_scan_images_on_push_enabled", - "ecr_repositories_scan_vulnerabilities_in_latest_image" - ] - }, - { - "Id": "CCC.DataWar.CN01.AR01", - "Description": "Attempt to access underlying database tables directly without using managed views and verify that access is denied.", + "Id": "CCC.Logging.CN01.AR01", + "Description": "When a new cloud account is created, provider-level audit and network flow logging MUST be enabled by default and directed to the central sink.", "Attributes": [ { "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.DataWar.CN01 Enforce Use of Managed Views for Data Access", + "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data.", + "Section": "CCC.Logging.CN01 Centralized and Comprehensive Log Aggregation", "SubSection": "", - "SubSectionObjective": "Ensure that data access is provided through managed views, restricting users from accessing underlying tables directly and enforcing consistent security policies.", + "SubSectionObjective": "Ensure all operational and security logs from across the cloud environment, including applications, operating systems, network traffic, and cloud service activity, are captured automatically and streamed to a central, secure log management service.", "Applicability": [ - "tlp-red", - "tlp-amber" + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" ], "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01" + "CCC.Logging.TH07" ] } ], @@ -856,14 +2459,490 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.AC-4" + "PR.PS-04" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AU-2", + "AU-3" + ] + } + ] + } + ], + "Checks": [ + "cloudtrail_multi_region_enabled", + "cloudtrail_multi_region_enabled_logging_management_events", + "vpc_flow_logs_enabled" + ] + }, + { + "Id": "CCC.Logging.CN01.AR02", + "Description": "When a new cloud compute resource is deployed, it MUST be configured to forward all relevant logs (e.g., OS, application, service logs) to the central log sink.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data.", + "Section": "CCC.Logging.CN01 Centralized and Comprehensive Log Aggregation", + "SubSection": "", + "SubSectionObjective": "Ensure all operational and security logs from across the cloud environment, including applications, operating systems, network traffic, and cloud service activity, are captured automatically and streamed to a central, secure log management service.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Logging.TH07" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.PS-04" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AU-2", + "AU-3" + ] + } + ] + } + ], + "Checks": [ + "vpc_flow_logs_enabled", + "cloudtrail_cloudwatch_logging_enabled", + "apigateway_restapi_logging_enabled", + "apigatewayv2_api_access_logging_enabled", + "awslambda_function_invoke_api_operations_cloudtrail_logging_enabled" + ] + }, + { + "Id": "CCC.Logging.CN02.AR01", + "Description": "When a new log bucket or stream is created, its retention policy MUST be configured in accordance with organisation's data retention policy.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data.", + "Section": "CCC.Logging.CN02 Enforce Data Retention Policy for Logs", + "SubSection": "", + "SubSectionObjective": "Ensure that the retention period configured for logs aligns with the organization's data retention policy.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Logging.TH05" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "GV.PO-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AU-11" + ] + } + ] + } + ], + "Checks": [ + "cloudwatch_log_group_retention_policy_specific_days_enabled" + ] + }, + { + "Id": "CCC.Logging.CN02.AR02", + "Description": "When a query is performed to retrieve log events older than the number of days defined in the organisation's data retention policy, it MUST return an empty result.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data.", + "Section": "CCC.Logging.CN02 Enforce Data Retention Policy for Logs", + "SubSection": "", + "SubSectionObjective": "Ensure that the retention period configured for logs aligns with the organization's data retention policy.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Logging.TH05" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "GV.PO-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AU-11" + ] + } + ] + } + ], + "Checks": [ + "cloudwatch_log_group_retention_policy_specific_days_enabled" + ] + }, + { + "Id": "CCC.Logging.CN03.AR01", + "Description": "When an attempt is made to modify or delete data before the object lock period expires, then the action MUST be denied.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data.", + "Section": "CCC.Logging.CN03 Enable Object Lock On Log Bucket", + "SubSection": "", + "SubSectionObjective": "Ensure log immutability by enabling Write Once, Read Many (WORM) protection using object lock on log storage buckets. This prevents logs from being modified or deleted during the defined retention period, supporting compliance and forensic integrity.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "Configure object lock policy.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH07" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.PS-04" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AU-9", + "AU-11" + ] + } + ] + } + ], + "Checks": [ + "s3_bucket_object_lock" + ] + }, + { + "Id": "CCC.Logging.CN04.AR01", + "Description": "When restricted fields are accessed by unauthorized users, then those fields MUST remain masked.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify logs.", + "Section": "CCC.Logging.CN04 Restrict Field And Log Type Access", + "SubSection": "", + "SubSectionObjective": "Configure access to logs to follow the principle of least privilege in particular where technically possible limit the log fields users have access to to prevent accidental exposure to sensitive information such as PII.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "Review field level access controls on log data.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Logging.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.PS-04" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6", + "AU-9", + "AC-3", + "PT-2", + "PT-3", + "PT-3" + ] + } + ] + } + ], + "Checks": [ + "cloudwatch_log_group_not_publicly_accessible", + "cloudtrail_logs_s3_bucket_is_not_publicly_accessible" + ] + }, + { + "Id": "CCC.Logging.CN05.AR01", + "Description": "When a log storage bucket is created, the bucket's access control settings MUST explicitly deny public read and write access.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify logs.", + "Section": "CCC.Logging.CN05 Ensure Log Bucket is Not Publicly Accessible", + "SubSection": "", + "SubSectionObjective": "Ensure that log storage buckets are not publicly accessible to prevent unauthorized access to sensitive log data. In addition, logs should be replicated to another cloud region to enhance availability, durability, and support disaster recovery requirements.", + "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green" + ], + "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-05" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ "AC-3", - "AC-6" + "SC-7" + ] + } + ] + } + ], + "Checks": [ + "cloudtrail_logs_s3_bucket_is_not_publicly_accessible", + "s3_bucket_public_access", + "s3_bucket_public_list_acl", + "s3_bucket_public_write_acl", + "s3_account_level_public_access_blocks", + "s3_bucket_level_public_access_block" + ] + }, + { + "Id": "CCC.Logging.CN05.AR02", + "Description": "When the URL of a log storage bucket's object is accessed publicly, the action MUST be denied by bucket policy.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify logs.", + "Section": "CCC.Logging.CN05 Ensure Log Bucket is Not Publicly Accessible", + "SubSection": "", + "SubSectionObjective": "Ensure that log storage buckets are not publicly accessible to prevent unauthorized access to sensitive log data. In addition, logs should be replicated to another cloud region to enhance availability, durability, and support disaster recovery requirements.", + "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green" + ], + "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-05" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-3", + "SC-7" + ] + } + ] + } + ], + "Checks": [ + "s3_bucket_public_access", + "s3_bucket_public_list_acl", + "s3_bucket_public_write_acl", + "cloudtrail_logs_s3_bucket_is_not_publicly_accessible", + "s3_account_level_public_access_blocks" + ] + }, + { + "Id": "CCC.Logging.CN06.AR01", + "Description": "When a single principal executes an anomalously high number of log queries, an alert MUST be generated.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that collect, alert, and retain logging-related events.", + "Section": "CCC.Logging.CN06 Detect and Alert on Potential Log Exfiltration", + "SubSection": "", + "SubSectionObjective": "Identify and alert on anomalous data access patterns that may indicate an attempt to exfiltrate log data.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Logging.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "DE.CM-03", + "DE.CM-09" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-4", + "CA-7", + "AU-6" + ] + } + ] + } + ], + "Checks": [ + "cloudtrail_threat_detection_enumeration" + ] + }, + { + "Id": "CCC.Logging.CN07.AR01", + "Description": "When an audit log event is recorded that corresponds to a modification of the logging service configuration such as disabling a log trail, deleting a log sink, or altering a log forwarding rule, an alert MUST be generated.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that collect, alert, and retain logging-related events.", + "Section": "CCC.Logging.CN07 Detect and Alert on Log Service Tampering", + "SubSection": "", + "SubSectionObjective": "Alert when any component of the critical logging infrastructure is disabled, modified, or deleted, indicating a defense evasion attempt.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH16" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "DE.CM-03", + "DE.CM-09" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-4", + "CA-7", + "AU-6" + ] + } + ] + } + ], + "Checks": [ + "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled" + ] + }, + { + "Id": "CCC.Monitor.CN01.AR01", + "Description": "When an External Monitoring system exceeds the anticipated rate of monitoring checks then Rate Limiting MUST be applied and an Audit Alert MUST be generated.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that collect, alert, and retain events from other monitoring services.", + "Section": "CCC.Monitor.CN01 Rate Limiting on External Monitoring", + "SubSection": "", + "SubSectionObjective": "Prevent DoS attacks using External Monitoring tools.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Monitor.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.IR-01", + "DE.CM-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-5", + "SC-7" ] } ] @@ -872,25 +2951,27 @@ "Checks": [] }, { - "Id": "CCC.DataWar.CN02.AR01", - "Description": "Attempt to query sensitive columns without the necessary permissions and verify that access is denied or data is masked.", + "Id": "CCC.Monitor.CN02.AR01", + "Description": "When an Custom or User-Defined Metric starts to flood a collector, then a rate limit MUST be applied to reduce the network impact of traffic and an alert must triggered.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.DataWar.CN02 Enforce Column-Level Security Policies", + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that collect, alert, and retain events from other monitoring services.", + "Section": "CCC.Monitor.CN02 Rate Limiting on Metric Generation", "SubSection": "", - "SubSectionObjective": "Ensure that access to sensitive data columns is restricted based on user roles, preventing unauthorized access to sensitive information.", + "SubSectionObjective": "Prevent Malicious Actor or misconfiguration from flooding services with metric data.", "Applicability": [ - "tlp-red", - "tlp-amber" + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" ], "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01" + "CCC.Monitor.TH06" ] } ], @@ -898,14 +2979,15 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.AC-4" + "DE.CM-01" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "AC-3", - "AC-6" + "SC-5(2)", + "CA-7", + "SI-4" ] } ] @@ -914,25 +2996,27 @@ "Checks": [] }, { - "Id": "CCC.DataWar.CN03.AR01", - "Description": "Attempt to query data rows that the user should not have access to and verify that access is denied or data is not returned.", + "Id": "CCC.Monitor.CN03.AR01", + "Description": "When external systems have approved access to internal systems not normally available for public access then they MUST be secured to prevent unauthorised access jumping through to the internal systems and only allow access to specific internal services.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.DataWar.CN03 Enforce Row-Level Security Policies", + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls designed to prevent unauthorised access to monitoring features.", + "Section": "CCC.Monitor.CN03 Access External Monitoring", "SubSection": "", - "SubSectionObjective": "Ensure that access to data rows is restricted based on user roles or attributes, preventing unauthorized access to specific subsets of data.", + "SubSectionObjective": "Control access to Synthetic monitoring solutions using API keys or Certificate based authentication to ensure they don't become an attack path, preventing monitoring systems from forging network requests to gain access to internal systems.", "Applicability": [ - "tlp-red", - "tlp-amber" + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" ], "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01" + "CCC.Monitor.TH04" ] } ], @@ -940,14 +3024,111 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.AC-4" + "DE.CM-06", + "PR.IR-01", + "PR.AA-05" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "AC-3", - "AC-6" + "AC-3" + ] + } + ] + } + ], + "Checks": [ + "apigateway_restapi_authorizers_enabled", + "apigateway_restapi_client_certificate_enabled", + "apigatewayv2_api_authorizers_enabled", + "apigateway_restapi_public_with_authorizer" + ] + }, + { + "Id": "CCC.Monitor.CN04.AR01", + "Description": "When monitoring dashboards display degraded services which may become potential targets then the dashboard MUST be protected from unauthorised access.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls designed to prevent unauthorised access to monitoring features.", + "Section": "CCC.Monitor.CN04 Restrict access to Monitoring Dashboards", + "SubSection": "", + "SubSectionObjective": "Control access to Monitoring Dashboards and reports to ensure they don't highlight an attack path.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Monitor.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "DE.CM-09", + "DE.AE-03" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-4", + "AC-3" + ] + } + ] + } + ], + "Checks": [ + "cloudwatch_log_group_not_publicly_accessible" + ] + }, + { + "Id": "CCC.Monitor.CN05.AR01", + "Description": "When monitoring services have generated an alert, the service MUST ensure only authorised responders silence or acknowledge the alert.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls designed to prevent unauthorised access to monitoring features.", + "Section": "CCC.Monitor.CN05 Restrict access to silence or acknowledge an alert", + "SubSection": "", + "SubSectionObjective": "Ensure only a subset of users can silence or acknowledge alerts to prevent attackers hiding their activity.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH10" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.IR-01", + "PR.AA-05" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-3" ] } ] @@ -956,179 +3137,762 @@ "Checks": [] }, { - "Id": "CCC.KeyMgmt.CN01.AR01", - "Description": "When a key version is scheduled for deletion or disabled, an alert MUST be generated within five minutes.", + "Id": "CCC.Monitor.CN06.AR01", + "Description": "When systems push metrics or traces they MUST be authenticated for that particular type of metric or trace", "Attributes": [ { - "FamilyName": "Logging and Metrics Publication", - "FamilyDescription": "Controls that collect, alert, and retain key-management events.", - "Section": "CCC.KeyMgmt.CN01 Alert on Key-version Changes", + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls designed to prevent unauthorised access to monitoring features.", + "Section": "CCC.Monitor.CN06 Metrics pushed for authorised services only", "SubSection": "", - "SubSectionObjective": "Generate near-real-time alerts when a KMS key version is disabled or scheduled for deletion, enabling rapid investigation and recovery.", + "SubSectionObjective": "Use IAM to control which types of metrics or traces can be pushed by different system to avoid a compromised system pushing fabricated metrics about a different service", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Monitor.TH05" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-05" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-5" + ] + } + ] + } + ], + "Checks": [ + "apigateway_restapi_authorizers_enabled", + "apigatewayv2_api_authorizers_enabled" + ] + }, + { + "Id": "CCC.ObjStor.CN01.AR01", + "Description": "When a request is made to read a bucket, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", + "SubSection": "", + "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption, or sensitive data decryption.", "Applicability": [ "tlp-amber", "tlp-red" ], - "Recommendation": "Use native event services (e.g., CloudWatch Events, Azure Monitor, Cloud Audit Logs) to route notifications to an incident-response channel.", + "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.KeyMgmt.TH01" + "CCC.Core.TH01", + "CCC.Core.TH06" ] } ], "SectionGuidelineMappings": [ { - "ReferenceId": "NIST-CSF", + "ReferenceId": "CCM", "Identifiers": [ - "RS.AN-1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "IR-5" + "IAM-01", + "IAM-03", + "DSP-17" ] } ] } ], "Checks": [ - "kms_cmk_not_deleted_unintentionally" + "s3_bucket_kms_encryption", + "iam_policy_no_full_access_to_kms", + "iam_inline_policy_no_full_access_to_kms", + "kms_key_not_publicly_accessible" ] }, { - "Id": "CCC.KeyMgmt.CN02.AR01", - "Description": "When IAM roles and key policies are reviewed, Decrypt permission MUST be granted exclusively to documented authorised principals.", + "Id": "CCC.ObjStor.CN01.AR02", + "Description": "When a request is made to read an object, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", + "SubSection": "", + "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption, or sensitive data decryption.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-01", + "IAM-03", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "s3_bucket_kms_encryption", + "iam_policy_no_full_access_to_kms", + "iam_inline_policy_no_full_access_to_kms", + "kms_key_not_publicly_accessible" + ] + }, + { + "Id": "CCC.ObjStor.CN01.AR03", + "Description": "When a request is made to write to a bucket, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", + "SubSection": "", + "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption, or sensitive data decryption.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-01", + "IAM-03", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "s3_bucket_kms_encryption", + "iam_policy_no_full_access_to_kms", + "iam_inline_policy_no_full_access_to_kms", + "kms_key_not_publicly_accessible" + ] + }, + { + "Id": "CCC.ObjStor.CN01.AR04", + "Description": "When a request is made to write to an object, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", + "SubSection": "", + "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption, or sensitive data decryption.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-01", + "IAM-03", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "s3_bucket_kms_encryption", + "iam_policy_no_full_access_to_kms", + "iam_inline_policy_no_full_access_to_kms", + "kms_key_not_publicly_accessible" + ] + }, + { + "Id": "CCC.ObjStor.CN03.AR01", + "Description": "When an object storage bucket deletion is attempted, the bucket MUST be fully recoverable for a set time-frame after deletion is requested.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN03 Prevent Bucket Deletion Through Irrevocable Bucket Retention Policy", + "SubSection": "", + "SubSectionObjective": "Ensure that object storage bucket is not deleted after creation, and that the preventative measure cannot be unset.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "s3_bucket_object_lock", + "s3_bucket_object_versioning" + ] + }, + { + "Id": "CCC.ObjStor.CN03.AR02", + "Description": "When an attempt is made to modify the retention policy for an object storage bucket, the service MUST prevent the policy from being modified.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN03 Prevent Bucket Deletion Through Irrevocable Bucket Retention Policy", + "SubSection": "", + "SubSectionObjective": "Ensure that object storage bucket is not deleted after creation, and that the preventative measure cannot be unset.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "s3_bucket_object_lock" + ] + }, + { + "Id": "CCC.ObjStor.CN04.AR01", + "Description": "When an object is uploaded to the object storage system, the object MUST automatically receive a default retention policy that prevents premature deletion or modification.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN04 Objects have an Effective Retention Policy by Default", + "SubSection": "", + "SubSectionObjective": "Ensure that all objects stored in the object storage system have a retention policy applied by default, preventing premature deletion or modification of objects.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + }, + { + "ReferenceId": "CCC.ObjStor", + "Identifiers": [ + "CCC.ObjStor.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "s3_bucket_object_lock" + ] + }, + { + "Id": "CCC.ObjStor.CN04.AR02", + "Description": "When an attempt is made to delete or modify an object that is subject to an active retention policy, the service MUST prevent the action from being completed.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN04 Objects have an Effective Retention Policy by Default", + "SubSection": "", + "SubSectionObjective": "Ensure that all objects stored in the object storage system have a retention policy applied by default, preventing premature deletion or modification of objects.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + }, + { + "ReferenceId": "CCC.ObjStor", + "Identifiers": [ + "CCC.ObjStor.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "s3_bucket_object_lock" + ] + }, + { + "Id": "CCC.ObjStor.CN05.AR01", + "Description": "When an object is uploaded to the object storage bucket, the object MUST be stored with a unique identifier.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN05 Versioning is Enabled for All Objects in the Bucket", + "SubSection": "", + "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "s3_bucket_object_versioning" + ] + }, + { + "Id": "CCC.ObjStor.CN05.AR02", + "Description": "When an object is modified, the service MUST assign a new unique identifier to the modified object to differentiate it from the previous version.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN05 Versioning is Enabled for All Objects in the Bucket", + "SubSection": "", + "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "s3_bucket_object_versioning" + ] + }, + { + "Id": "CCC.ObjStor.CN05.AR03", + "Description": "When an object is modified, the service MUST allow for recovery of previous versions of the object.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN05 Versioning is Enabled for All Objects in the Bucket", + "SubSection": "", + "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "s3_bucket_object_versioning" + ] + }, + { + "Id": "CCC.ObjStor.CN05.AR04", + "Description": "When an object is deleted, the service MUST retain other versions of the object to allow for recovery of previous versions.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN05 Versioning is Enabled for All Objects in the Bucket", + "SubSection": "", + "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "s3_bucket_object_versioning" + ] + }, + { + "Id": "CCC.ObjStor.CN07.AR01", + "Description": "The object storage service MUST support a configuration option that requires MFA to be successfully completed before any object deletion can be attempted, regardless of the request interface.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN07 Multi-Factor Authentication Is Required for Object Deletion", + "SubSection": "", + "SubSectionObjective": "Ensure that deletion of objects stored in the object storage system is protected by multi-factor authentication (MFA), reducing the risk of accidental, unauthorized, or compromised-credential–based data destruction.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH06", + "CCC.Core.TH17" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "IAM-12" + ] + } + ] + } + ], + "Checks": [ + "s3_bucket_no_mfa_delete" + ] + }, + { + "Id": "CCC.ObjStor.CN07.AR02", + "Description": "When MFA deletion protection is enabled on a bucket or object namespace, the service MUST deny any deletion request from an identity that has not satisfied the MFA requirement at the time of the request.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN07 Multi-Factor Authentication Is Required for Object Deletion", + "SubSection": "", + "SubSectionObjective": "Ensure that deletion of objects stored in the object storage system is protected by multi-factor authentication (MFA), reducing the risk of accidental, unauthorized, or compromised-credential–based data destruction.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH06", + "CCC.Core.TH17" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "IAM-12" + ] + } + ] + } + ], + "Checks": [ + "s3_bucket_no_mfa_delete" + ] + }, + { + "Id": "CCC.ObjStor.CN07.AR03", + "Description": "When an attempt is made to delete an object, the service's audit logs MUST clearly record each deletion attempt, including whether MFA was required and whether validation was met.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN07 Multi-Factor Authentication Is Required for Object Deletion", + "SubSection": "", + "SubSectionObjective": "Ensure that deletion of objects stored in the object storage system is protected by multi-factor authentication (MFA), reducing the risk of accidental, unauthorized, or compromised-credential–based data destruction.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH06", + "CCC.Core.TH17" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "IAM-12" + ] + } + ] + } + ], + "Checks": [ + "s3_bucket_no_mfa_delete" + ] + }, + { + "Id": "CCC.ObjStor.CN02.AR01", + "Description": "When a permission set is allowed for an object in a bucket, the service MUST allow the same permission set to access all objects in the same bucket.", "Attributes": [ { "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls that enforce least-privilege use of KMS operations.", - "Section": "CCC.KeyMgmt.CN02 Limit Decrypt Permissions", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources.", + "Section": "CCC.ObjStor.CN02 Enforce Uniform Bucket-level Access to Prevent Inconsistent Permissions", "SubSection": "", - "SubSectionObjective": "Restrict the Decrypt operation to authorised principals only, applying the principle of least privilege to protect sensitive data.", + "SubSectionObjective": "Ensure that uniform bucket-level access is enforced across all object storage buckets. This prevents the use of ad-hoc or inconsistent object-level permissions, ensuring centralized, consistent, and secure access management in accordance with the principle of least privilege.", "Applicability": [ - "tlp-green" + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" ], - "Recommendation": "Periodically audit policy documents via automated tooling and report any deviations.", + "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.KeyMgmt.TH02" + "CCC.Core.TH01" ] } ], "SectionGuidelineMappings": [ { - "ReferenceId": "NIST-CSF", + "ReferenceId": "CCM", "Identifiers": [ - "PR.AC-4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-6" + "IAM-08" ] } ] } ], "Checks": [ - "kms_cmk_not_deleted_unintentionally", - "kms_cmk_not_multi_region", - "kms_cmk_rotation_enabled", - "kms_cmk_are_used", - "iam_inline_policy_no_full_access_to_kms" + "s3_bucket_acl_prohibited" ] }, { - "Id": "CCC.KeyMgmt.CN03.AR01", - "Description": "When rotation settings are examined, rotation MUST be enabled with an interval not exceeding 365 days.", + "Id": "CCC.ObjStor.CN02.AR02", + "Description": "When a permission set is denied for an object in a bucket, the service MUST deny the same permission set to access all objects in the same bucket.", "Attributes": [ { - "FamilyName": "Key Lifecycle Management", - "FamilyDescription": "Controls that govern creation, rotation, import, and retirement of cryptographic keys.", - "Section": "CCC.KeyMgmt.CN03 Enforce Automatic Rotation", + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources.", + "Section": "CCC.ObjStor.CN02 Enforce Uniform Bucket-level Access to Prevent Inconsistent Permissions", "SubSection": "", - "SubSectionObjective": "Ensure symmetric keys rotate automatically within policy intervals to reduce exposure of key material.", + "SubSectionObjective": "Ensure that uniform bucket-level access is enforced across all object storage buckets. This prevents the use of ad-hoc or inconsistent object-level permissions, ensuring centralized, consistent, and secure access management in accordance with the principle of least privilege.", "Applicability": [ - "tlp-green" + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" ], - "Recommendation": "Use cloud-provider rotation features and verify via configuration scanning.", + "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.KeyMgmt.TH03" + "CCC.Core.TH01" ] } ], "SectionGuidelineMappings": [ { - "ReferenceId": "NIST-CSF", + "ReferenceId": "CCM", "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-12" + "IAM-08" ] } ] } ], "Checks": [ - "kms_cmk_rotation_enabled" - ] - }, - { - "Id": "CCC.KeyMgmt.CN04.AR01", - "Description": "When a key import request is processed, the key MUST use an approved algorithm (RSA-2048+, EC-P256+) and originate from a certified HSM.", - "Attributes": [ - { - "FamilyName": "Key Lifecycle Management", - "FamilyDescription": "Controls that govern creation, rotation, import, and retirement of cryptographic keys.", - "Section": "CCC.KeyMgmt.CN04 Validate Imported Keys", - "SubSection": "", - "SubSectionObjective": "Accept only externally generated keys that meet approved cryptographic strength and provenance requirements.", - "Applicability": [ - "tlp-green" - ], - "Recommendation": "Implement an approval workflow that validates attestation data before import.", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.KeyMgmt.TH04" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28" - ] - } - ] - } - ], - "Checks": [ - "kms_cmk_not_deleted_unintentionally", - "kms_cmk_rotation_enabled", - "kms_cmk_not_multi_region", - "kms_cmk_are_used" + "s3_bucket_acl_prohibited" ] }, { @@ -1136,8 +3900,8 @@ "Description": "When a single client sends more than 2000 requests within any 5-minute sliding window, the load balancer MUST throttle all subsequent requests from that client for at least 60 seconds.", "Attributes": [ { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "Controls that detect anomalous traffic and record load-balancer activity. ", + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that detect anomalous traffic and record load-balancer activity.", "Section": "CCC.LB.CN01 Enforce and Detect Rate Limiting", "SubSection": "", "SubSectionObjective": "Detect and throttle malicious or excessive requests to prevent downstream resource exhaustion and brute-force activity.", @@ -1146,7 +3910,7 @@ "tlp-amber", "tlp-red" ], - "Recommendation": "Implement per-IP token-bucket limits with and verify via synthetic traffic tests. ", + "Recommendation": "Implement per-IP token-bucket limits with and verify via synthetic traffic tests.", "SectionThreatMappings": [ { "ReferenceId": "LB", @@ -1177,9 +3941,9 @@ } ], "Checks": [ - "elbv2_logging_enabled", - "elb_logging_enabled", - "vpc_flow_logs_enabled" + "wafv2_webacl_with_rules", + "waf_regional_webacl_with_rules", + "waf_global_webacl_with_rules" ] }, { @@ -1187,8 +3951,8 @@ "Description": "When throttling is invoked, the load balancer MUST record the event in the access log within 5 minutes for alerting and trend analysis.", "Attributes": [ { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "Controls that detect anomalous traffic and record load-balancer activity. ", + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that detect anomalous traffic and record load-balancer activity.", "Section": "CCC.LB.CN01 Enforce and Detect Rate Limiting", "SubSection": "", "SubSectionObjective": "Detect and throttle malicious or excessive requests to prevent downstream resource exhaustion and brute-force activity.", @@ -1197,7 +3961,7 @@ "tlp-amber", "tlp-red" ], - "Recommendation": "Enable access logging and configure metric filters on HTTP 429 counts to trigger alerts. ", + "Recommendation": "Enable access logging and configure metric filters on HTTP 429 counts to trigger alerts.", "SectionThreatMappings": [ { "ReferenceId": "LB", @@ -1228,9 +3992,10 @@ } ], "Checks": [ + "wafv2_webacl_logging_enabled", + "waf_global_webacl_logging_enabled", "elbv2_logging_enabled", - "elb_logging_enabled", - "vpc_flow_logs_enabled" + "elb_logging_enabled" ] }, { @@ -1238,8 +4003,8 @@ "Description": "When more than 10 percent of targets change from healthy to unhealthy within five minutes, an alert MUST be issued.", "Attributes": [ { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "Controls that detect anomalous traffic and record load-balancer activity. ", + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that detect anomalous traffic and record load-balancer activity.", "Section": "CCC.LB.CN06 Secure Health-Check Telemetry", "SubSection": "", "SubSectionObjective": "Monitor health-check endpoints for tampering and alert on abnormal status changes.", @@ -1248,7 +4013,7 @@ "tlp-amber", "tlp-red" ], - "Recommendation": "Instrument metrics for health check results and target removal events. Configure monitoring alarms to alert on abnormal spikes in unhealthy targets. ", + "Recommendation": "Instrument metrics for health check results and target removal events. Configure monitoring alarms to alert on abnormal spikes in unhealthy targets.", "SectionThreatMappings": [ { "ReferenceId": "LB", @@ -1276,9 +4041,7 @@ "Checks": [ "elbv2_logging_enabled", "elb_logging_enabled", - "cloudwatch_alarm_actions_enabled", - "cloudtrail_cloudwatch_logging_enabled", - "vpc_flow_logs_enabled" + "cloudwatch_alarm_actions_enabled" ] }, { @@ -1287,7 +4050,7 @@ "Attributes": [ { "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls that restrict who can change or query load-balancer resources. ", + "FamilyDescription": "Controls that restrict who can change or query load-balancer resources.", "Section": "CCC.LB.CN04 Enforce Distribution Policies", "SubSection": "", "SubSectionObjective": "Ensure traffic-splitting weights and algorithms are modified only by trusted identities.", @@ -1296,7 +4059,7 @@ "tlp-amber", "tlp-red" ], - "Recommendation": "Define a list of trusted principals allowed to modify routing configurations. Enforce via conditional access policies, and log changes using audit logging. ", + "Recommendation": "Define a list of trusted principals allowed to modify routing configurations. Enforce via conditional access policies, and log changes using audit logging.", "SectionThreatMappings": [ { "ReferenceId": "LB", @@ -1323,11 +4086,7 @@ ], "Checks": [ "cloudtrail_cloudwatch_logging_enabled", - "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", - "iam_policy_attached_only_to_group_or_roles", - "iam_group_administrator_access_policy", - "iam_role_administratoraccess_policy", - "iam_user_administrator_access_policy" + "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled" ] }, { @@ -1336,7 +4095,7 @@ "Attributes": [ { "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls that restrict who can change or query load-balancer resources. ", + "FamilyDescription": "Controls that restrict who can change or query load-balancer resources.", "Section": "CCC.LB.CN05 Validate Session Affinity", "SubSection": "", "SubSectionObjective": "Configure session persistence to minimise fixation and hijacking risks.", @@ -1345,7 +4104,7 @@ "tlp-amber", "tlp-red" ], - "Recommendation": "Audit CCC.LB.F15 parameters via configuration scans.", + "Recommendation": "Audit CCC.LB.CP15 parameters via configuration scans.", "SectionThreatMappings": [ { "ReferenceId": "LB", @@ -1370,22 +4129,7 @@ ] } ], - "Checks": [ - "iam_user_administrator_access_policy", - "iam_group_administrator_access_policy", - "iam_role_administratoraccess_policy", - "iam_inline_policy_allows_privilege_escalation", - "iam_inline_policy_no_full_access_to_cloudtrail", - "iam_inline_policy_no_full_access_to_kms", - "iam_inline_policy_no_administrative_privileges", - "iam_policy_allows_privilege_escalation", - "iam_customer_attached_policy_no_administrative_privileges", - "iam_aws_attached_policy_no_administrative_privileges", - "iam_policy_no_full_access_to_cloudtrail", - "iam_policy_no_full_access_to_kms", - "iam_customer_unattached_policy_no_administrative_privileges", - "iam_policy_attached_only_to_group_or_roles" - ] + "Checks": [] }, { "Id": "CCC.LB.CN09.AR01", @@ -1393,7 +4137,7 @@ "Attributes": [ { "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls that restrict who can change or query load-balancer resources. ", + "FamilyDescription": "Controls that restrict who can change or query load-balancer resources.", "Section": "CCC.LB.CN09 Restrict Management API Access", "SubSection": "", "SubSectionObjective": "Limit load-balancer API calls to authorised identities and trusted networks.", @@ -1429,8 +4173,7 @@ ], "Checks": [ "vpc_endpoint_services_allowed_principals_trust_boundaries", - "vpc_endpoint_connections_trust_boundaries", - "vpc_endpoint_for_ec2_enabled" + "vpc_endpoint_connections_trust_boundaries" ] }, { @@ -1439,7 +4182,7 @@ "Attributes": [ { "FamilyName": "Data", - "FamilyDescription": "Controls that preserve availability and confidentiality of traffic processed by the load balancer. ", + "FamilyDescription": "Controls that preserve availability and confidentiality of traffic processed by the load balancer.", "Section": "CCC.LB.CN02 Auto-Scale Load Balancer Capacity", "SubSection": "", "SubSectionObjective": "Expand load-balancer capacity to maintain availability during traffic spikes.", @@ -1476,9 +4219,7 @@ "Checks": [ "autoscaling_group_capacity_rebalance_enabled", "autoscaling_group_elb_health_check_enabled", - "autoscaling_group_multiple_az", - "autoscaling_group_multiple_instance_types", - "autoscaling_group_using_ec2_launch_template" + "autoscaling_group_multiple_az" ] }, { @@ -1487,7 +4228,7 @@ "Attributes": [ { "FamilyName": "Data", - "FamilyDescription": "Controls that preserve availability and confidentiality of traffic processed by the load balancer. ", + "FamilyDescription": "Controls that preserve availability and confidentiality of traffic processed by the load balancer.", "Section": "CCC.LB.CN07 Scrub Sensitive Headers", "SubSection": "", "SubSectionObjective": "Remove headers that disclose internal details or software versions from HTTP responses.", @@ -1501,7 +4242,7 @@ { "ReferenceId": "LB", "Identifiers": [ - "CCC.TH15" + "CCC.Core.TH15" ] } ], @@ -1564,1733 +4305,7 @@ } ], "Checks": [ - "acm_certificates_expiration_check", - "acm_certificates_transparency_logs_enabled", - "acm_certificates_with_secure_key_algorithms" - ] - }, - { - "Id": "CCC.Logging.CN01.AR01", - "Description": "When a new cloud account is created, provider-level audit and network flow logging MUST be enabled by default and directed to the central sink.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data. ", - "Section": "CCC.Logging.CN01 Centralized and Comprehensive Log Aggregation", - "SubSection": "", - "SubSectionObjective": "Ensure all operational and security logs from across the cloud environment, including applications, operating systems, network traffic, and cloud service activity, are captured automatically and streamed to a central, secure log management service.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Logging.TH07" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.PS-04" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-2", - "AU-3" - ] - } - ] - } - ], - "Checks": [ - "cloudtrail_multi_region_enabled", - "cloudtrail_cloudwatch_logging_enabled", - "cloudtrail_kms_encryption_enabled", - "cloudtrail_log_file_validation_enabled", - "vpc_flow_logs_enabled", - "cloudtrail_logs_s3_bucket_access_logging_enabled", - "cloudtrail_logs_s3_bucket_is_not_publicly_accessible", - "cloudtrail_s3_dataevents_read_enabled", - "cloudtrail_s3_dataevents_write_enabled", - "apigateway_restapi_logging_enabled", - "apigateway_restapi_authorizers_enabled", - "apigateway_restapi_public", - "apigatewayv2_api_access_logging_enabled" - ] - }, - { - "Id": "CCC.Logging.CN01.AR02", - "Description": "When a new cloud compute resource is deployed, it MUST be configured to forward all relevant logs (e.g., OS, application, service logs) to the central log sink.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data. ", - "Section": "CCC.Logging.CN01 Centralized and Comprehensive Log Aggregation", - "SubSection": "", - "SubSectionObjective": "Ensure all operational and security logs from across the cloud environment, including applications, operating systems, network traffic, and cloud service activity, are captured automatically and streamed to a central, secure log management service.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Logging.TH07" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.PS-04" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-2", - "AU-3" - ] - } - ] - } - ], - "Checks": [ - "vpc_flow_logs_enabled", - "cloudtrail_cloudwatch_logging_enabled", - "cloudtrail_multi_region_enabled", - "cloudtrail_kms_encryption_enabled", - "cloudtrail_s3_dataevents_read_enabled", - "cloudtrail_s3_dataevents_write_enabled", - "apigateway_restapi_logging_enabled", - "apigatewayv2_api_access_logging_enabled", - "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", - "cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled", - "cloudwatch_log_metric_filter_for_s3_bucket_policy_changes", - "cloudtrail_logs_s3_bucket_is_not_publicly_accessible" - ] - }, - { - "Id": "CCC.Logging.CN02.AR01", - "Description": "When a new log bucket or stream is created, its retention policy MUST be configured in accordance with organisation's data retention policy.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data. ", - "Section": "CCC.Logging.CN02 Enforce Data Retention Policy for Logs", - "SubSection": "", - "SubSectionObjective": "Ensure that the retention period configured for logs aligns with the organization's data retention policy.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Logging.TH05" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "GV.PO-01" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-11" - ] - } - ] - } - ], - "Checks": [ - "cloudwatch_log_group_retention_policy_specific_days_enabled" - ] - }, - { - "Id": "CCC.Logging.CN02.AR02", - "Description": "When a query is performed to retrieve log events older than the number of days defined in the organisation's data retention policy, it MUST return an empty result.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data. ", - "Section": "CCC.Logging.CN02 Enforce Data Retention Policy for Logs", - "SubSection": "", - "SubSectionObjective": "Ensure that the retention period configured for logs aligns with the organization's data retention policy.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Logging.TH05" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "GV.PO-01" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-11" - ] - } - ] - } - ], - "Checks": [ - "cloudwatch_log_group_retention_policy_specific_days_enabled" - ] - }, - { - "Id": "CCC.AuditLog.CN08.AR01", - "Description": "When an attempt is made to modify or delete data before the object lock period expires, then the action MUST be denied.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data. ", - "Section": "CCC.Logging.CN03 Enable Object Lock On Log Bucket", - "SubSection": "", - "SubSectionObjective": "Ensure log immutability by enabling Write Once, Read Many (WORM) protection using object lock on log storage buckets. This prevents logs from being modified or deleted during the defined retention period, supporting compliance and forensic integrity.", - "Applicability": [ - "tlp-red", - "tlp-amber" - ], - "Recommendation": "Configure object lock policy. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH07" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.PS-04" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-9", - "AU-11" - ] - } - ] - } - ], - "Checks": [ - "s3_bucket_object_lock" - ] - }, - { - "Id": "CCC.AuditLog.CN04.AR01", - "Description": "When restricted fields are accessed by unauthorized users, then those fields MUST remain masked.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls that restrict who can access and modify logs. ", - "Section": "CCC.Logging.CN04 Restrict Field And Log Type Access", - "SubSection": "", - "SubSectionObjective": "Configure access to logs to follow the principle of least privilege in particular where technically possible limit the log fields users have access to to prevent accidental exposure to sensitive information such as PII.", - "Applicability": [ - "tlp-red", - "tlp-amber" - ], - "Recommendation": "Review field level access controls on log data. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Logging.TH04" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.PS-04" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-6", - "AU-9", - "AC-3", - "PT-2", - "PT-3", - "PT-3" - ] - } - ] - } - ], - "Checks": [ - "cloudwatch_log_group_not_publicly_accessible", - "cloudtrail_logs_s3_bucket_is_not_publicly_accessible" - ] - }, - { - "Id": "CCC.Logging.CN05.AR01", - "Description": "When a log storage bucket is created, the bucket's access control settings MUST explicitly deny public read and write access.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls that restrict who can access and modify logs. ", - "Section": "CCC.Logging.CN05 Ensure Log Bucket is Not Publicly Accessible", - "SubSection": "", - "SubSectionObjective": "Ensure that log storage buckets are not publicly accessible to prevent unauthorized access to sensitive log data. In addition, logs should be replicated to another cloud region to enhance availability, durability, and support disaster recovery requirements.", - "Applicability": [ - "tlp-red", - "tlp-amber", - "tlp-green" - ], - "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AA-05" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3", - "SC-7" - ] - } - ] - } - ], - "Checks": [ - "cloudtrail_logs_s3_bucket_is_not_publicly_accessible", - "cloudtrail_multi_region_enabled", - "cloudtrail_logs_s3_bucket_access_logging_enabled", - "cloudtrail_kms_encryption_enabled", - "s3_bucket_public_list_acl", - "s3_bucket_public_write_acl", - "s3_bucket_public_access", - "s3_account_level_public_access_blocks", - "s3_bucket_level_public_access_block" - ] - }, - { - "Id": "CCC.Logging.CN05.AR02", - "Description": "When the URL of a log storage bucket's object is accessed publicly, the action MUST be denied by bucket policy.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls that restrict who can access and modify logs. ", - "Section": "CCC.Logging.CN05 Ensure Log Bucket is Not Publicly Accessible", - "SubSection": "", - "SubSectionObjective": "Ensure that log storage buckets are not publicly accessible to prevent unauthorized access to sensitive log data. In addition, logs should be replicated to another cloud region to enhance availability, durability, and support disaster recovery requirements.", - "Applicability": [ - "tlp-red", - "tlp-amber", - "tlp-green" - ], - "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AA-05" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3", - "SC-7" - ] - } - ] - } - ], - "Checks": [ - "s3_bucket_public_access", - "s3_bucket_public_list_acl", - "s3_bucket_public_write_acl", - "cloudtrail_logs_s3_bucket_is_not_publicly_accessible", - "s3_bucket_cross_region_replication", - "s3_account_level_public_access_blocks" - ] - }, - { - "Id": "CCC.Logging.CN06.AR01", - "Description": "When a single principal executes an anomalously high number of log queries, an alert MUST be generated.", - "Attributes": [ - { - "FamilyName": "Logging and Monitoring", - "FamilyDescription": "Controls that collect, alert, and retain logging-related events. ", - "Section": "CCC.Logging.CN06 Detect and Alert on Potential Log Exfiltration", - "SubSection": "", - "SubSectionObjective": "Identify and alert on anomalous data access patterns that may indicate an attempt to exfiltrate log data.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Logging.TH02" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.CM-03", - "DE.CM-09" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SI-4", - "CA-7", - "AU-6" - ] - } - ] - } - ], - "Checks": [ - "cloudtrail_threat_detection_enumeration", - "cloudtrail_threat_detection_privilege_escalation" - ] - }, - { - "Id": "CCC.Logging.CN07.AR01", - "Description": "When an audit log event is recorded that corresponds to a modification of the logging service configuration such as disabling a log trail, deleting a log sink, or altering a log forwarding rule, an alert MUST be generated.", - "Attributes": [ - { - "FamilyName": "Logging and Monitoring", - "FamilyDescription": "Controls that collect, alert, and retain logging-related events. ", - "Section": "CCC.Logging.CN07 Detect and Alert on Log Service Tampering", - "SubSection": "", - "SubSectionObjective": "Alert when any component of the critical logging infrastructure is disabled, modified, or deleted, indicating a defense evasion attempt.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH16" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.CM-03", - "DE.CM-09" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SI-4", - "CA-7", - "AU-6" - ] - } - ] - } - ], - "Checks": [ - "cloudtrail_cloudwatch_logging_enabled", - "cloudtrail_insights_exist", - "cloudtrail_log_file_validation_enabled", - "cloudtrail_kms_encryption_enabled", - "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", - "cloudtrail_multi_region_enabled_logging_management_events", - "cloudwatch_changes_to_network_acls_alarm_configured", - "cloudwatch_changes_to_network_gateways_alarm_configured", - "cloudwatch_changes_to_network_route_tables_alarm_configured", - "cloudwatch_changes_to_vpcs_alarm_configured" - ] - }, - { - "Id": "CCC.ObjStor.CN01.AR01", - "Description": "When a request is made to read a protected bucket, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN01 Prevent Unencrypted Requests", - "SubSection": "CCC.ObjStor.C01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", - "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption that can impact data availability and integrity.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01", - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DCS-04", - "DCS-06" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.10.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28" - ] - } - ] - } - ], - "Checks": [ - "s3_bucket_kms_encryption", - "kms_key_not_publicly_accessible", - "iam_policy_no_full_access_to_kms", - "storagegateway_fileshare_encryption_enabled" - ] - }, - { - "Id": "CCC.ObjStor.CN01.AR02", - "Description": "When a request is made to read a protected object, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN01 Prevent Unencrypted Requests", - "SubSection": "CCC.ObjStor.C01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", - "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption that can impact data availability and integrity.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01", - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DCS-04", - "DCS-06" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.10.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28" - ] - } - ] - } - ], - "Checks": [ - "kms_key_not_publicly_accessible", - "kms_cmk_not_deleted_unintentionally", - "iam_policy_no_full_access_to_kms", - "iam_inline_policy_no_full_access_to_kms" - ] - }, - { - "Id": "CCC.ObjStor.CN01.AR03", - "Description": "When a request is made to write to a bucket, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN01 Prevent Unencrypted Requests", - "SubSection": "CCC.ObjStor.C01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", - "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption that can impact data availability and integrity.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01", - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DCS-04", - "DCS-06" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.10.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28" - ] - } - ] - } - ], - "Checks": [ - "s3_bucket_kms_encryption", - "iam_policy_no_full_access_to_kms", - "kms_key_not_publicly_accessible", - "kms_cmk_not_deleted_unintentionally", - "storagegateway_fileshare_encryption_enabled" - ] - }, - { - "Id": "CCC.ObjStor.CN01.AR04", - "Description": "When a request is made to write to an object, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN01 Prevent Unencrypted Requests", - "SubSection": "CCC.ObjStor.C01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", - "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption that can impact data availability and integrity.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01", - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DCS-04", - "DCS-06" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.10.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28" - ] - } - ] - } - ], - "Checks": [ - "iam_policy_no_full_access_to_kms", - "iam_inline_policy_no_full_access_to_kms", - "kms_cmk_not_deleted_unintentionally", - "kms_key_not_publicly_accessible", - "kms_cmk_not_multi_region" - ] - }, - { - "Id": "CCC.ObjStor.CN03.AR01", - "Description": "When an object storage bucket deletion is attempted, the bucket MUST be fully recoverable for a set time-frame after deletion is requested.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN03 Implement Multi-factor Authentication (MFA) for Access", - "SubSection": "CCC.ObjStor.C03 Prevent Bucket Deletion Through Irrevocable Bucket Retention Policy", - "SubSectionObjective": "Ensure that object storage bucket is not deleted after creation, and that the preventative measure cannot be unset.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - } - ] - } - ], - "Checks": [ - "s3_bucket_object_versioning", - "s3_bucket_object_lock", - "s3_bucket_lifecycle_enabled" - ] - }, - { - "Id": "CCC.ObjStor.CN03.AR02", - "Description": "When an attempt is made to modify the retention policy for an object storage bucket, the service MUST prevent the policy from being modified.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN03 Implement Multi-factor Authentication (MFA) for Access", - "SubSection": "CCC.ObjStor.C03 Prevent Bucket Deletion Through Irrevocable Bucket Retention Policy", - "SubSectionObjective": "Ensure that object storage bucket is not deleted after creation, and that the preventative measure cannot be unset.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - } - ] - } - ], - "Checks": [ - "s3_bucket_object_versioning", - "s3_bucket_lifecycle_enabled" - ] - }, - { - "Id": "CCC.ObjStor.CN04.AR01", - "Description": "When an object is uploaded to the object storage system, the object MUST automatically receive a default retention policy that prevents premature deletion or modification.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN04 Log All Access and Changes", - "SubSection": "CCC.ObjStor.C04 Objects have an Effective Retention Policy by Default", - "SubSectionObjective": "Ensure that all objects stored in the object storage system have a retention policy applied by default, preventing premature deletion or modification of objects and ensuring compliance with data retention regulations.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - } - ] - } - ], - "Checks": [ - "kinesis_stream_data_retention_period", - "s3_bucket_object_versioning", - "s3_bucket_object_lock" - ] - }, - { - "Id": "CCC.ObjStor.CN04.AR02", - "Description": "When an attempt is made to delete or modify an object that is subject to an active retention policy, the service MUST prevent the action from being completed.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN04 Log All Access and Changes", - "SubSection": "CCC.ObjStor.C04 Objects have an Effective Retention Policy by Default", - "SubSectionObjective": "Ensure that all objects stored in the object storage system have a retention policy applied by default, preventing premature deletion or modification of objects and ensuring compliance with data retention regulations.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - } - ] - } - ], - "Checks": [ - "kinesis_stream_data_retention_period", - "dynamodb_table_deletion_protection_enabled" - ] - }, - { - "Id": "CCC.ObjStor.CN05.AR01", - "Description": "When an object is uploaded to the object storage bucket, the object MUST be stored with a unique identifier.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN05 Prevent Access from Untrusted Entities", - "SubSection": "CCC.ObjStor.C05 Versioning is Enabled for All Objects in the Bucket", - "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - } - ] - } - ], - "Checks": [ - "s3_bucket_object_versioning", - "s3_bucket_object_lock" - ] - }, - { - "Id": "CCC.ObjStor.CN05.AR02", - "Description": "When an object is modified, the service MUST assign a new unique identifier to the modified object to differentiate it from the previous version.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN05 Prevent Access from Untrusted Entities", - "SubSection": "CCC.ObjStor.C05 Versioning is Enabled for All Objects in the Bucket", - "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - } - ] - } - ], - "Checks": [ - "s3_bucket_object_versioning", - "s3_bucket_object_lock", - "iam_rotate_access_key_90_days", - "ecr_repositories_tag_immutability" - ] - }, - { - "Id": "CCC.ObjStor.CN05.AR03", - "Description": "When an object is modified, the service MUST allow for recovery of previous versions of the object.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN05 Prevent Access from Untrusted Entities", - "SubSection": "CCC.ObjStor.C05 Versioning is Enabled for All Objects in the Bucket", - "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - } - ] - } - ], - "Checks": [ - "s3_bucket_object_versioning", - "dynamodb_tables_pitr_enabled", - "backup_recovery_point_encrypted" - ] - }, - { - "Id": "CCC.ObjStor.CN05.AR04", - "Description": "When an object is deleted, the service MUST retain other versions of the object to allow for recovery of previous versions.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN05 Prevent Access from Untrusted Entities", - "SubSection": "CCC.ObjStor.C05 Versioning is Enabled for All Objects in the Bucket", - "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - } - ] - } - ], - "Checks": [ - "s3_bucket_object_versioning", - "kinesis_stream_data_retention_period", - "kms_cmk_not_deleted_unintentionally" - ] - }, - { - "Id": "CCC.ObjStor.CN06.AR01", - "Description": "When an object storage bucket is accessed, the service MUST store access logs in a separate data store.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN06 Prevent Deployment in Restricted Regions", - "SubSection": "CCC.ObjStor.C06 Access Logs are Stored in a Separate Data Store", - "SubSectionObjective": "Ensure that access logs for object storage buckets are stored in a separate data store to protect against unauthorized access, tampering, or deletion of logs (Logbuckets are exempt from this requirement, but must be tlp-red).", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH07", - "CCC.TH09" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-6" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-07", - "DSP-17" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.15.0" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-9", - "SC-28" - ] - } - ] - } - ], - "Checks": [ - "cloudtrail_s3_dataevents_read_enabled", - "cloudtrail_logs_s3_bucket_access_logging_enabled", - "s3_bucket_server_access_logging_enabled" - ] - }, - { - "Id": "CCC.ObjStor.CN02.AR01", - "Description": "When a permission set is allowed for an object in a bucket, the service MUST allow the same permission set to access all objects in the same bucket.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN02 Ensure Data Encryption at Rest for All Stored Data", - "SubSection": "CCC.ObjStor.C02 Enforce Uniform Bucket-level Access to Prevent Inconsistent Permissions", - "SubSectionObjective": "Ensure that uniform bucket-level access is enforced across all object storage buckets. This prevents the use of ad-hoc or inconsistent object-level permissions, ensuring centralized, consistent, and secure access management in accordance with the principle of least privilege.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-4" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.9.4.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3", - "AC-6" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DCS-09" - ] - } - ] - } - ], - "Checks": [ - "s3_bucket_public_write_acl" - ] - }, - { - "Id": "CCC.ObjStor.CN02.AR02", - "Description": "When a permission set is denied for an object in a bucket, the service MUST deny the same permission set to access all objects in the same bucket.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN02 Ensure Data Encryption at Rest for All Stored Data", - "SubSection": "CCC.ObjStor.C02 Enforce Uniform Bucket-level Access to Prevent Inconsistent Permissions", - "SubSectionObjective": "Ensure that uniform bucket-level access is enforced across all object storage buckets. This prevents the use of ad-hoc or inconsistent object-level permissions, ensuring centralized, consistent, and secure access management in accordance with the principle of least privilege.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-4" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.9.4.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3", - "AC-6" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DCS-09" - ] - } - ] - } - ], - "Checks": [ - "s3_bucket_public_write_acl", - "s3_bucket_acl_prohibited", - "s3_bucket_public_access" - ] - }, - { - "Id": "CCC.Monitor.CN01.AR01", - "Description": "When an External Monitoring system exceeds the anticipated rate of monitoring checks then Rate Limiting MUST be applied and an Audit Alert MUST be generated.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "Controls that collect, alert, and retain events from other monitoring services.", - "Section": "CCC.Monitor.CN01 Rate Limiting on External Monitoring", - "SubSection": "", - "SubSectionObjective": "Prevent DoS attacks using External Monitoring tools.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Monitor.TH03" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.IR-01", - "DE.CM-01" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-5", - "SC-7" - ] - } - ] - } - ], - "Checks": [ - "cloudtrail_threat_detection_enumeration", - "cloudtrail_threat_detection_llm_jacking", - "cloudtrail_threat_detection_privilege_escalation", - "cloudtrail_cloudwatch_logging_enabled", - "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", - "cloudwatch_alarm_actions_alarm_state_configured", - "cloudtrail_multi_region_enabled_logging_management_events" - ] - }, - { - "Id": "CCC.Monitor.CN02.AR01", - "Description": "When an Custom or User-Defined Metric starts to flood a collector, then a rate limit MUST be applied to reduce the network impact of traffic and an alert must triggered.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "Controls that collect, alert, and retain events from other monitoring services.", - "Section": "CCC.Monitor.CN02 Rate Limiting on Metric Generation", - "SubSection": "", - "SubSectionObjective": "Prevent Malicious Actor or misconfiguration from flooding services with metric data.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Monitor.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.CM-01" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-5(2)", - "CA-7", - "SI-4" - ] - } - ] - } - ], - "Checks": [ - "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", - "cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled", - "cloudwatch_changes_to_network_acls_alarm_configured", - "cloudwatch_changes_to_network_gateways_alarm_configured", - "cloudwatch_changes_to_network_route_tables_alarm_configured", - "cloudwatch_changes_to_vpcs_alarm_configured", - "cloudwatch_alarm_actions_enabled", - "cloudwatch_alarm_actions_alarm_state_configured", - "cloudwatch_log_metric_filter_authentication_failures", - "cloudwatch_log_metric_filter_unauthorized_api_calls", - "cloudwatch_log_metric_filter_policy_changes", - "cloudwatch_log_metric_filter_for_s3_bucket_policy_changes", - "cloudwatch_log_metric_filter_security_group_changes" - ] - }, - { - "Id": "CCC.Monitor.CN03.AR01", - "Description": "When external systems have approved access to internal systems not normally available for public access then they MUST be secured to prevent unauthorised access jumping through to the internal systems and only allow access to specific internal services.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls designed to prevent unauthorised access to monitoring features.", - "Section": "CCC.Monitor.CN03 Access External Monitoring", - "SubSection": "", - "SubSectionObjective": "Control access to Synthetic monitoring solutions using API keys or Certificate based authentication to ensure they don't become an attack path, preventing monitoring systems from forging network requests to gain access to internal systems.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Monitor.TH04" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.CM-06", - "PR.IR-01", - "PR.AA-05" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "awslambda_function_url_public", - "apigateway_restapi_public", - "apigateway_restapi_authorizers_enabled", - "apigateway_restapi_public_with_authorizer", - "apigateway_restapi_client_certificate_enabled", - "apigatewayv2_api_authorizers_enabled" - ] - }, - { - "Id": "CCC.Monitor.CN04.AR01", - "Description": "When monitoring dashboards display degraded services which may become potential targets then the dashboard MUST be protected from unauthorised access.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls designed to prevent unauthorised access to monitoring features.", - "Section": "CCC.Monitor.CN04 Restrict access to Monitoring Dashboards", - "SubSection": "", - "SubSectionObjective": "Control access to Monitoring Dashboards and reports to ensure they don't highlight an attack path.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Monitor.TH02" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.CM-09", - "DE.AE-03" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SI-4", - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "cloudwatch_log_group_not_publicly_accessible", - "cloudwatch_log_group_kms_encryption_enabled", - "cloudtrail_logs_s3_bucket_is_not_publicly_accessible", - "cloudtrail_cloudwatch_logging_enabled" - ] - }, - { - "Id": "CCC.Monitor.CN05.AR01", - "Description": "When monitoring services have generated an alert, the service MUST ensure only authorised responders silence or acknowledge the alert.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls designed to prevent unauthorised access to monitoring features.", - "Section": "CCC.Monitor.CN05 Restrict access to silence or acknowledge an alert", - "SubSection": "", - "SubSectionObjective": "Ensure only a subset of users can silence or acknowledge alerts to prevent attackers hiding their activity.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH10" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.IR-01", - "PR.AA-05" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "iam_administrator_access_with_mfa", - "iam_root_mfa_enabled", - "iam_group_administrator_access_policy", - "iam_policy_attached_only_to_group_or_roles", - "iam_inline_policy_allows_privilege_escalation", - "iam_inline_policy_no_full_access_to_cloudtrail" - ] - }, - { - "Id": "CCC.Monitor.CN06.AR01", - "Description": "When systems push metrics or traces they MUST be authenticated for that particular type of metric or trace", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls designed to prevent unauthorised access to monitoring features.", - "Section": "CCC.Monitor.CN06 Metrics pushed for authorised services only", - "SubSection": "", - "SubSectionObjective": "Use IAM to control which types of metrics or traces can be pushed by different system to avoid a compromised system pushing fabricated metrics about a different service", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Monitor.TH05" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AA-05" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-5" - ] - } - ] - } - ], - "Checks": [ - "awslambda_function_url_public", - "awslambda_function_not_publicly_accessible", - "apigateway_restapi_authorizers_enabled", - "apigatewayv2_api_authorizers_enabled", - "apigateway_restapi_public_with_authorizer", - "apigateway_restapi_public", - "cloudwatch_log_group_not_publicly_accessible" + "acm_certificates_expiration_check" ] }, { @@ -3345,11 +4360,61 @@ } ], "Checks": [ - "ec2_securitygroup_default_restrict_traffic", - "ec2_securitygroup_allow_ingress_from_internet_to_all_ports", - "ec2_securitygroup_allow_ingress_from_internet_to_any_port" + "ec2_securitygroup_default_restrict_traffic" ] }, + { + "Id": "CCC.VPC.CN02.AR01", + "Description": "When a resource is created in a public subnet, that resource MUST NOT be assigned an external IP address by default.", + "Attributes": [ + { + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.VPC.CN02 Limit Resource Creation in Public Subnet", + "SubSection": "", + "SubSectionObjective": "Restrict the creation of resources in the public subnet with direct access to the internet to minimize attack surfaces.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.VPC.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-3" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "SEF-05" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.13.1.1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-4" + ] + } + ] + } + ], + "Checks": [] + }, { "Id": "CCC.VPC.CN03.AR01", "Description": "When a VPC peering connection is requested, the service MUST prevent connections from VPCs that are not explicitly allowed.", @@ -3461,6 +4526,390 @@ "vpc_flow_logs_enabled" ] }, + { + "Id": "CCC.KeyMgmt.CN01.AR01", + "Description": "When a key version is scheduled for deletion or disabled, an alert MUST be generated within five minutes.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that collect, alert, and retain key-management events.", + "Section": "CCC.KeyMgmt.CN01 Alert on Key-version Changes", + "SubSection": "", + "SubSectionObjective": "Generate near-real-time alerts when a KMS key version is disabled or scheduled for deletion, enabling rapid investigation and recovery.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Use native event services (e.g., CloudWatch Events, Azure Monitor, Cloud Audit Logs) to route notifications to an incident-response channel.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.KeyMgmt.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "RS.AN-1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "IR-5" + ] + } + ] + } + ], + "Checks": [ + "kms_cmk_not_deleted_unintentionally", + "cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk" + ] + }, + { + "Id": "CCC.KeyMgmt.CN02.AR01", + "Description": "When IAM roles and key policies are reviewed, Decrypt permission MUST be granted exclusively to documented authorised principals.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that enforce least-privilege use of KMS operations.", + "Section": "CCC.KeyMgmt.CN02 Limit Decrypt Permissions", + "SubSection": "", + "SubSectionObjective": "Restrict the Decrypt operation to authorised principals only, applying the principle of least privilege to protect sensitive data.", + "Applicability": [ + "tlp-green" + ], + "Recommendation": "Periodically audit policy documents via automated tooling and report any deviations.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.KeyMgmt.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6" + ] + } + ] + } + ], + "Checks": [ + "iam_inline_policy_no_full_access_to_kms", + "iam_policy_no_full_access_to_kms" + ] + }, + { + "Id": "CCC.KeyMgmt.CN03.AR01", + "Description": "When rotation settings are examined, rotation MUST be enabled with an interval not exceeding 365 days.", + "Attributes": [ + { + "FamilyName": "Key Lifecycle Management", + "FamilyDescription": "Controls that govern creation, rotation, import, and retirement of cryptographic keys.", + "Section": "CCC.KeyMgmt.CN03 Enforce Automatic Rotation", + "SubSection": "", + "SubSectionObjective": "Ensure symmetric keys rotate automatically within policy intervals to reduce exposure of key material.", + "Applicability": [ + "tlp-green" + ], + "Recommendation": "Use cloud-provider rotation features and verify via configuration scanning.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.KeyMgmt.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.DS-1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-12" + ] + } + ] + } + ], + "Checks": [ + "kms_cmk_rotation_enabled" + ] + }, + { + "Id": "CCC.KeyMgmt.CN04.AR01", + "Description": "When a key import request is processed, the key MUST use an approved algorithm (RSA-2048+, EC-P256+) and originate from a certified HSM.", + "Attributes": [ + { + "FamilyName": "Key Lifecycle Management", + "FamilyDescription": "Controls that govern creation, rotation, import, and retirement of cryptographic keys.", + "Section": "CCC.KeyMgmt.CN04 Validate Imported Keys", + "SubSection": "", + "SubSectionObjective": "Accept only externally generated keys that meet approved cryptographic strength and provenance requirements.", + "Applicability": [ + "tlp-green" + ], + "Recommendation": "Implement an approval workflow that validates attestation data before import.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.KeyMgmt.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.DS-1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-28" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.SecMgmt.CN01.AR01", + "Description": "Attempt to use an outdated version of a secret after its rotation period has passed and verify that access is denied.", + "Attributes": [ + { + "FamilyName": "Data Protection", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.SecMgmt.CN01 Enforce Automatic Secret Rotation", + "SubSection": "", + "SubSectionObjective": "Ensure that secrets are automatically rotated on a defined schedule to reduce the risk of secret compromise and unauthorized access.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH14" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.DS-6" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-12", + "SC-28" + ] + } + ] + } + ], + "Checks": [ + "secretsmanager_automatic_rotation_enabled", + "secretsmanager_secret_rotated_periodically" + ] + }, + { + "Id": "CCC.SecMgmt.CN02.AR01", + "Description": "Attempt to retrieve a secret from an unauthorized region and verify that access is denied.", + "Attributes": [ + { + "FamilyName": "Data Protection", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.SecMgmt.CN02 Enforce Secret Replication Policies", + "SubSection": "", + "SubSectionObjective": "Ensure that secrets are replicated only to authorized locations as per organizational data residency and compliance requirements.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH03", + "CCC.Core.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.DS-5" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-3", + "SC-7" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.DataWar.CN01.AR01", + "Description": "Attempt to access underlying database tables directly without using managed views and verify that access is denied.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.DataWar.CN01 Enforce Use of Managed Views for Data Access", + "SubSection": "", + "SubSectionObjective": "Ensure that data access is provided through managed views, restricting users from accessing underlying tables directly and enforcing consistent security policies.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-3", + "AC-6" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.DataWar.CN02.AR01", + "Description": "Attempt to query sensitive columns without the necessary permissions and verify that access is denied or data is masked.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.DataWar.CN02 Enforce Column-Level Security Policies", + "SubSection": "", + "SubSectionObjective": "Ensure that access to sensitive data columns is restricted based on user roles, preventing unauthorized access to sensitive information.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-3", + "AC-6" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.DataWar.CN03.AR01", + "Description": "Attempt to query data rows that the user should not have access to and verify that access is denied or data is not returned.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.DataWar.CN03 Enforce Row-Level Security Policies", + "SubSection": "", + "SubSectionObjective": "Ensure that access to data rows is restricted based on user roles or attributes, preventing unauthorized access to specific subsets of data.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-3", + "AC-6" + ] + } + ] + } + ], + "Checks": [] + }, { "Id": "CCC.Vector.CN01.AR01", "Description": "When a vector embedding is submitted for indexing, the system MUST validate that it matches expected schema, dimension, and format profiles.", @@ -3484,7 +4933,7 @@ "Identifiers": [ "CCC.Vector.TH02", "CCC.Vector.TH05", - "CCC.TH12" + "CCC.Core.TH12" ] } ], @@ -3523,7 +4972,7 @@ "Identifiers": [ "CCC.Vector.TH02", "CCC.Vector.TH04", - "CCC.TH01" + "CCC.Core.TH01" ] } ], @@ -3538,17 +4987,9 @@ } ], "Checks": [ - "iam_group_administrator_access_policy", - "iam_user_administrator_access_policy", - "iam_role_administratoraccess_policy", - "iam_administrator_access_with_mfa", - "iam_inline_policy_allows_privilege_escalation", - "iam_inline_policy_no_full_access_to_cloudtrail", - "iam_inline_policy_no_full_access_to_kms", - "iam_policy_attached_only_to_group_or_roles", - "iam_customer_attached_policy_no_administrative_privileges", - "iam_customer_unattached_policy_no_administrative_privileges", - "iam_no_custom_policy_permissive_role_assumption" + "opensearch_service_domains_access_control_enabled", + "opensearch_service_domains_internal_user_database_enabled", + "iam_role_administratoraccess_policy" ] }, { @@ -3571,7 +5012,7 @@ "ReferenceId": "CCC", "Identifiers": [ "CCC.Vector.TH03", - "CCC.TH01" + "CCC.Core.TH01" ] } ], @@ -3610,7 +5051,7 @@ "ReferenceId": "CCC", "Identifiers": [ "CCC.Vector.TH02", - "CCC.TH12" + "CCC.Core.TH12" ] } ], @@ -3646,8 +5087,8 @@ "ReferenceId": "CCC", "Identifiers": [ "CCC.Vector.TH04", - "CCC.TH09", - "CCC.TH04" + "CCC.Core.TH09", + "CCC.Core.TH04" ] } ], @@ -3685,7 +5126,7 @@ "ReferenceId": "CCC", "Identifiers": [ "CCC.Vector.TH05", - "CCC.TH06" + "CCC.Core.TH06" ] } ], @@ -3730,457 +5171,25 @@ "Checks": [] }, { - "Id": "CCC.Core.CN01.AR01", - "Description": "When a port is exposed for non-SSH network traffic, all traffic MUST include a TLS handshake AND be encrypted using TLS 1.3 or higher.", + "Id": "CCC.RDMS.CN01.AR02", + "Description": "When an attempt is made to authenticate to the database using known default credentials, the authentication attempt must fail and no access should be granted.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN01 Encrypt Data for Transmission", + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.RDMS.CN01 Password Management", "SubSection": "", - "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Most cloud services enable TLS 1.3 by default. Where it is not already set, ensure that your services are configured or updated accordingly. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH02" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-03", - "CEK-04", - "IVS-03", - "IVS-07" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-02" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-8", - "SC-13" - ] - } - ] - } - ], - "Checks": [ - "cloudfront_distributions_origin_traffic_encrypted", - "cloudfront_distributions_https_enabled", - "cloudfront_distributions_https_sni_enabled", - "s3_bucket_secure_transport_policy", - "dms_endpoint_redis_in_transit_encryption_enabled", - "kafka_cluster_in_transit_encryption_enabled", - "transfer_server_in_transit_encryption_enabled", - "redshift_cluster_in_transit_encryption_enabled", - "rds_instance_transport_encrypted" - ] - }, - { - "Id": "CCC.Core.CN01.AR02", - "Description": "When a port is exposed for SSH network traffic, all traffic MUST include a SSH handshake AND be encrypted using SSHv2 or higher.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN01 Encrypt Data for Transmission", - "SubSection": "", - "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Any time port 22 is exposed, ensure that it has a properly implemented SSH server with SSHv2 enabled and configured with strong ciphers. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH02" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-03", - "CEK-04", - "IVS-03", - "IVS-07" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-02" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-8", - "SC-13" - ] - } - ] - } - ], - "Checks": [ - "ec2_instance_port_ssh_exposed_to_internet", - "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22", - "ec2_securitygroup_allow_ingress_from_internet_to_all_ports", - "ec2_networkacl_allow_ingress_tcp_port_22" - ] - }, - { - "Id": "CCC.Core.CN01.AR03", - "Description": "When the service receives unencrypted traffic, then it MUST either block the request or automatically redirect it to the secure equivalent.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN01 Encrypt Data for Transmission", - "SubSection": "", - "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Review firewall, load balancer, and application configurations to ensure insecure protocols such as HTTP, FTP, and Telnet are not exposed. Where possible, implement automatic redirection to secure protocols such as HTTPS, SFTP, SSH, and regularly scan for protocol drift. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH02" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-03", - "CEK-04", - "IVS-03", - "IVS-07" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-02" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-8", - "SC-13" - ] - } - ] - } - ], - "Checks": [ - "cloudfront_distributions_https_enabled", - "cloudfront_distributions_https_sni_enabled", - "cloudfront_distributions_origin_traffic_encrypted", - "opensearch_service_domains_https_communications_enforced", - "transfer_server_in_transit_encryption_enabled", - "s3_bucket_secure_transport_policy", - "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_ftp_20_21", - "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_telnet_23" - ] - }, - { - "Id": "CCC.Core.CN01.AR07", - "Description": "When a port is exposed, the service MUST ensure that the protocol and service officially assigned to that port number by the IANA Service Name and Transport Protocol Port Number Registry, and no other, is run on that port.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN01 Encrypt Data for Transmission", - "SubSection": "", - "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Reference the IANA Service Name and Transport Protocol Port Number Registry for more information about correct protocol-to-port assignments. Avoid running non-standard services on well-known ports. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH02" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-03", - "CEK-04", - "IVS-03", - "IVS-07" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-02" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-8", - "SC-13" - ] - } - ] - } - ], - "Checks": [ - "cloudfront_distributions_origin_traffic_encrypted", - "rds_instance_transport_encrypted", - "redshift_cluster_in_transit_encryption_enabled", - "kafka_cluster_in_transit_encryption_enabled", - "transfer_server_in_transit_encryption_enabled", - "dms_endpoint_redis_in_transit_encryption_enabled", - "dms_endpoint_ssl_enabled", - "s3_bucket_secure_transport_policy" - ] - }, - { - "Id": "CCC.Core.CN01.AR08", - "Description": "When a service transmits data using TLS, mutual TLS (mTLS) MUST be implemented to require both client and server certificate authentication for all connections.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN01 Encrypt Data for Transmission", - "SubSection": "", - "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Configure mTLS for all endpoints that process or transmit sensitive data. Ensure both client and server certificates are validated and managed securely. Regularly review certificate authorities and automate certificate rotation where possible. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH02" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-03", - "CEK-04", - "IVS-03", - "IVS-07" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-02" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-8", - "SC-13" - ] - } - ] - } - ], - "Checks": [ - "apigateway_restapi_client_certificate_enabled", - "cloudfront_distributions_custom_ssl_certificate", - "cloudfront_distributions_https_sni_enabled", - "cloudfront_distributions_origin_traffic_encrypted", - "acm_certificates_expiration_check", - "acm_certificates_with_secure_key_algorithms", - "acm_certificates_transparency_logs_enabled", - "s3_bucket_secure_transport_policy", - "dms_endpoint_ssl_enabled", - "dms_endpoint_redis_in_transit_encryption_enabled", - "transfer_server_in_transit_encryption_enabled", - "kafka_cluster_in_transit_encryption_enabled", - "kafka_cluster_mutual_tls_authentication_enabled" - ] - }, - { - "Id": "CCC.Core.CN13.AR01", - "Description": "When a port is exposed that uses certificate-based encryption, the service MUST only use valid, unexpired certificates issued by a trusted certificate authority.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN13 Minimize Lifetime of Encryption and Authentication Certificates", - "SubSection": "", - "SubSectionObjective": "Ensure that encryption and authentication certificates have a limited lifetime to reduce the risk of compromise and ensure the use of up-to-date security practices.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Track certificate expiration dates and automate certificate renewal where possible. Use certificate management tools to ensure only certificates from trusted authorities are deployed. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH18" - ] - } - ], - "SectionGuidelineMappings": [] - } - ], - "Checks": [ - "acm_certificates_expiration_check", - "acm_certificates_transparency_logs_enabled", - "acm_certificates_with_secure_key_algorithms" - ] - }, - { - "Id": "CCC.Core.CN13.AR02", - "Description": "When a port is exposed that uses certificate-based encryption, the service MUST rotate active certificates within 180 days of issuance.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN13 Minimize Lifetime of Encryption and Authentication Certificates", - "SubSection": "", - "SubSectionObjective": "Ensure that encryption and authentication certificates have a limited lifetime to reduce the risk of compromise and ensure the use of up-to-date security practices.", + "SubSectionObjective": "Ensure default vendor-supplied DB administrator credentials are replaced with strong, unique passwords and that these credentials are properly managed using a secure password or secrets management solution.", "Applicability": [ + "tlp-red", "tlp-amber" ], - "Recommendation": "Track certificate expiration dates and automate certificate renewal where possible. Use certificate management tools to ensure only certificates from trusted authorities are deployed. ", + "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH18" - ] - } - ], - "SectionGuidelineMappings": [] - } - ], - "Checks": [ - "acm_certificates_expiration_check" - ] - }, - { - "Id": "CCC.Core.CN13.AR03", - "Description": "When a port is exposed that uses certificate-based encryption, the service MUST rotate active certificates within 90 days of issuance.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN13 Minimize Lifetime of Encryption and Authentication Certificates", - "SubSection": "", - "SubSectionObjective": "Ensure that encryption and authentication certificates have a limited lifetime to reduce the risk of compromise and ensure the use of up-to-date security practices.", - "Applicability": [ - "tlp-red" - ], - "Recommendation": "Track certificate expiration dates and automate certificate renewal where possible. Use certificate management tools to ensure only certificates from trusted authorities are deployed. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH18" - ] - } - ], - "SectionGuidelineMappings": [] - } - ], - "Checks": [ - "acm_certificates_expiration_check" - ] - }, - { - "Id": "CCC.Core.CN06.AR01", - "Description": "When the service is running, its region and availability zone MUST be included in a list of explicitly trusted or approved locations within the trust perimeter.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN06 Restrict Deployments to Trust Perimeter", - "SubSection": "", - "SubSectionObjective": "Ensure that the service and its child resources are only deployed on infrastructure in locations that are explicitly included within a defined trust perimeter.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Maintain an up-to-date list of trusted and approved regions based on organizational policies. Validate the service's deployment location is included in this list. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH03" + "CCC.RDMS.TH01" ] } ], @@ -4188,19 +5197,92 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-1" + "PR.AA-01" ] }, { - "ReferenceId": "CCM", + "ReferenceId": "NIST_800_53", "Identifiers": [ - "DSP-19" + "AC-2" + ] + } + ] + } + ], + "Checks": [ + "rds_cluster_default_admin", + "rds_instance_default_admin" + ] + }, + { + "Id": "CCC.RDMS.CN02.AR01", + "Description": "When repeated failed login attempts are made in a short timeframe, the account must be locked out or rate-limited to prevent further login attempts.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.RDMS.CN02 Account Lockout and Rate-Limiting", + "SubSection": "", + "SubSectionObjective": "Ensure the database enforces lockouts or rate-limiting after a specified number of failed authentication attempts. This prevents brute force or password-guessing attacks from succeeding.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.RDMS.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-1" ] }, { - "ReferenceId": "ISO_27001", + "ReferenceId": "NIST_800_53", "Identifiers": [ - "2013 A.11.1.1" + "AC-7" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.RDMS.CN04.AR01", + "Description": "When there is an attempt to perform a backup or restore, then the attempt must fail with an access denied message if credentials or roles that are not explicitly authorized for backup/restore functions.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.RDMS.CN04 Access Control for Backup and Restore Operations", + "SubSection": "", + "SubSectionObjective": "Restrict who can initiate, manage, and validate database backup or restore operations through strict role-based or least-privilege access. Prevents accidental or malicious restorations, protecting data integrity and availability.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.RDMS.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" ] }, { @@ -4213,33 +5295,30 @@ } ], "Checks": [ - "organizations_scp_check_deny_regions", - "vpc_endpoint_connections_trust_boundaries", - "vpc_endpoint_services_allowed_principals_trust_boundaries" + "iam_inline_policy_no_administrative_privileges", + "iam_customer_attached_policy_no_administrative_privileges" ] }, { - "Id": "CCC.Core.CN06.AR02", - "Description": "When a child resource is deployed, its region and availability zone MUST be included in a list of explicitly trusted or approved locations within the trust perimeter.", + "Id": "CCC.RDMS.CN05.AR01", + "Description": "When an attempt is made to share a snapshot with an unauthorized account, the sharing request must be denied.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN06 Restrict Deployments to Trust Perimeter", + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.RDMS.CN05 Restrict Snapshot Sharing to Authorized Accounts", "SubSection": "", - "SubSectionObjective": "Ensure that the service and its child resources are only deployed on infrastructure in locations that are explicitly included within a defined trust perimeter.", + "SubSectionObjective": "Ensure database snapshots can only be shared with explicitly authorized accounts, thereby minimizing the risk of data exposure or exfiltration.", "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" + "tlp-red", + "tlp-amber" ], - "Recommendation": "Maintain an up-to-date list of trusted and approved regions based on organizational policies. Validate that child resources can only be deployed to locations included in this list. ", + "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH03" + "CCC.RDMS.TH05" ] } ], @@ -4247,24 +5326,105 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-19" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.11.1.1" + "PR.DS-10" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ + "AC-4" + ] + } + ] + } + ], + "Checks": [ + "rds_snapshots_public_access", + "neptune_cluster_public_snapshot", + "documentdb_cluster_public_snapshot", + "ec2_ami_public" + ] + }, + { + "Id": "CCC.RDMS.CN03.AR01", + "Description": "When backups are disabled, paused, or fail to run as scheduled, an alert must be triggered and logged.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.RDMS.CN03 Enforce and Monitor Automated Backups", + "SubSection": "", + "SubSectionObjective": "Ensure database backups are automatically scheduled, actively monitored, and promptly reported if any disruptions occur. This helps maintain data integrity, facilitates disaster recovery, and supports business continuity when a system failure or breach occurs.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.RDMS.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.IP-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "CP-9" + ] + } + ] + } + ], + "Checks": [ + "rds_instance_backup_enabled", + "rds_cluster_critical_event_subscription", + "neptune_cluster_backup_enabled", + "documentdb_cluster_backup_enabled" + ] + }, + { + "Id": "CCC.Build.CN01.AR01", + "Description": "Attempt to initiate a build using an unauthorized build agent and verify that the build is rejected.", + "Attributes": [ + { + "FamilyName": "Access Control", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.Build.CN01 Restrict Allowed Build Agents", + "SubSection": "", + "SubSectionObjective": "Ensure that builds are executed only on authorized build agents to maintain control over the build environment and prevent unauthorized code execution.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-3", "AC-6" ] } @@ -4272,92 +5432,31 @@ } ], "Checks": [ - "organizations_scp_check_deny_regions", - "vpc_endpoint_services_allowed_principals_trust_boundaries", - "vpc_endpoint_connections_trust_boundaries" + "codebuild_project_uses_allowed_github_organizations", + "codebuild_project_user_controlled_buildspec", + "codebuild_project_no_secrets_in_variables" ] }, { - "Id": "CCC.Core.CN08.AR01", - "Description": "When data is created or modified, the data MUST have a complete and recoverable duplicate that is stored in a physically separate data center.", + "Id": "CCC.Build.CN02.AR01", + "Description": "Attempt to trigger a build from an unauthorized external service or repository and verify that the build does not start.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN08 Replicate Data to Multiple Locations", + "FamilyName": "Access Control", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.Build.CN02 Restrict Allowed External Services for Build Triggers", "SubSection": "", - "SubSectionObjective": "Ensure that data is replicated across multiple physical locations to protect against data loss due to hardware failures, natural disasters, or other catastrophic events.", + "SubSectionObjective": "Ensure that builds can only be triggered by authorized external services or repositories to prevent unauthorized code execution or tampering.", "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Implement automated data replication processes to ensure that data is consistently duplicated in another region or availability zone. Regularly test data recovery from the replicated location to ensure integrity and availability. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.PT-5" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "BCR-08", - "BCR-10", - "BCR-11" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "CP-2", - "CP-10" - ] - } - ] - } - ], - "Checks": [ - "s3_bucket_cross_region_replication", - "cloudtrail_multi_region_enabled", - "backup_plans_exist", - "backup_vaults_exist", - "backup_vaults_encrypted", - "dynamodb_table_protected_by_backup_plan", - "rds_cluster_protected_by_backup_plan", - "rds_instance_protected_by_backup_plan" - ] - }, - { - "Id": "CCC.Core.CN08.AR02", - "Description": "When data is replicated into a second location, the service MUST be able to accurately represent the replication locations, replication status, and data synchronization status.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN08 Replicate Data to Multiple Locations", - "SubSection": "", - "SubSectionObjective": "Ensure that data is replicated across multiple physical locations to protect against data loss due to hardware failures, natural disasters, or other catastrophic events.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" + "tlp-red", + "tlp-amber" ], "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH06" + "CCC.Core.TH01" ] } ], @@ -4365,41 +5464,165 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.PT-5" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "BCR-08", - "BCR-10", - "BCR-11" + "PR.AC-4" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "CP-2", - "CP-10" + "AC-3", + "AC-6" ] } ] } ], "Checks": [ - "s3_bucket_cross_region_replication" + "codebuild_project_uses_allowed_github_organizations", + "codebuild_project_source_repo_url_no_sensitive_credentials" ] }, { - "Id": "CCC.Core.CN09.AR01", - "Description": "When the service is operational, its logs and any child resource logs MUST NOT be accessible from the resource they record access to.", + "Id": "CCC.Build.CN03.AR01", + "Description": "Attempt to access the build environment from an external network and verify that access is denied.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN09 Ensure Integrity of Access Logs", + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.Build.CN03 Deny External Network Access for Build Environments", "SubSection": "", - "SubSectionObjective": "Ensure that access logs are always recorded to an external location that cannot be manipulated from the context of the service(s) it contains logs for.", + "SubSectionObjective": "Ensure that build environments do not have external network access to prevent unauthorized external access and data exfiltration.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH02", + "CCC.Core.TH05" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-5" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-7", + "SC-5" + ] + } + ] + } + ], + "Checks": [ + "codebuild_project_not_publicly_accessible" + ] + }, + { + "Id": "CCC.CntrReg.CN01.AR01", + "Description": "Attempt to push an artifact with known vulnerabilities to the registry and observe if it is flagged or rejected by the vulnerability scanning process.", + "Attributes": [ + { + "FamilyName": "Risk Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.CntrReg.CN01 Implement Vulnerability Scanning for Artifacts", + "SubSection": "", + "SubSectionObjective": "Ensure that container images and artifacts stored in the container registry are scanned for vulnerabilities to identify and remediate security issues before deployment.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.CntrReg.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "ID.RA-1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "RA-5", + "SI-5" + ] + } + ] + } + ], + "Checks": [ + "ecr_registry_scan_images_on_push_enabled", + "ecr_repositories_scan_vulnerabilities_in_latest_image" + ] + }, + { + "Id": "CCC.CntrReg.CN02.AR01", + "Description": "Confirm that artifacts older than the specified retention period are automatically deleted from the registry.", + "Attributes": [ + { + "FamilyName": "Data Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.CntrReg.CN02 Implement Cleanup Policies for Artifacts", + "SubSection": "", + "SubSectionObjective": "Ensure that unused or outdated artifacts are cleaned up according to defined policies to manage storage effectively and reduce security risks associated with outdated versions.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH14" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.IP-6" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-12" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.IAM.CN01.AR01", + "Description": "When an identity policy for a non-administrative principal is evaluated, it MUST NOT grant permissions for creating credentials or generating temporary session tokens.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN01 Restrict IAM User Credentials Creation", + "SubSection": "", + "SubSectionObjective": "Prevent non-administrative principals from creating new long-lived credentials like access keys or generating temporary session tokens. This blocks a common privilege escalation and persistence vector.", "Applicability": [ "tlp-clear", "tlp-green", @@ -4411,9 +5634,7 @@ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07", - "CCC.TH09", - "CCC.TH04" + "CCC.IAM.TH03" ] } ], @@ -4421,61 +5642,52 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-6" + "PR.AA-05" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "AU-9" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-02", - "LOG-04", - "LOG-09" + "AC-2", + "AC-3", + "AC-5", + "AC-6" ] } ] } ], "Checks": [ - "cloudtrail_bucket_requires_mfa_delete", - "cloudtrail_log_file_validation_enabled", - "cloudtrail_kms_encryption_enabled", - "cloudtrail_logs_s3_bucket_is_not_publicly_accessible", - "cloudtrail_cloudwatch_logging_enabled", - "cloudtrail_multi_region_enabled", - "cloudtrail_s3_dataevents_read_enabled", - "cloudtrail_s3_dataevents_write_enabled" + "iam_no_root_access_key", + "iam_user_no_setup_initial_access_key", + "iam_user_two_active_access_key", + "iam_aws_attached_policy_no_administrative_privileges", + "iam_customer_attached_policy_no_administrative_privileges", + "iam_inline_policy_no_administrative_privileges" ] }, { - "Id": "CCC.Core.CN09.AR02", - "Description": "When the service is operational, disabling the logs for the service or its child resources MUST NOT be possible without also disabling the corresponding resource.", + "Id": "CCC.IAM.CN01.AR02", + "Description": "When a non-administrative principal attempts to create new credentials or a temporary session token, the service MUST deny the action.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN09 Ensure Integrity of Access Logs", + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN01 Restrict IAM User Credentials Creation", "SubSection": "", - "SubSectionObjective": "Ensure that access logs are always recorded to an external location that cannot be manipulated from the context of the service(s) it contains logs for.", + "SubSectionObjective": "Prevent non-administrative principals from creating new long-lived credentials like access keys or generating temporary session tokens. This blocks a common privilege escalation and persistence vector.", "Applicability": [ "tlp-clear", "tlp-green", "tlp-amber", "tlp-red" ], - "Recommendation": "No normal business operations should disable logs, as this could indicate an attempt to cover up unauthorized access. Ensure that logging mechanisms are tightly integrated with service operations, so that logging cannot be disabled without stopping the service itself. ", + "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07", - "CCC.TH09", - "CCC.TH04" + "CCC.IAM.TH03" ] } ], @@ -4483,63 +5695,52 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-6" + "PR.AA-05" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "AU-9" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-02", - "LOG-04", - "LOG-09" + "AC-2", + "AC-3", + "AC-5", + "AC-6" ] } ] } ], "Checks": [ - "cloudtrail_kms_encryption_enabled", - "cloudtrail_cloudwatch_logging_enabled", - "cloudtrail_logs_s3_bucket_is_not_publicly_accessible", - "cloudtrail_logs_s3_bucket_access_logging_enabled", - "cloudtrail_s3_dataevents_read_enabled", - "cloudtrail_s3_dataevents_write_enabled", - "cloudtrail_log_file_validation_enabled", - "vpc_flow_logs_enabled", - "apigateway_restapi_logging_enabled", - "apigatewayv2_api_access_logging_enabled", - "cloudwatch_log_group_not_publicly_accessible", - "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled" + "iam_no_root_access_key", + "iam_user_no_setup_initial_access_key", + "iam_user_two_active_access_key", + "iam_aws_attached_policy_no_administrative_privileges", + "iam_customer_attached_policy_no_administrative_privileges", + "iam_inline_policy_no_administrative_privileges" ] }, { - "Id": "CCC.Core.CN09.AR03", - "Description": "When the service is operational, any attempt to redirect logs for the service or its child resources MUST NOT be possible without halting operation of the corresponding resource and publishing corresponding events to monitored channels.", + "Id": "CCC.IAM.CN02.AR01", + "Description": "When an identity policy for a non-administrative principal is evaluated, it MUST NOT grant permissions for creating, updating, or attaching policies.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN09 Ensure Integrity of Access Logs", + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN02 Restrict IAM Policies Modification", "SubSection": "", - "SubSectionObjective": "Ensure that access logs are always recorded to an external location that cannot be manipulated from the context of the service(s) it contains logs for.", + "SubSectionObjective": "Ensure that only designated administrative accounts have the ability to create, modify, or attach policies that define permissions for other identities.", "Applicability": [ + "tlp-clear", + "tlp-green", "tlp-amber", "tlp-red" ], - "Recommendation": "No normal business operations should result in the redirection of logs, as this could indicate an attempt to cover up unauthorized access. Ensure that logging configurations are immutable during service operation so that any changes require stopping the service and publishing corresponding events to monitored channels. ", + "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07", - "CCC.TH09", - "CCC.TH04" + "CCC.IAM.TH06" ] } ], @@ -4547,55 +5748,91 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-6" + "PR.AA-05" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "AU-9" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-02", - "LOG-04", - "LOG-09" + "AC-2", + "AC-3", + "AC-5", + "AC-6" ] } ] } ], "Checks": [ - "cloudtrail_insights_exist", - "cloudtrail_logs_s3_bucket_access_logging_enabled", - "cloudtrail_cloudwatch_logging_enabled", - "cloudtrail_s3_dataevents_read_enabled", - "cloudtrail_s3_dataevents_write_enabled", - "cloudtrail_kms_encryption_enabled", - "cloudtrail_logs_s3_bucket_is_not_publicly_accessible", - "cloudtrail_log_file_validation_enabled", - "cloudwatch_log_group_kms_encryption_enabled", - "cloudwatch_log_group_not_publicly_accessible", - "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", - "cloudwatch_changes_to_network_acls_alarm_configured", - "cloudwatch_changes_to_network_gateways_alarm_configured", - "cloudwatch_changes_to_network_route_tables_alarm_configured", - "cloudwatch_changes_to_vpcs_alarm_configured", - "s3_bucket_server_access_logging_enabled" + "iam_aws_attached_policy_no_administrative_privileges", + "iam_customer_attached_policy_no_administrative_privileges", + "iam_customer_unattached_policy_no_administrative_privileges", + "iam_policy_allows_privilege_escalation", + "iam_inline_policy_allows_privilege_escalation" ] }, { - "Id": "CCC.Core.CN10.AR01", - "Description": "When data is replicated, the service MUST ensure that replication only occurs to destinations that are explicitly included within the defined trust perimeter.", + "Id": "CCC.IAM.CN02.AR02", + "Description": "When a non-administrative principal attempts to create, update, or attach policies, the service MUST deny the action.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN10 Restrict Data Replication to Trust Perimeter", + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN02 Restrict IAM Policies Modification", "SubSection": "", - "SubSectionObjective": "Ensure that data is only replicated on infrastructure in locations that are explicitly included within a defined trust perimeter.", + "SubSectionObjective": "Ensure that only designated administrative accounts have the ability to create, modify, or attach policies that define permissions for other identities.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-05" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2", + "AC-3", + "AC-5", + "AC-6" + ] + } + ] + } + ], + "Checks": [ + "iam_aws_attached_policy_no_administrative_privileges", + "iam_customer_attached_policy_no_administrative_privileges", + "iam_customer_unattached_policy_no_administrative_privileges", + "iam_policy_allows_privilege_escalation", + "iam_inline_policy_allows_privilege_escalation" + ] + }, + { + "Id": "CCC.IAM.CN03.AR01", + "Description": "When a policy is created or updated that grants a principal permission to assume a role or impersonate a service identity, the principal MUST NOT contain a wildcard or be public/anonymous.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN03 Restrict Role Assumption / Delegation", + "SubSection": "", + "SubSectionObjective": "Limit which principals can assume a role or impersonate a service identity to only those required. This prevents unintended cross-account or public access by securing the \"who can act as this identity\" boundary.", "Applicability": [ "tlp-green", "tlp-amber", @@ -4606,7 +5843,1511 @@ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH04" + "CCC.IAM.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-3", + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2", + "AC-3", + "AC-6" + ] + } + ] + } + ], + "Checks": [ + "iam_role_cross_account_readonlyaccess_policy", + "iam_role_cross_service_confused_deputy_prevention", + "iam_no_custom_policy_permissive_role_assumption" + ] + }, + { + "Id": "CCC.IAM.CN03.AR02", + "Description": "When an external or unauthenticated principal tries to assume a role or impersonate a service identity, the service MUST deny the action.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN03 Restrict Role Assumption / Delegation", + "SubSection": "", + "SubSectionObjective": "Limit which principals can assume a role or impersonate a service identity to only those required. This prevents unintended cross-account or public access by securing the \"who can act as this identity\" boundary.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-3", + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2", + "AC-3", + "AC-6" + ] + } + ] + } + ], + "Checks": [ + "iam_role_cross_account_readonlyaccess_policy", + "iam_role_cross_service_confused_deputy_prevention", + "iam_no_custom_policy_permissive_role_assumption" + ] + }, + { + "Id": "CCC.IAM.CN04.AR01", + "Description": "When an IAM policy is created or updated, it MUST NOT contain allow statements with wildcard permissions, unless the statement is restricted by a condition.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN04 Restrict Wildcard Usage in IAM Policies", + "SubSection": "", + "SubSectionObjective": "Limit the use of wildcard permissions in IAM policies to prevent overly broad access from being granted by default.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH01", + "CCC.IAM.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-6" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2", + "AC-3", + "AC-6" + ] + } + ] + } + ], + "Checks": [ + "iam_aws_attached_policy_no_administrative_privileges", + "iam_customer_attached_policy_no_administrative_privileges", + "iam_customer_unattached_policy_no_administrative_privileges", + "iam_inline_policy_no_administrative_privileges", + "iam_policy_no_full_access_to_kms", + "iam_policy_no_full_access_to_cloudtrail", + "iam_inline_policy_no_full_access_to_kms", + "iam_inline_policy_no_full_access_to_cloudtrail" + ] + }, + { + "Id": "CCC.IAM.CN05.AR01", + "Description": "When a new cloud account is provisioned, a password policy MUST be configured for IAM users following the minimum PCI DSS v4.0.1 configurations.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN05 Strong Password Policies for IAM Users", + "SubSection": "", + "SubSectionObjective": "Ensure that the password policies for IAM users have strong configurations.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "When a new cloud account is provisioned, a password policy must be configured for all IAM users to align with the minimum requirements defined in PCI DSS v4.0.1. This includes, at a minimum: strength: 0 # Not yet specified - reference-id: A password length of at least 12 characters. strength: 0 # Not yet specified - reference-id: A mix of upper- and lower-case letters, numbers, and special characters. strength: 0 # Not yet specified - reference-id: Prevention of the use of previously used passwords (password history). strength: 0 # Not yet specified - reference-id: Password expiration at a defined interval (e.g., every 90 days). strength: 0 # Not yet specified - reference-id: Account lockout after a defined number of failed login attempts.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-05" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "IA-5" + ] + }, + { + "ReferenceId": "PCI-DSS", + "Identifiers": [ + "8.3.9", + "8.6.3" + ] + } + ] + } + ], + "Checks": [ + "iam_password_policy_minimum_length_14", + "iam_password_policy_uppercase", + "iam_password_policy_lowercase", + "iam_password_policy_symbol", + "iam_password_policy_number", + "iam_password_policy_expires_passwords_within_90_days_or_less", + "iam_password_policy_reuse_24" + ] + }, + { + "Id": "CCC.IAM.CN06.AR01", + "Description": "When a static credential such as an access key has existed for 90 days or more, it MUST be rotated.", + "Attributes": [ + { + "FamilyName": "Identity Provisioning and Lifecycle", + "FamilyDescription": "Controls related to the provisioning and lifecycle of IAM identities.", + "Section": "CCC.IAM.CN06 Maximum Age for Long-Term Static Credentials", + "SubSection": "", + "SubSectionObjective": "Ensure that long-lived static credentials like access keys are programmatically rotated within a defined time period to limit the window of opportunity if compromised.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "When a static credential such as an access key has existed for 90 days or more, it must be automatically rotated to reduce the risk of compromise due to long-term exposure. Organizations should implement automated checks to identify aging credentials and enforce rotation policies. Additionally, access key usage should be regularly monitored, and credentials that are no longer in use should be deactivated or deleted promptly. Where possible, prefer temporary, short-lived credentials over long-lived static ones to further minimize risk.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH09", + "CCC.IAM.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2" + ] + } + ] + } + ], + "Checks": [ + "iam_rotate_access_key_90_days" + ] + }, + { + "Id": "CCC.IAM.CN07.AR01", + "Description": "When a user account is disabled or deleted in the organization's IdP, the corresponding cloud identity and its access policies MUST be disabled or deleted within 24 hours.", + "Attributes": [ + { + "FamilyName": "Identity Provisioning and Lifecycle", + "FamilyDescription": "Controls related to the provisioning and lifecycle of IAM identities.", + "Section": "CCC.IAM.CN07 Automate Identity De-provisioning", + "SubSection": "", + "SubSectionObjective": "Ensure that when an identity is terminated in the central Identity Provider (IdP), ts corresponding access to cloud resources is revoked automatically.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH10", + "CCC.IAM.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2" + ] + } + ] + } + ], + "Checks": [ + "iam_user_console_access_unused", + "iam_user_accesskey_unused" + ] + }, + { + "Id": "CCC.IAM.CN08.AR01", + "Description": "When an IAM user has credentials, such as passwords or access keys, that have not been used for 90 days or more, the unused credentials MUST be removed or deactivated.", + "Attributes": [ + { + "FamilyName": "Identity Provisioning and Lifecycle", + "FamilyDescription": "Controls related to the provisioning and lifecycle of IAM identities.", + "Section": "CCC.IAM.CN08 Maximum Age for Unused Credentials", + "SubSection": "", + "SubSectionObjective": "Ensure that unused IAM credentals are removed to reduce exposure in the event of potential compromise.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "IAM user credentials (such as passwords or access keys) that have not been used for 90 days or more must be automatically removed or deactivated.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH11", + "CCC.IAM.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2" + ] + } + ] + } + ], + "Checks": [ + "iam_user_accesskey_unused", + "iam_user_console_access_unused" + ] + }, + { + "Id": "CCC.IAM.CN09.AR01", + "Description": "When a human user accesses the cloud environment, they MUST authenticate through the organization's federated IdP via a standard protocol (e.g., SAML, OIDC).", + "Attributes": [ + { + "FamilyName": "Identity Provisioning and Lifecycle", + "FamilyDescription": "Controls related to the provisioning and lifecycle of IAM identities.", + "Section": "CCC.IAM.CN09 Enforce Federated Single Sign-On (SSO) for Human Users", + "SubSection": "", + "SubSectionObjective": "Ensure that all human users must authenticate through a central, federated Identity Provider (IdP) to access the cloud environment. This eliminates cloud-native user accounts with long-lived passwords, centralizes authentication controls, and simplifies lifecycle management.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH01", + "CCC.IAM.TH09" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "IA-2" + ] + } + ] + } + ], + "Checks": [ + "iam_check_saml_providers_sts" + ] + }, + { + "Id": "CCC.IAM.CN10.AR01", + "Description": "When suspicious API requests are detected, real time alerts MUST be generated to notify security personnel.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that collect, alert, and retain IAM-related events.", + "Section": "CCC.IAM.CN10 Alert On Anomalous Behaviour", + "SubSection": "", + "SubSectionObjective": "Ensure that logs and associated alerts are generated when anomalous API requests are made by a single identity, such as API requests commonly associated with privilege escalation tactics, originating from an external or malicious IP address or performed by a previously dormant identity, which may indicate that credentals may be compromised, as well as for password brute-force attempts and account lockouts.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "DE.CM-03", + "DE.CM-06", + "DE.CM-09" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-4", + "SI-5", + "AC-2" + ] + } + ] + } + ], + "Checks": [ + "cloudwatch_log_metric_filter_authentication_failures", + "cloudwatch_log_metric_filter_unauthorized_api_calls", + "cloudwatch_log_metric_filter_root_usage", + "cloudwatch_log_metric_filter_sign_in_without_mfa", + "guardduty_is_enabled" + ] + }, + { + "Id": "CCC.IAM.CN10.AR02", + "Description": "When suspicious API requests are detected, the associated events MUST be logged, including the source details, time, and nature of the activity.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that collect, alert, and retain IAM-related events.", + "Section": "CCC.IAM.CN10 Alert On Anomalous Behaviour", + "SubSection": "", + "SubSectionObjective": "Ensure that logs and associated alerts are generated when anomalous API requests are made by a single identity, such as API requests commonly associated with privilege escalation tactics, originating from an external or malicious IP address or performed by a previously dormant identity, which may indicate that credentals may be compromised, as well as for password brute-force attempts and account lockouts.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "DE.CM-03", + "DE.CM-06", + "DE.CM-09" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-4", + "SI-5", + "AC-2" + ] + } + ] + } + ], + "Checks": [ + "cloudtrail_multi_region_enabled", + "cloudtrail_multi_region_enabled_logging_management_events", + "cloudwatch_log_metric_filter_authentication_failures", + "cloudwatch_log_metric_filter_unauthorized_api_calls" + ] + }, + { + "Id": "CCC.IAM.CN11.AR01", + "Description": "When a cloud account or organization is provisioned, the native automated access and usage analysis services MUST be enabled to continuously monitor for external or public access to resources, and unused access.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that collect, alert, and retain IAM-related events.", + "Section": "CCC.IAM.CN11 Enable Continuous IAM Access and Usage Analysis", + "SubSection": "", + "SubSectionObjective": "Enable and configure the cloud provider's native access and usage analysis services to continuously monitor for external access paths and internal unused access.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH02", + "CCC.IAM.TH10", + "CCC.IAM.TH11" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "ID.RA-01", + "ID.IM-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2", + "CA-7", + "RA-5" + ] + } + ] + } + ], + "Checks": [ + "accessanalyzer_enabled", + "accessanalyzer_enabled_without_findings" + ] + }, + { + "Id": "CCC.GenAI.CN01.AR01", + "Description": "Untrusted input such as user queries, RAG data or tool output MUST be validated before it is passed to a GenAI model.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN01 Model Input Filtering and Sanitisation", + "SubSection": "", + "SubSectionObjective": "Inspect and validate input before it is passed to a GenAI model in order to filter or sanitise adversarial queries and prevent sensitive data leakage.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH01", + "CCC.GenAI.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-003", + "AIR-PREV-017", + "AIR-PREV-002", + "AIR-DET-001" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Input Validation and Sanitization" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0020", + "AML.M0021", + "AML.M0015" + ] + } + ] + } + ], + "Checks": [ + "bedrock_guardrail_prompt_attack_filter_enabled", + "bedrock_guardrail_sensitive_information_filter_enabled", + "bedrock_agent_guardrail_enabled" + ] + }, + { + "Id": "CCC.GenAI.CN01.AR02", + "Description": "If malicious patterns such as prompt injection or sensitive data are detected during input validation, the input MUST be blocked or sanitised.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN01 Model Input Filtering and Sanitisation", + "SubSection": "", + "SubSectionObjective": "Inspect and validate input before it is passed to a GenAI model in order to filter or sanitise adversarial queries and prevent sensitive data leakage.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH01", + "CCC.GenAI.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-003", + "AIR-PREV-017", + "AIR-PREV-002", + "AIR-DET-001" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Input Validation and Sanitization" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0020", + "AML.M0021", + "AML.M0015" + ] + } + ] + } + ], + "Checks": [ + "bedrock_guardrail_prompt_attack_filter_enabled", + "bedrock_guardrail_sensitive_information_filter_enabled", + "bedrock_agent_guardrail_enabled" + ] + }, + { + "Id": "CCC.GenAI.CN02.AR01", + "Description": "GenAI model output MUST be validated for format conformance, malicious patterns, sensitive data and inapropriate content before being passed to users, application or plugins.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN02 Model Output Filtering and Sanitisation", + "SubSection": "", + "SubSectionObjective": "Inspect and validate GenAI model output before passing it to users, applications or plugins in order to filter or sanitise insecure or unreliable output and prevent sensitive data leakage.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH01", + "CCC.GenAI.TH03", + "CCC.GenAI.TH04", + "CCC.GenAI.TH05", + "CCC.GenAI.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-003", + "AIR-PREV-017", + "AIR-PREV-002", + "AIR-DET-001" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Output Validation and Sanitization" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0020", + "AML.M0002" + ] + } + ] + } + ], + "Checks": [ + "bedrock_guardrail_sensitive_information_filter_enabled", + "bedrock_agent_guardrail_enabled" + ] + }, + { + "Id": "CCC.GenAI.CN02.AR02", + "Description": "In the event of policy violations, the AI-generated content MUST be redacted, encoded or rejected.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN02 Model Output Filtering and Sanitisation", + "SubSection": "", + "SubSectionObjective": "Inspect and validate GenAI model output before passing it to users, applications or plugins in order to filter or sanitise insecure or unreliable output and prevent sensitive data leakage.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH01", + "CCC.GenAI.TH03", + "CCC.GenAI.TH04", + "CCC.GenAI.TH05", + "CCC.GenAI.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-003", + "AIR-PREV-017", + "AIR-PREV-002", + "AIR-DET-001" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Output Validation and Sanitization" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0020", + "AML.M0002" + ] + } + ] + } + ], + "Checks": [ + "bedrock_guardrail_sensitive_information_filter_enabled", + "bedrock_agent_guardrail_enabled" + ] + }, + { + "Id": "CCC.GenAI.CN03.AR01", + "Description": "When data is designated for model training or RAG ingestion, then its source MUST be explicitly approved and its provenance documented.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN03 Data Provenance and Source Vetting", + "SubSection": "", + "SubSectionObjective": "Ensure that all data for training, fine-tuning or RAG comes from trusted, approved sources and is authorised for the intended purposes in order to prevent the initial introduction of malicious content or leaked sensitive data.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH02", + "CCC.GenAI.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-006" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Training Data Management" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0025" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN03.AR02", + "Description": "Data from unvetted sources MUST NOT be used in production systems.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN03 Data Provenance and Source Vetting", + "SubSection": "", + "SubSectionObjective": "Ensure that all data for training, fine-tuning or RAG comes from trusted, approved sources and is authorised for the intended purposes in order to prevent the initial introduction of malicious content or leaked sensitive data.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH02", + "CCC.GenAI.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-006" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Training Data Management" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0025" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN04.AR01", + "Description": "When data is ingested for training, fine-tuning or conversion to vector embeddings, it MUST be validated for sensitive information or malicious content.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN04 Sanitisation of Ingested Data", + "SubSection": "", + "SubSectionObjective": "Validate and sanitise all data ingested by GenAI systems from extenal sources or internal knowledge bases, whether for training, conversion to vector embeddings, or real-time retireval, in order to remove or redact poisoned or sensitive data before further processing.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH02", + "CCC.GenAI.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-002" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Training Data Sanitization" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0007" + ] + } + ] + } + ], + "Checks": [ + "bedrock_guardrail_sensitive_information_filter_enabled" + ] + }, + { + "Id": "CCC.GenAI.CN04.AR02", + "Description": "If sensitive data or malicious content is detected, it must be rejected, redacted or flagged for manual review.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN04 Sanitisation of Ingested Data", + "SubSection": "", + "SubSectionObjective": "Validate and sanitise all data ingested by GenAI systems from extenal sources or internal knowledge bases, whether for training, conversion to vector embeddings, or real-time retireval, in order to remove or redact poisoned or sensitive data before further processing.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH02", + "CCC.GenAI.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-002" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Training Data Sanitization" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0007" + ] + } + ] + } + ], + "Checks": [ + "bedrock_guardrail_sensitive_information_filter_enabled" + ] + }, + { + "Id": "CCC.GenAI.CN05.AR01", + "Description": "When a RAG-enabled system generates a response containing information retrieved from its knowledge base, then the response MUST include a verifiable citation that links back to the specific source document.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN05 Citations and Source Traceability", + "SubSection": "", + "SubSectionObjective": "Require the GenAI system to provide citations or direct links back to the source documents used to generate a response, in to enhance the transparency, trustworthiness, and verifiability of AI-generated content.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH09", + "CCC.GenAI.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-DET-013" + ] + } + ] + } + ], + "Checks": [ + "bedrock_model_invocation_logging_enabled", + "bedrock_model_invocation_logs_encryption_enabled" + ] + }, + { + "Id": "CCC.GenAI.CN06.AR01", + "Description": "When an LLM invokes an external tool (e.g., an API, a plugin), then the tool MUST operate with the least privileges required for performing its intended functionality.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.GenAI.CN06 Least Privilege for Plugins", + "SubSection": "", + "SubSectionObjective": "Restricts the permissions of any external tools the GenAI system can call to limit the potential damage if an agent is coerced to perform unintended actions or vulnerabilities in the tools are exploited.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH07", + "CCC.GenAI.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Agent Permissions" + ] + } + ] + } + ], + "Checks": [ + "bedrock_api_key_no_administrative_privileges", + "bedrock_api_key_no_long_term_credentials" + ] + }, + { + "Id": "CCC.GenAI.CN07.AR01", + "Description": "When an application makes an API call to a foundational model in a production environment, then it MUST specify an explicit version identifier.", + "Attributes": [ + { + "FamilyName": "Configuration Management", + "FamilyDescription": "The Configuration Management control family involves establishing, maintaining and monitoring the configuration of the service and related applications and infrastructure to ensure consistency, secure defaults and compliance.", + "Section": "CCC.GenAI.CN07 Model Version Pinning", + "SubSection": "", + "SubSectionObjective": "Mandate that applications are locked (\"pinned\") to a specific, tested version of a foundational model to prevent unexpected behaviour changes introduced by provider-side updates.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH10" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-010" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN08.AR01", + "Description": "When a new AI model is considered for production deployment, it MUST undergo a formal red teaming and quality assurance review.", + "Attributes": [ + { + "FamilyName": "Model Assurance and Evaluation", + "FamilyDescription": "The Model Assurance and Evaluation control family encompasses the proactiveand continuous processes of testing and validating the AI model's behavior to ensure it aligns with safety, ethical, and quality standards.", + "Section": "CCC.GenAI.CN08 Quality Control and Red Teaming", + "SubSection": "", + "SubSectionObjective": "Establish a formal program for quality evaluation and adversarial testing (red teaming) to ensure GenAI system meet all business, quality, security and compliance requirements before getting deployed into production environments.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH01", + "CCC.GenAI.TH02", + "CCC.GenAI.TH04", + "CCC.GenAI.TH08", + "CCC.GenAI.TH10" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-005" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Adversarial Training and Testing", + "Red Teaming", + "Product Governance" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0008" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN08.AR02", + "Description": "If model quality review or red teaming identifies an issue that exceeds the organization's risk tolerance, the model MUST NOT be deployed until the issue is remediated.", + "Attributes": [ + { + "FamilyName": "Model Assurance and Evaluation", + "FamilyDescription": "The Model Assurance and Evaluation control family encompasses the proactiveand continuous processes of testing and validating the AI model's behavior to ensure it aligns with safety, ethical, and quality standards.", + "Section": "CCC.GenAI.CN08 Quality Control and Red Teaming", + "SubSection": "", + "SubSectionObjective": "Establish a formal program for quality evaluation and adversarial testing (red teaming) to ensure GenAI system meet all business, quality, security and compliance requirements before getting deployed into production environments.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH01", + "CCC.GenAI.TH02", + "CCC.GenAI.TH04", + "CCC.GenAI.TH08", + "CCC.GenAI.TH10" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-005" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Adversarial Training and Testing", + "Red Teaming", + "Product Governance" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0008" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN01.AR01", + "Description": "Verify that only authorized users can access MLDE resources, and that access modes are properly defined and enforced.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN01 Define Access Mode for ML Development Environments", + "SubSection": "", + "SubSectionObjective": "Ensure that access to Machine Learning Development Environment (MLDE) resources is strictly defined and controlled. Only authorized users with appropriate permissions can access these environments, mitigating the risk of unauthorized access, data leakage, or service disruption.", + "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green", + "tlp-clear" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01", + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-3" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.1.1", + "2013 A.9.2.1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2", + "AC-3" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-01", + "IAM-02" + ] + } + ] + } + ], + "Checks": [ + "sagemaker_notebook_instance_root_access_disabled", + "sagemaker_notebook_instance_vpc_settings_configured" + ] + }, + { + "Id": "CCC.MLDE.CN03.AR01", + "Description": "Verify that root access is disabled on MLDE instances containing sensitive data.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN03 Disable Root Access on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Prevent users from obtaining root access on MLDE instances to reduce the risk of unauthorized system modifications and potential security breaches.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-08", + "IAM-12" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.2.3" + ] + } + ] + } + ], + "Checks": [ + "sagemaker_notebook_instance_root_access_disabled" + ] + }, + { + "Id": "CCC.MLDE.CN03.AR02", + "Description": "For MLDE instances without sensitive data, ensure that root access is only enabled when necessary and properly authorized.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN03 Disable Root Access on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Prevent users from obtaining root access on MLDE instances to reduce the risk of unauthorized system modifications and potential security breaches.", + "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green", + "tlp-clear" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-08", + "IAM-12" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.2.3" + ] + } + ] + } + ], + "Checks": [ + "sagemaker_notebook_instance_root_access_disabled" + ] + }, + { + "Id": "CCC.MLDE.CN04.AR01", + "Description": "Verify that terminal access is disabled on MLDE instances containing sensitive data.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN04 Disable Terminal Access on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Prevent users from accessing the terminal on MLDE instances to limit the risk of unauthorized commands and potential system compromise.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-08" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.2.3" + ] + } + ] + } + ], + "Checks": [ + "sagemaker_notebook_instance_root_access_disabled" + ] + }, + { + "Id": "CCC.MLDE.CN04.AR02", + "Description": "For MLDE instances without sensitive data, ensure that terminal access is only enabled when necessary and properly authorized.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN04 Disable Terminal Access on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Prevent users from accessing the terminal on MLDE instances to limit the risk of unauthorized commands and potential system compromise.", + "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green", + "tlp-clear" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-08" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.2.3" + ] + } + ] + } + ], + "Checks": [ + "sagemaker_notebook_instance_root_access_disabled" + ] + }, + { + "Id": "CCC.MLDE.CN02.AR01", + "Description": "Confirm that file download functionality is disabled on MLDE instances containing sensitive data.", + "Attributes": [ + { + "FamilyName": "Data Protection", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN02 Disable File Downloads on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Prevent unauthorized file downloads from MLDE instances to protect sensitive data from being exfiltrated.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH02", + "CCC.Core.TH02" ] } ], @@ -4620,166 +7361,98 @@ { "ReferenceId": "CCM", "Identifiers": [ - "DSP-10", - "DSP-19" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-4" - ] - } - ] - } - ], - "Checks": [ - "vpc_endpoint_services_allowed_principals_trust_boundaries", - "vpc_endpoint_connections_trust_boundaries", - "s3_bucket_cross_account_access" - ] - }, - { - "Id": "CCC.Core.CN02.AR01", - "Description": "When data is stored, it MUST be encrypted using the latest industry-standard encryption methods.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN02 Encrypt Data for Storage", - "SubSection": "", - "SubSectionObjective": "Ensure that all data stored is encrypted at rest using strong encryption algorithms.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-03", - "CEK-04", - "UEM-08", - "DSP-17" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-13", - "SC-28" - ] - } - ] - } - ], - "Checks": [ - "s3_bucket_default_encryption", - "s3_bucket_kms_encryption", - "firehose_stream_encrypted_at_rest", - "backup_vaults_encrypted", - "backup_recovery_point_encrypted", - "cloudtrail_kms_encryption_enabled", - "cloudwatch_log_group_kms_encryption_enabled", - "opensearch_service_domains_encryption_at_rest_enabled", - "opensearch_service_domains_node_to_node_encryption_enabled", - "kafka_cluster_encryption_at_rest_uses_cmk", - "kinesis_stream_encrypted_at_rest", - "dynamodb_tables_kms_cmk_encryption_enabled", - "dynamodb_accelerator_cluster_encryption_enabled", - "ec2_ebs_default_encryption", - "ec2_ebs_volume_encryption", - "storagegateway_fileshare_encryption_enabled" - ] - }, - { - "Id": "CCC.Core.CN11.AR01", - "Description": "When encryption keys are used, the service MUST verify that all encryption keys use the latest industry-standard cryptographic algorithms.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN11 Protect Encryption Keys", - "SubSection": "", - "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH16" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-08", - "CEK-10", - "CEK-12" + "DSI-05", + "DSI-07" ] }, { "ReferenceId": "ISO_27001", "Identifiers": [ - "2013 A.10.1.2" + "2013 A.13.2.1" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "SC-12", - "SC-17" + "SC-7", + "SC-8" ] } ] } ], - "Checks": [ - "kms_cmk_rotation_enabled", - "kms_cmk_not_deleted_unintentionally", - "kms_cmk_not_multi_region", - "kms_cmk_are_used" - ] + "Checks": [] }, { - "Id": "CCC.Core.CN11.AR02", - "Description": "When encryption keys are used, the service MUST rotate active keys within 180 days of issuance.", + "Id": "CCC.MLDE.CN02.AR02", + "Description": "For MLDE instances without sensitive data, ensure that file downloads are monitored and logged.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN11 Protect Encryption Keys", + "FamilyName": "Data Protection", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN02 Disable File Downloads on MLDE Instances", "SubSection": "", - "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "SubSectionObjective": "Prevent unauthorized file downloads from MLDE instances to protect sensitive data from being exfiltrated.", "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green", + "tlp-clear" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH02", + "CCC.Core.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.DS-5" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSI-05", + "DSI-07" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.13.2.1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-7", + "SC-8" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN05.AR01", + "Description": "Verify that only approved VM and container images can be selected when creating MLDE instances.", + "Attributes": [ + { + "FamilyName": "Configuration Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN05 Restrict Environment Options on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Limit the virtual machine and container image options available when creating new MLDE instances to approved and secure configurations.", + "Applicability": [ + "tlp-red", "tlp-amber" ], "Recommendation": "", @@ -4787,7 +7460,7 @@ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH16" + "CCC.MLDE.TH04" ] } ], @@ -4795,52 +7468,43 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-1" + "PR.IP-1" ] }, { "ReferenceId": "CCM", "Identifiers": [ - "CEK-08", - "CEK-10", - "CEK-12" + "TVM-02" ] }, { "ReferenceId": "ISO_27001", "Identifiers": [ - "2013 A.10.1.2" + "2013 A.12.5.1" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "SC-12", - "SC-17" + "CM-2" ] } ] } ], - "Checks": [ - "kms_cmk_rotation_enabled", - "kms_cmk_not_deleted_unintentionally", - "kms_cmk_not_multi_region", - "kms_cmk_are_used" - ] + "Checks": [] }, { - "Id": "CCC.Core.CN11.AR03", - "Description": "When encrypting data, the service MUST verify that customer-managed encryption keys (CMEKs) are used.", + "Id": "CCC.MLDE.CN05.AR02", + "Description": "Attempt to create an MLDE instance with an unapproved image and confirm that it is denied.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "", - "SubSection": "CCC.Core.CN11 Protect Encryption Keys", - "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "FamilyName": "Configuration Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN05 Restrict Environment Options on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Limit the virtual machine and container image options available when creating new MLDE instances to approved and secure configurations.", "Applicability": [ - "tlp-amber", "tlp-red" ], "Recommendation": "", @@ -4848,7 +7512,7 @@ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH16" + "CCC.MLDE.TH04" ] } ], @@ -4856,62 +7520,109 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-1" + "PR.IP-1" ] }, { "ReferenceId": "CCM", "Identifiers": [ - "CEK-08", - "CEK-10", - "CEK-12" + "TVM-02" ] }, { "ReferenceId": "ISO_27001", "Identifiers": [ - "2013 A.10.1.2" + "2013 A.12.5.1" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "SC-12", - "SC-17" + "CM-2" ] } ] } ], - "Checks": [ - "kms_cmk_not_deleted_unintentionally", - "kms_cmk_rotation_enabled", - "kms_cmk_not_multi_region", - "kms_cmk_are_used" - ] + "Checks": [] }, { - "Id": "CCC.Core.CN11.AR04", - "Description": "When encryption keys are accessed, the service MUST verify that access to encryption keys is restricted to authorized personnel and services, following the principle of least privilege.", + "Id": "CCC.MLDE.CN06.AR01", + "Description": "Verify that automatic scheduled upgrades are enabled on user-managed MLDE instances containing sensitive data.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN11 Protect Encryption Keys", + "FamilyName": "Vulnerability Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN06 Require Automatic Scheduled Upgrades on User-Managed MLDE Instances", "SubSection": "", - "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "SubSectionObjective": "Ensure that MLDE instances are kept up-to-date with the latest security patches by enforcing automatic scheduled upgrades.", "Applicability": [ - "tlp-clear", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH04", + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.IP-12" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "TVM-01", + "TVM-02" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.12.6.1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-2" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN06.AR02", + "Description": "Ensure that the upgrade schedule is appropriately configured and does not interfere with critical operations.", + "Attributes": [ + { + "FamilyName": "Vulnerability Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN06 Require Automatic Scheduled Upgrades on User-Managed MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Ensure that MLDE instances are kept up-to-date with the latest security patches by enforcing automatic scheduled upgrades.", + "Applicability": [ + "tlp-red", + "tlp-amber", "tlp-green", - "tlp-amber", - "tlp-red" + "tlp-clear" ], "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH16" + "CCC.MLDE.TH04", + "CCC.Core.TH06" ] } ], @@ -4919,109 +7630,43 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-1" + "PR.IP-12" ] }, { "ReferenceId": "CCM", "Identifiers": [ - "CEK-08", - "CEK-10", - "CEK-12" + "TVM-01", + "TVM-02" ] }, { "ReferenceId": "ISO_27001", "Identifiers": [ - "2013 A.10.1.2" + "2013 A.12.6.1" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "SC-12", - "SC-17" + "SI-2" ] } ] } ], - "Checks": [ - "kms_cmk_rotation_enabled", - "kms_cmk_not_deleted_unintentionally", - "kms_cmk_not_multi_region", - "kms_cmk_are_used" - ] + "Checks": [] }, { - "Id": "CCC.Core.CN11.AR05", - "Description": "When encryption keys are used, the service MUST rotate active keys within 365 days of issuance.", + "Id": "CCC.MLDE.CN07.AR01", + "Description": "Verify that MLDE instances containing sensitive data cannot be accessed via public IP addresses.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN11 Protect Encryption Keys", + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN07 Restrict Public IP Access on MLDE Instances", "SubSection": "", - "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", - "Applicability": [ - "tlp-clear", - "tlp-green" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH16" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-08", - "CEK-10", - "CEK-12" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.10.1.2" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-12", - "SC-17" - ] - } - ] - } - ], - "Checks": [ - "kms_cmk_rotation_enabled", - "kms_cmk_not_multi_region" - ] - }, - { - "Id": "CCC.Core.CN11.AR06", - "Description": "When encryption keys are used, the service MUST rotate active keys within 90 days of issuance.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN11 Protect Encryption Keys", - "SubSection": "", - "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "SubSectionObjective": "Prevent public IP access to MLDE instances to reduce exposure to the internet and enhance security.", "Applicability": [ "tlp-red" ], @@ -5030,444 +7675,8 @@ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH16" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-08", - "CEK-10", - "CEK-12" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.10.1.2" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-12", - "SC-17" - ] - } - ] - } - ], - "Checks": [ - "kms_cmk_rotation_enabled" - ] - }, - { - "Id": "CCC.Core.CN14.AR01", - "Description": "When backups are created for disaster recovery purposes, the storage mechanism MUST NOT allow modification or deletion within 30 days of creation.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN14 Maintain Recent Backups", - "SubSection": "", - "SubSectionObjective": "Ensure that all backups used for disaster recovery are recent and subject to a retention policy that limits deletion.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Use immutable storage solutions where possible. Implement backup retention policies that enforce a minimum retention period of 30 days. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [] - } - ], - "Checks": [ - "neptune_cluster_backup_enabled" - ] - }, - { - "Id": "CCC.Core.CN14.AR02", - "Description": "When backups are created for disaster recovery purposes, the most recent backup MUST have a creation date within the past 30 days.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN14 Maintain Recent Backups", - "SubSection": "", - "SubSectionObjective": "Ensure that all backups used for disaster recovery are recent and subject to a retention policy that limits deletion.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber" - ], - "Recommendation": "Implement automated backup processes to ensure that backups are created regularly. Monitor backup schedules and verify that the most recent backup creation date is within the last 30 days. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [] - } - ], - "Checks": [ - "backup_vaults_exist", - "backup_plans_exist", - "backup_reportplans_exist", - "backup_vaults_encrypted", - "backup_recovery_point_encrypted", - "neptune_cluster_backup_enabled", - "rds_instance_backup_enabled", - "rds_instance_protected_by_backup_plan", - "rds_cluster_protected_by_backup_plan", - "dynamodb_table_protected_by_backup_plan" - ] - }, - { - "Id": "CCC.Core.CN14.AR02", - "Description": "When backups are created for disaster recovery purposes, the most recent backup MUST have a creation date within the past 14 days.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN14 Maintain Recent Backups", - "SubSection": "", - "SubSectionObjective": "Ensure that all backups used for disaster recovery are recent and subject to a retention policy that limits deletion.", - "Applicability": [ - "tlp-red" - ], - "Recommendation": "Implement automated backup processes to ensure that backups are created regularly. Monitor backup schedules and verify that the most recent backup creation date is within the last 14 days. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [] - } - ], - "Checks": [ - "backup_vaults_exist", - "backup_vaults_encrypted", - "backup_plans_exist", - "backup_reportplans_exist", - "backup_recovery_point_encrypted", - "neptune_cluster_backup_enabled" - ] - }, - { - "Id": "CCC.Core.CN03.AR01", - "Description": "When an entity attempts to modify the service through a user interface, the authentication process MUST require multiple identifying factors for authentication.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", - "SubSection": "", - "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-14" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-7" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-03", - "IAM-08" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.9.4.2" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "IA-2" - ] - } - ] - } - ], - "Checks": [ - "iam_root_mfa_enabled", - "iam_root_hardware_mfa_enabled", - "iam_user_mfa_enabled_console_access", - "iam_administrator_access_with_mfa", - "cognito_user_pool_mfa_enabled" - ] - }, - { - "Id": "CCC.Core.CN03.AR02", - "Description": "When an entity attempts to modify the service through an API endpoint, the authentication process MUST require a credential such as an API key or token AND originate from within the trust perimeter.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", - "SubSection": "", - "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-14" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-7" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-03", - "IAM-08" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.9.4.2" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "IA-2" - ] - } - ] - } - ], - "Checks": [ - "iam_root_mfa_enabled", - "iam_root_hardware_mfa_enabled", - "iam_user_mfa_enabled_console_access", - "iam_administrator_access_with_mfa", - "cognito_user_pool_mfa_enabled" - ] - }, - { - "Id": "CCC.Core.CN03.AR03", - "Description": "When an entity attempts to view information on the service through a user interface, the authentication process MUST require multiple identifying factors from the user.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", - "SubSection": "", - "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-14" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-7" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-03", - "IAM-08" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.9.4.2" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "IA-2" - ] - } - ] - } - ], - "Checks": [ - "cognito_user_pool_mfa_enabled", - "iam_root_mfa_enabled", - "iam_root_hardware_mfa_enabled", - "iam_user_mfa_enabled_console_access", - "iam_user_hardware_mfa_enabled", - "iam_administrator_access_with_mfa" - ] - }, - { - "Id": "CCC.Core.CN03.AR04", - "Description": "When an entity attempts to view information on the service through an API endpoint, the authentication process MUST require a credential such as an API key or token AND originate from within the trust perimeter.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", - "SubSection": "", - "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-14" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-7" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-03", - "IAM-08" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.9.4.2" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "IA-2" - ] - } - ] - } - ], - "Checks": [ - "iam_user_mfa_enabled_console_access", - "iam_root_mfa_enabled", - "iam_root_hardware_mfa_enabled", - "iam_user_hardware_mfa_enabled", - "iam_administrator_access_with_mfa", - "apigateway_restapi_authorizers_enabled", - "apigateway_restapi_public_with_authorizer" - ] - }, - { - "Id": "CCC.Core.CN05.AR01", - "Description": "When an attempt is made to modify data on the service or a child resource, the service MUST block requests from unauthorized entities.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", - "SubSection": "", - "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" + "CCC.MLDE.TH02", + "CCC.VPC.TH02" ] } ], @@ -5481,75 +7690,317 @@ { "ReferenceId": "CCM", "Identifiers": [ - "DSP-01", - "DSP-07", - "DSP-08", - "DSP-10", - "DSP-17" + "SEF-05" ] }, { "ReferenceId": "ISO_27001", "Identifiers": [ - "2013 A.13.1.3" + "2013 A.13.1.1" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "AC-3" + "SC-7" ] } ] } ], "Checks": [ - "apigateway_restapi_authorizers_enabled", - "apigateway_restapi_public", - "apigateway_restapi_public_with_authorizer", - "apigatewayv2_api_authorizers_enabled", - "awslambda_function_url_public", + "sagemaker_notebook_instance_without_direct_internet_access_configured" + ] + }, + { + "Id": "CCC.MLDE.CN07.AR02", + "Description": "For MLDE instances without sensitive data requiring public access, ensure that appropriate security controls are in place and access is approved.", + "Attributes": [ + { + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN07 Restrict Public IP Access on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Prevent public IP access to MLDE instances to reduce exposure to the internet and enhance security.", + "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green", + "tlp-clear" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH02", + "CCC.VPC.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-3" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "SEF-05" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.13.1.1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-7" + ] + } + ] + } + ], + "Checks": [ + "sagemaker_notebook_instance_without_direct_internet_access_configured" + ] + }, + { + "Id": "CCC.MLDE.CN08.AR01", + "Description": "Verify that MLDE instances containing sensitive data can only be deployed in approved virtual networks with appropriate security controls.", + "Attributes": [ + { + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN08 Restrict Virtual Networks for MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Limit the virtual networks that can be used when creating new MLDE instances to ensure they are deployed within approved and secure network environments.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01", + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-12" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.1.2" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6" + ] + } + ] + } + ], + "Checks": [ + "sagemaker_notebook_instance_vpc_settings_configured", + "sagemaker_models_vpc_settings_configured", + "sagemaker_training_jobs_vpc_settings_configured" + ] + }, + { + "Id": "CCC.MLDE.CN08.AR02", + "Description": "Ensure that MLDE instances without sensitive data are deployed in networks that meet organizational security standards.", + "Attributes": [ + { + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN08 Restrict Virtual Networks for MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Limit the virtual networks that can be used when creating new MLDE instances to ensure they are deployed within approved and secure network environments.", + "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green", + "tlp-clear" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01", + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-12" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.1.2" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6" + ] + } + ] + } + ], + "Checks": [ + "sagemaker_notebook_instance_vpc_settings_configured", + "sagemaker_models_vpc_settings_configured", + "sagemaker_training_jobs_vpc_settings_configured" + ] + }, + { + "Id": "CCC.Message.CN01.AR01", + "Description": "Attempt to publish a message without using a customer-managed encryption key and verify that the message is rejected or not stored.", + "Attributes": [ + { + "FamilyName": "Encryption", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.Message.CN01 Use Customer-Managed Encryption Keys (CMEK) for Messages", + "SubSection": "", + "SubSectionObjective": "Ensure that messages are encrypted using customer-managed encryption keys (CMEK) to provide enhanced control over encryption processes and keys, meeting compliance and security requirements.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.DS-1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-12", + "SC-13" + ] + } + ] + } + ], + "Checks": [ + "sns_topics_kms_encryption_at_rest_enabled", + "sqs_queues_server_side_encryption_enabled", + "kafka_cluster_encryption_at_rest_uses_cmk" + ] + }, + { + "Id": "CCC.SvlsComp.CN01.AR01", + "Description": "Attempt to access the serverless function over the public internet and verify that access is denied.", + "Attributes": [ + { + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.SvlsComp.CN01 Enforce Use of Private Endpoints for Serverless Function", + "SubSection": "", + "SubSectionObjective": "Ensure that the serverless function is accessible only through a private endpoint, allowing it to communicate securely within a virtual private network and preventing unauthorized external access.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-5" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-7", + "SC-8" + ] + } + ] + } + ], + "Checks": [ + "awslambda_function_inside_vpc", "awslambda_function_not_publicly_accessible", - "ec2_securitygroup_allow_ingress_from_internet_to_all_ports", - "s3_bucket_public_access", - "s3_bucket_public_list_acl", - "s3_bucket_public_write_acl", - "s3_bucket_cross_account_access", - "s3_account_level_public_access_blocks", - "iam_policy_no_full_access_to_cloudtrail", - "iam_policy_no_full_access_to_kms", - "iam_inline_policy_no_full_access_to_cloudtrail", - "iam_inline_policy_no_full_access_to_kms", - "iam_role_administratoraccess_policy", - "iam_group_administrator_access_policy", - "iam_user_administrator_access_policy", - "iam_policy_attached_only_to_group_or_roles", - "iam_role_cross_account_readonlyaccess_policy", - "iam_role_cross_service_confused_deputy_prevention" + "awslambda_function_url_public" ] }, { - "Id": "CCC.Core.CN05.AR02", - "Description": "When administrative access or configuration change is attempted on the service or a child resource, the service MUST refuse requests from unauthorized entities.", + "Id": "CCC.SvlsComp.CN02.AR01", + "Description": "Send requests to invoke the function up to the allowed threshold and confirm they are successful; then send additional requests exceeding the threshold from the same entity and verify that they are denied.", "Attributes": [ { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", + "FamilyName": "Availability", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.SvlsComp.CN02 Implement Function Invocation Rate Limits", "SubSection": "", - "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", + "SubSectionObjective": "Ensure that function invocation is limited to a specified threshold from any single entity, preventing resource exhaustion and denial of service attacks.", "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" + "tlp-red", + "tlp-amber" ], "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01" + "CCC.Core.TH12" ] } ], @@ -5557,650 +8008,19 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.AC-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-01", - "DSP-07", - "DSP-08", - "DSP-10", - "DSP-17" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.3" + "PR.DS-4" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "AC-3" + "SC-5" ] } ] } ], - "Checks": [ - "iam_root_mfa_enabled", - "iam_root_hardware_mfa_enabled", - "iam_avoid_root_usage", - "iam_user_mfa_enabled_console_access", - "iam_administrator_access_with_mfa", - "iam_group_administrator_access_policy", - "iam_role_administratoraccess_policy", - "iam_inline_policy_no_full_access_to_cloudtrail", - "iam_inline_policy_no_full_access_to_kms", - "iam_policy_no_full_access_to_cloudtrail", - "iam_policy_no_full_access_to_kms", - "iam_policy_allows_privilege_escalation", - "iam_inline_policy_allows_privilege_escalation", - "iam_customer_attached_policy_no_administrative_privileges", - "iam_customer_unattached_policy_no_administrative_privileges", - "iam_aws_attached_policy_no_administrative_privileges", - "iam_password_policy_minimum_length_14", - "iam_password_policy_uppercase", - "iam_password_policy_lowercase", - "iam_password_policy_symbol", - "iam_password_policy_number", - "iam_password_policy_expires_passwords_within_90_days_or_less", - "iam_password_policy_reuse_24", - "iam_check_saml_providers_sts", - "iam_policy_attached_only_to_group_or_roles" - ] - }, - { - "Id": "CCC.Core.CN05.AR03", - "Description": "When administrative access or configuration change is attempted on the service or a child resource in a multi-tenant environment, the service MUST refuse requests across tenant boundaries unless the origin is explicitly included in a pre-approved allowlist.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", - "SubSection": "", - "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-01", - "DSP-07", - "DSP-08", - "DSP-10", - "DSP-17" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.3" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "vpc_endpoint_services_allowed_principals_trust_boundaries", - "vpc_endpoint_connections_trust_boundaries", - "iam_role_cross_service_confused_deputy_prevention", - "iam_role_cross_account_readonlyaccess_policy", - "s3_bucket_cross_account_access", - "eventbridge_bus_cross_account_access", - "eventbridge_schema_registry_cross_account_access" - ] - }, - { - "Id": "CCC.Core.CN05.AR04", - "Description": "When data is requested from outside the trust perimeter, the service MUST refuse requests from unauthorized entities.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "", - "SubSection": "CCC.Core.CN05 Prevent Access from Untrusted Entities", - "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-01", - "DSP-07", - "DSP-08", - "DSP-10", - "DSP-17" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.3" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "accessanalyzer_enabled", - "accessanalyzer_enabled_without_findings", - "vpc_endpoint_connections_trust_boundaries", - "vpc_endpoint_services_allowed_principals_trust_boundaries", - "s3_bucket_cross_account_access", - "s3_bucket_public_access", - "iam_administrator_access_with_mfa", - "iam_group_administrator_access_policy", - "iam_user_administrator_access_policy", - "iam_inline_policy_allows_privilege_escalation", - "iam_inline_policy_no_full_access_to_kms", - "iam_inline_policy_no_full_access_to_cloudtrail", - "iam_policy_no_full_access_to_kms", - "iam_policy_no_full_access_to_cloudtrail", - "iam_policy_attached_only_to_group_or_roles", - "iam_user_mfa_enabled_console_access" - ] - }, - { - "Id": "CCC.Core.CN05.AR05", - "Description": "When any request is made from outside the trust perimeter, the service MUST NOT provide any response that may indicate the service exists.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", - "SubSection": "", - "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", - "Applicability": [ - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-01", - "DSP-07", - "DSP-08", - "DSP-10", - "DSP-17" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.3" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "awslambda_function_url_public", - "apigateway_restapi_public", - "apigateway_restapi_public_with_authorizer", - "s3_bucket_public_list_acl", - "s3_bucket_public_write_acl", - "s3_bucket_public_access", - "s3_bucket_policy_public_write_access", - "sns_topics_not_publicly_accessible", - "ec2_securitygroup_allow_ingress_from_internet_to_all_ports", - "ec2_networkacl_allow_ingress_any_port", - "ec2_networkacl_allow_ingress_tcp_port_22", - "ec2_networkacl_allow_ingress_tcp_port_3389", - "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22", - "ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389", - "ec2_securitygroup_default_restrict_traffic", - "vpc_endpoint_services_allowed_principals_trust_boundaries", - "vpc_endpoint_connections_trust_boundaries", - "vpc_endpoint_for_ec2_enabled" - ] - }, - { - "Id": "CCC.Core.CN05.AR06", - "Description": "When any request is made to the service or a child resource, the service MUST refuse requests from unauthorized entities.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", - "SubSection": "", - "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-01", - "DSP-07", - "DSP-08", - "DSP-10", - "DSP-17" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.3" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "vpc_endpoint_connections_trust_boundaries", - "vpc_endpoint_services_allowed_principals_trust_boundaries", - "iam_role_cross_service_confused_deputy_prevention", - "iam_role_cross_account_readonlyaccess_policy", - "iam_policy_attached_only_to_group_or_roles", - "iam_group_administrator_access_policy", - "iam_user_mfa_enabled_console_access", - "iam_root_mfa_enabled", - "iam_root_hardware_mfa_enabled", - "iam_no_root_access_key", - "iam_administrator_access_with_mfa", - "iam_root_credentials_management_enabled", - "iam_check_saml_providers_sts", - "iam_user_hardware_mfa_enabled" - ] - }, - { - "Id": "CCC.Core.CN04.AR01", - "Description": "When administrative access or configuration change is attempted on the service or a child resource, the service MUST log the client identity, time, and result of the attempt.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements. ", - "Section": "CCC.Core.CN04 Log All Access and Changes", - "SubSection": "", - "SubSectionObjective": "Ensure that all access attempts are logged to maintain a detailed audit trail for security and compliance purposes.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.AE-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-08" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-2", - "AU-3", - "AU-12" - ] - } - ] - } - ], - "Checks": [ - "cloudtrail_multi_region_enabled", - "cloudtrail_cloudwatch_logging_enabled", - "cloudtrail_log_file_validation_enabled", - "awslambda_function_invoke_api_operations_cloudtrail_logging_enabled", - "cloudwatch_log_metric_filter_authentication_failures", - "cloudwatch_log_metric_filter_unauthorized_api_calls", - "cloudwatch_log_metric_filter_root_usage", - "cloudwatch_log_metric_filter_sign_in_without_mfa", - "cloudwatch_log_metric_filter_for_s3_bucket_policy_changes", - "cloudwatch_log_metric_filter_policy_changes", - "cloudwatch_log_metric_filter_security_group_changes", - "cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk", - "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", - "cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_changes_enabled", - "cloudwatch_changes_to_network_acls_alarm_configured", - "cloudwatch_changes_to_network_gateways_alarm_configured", - "cloudwatch_changes_to_network_route_tables_alarm_configured", - "cloudwatch_changes_to_vpcs_alarm_configured", - "vpc_flow_logs_enabled", - "apigateway_restapi_logging_enabled", - "apigatewayv2_api_access_logging_enabled" - ] - }, - { - "Id": "CCC.Core.CN04.AR02", - "Description": "When any attempt is made to modify data on the service or a child resource, the service MUST log the client identity, time, and result of the attempt.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements. ", - "Section": "CCC.Core.CN04 Log All Access and Changes", - "SubSection": "", - "SubSectionObjective": "Ensure that all access attempts are logged to maintain a detailed audit trail for security and compliance purposes.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.AE-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-08" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-2", - "AU-3", - "AU-12" - ] - } - ] - } - ], - "Checks": [ - "cloudtrail_s3_dataevents_read_enabled", - "cloudtrail_s3_dataevents_write_enabled", - "awslambda_function_invoke_api_operations_cloudtrail_logging_enabled", - "cloudtrail_multi_region_enabled_logging_management_events", - "cloudtrail_cloudwatch_logging_enabled", - "vpc_flow_logs_enabled", - "apigateway_restapi_logging_enabled", - "apigatewayv2_api_access_logging_enabled", - "cloudtrail_threat_detection_enumeration", - "cloudtrail_threat_detection_privilege_escalation", - "cloudtrail_threat_detection_llm_jacking" - ] - }, - { - "Id": "CCC.Core.CN04.AR03", - "Description": "When any attempt is made to read data on the service or a child resource, the service MUST log the client identity, time, and result of the attempt.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements. ", - "Section": "CCC.Core.CN04 Log All Access and Changes", - "SubSection": "", - "SubSectionObjective": "Ensure that all access attempts are logged to maintain a detailed audit trail for security and compliance purposes.", - "Applicability": [ - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.AE-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-08" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-2", - "AU-3", - "AU-12" - ] - } - ] - } - ], - "Checks": [ - "cloudtrail_insights_exist", - "cloudtrail_multi_region_enabled", - "cloudtrail_cloudwatch_logging_enabled", - "cloudtrail_kms_encryption_enabled", - "cloudtrail_log_file_validation_enabled", - "cloudtrail_logs_s3_bucket_is_not_publicly_accessible", - "cloudtrail_s3_dataevents_read_enabled", - "cloudtrail_s3_dataevents_write_enabled", - "cloudwatch_log_group_kms_encryption_enabled", - "cloudwatch_log_group_not_publicly_accessible", - "cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_changes_enabled", - "cloudwatch_log_metric_filter_authentication_failures", - "apigateway_restapi_logging_enabled", - "apigatewayv2_api_access_logging_enabled", - "vpc_flow_logs_enabled" - ] - }, - { - "Id": "CCC.Core.CN07.AR01", - "Description": "When enumeration activities are detected, the service MUST publish an event to a monitored channel which includes the client identity, time, and nature of the activity.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements. ", - "Section": "CCC.Core.CN07 Alert on Unusual Enumeration Activity", - "SubSection": "", - "SubSectionObjective": "Ensure that logs and associated alerts are generated when unusual enumeration activity is detected that may indicate reconnaissance activities.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Implement event publication mechanisms and alerts for patterns indicative of enumeration activities, such as repeated access attempts, requests, or liveness probes. Configure alerts to notify security teams of any activities that merit further investigation. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH15" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.AE-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-05", - "SEF-05" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-6" - ] - } - ] - } - ], - "Checks": [ - "cloudtrail_threat_detection_enumeration" - ] - }, - { - "Id": "CCC.Core.CN07.AR02", - "Description": "When enumeration activities are detected, the service MUST log the client identity, time, and nature of the activity.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements. ", - "Section": "CCC.Core.CN07 Alert on Unusual Enumeration Activity", - "SubSection": "", - "SubSectionObjective": "Ensure that logs and associated alerts are generated when unusual enumeration activity is detected that may indicate reconnaissance activities.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Implement logging mechanisms to capture details of enumeration activities, including client identity, timestamps, and activity nature. Retain logs according to organizational policies, and occasionally review them for patterns that may indicate reconnaissance activities. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH15" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.AE-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-05", - "SEF-05" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-6" - ] - } - ] - } - ], - "Checks": [ - "cloudtrail_threat_detection_enumeration" - ] + "Checks": [] } ] } diff --git a/prowler/compliance/azure/ccc_azure.json b/prowler/compliance/azure/ccc_azure.json index 004137ad53..cb87346d13 100644 --- a/prowler/compliance/azure/ccc_azure.json +++ b/prowler/compliance/azure/ccc_azure.json @@ -1,10 +1,1698 @@ { "Framework": "CCC", - "Version": "", + "Version": "v2025.10", "Provider": "Azure", "Name": "Common Cloud Controls Catalog (CCC)", "Description": "Common Cloud Controls Catalog (CCC) for Azure", "Requirements": [ + { + "Id": "CCC.Core.CN01.AR01", + "Description": "When a port is exposed for non-SSH network traffic, all traffic MUST include a TLS handshake AND be encrypted using TLS 1.3 or higher.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN01 Encrypt Data for Transmission", + "SubSection": "", + "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Most cloud services enable TLS 1.3 by default. Where it is not already set, ensure that your services are configured or updated accordingly.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-03", + "CEK-04", + "IVS-03", + "IVS-07" + ] + } + ] + } + ], + "Checks": [ + "storage_secure_transfer_required_is_enabled", + "storage_ensure_minimum_tls_version_12", + "storage_smb_channel_encryption_with_secure_algorithm", + "storage_smb_protocol_version_is_latest", + "postgresql_flexible_server_enforce_ssl_enabled", + "mysql_flexible_server_ssl_connection_enabled", + "mysql_flexible_server_minimum_tls_version_12", + "sqlserver_recommended_minimal_tls_version", + "app_minimum_tls_version_12", + "app_ensure_http_is_redirected_to_https", + "app_ensure_using_http20", + "app_ftp_deployment_disabled", + "app_function_ftps_deployment_disabled" + ] + }, + { + "Id": "CCC.Core.CN01.AR02", + "Description": "When a port is exposed for SSH network traffic, all traffic MUST include a SSH handshake AND be encrypted using SSHv2 or higher.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN01 Encrypt Data for Transmission", + "SubSection": "", + "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Any time port 22 is exposed, ensure that it has a properly implemented SSH server with SSHv2 enabled and configured with strong ciphers.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-03", + "CEK-04", + "IVS-03", + "IVS-07" + ] + } + ] + } + ], + "Checks": [ + "network_ssh_internet_access_restricted", + "vm_linux_enforce_ssh_authentication" + ] + }, + { + "Id": "CCC.Core.CN01.AR03", + "Description": "When the service receives unencrypted traffic, then it MUST either block the request or automatically redirect it to the secure equivalent.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN01 Encrypt Data for Transmission", + "SubSection": "", + "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Review firewall, load balancer, and application configurations to ensure insecure protocols such as HTTP, FTP, and Telnet are not exposed. Where possible, implement automatic redirection to secure protocols such as HTTPS, SFTP, SSH, and regularly scan for protocol drift.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-03", + "CEK-04", + "IVS-03", + "IVS-07" + ] + } + ] + } + ], + "Checks": [ + "app_ensure_http_is_redirected_to_https", + "storage_secure_transfer_required_is_enabled", + "app_ftp_deployment_disabled", + "app_function_ftps_deployment_disabled" + ] + }, + { + "Id": "CCC.Core.CN01.AR07", + "Description": "When a port is exposed, the service MUST ensure that the protocol and service officially assigned to that port number by the IANA Service Name and Transport Protocol Port Number Registry, and no other, is run on that port.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN01 Encrypt Data for Transmission", + "SubSection": "", + "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Reference the IANA Service Name and Transport Protocol Port Number Registry for more information about correct protocol-to-port assignments. Avoid running non-standard services on well-known ports.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-03", + "CEK-04", + "IVS-03", + "IVS-07" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Core.CN01.AR08", + "Description": "When a service transmits data using TLS, mutual TLS (mTLS) MUST be implemented to require both client and server certificate authentication for all connections.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN01 Encrypt Data for Transmission", + "SubSection": "", + "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Configure mTLS for all endpoints that process or transmit sensitive data. Ensure both client and server certificates are validated and managed securely. Regularly review certificate authorities and automate certificate rotation where possible.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-03", + "CEK-04", + "IVS-03", + "IVS-07" + ] + } + ] + } + ], + "Checks": [ + "app_client_certificates_on" + ] + }, + { + "Id": "CCC.Core.CN13.AR01", + "Description": "When a port is exposed that uses certificate-based encryption, the service MUST only use valid, unexpired certificates issued by a trusted certificate authority.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN13 Minimize Lifetime of Encryption and Authentication Certificates", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption and authentication certificates have a limited lifetime to reduce the risk of compromise and ensure the use of up-to-date security practices.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Track certificate expiration dates and automate certificate renewal where possible. Use certificate management tools to ensure only certificates from trusted authorities are deployed.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH18" + ] + } + ], + "SectionGuidelineMappings": [] + } + ], + "Checks": [ + "keyvault_key_expiration_set_in_non_rbac", + "keyvault_rbac_key_expiration_set", + "keyvault_non_rbac_secret_expiration_set", + "keyvault_rbac_secret_expiration_set" + ] + }, + { + "Id": "CCC.Core.CN13.AR02", + "Description": "When a port is exposed that uses certificate-based encryption, the service MUST rotate active certificates within 180 days of issuance.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN13 Minimize Lifetime of Encryption and Authentication Certificates", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption and authentication certificates have a limited lifetime to reduce the risk of compromise and ensure the use of up-to-date security practices.", + "Applicability": [ + "tlp-amber" + ], + "Recommendation": "Track certificate expiration dates and automate certificate renewal where possible. Use certificate management tools to ensure only certificates from trusted authorities are deployed.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH18" + ] + } + ], + "SectionGuidelineMappings": [] + } + ], + "Checks": [ + "keyvault_key_rotation_enabled" + ] + }, + { + "Id": "CCC.Core.CN13.AR03", + "Description": "When a port is exposed that uses certificate-based encryption, the service MUST rotate active certificates within 90 days of issuance.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN13 Minimize Lifetime of Encryption and Authentication Certificates", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption and authentication certificates have a limited lifetime to reduce the risk of compromise and ensure the use of up-to-date security practices.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "Track certificate expiration dates and automate certificate renewal where possible. Use certificate management tools to ensure only certificates from trusted authorities are deployed.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH18" + ] + } + ], + "SectionGuidelineMappings": [] + } + ], + "Checks": [ + "keyvault_key_rotation_enabled" + ] + }, + { + "Id": "CCC.Core.CN06.AR01", + "Description": "When the service is running, its region and availability zone MUST be included in a list of explicitly trusted or approved locations within the trust perimeter.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN06 Restrict Deployments to Trust Perimeter", + "SubSection": "", + "SubSectionObjective": "Ensure that the service and its child resources are only deployed on infrastructure in locations that are explicitly included within a defined trust perimeter.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Maintain an up-to-date list of trusted and approved regions based on organizational policies. Validate the service's deployment location is included in this list.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-19" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Core.CN06.AR02", + "Description": "When a child resource is deployed, its region and availability zone MUST be included in a list of explicitly trusted or approved locations within the trust perimeter.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN06 Restrict Deployments to Trust Perimeter", + "SubSection": "", + "SubSectionObjective": "Ensure that the service and its child resources are only deployed on infrastructure in locations that are explicitly included within a defined trust perimeter.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Maintain an up-to-date list of trusted and approved regions based on organizational policies. Validate that child resources can only be deployed to locations included in this list.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-19" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Core.CN08.AR01", + "Description": "When data is created or modified, the data MUST have a complete and recoverable duplicate that is stored in a physically separate data center.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN08 Replicate Data to Multiple Locations", + "SubSection": "", + "SubSectionObjective": "Ensure that data is replicated across multiple physical locations to protect against data loss due to hardware failures, natural disasters, or other catastrophic events.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Implement automated data replication processes to ensure that data is consistently duplicated in another region or availability zone. Regularly test data recovery from the replicated location to ensure integrity and availability.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "BCR-08", + "BCR-10", + "BCR-11" + ] + } + ] + } + ], + "Checks": [ + "storage_geo_redundant_enabled", + "vm_backup_enabled" + ] + }, + { + "Id": "CCC.Core.CN08.AR02", + "Description": "When data is replicated into a second location, the service MUST be able to accurately represent the replication locations, replication status, and data synchronization status.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN08 Replicate Data to Multiple Locations", + "SubSection": "", + "SubSectionObjective": "Ensure that data is replicated across multiple physical locations to protect against data loss due to hardware failures, natural disasters, or other catastrophic events.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "BCR-08", + "BCR-10", + "BCR-11" + ] + } + ] + } + ], + "Checks": [ + "storage_geo_redundant_enabled" + ] + }, + { + "Id": "CCC.Core.CN09.AR01", + "Description": "When the service is operational, its logs and any child resource logs MUST NOT be accessible from the resource they record access to.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN09 Ensure Integrity of Access Logs", + "SubSection": "", + "SubSectionObjective": "Ensure that access logs are always recorded to an external location that cannot be manipulated from the context of the service(s) it contains logs for.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH07", + "CCC.Core.TH09", + "CCC.Core.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-02", + "LOG-04", + "LOG-09" + ] + } + ] + } + ], + "Checks": [ + "monitor_storage_account_with_activity_logs_is_private", + "monitor_storage_account_with_activity_logs_cmk_encrypted" + ] + }, + { + "Id": "CCC.Core.CN09.AR02", + "Description": "When the service is operational, disabling the logs for the service or its child resources MUST NOT be possible without also disabling the corresponding resource.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN09 Ensure Integrity of Access Logs", + "SubSection": "", + "SubSectionObjective": "Ensure that access logs are always recorded to an external location that cannot be manipulated from the context of the service(s) it contains logs for.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "No normal business operations should disable logs, as this could indicate an attempt to cover up unauthorized access. Ensure that logging mechanisms are tightly integrated with service operations, so that logging cannot be disabled without stopping the service itself.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH07", + "CCC.Core.TH09", + "CCC.Core.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-02", + "LOG-04", + "LOG-09" + ] + } + ] + } + ], + "Checks": [ + "monitor_diagnostic_settings_exists", + "monitor_diagnostic_setting_with_appropriate_categories" + ] + }, + { + "Id": "CCC.Core.CN09.AR03", + "Description": "When the service is operational, any attempt to redirect logs for the service or its child resources MUST NOT be possible without halting operation of the corresponding resource and publishing corresponding events to monitored channels.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN09 Ensure Integrity of Access Logs", + "SubSection": "", + "SubSectionObjective": "Ensure that access logs are always recorded to an external location that cannot be manipulated from the context of the service(s) it contains logs for.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "No normal business operations should result in the redirection of logs, as this could indicate an attempt to cover up unauthorized access. Ensure that logging configurations are immutable during service operation so that any changes require stopping the service and publishing corresponding events to monitored channels.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH07", + "CCC.Core.TH09", + "CCC.Core.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-02", + "LOG-04", + "LOG-09" + ] + } + ] + } + ], + "Checks": [ + "monitor_diagnostic_settings_exists" + ] + }, + { + "Id": "CCC.Core.CN10.AR01", + "Description": "When data is replicated, the service MUST ensure that replication only occurs to destinations that are explicitly included within the defined trust perimeter.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN10 Restrict Data Replication to Trust Perimeter", + "SubSection": "", + "SubSectionObjective": "Ensure that data is only replicated on infrastructure in locations that are explicitly included within a defined trust perimeter.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-10", + "DSP-19" + ] + } + ] + } + ], + "Checks": [ + "storage_cross_tenant_replication_disabled" + ] + }, + { + "Id": "CCC.Core.CN02.AR01", + "Description": "When data is stored, it MUST be encrypted using the latest industry-standard encryption methods.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN02 Encrypt Data for Storage", + "SubSection": "", + "SubSectionObjective": "Ensure that all data stored is encrypted at rest using strong encryption algorithms.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-03", + "CEK-04", + "UEM-08", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "storage_infrastructure_encryption_is_enabled", + "storage_ensure_encryption_with_customer_managed_keys", + "vm_ensure_attached_disks_encrypted_with_cmk", + "vm_ensure_unattached_disks_encrypted_with_cmk", + "sqlserver_tde_encryption_enabled", + "sqlserver_tde_encrypted_with_cmk", + "databricks_workspace_cmk_encryption_enabled", + "monitor_storage_account_with_activity_logs_cmk_encrypted" + ] + }, + { + "Id": "CCC.Core.CN11.AR01", + "Description": "When encryption keys are used, the service MUST verify that all encryption keys use the latest industry-standard cryptographic algorithms.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN11 Protect Encryption Keys", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH16" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-08", + "CEK-10", + "CEK-12" + ] + } + ] + } + ], + "Checks": [ + "storage_smb_channel_encryption_with_secure_algorithm" + ] + }, + { + "Id": "CCC.Core.CN11.AR02", + "Description": "When encryption keys are used, the service MUST rotate active keys within 180 days of issuance.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN11 Protect Encryption Keys", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "Applicability": [ + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH16" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-08", + "CEK-10", + "CEK-12" + ] + } + ] + } + ], + "Checks": [ + "keyvault_key_rotation_enabled" + ] + }, + { + "Id": "CCC.Core.CN11.AR03", + "Description": "When encrypting data, the service MUST verify that customer-managed encryption keys (CMEKs) are used.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN11 Protect Encryption Keys", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH16" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-08", + "CEK-10", + "CEK-12" + ] + } + ] + } + ], + "Checks": [ + "storage_ensure_encryption_with_customer_managed_keys", + "vm_ensure_attached_disks_encrypted_with_cmk", + "vm_ensure_unattached_disks_encrypted_with_cmk", + "sqlserver_tde_encrypted_with_cmk", + "databricks_workspace_cmk_encryption_enabled" + ] + }, + { + "Id": "CCC.Core.CN11.AR04", + "Description": "When encryption keys are accessed, the service MUST verify that access to encryption keys is restricted to authorized personnel and services, following the principle of least privilege.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN11 Protect Encryption Keys", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH16" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-08", + "CEK-10", + "CEK-12" + ] + } + ] + } + ], + "Checks": [ + "keyvault_rbac_enabled", + "keyvault_private_endpoints", + "keyvault_access_only_through_private_endpoints", + "keyvault_logging_enabled", + "keyvault_recoverable" + ] + }, + { + "Id": "CCC.Core.CN11.AR05", + "Description": "When encryption keys are used, the service MUST rotate active keys within 365 days of issuance.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN11 Protect Encryption Keys", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "Applicability": [ + "tlp-clear", + "tlp-green" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH16" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-08", + "CEK-10", + "CEK-12" + ] + } + ] + } + ], + "Checks": [ + "keyvault_key_rotation_enabled" + ] + }, + { + "Id": "CCC.Core.CN11.AR06", + "Description": "When encryption keys are used, the service MUST rotate active keys within 90 days of issuance.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN11 Protect Encryption Keys", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH16" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-08", + "CEK-10", + "CEK-12" + ] + } + ] + } + ], + "Checks": [ + "storage_key_rotation_90_days", + "keyvault_key_rotation_enabled" + ] + }, + { + "Id": "CCC.Core.CN14.AR01", + "Description": "When backups are created for disaster recovery purposes, the storage mechanism MUST NOT allow modification or deletion within 30 days of creation.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN14 Maintain Recent Backups", + "SubSection": "", + "SubSectionObjective": "Ensure that all backups used for disaster recovery are recent and subject to a retention policy that limits deletion.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Use immutable storage solutions where possible. Implement backup retention policies that enforce a minimum retention period of 30 days.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [] + } + ], + "Checks": [ + "vm_backup_enabled", + "vm_sufficient_daily_backup_retention_period" + ] + }, + { + "Id": "CCC.Core.CN14.AR02", + "Description": "When backups are created for disaster recovery purposes, the most recent backup MUST have a creation date within the past 30 days.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN14 Maintain Recent Backups", + "SubSection": "", + "SubSectionObjective": "Ensure that all backups used for disaster recovery are recent and subject to a retention policy that limits deletion.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber" + ], + "Recommendation": "Implement automated backup processes to ensure that backups are created regularly. Monitor backup schedules and verify that the most recent backup creation date is within the last 30 days.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [] + } + ], + "Checks": [ + "vm_backup_enabled", + "vm_sufficient_daily_backup_retention_period" + ] + }, + { + "Id": "CCC.Core.CN14.AR03", + "Description": "When backups are created for disaster recovery purposes, the most recent backup MUST have a creation date within the past 14 days.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN14 Maintain Recent Backups", + "SubSection": "", + "SubSectionObjective": "Ensure that all backups used for disaster recovery are recent and subject to a retention policy that limits deletion.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "Implement automated backup processes to ensure that backups are created regularly. Monitor backup schedules and verify that the most recent backup creation date is within the last 14 days.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [] + } + ], + "Checks": [ + "vm_backup_enabled", + "vm_sufficient_daily_backup_retention_period" + ] + }, + { + "Id": "CCC.Core.CN03.AR01", + "Description": "When an entity attempts to modify the service through a user interface, the authentication process MUST require multiple identifying factors for authentication.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", + "SubSection": "", + "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-14" + ] + } + ] + } + ], + "Checks": [ + "entra_privileged_user_has_mfa", + "entra_non_privileged_user_has_mfa", + "entra_user_with_vm_access_has_mfa", + "entra_security_defaults_enabled" + ] + }, + { + "Id": "CCC.Core.CN03.AR02", + "Description": "When an entity attempts to modify the service through an API endpoint, the authentication process MUST require a credential such as an API key or token AND originate from within the trust perimeter.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", + "SubSection": "", + "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-14" + ] + } + ] + } + ], + "Checks": [ + "entra_conditional_access_policy_require_mfa_for_management_api", + "app_function_access_keys_configured", + "app_function_identity_is_configured" + ] + }, + { + "Id": "CCC.Core.CN03.AR03", + "Description": "When an entity attempts to view information on the service through a user interface, the authentication process MUST require multiple identifying factors from the user.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", + "SubSection": "", + "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-14" + ] + } + ] + } + ], + "Checks": [ + "entra_privileged_user_has_mfa", + "entra_non_privileged_user_has_mfa", + "entra_user_with_vm_access_has_mfa", + "entra_security_defaults_enabled" + ] + }, + { + "Id": "CCC.Core.CN03.AR04", + "Description": "When an entity attempts to view information on the service through an API endpoint, the authentication process MUST require a credential such as an API key or token AND originate from within the trust perimeter.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", + "SubSection": "", + "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-14" + ] + } + ] + } + ], + "Checks": [ + "entra_conditional_access_policy_require_mfa_for_management_api", + "app_function_access_keys_configured" + ] + }, + { + "Id": "CCC.Core.CN05.AR01", + "Description": "When an attempt is made to modify data on the service or a child resource, the service MUST block requests from unauthorized entities.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", + "SubSection": "", + "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-01", + "DSP-07", + "DSP-08", + "DSP-10", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "aks_cluster_rbac_enabled", + "aks_clusters_public_access_disabled", + "app_ensure_auth_is_set_up", + "app_register_with_identity", + "app_function_identity_is_configured", + "app_function_identity_without_admin_privileges", + "app_function_not_publicly_accessible", + "iam_role_user_access_admin_restricted", + "iam_subscription_roles_owner_custom_not_created", + "keyvault_rbac_enabled", + "keyvault_private_endpoints", + "keyvault_access_only_through_private_endpoints", + "entra_global_admin_in_less_than_five_users", + "entra_non_privileged_user_has_mfa", + "entra_privileged_user_has_mfa", + "vm_jit_access_enabled" + ] + }, + { + "Id": "CCC.Core.CN05.AR02", + "Description": "When administrative access or configuration change is attempted on the service or a child resource, the service MUST refuse requests from unauthorized entities.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", + "SubSection": "", + "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-01", + "DSP-07", + "DSP-08", + "DSP-10", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "iam_role_user_access_admin_restricted", + "iam_subscription_roles_owner_custom_not_created", + "iam_custom_role_has_permissions_to_administer_resource_locks", + "entra_global_admin_in_less_than_five_users", + "entra_privileged_user_has_mfa", + "entra_non_privileged_user_has_mfa", + "entra_conditional_access_policy_require_mfa_for_management_api", + "aks_cluster_rbac_enabled", + "containerregistry_admin_user_disabled", + "keyvault_rbac_enabled" + ] + }, + { + "Id": "CCC.Core.CN05.AR03", + "Description": "When administrative access or configuration change is attempted on the service or a child resource in a multi-tenant environment, the service MUST refuse requests across tenant boundaries unless the origin is explicitly included in a pre-approved allowlist.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", + "SubSection": "", + "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-01", + "DSP-07", + "DSP-08", + "DSP-10", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "storage_cross_tenant_replication_disabled", + "storage_default_to_entra_authorization_enabled", + "entra_trusted_named_locations_exists" + ] + }, + { + "Id": "CCC.Core.CN05.AR04", + "Description": "When data is requested from outside the trust perimeter, the service MUST refuse requests from unauthorized entities.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", + "SubSection": "", + "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-01", + "DSP-07", + "DSP-08", + "DSP-10", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "storage_default_network_access_rule_is_denied", + "storage_ensure_private_endpoints_in_storage_accounts", + "storage_account_key_access_disabled", + "storage_default_to_entra_authorization_enabled", + "containerregistry_not_publicly_accessible", + "containerregistry_uses_private_link", + "keyvault_private_endpoints", + "keyvault_access_only_through_private_endpoints", + "cosmosdb_account_use_private_endpoints", + "cosmosdb_account_firewall_use_selected_networks", + "sqlserver_unrestricted_inbound_access", + "network_http_internet_access_restricted" + ] + }, + { + "Id": "CCC.Core.CN05.AR05", + "Description": "When any request is made from outside the trust perimeter, the service MUST NOT provide any response that may indicate the service exists.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", + "SubSection": "", + "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-01", + "DSP-07", + "DSP-08", + "DSP-10", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "aks_clusters_created_with_private_nodes", + "aks_clusters_public_access_disabled", + "app_function_not_publicly_accessible", + "containerregistry_not_publicly_accessible", + "keyvault_private_endpoints", + "storage_ensure_private_endpoints_in_storage_accounts", + "network_http_internet_access_restricted", + "network_rdp_internet_access_restricted", + "network_ssh_internet_access_restricted" + ] + }, + { + "Id": "CCC.Core.CN05.AR06", + "Description": "When any request is made to the service or a child resource, the service MUST refuse requests from unauthorized entities.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", + "SubSection": "", + "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-01", + "DSP-07", + "DSP-08", + "DSP-10", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "entra_privileged_user_has_mfa", + "entra_non_privileged_user_has_mfa", + "entra_global_admin_in_less_than_five_users", + "entra_conditional_access_policy_require_mfa_for_management_api", + "iam_role_user_access_admin_restricted", + "iam_subscription_roles_owner_custom_not_created", + "keyvault_rbac_enabled", + "vm_jit_access_enabled" + ] + }, + { + "Id": "CCC.Core.CN04.AR01", + "Description": "When administrative access or configuration change is attempted on the service or a child resource, the service MUST log the client identity, time, and result of the attempt.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements.", + "Section": "CCC.Core.CN04 Log All Access and Changes", + "SubSection": "", + "SubSectionObjective": "Ensure that all access attempts are logged to maintain a detailed audit trail for security and compliance purposes.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-08" + ] + } + ] + } + ], + "Checks": [ + "monitor_diagnostic_settings_exists", + "monitor_diagnostic_setting_with_appropriate_categories", + "monitor_alert_create_update_nsg", + "monitor_alert_delete_nsg", + "monitor_alert_create_update_public_ip_address_rule", + "monitor_alert_delete_public_ip_address_rule", + "monitor_alert_create_update_security_solution", + "monitor_alert_delete_security_solution", + "monitor_alert_create_policy_assignment", + "monitor_alert_create_update_sqlserver_fr" + ] + }, + { + "Id": "CCC.Core.CN04.AR02", + "Description": "When any attempt is made to modify data on the service or a child resource, the service MUST log the client identity, time, and result of the attempt.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements.", + "Section": "CCC.Core.CN04 Log All Access and Changes", + "SubSection": "", + "SubSectionObjective": "Ensure that all access attempts are logged to maintain a detailed audit trail for security and compliance purposes.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-08" + ] + } + ] + } + ], + "Checks": [ + "monitor_diagnostic_settings_exists", + "monitor_diagnostic_setting_with_appropriate_categories", + "keyvault_logging_enabled", + "app_http_logs_enabled" + ] + }, + { + "Id": "CCC.Core.CN04.AR03", + "Description": "When any attempt is made to read data on the service or a child resource, the service MUST log the client identity, time, and result of the attempt.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements.", + "Section": "CCC.Core.CN04 Log All Access and Changes", + "SubSection": "", + "SubSectionObjective": "Ensure that all access attempts are logged to maintain a detailed audit trail for security and compliance purposes.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-08" + ] + } + ] + } + ], + "Checks": [ + "monitor_diagnostic_settings_exists", + "monitor_diagnostic_setting_with_appropriate_categories", + "keyvault_logging_enabled", + "app_http_logs_enabled" + ] + }, + { + "Id": "CCC.Core.CN07.AR01", + "Description": "When enumeration activities are detected, the service MUST publish an event to a monitored channel which includes the client identity, time, and nature of the activity.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements.", + "Section": "CCC.Core.CN07 Alert on Unusual Enumeration Activity", + "SubSection": "", + "SubSectionObjective": "Ensure that logs and associated alerts are generated when unusual enumeration activity is detected that may indicate reconnaissance activities.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Implement event publication mechanisms and alerts for patterns indicative of enumeration activities, such as repeated access attempts, requests, or liveness probes. Configure alerts to notify security teams of any activities that merit further investigation.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH15" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-05", + "SEF-05" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Core.CN07.AR02", + "Description": "When enumeration activities are detected, the service MUST log the client identity, time, and nature of the activity.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements.", + "Section": "CCC.Core.CN07 Alert on Unusual Enumeration Activity", + "SubSection": "", + "SubSectionObjective": "Ensure that logs and associated alerts are generated when unusual enumeration activity is detected that may indicate reconnaissance activities.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Implement logging mechanisms to capture details of enumeration activities, including client identity, timestamps, and activity nature. Retain logs according to organizational policies, and occasionally review them for patterns that may indicate reconnaissance activities.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH15" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-05", + "SEF-05" + ] + } + ] + } + ], + "Checks": [] + }, { "Id": "CCC.AuditLog.CN01.AR01", "Description": "When the signature validation process is performed, then it MUST detect any modification of data.", @@ -18,13 +1706,13 @@ "Applicability": [ "tlp-red" ], - "Recommendation": "Ensure hash of data is included in digital signature. ", + "Recommendation": "Ensure hash of data is included in digital signature.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH06", - "CCC.TH07" + "CCC.Core.TH06", + "CCC.Core.TH07" ] } ], @@ -59,13 +1747,13 @@ "Applicability": [ "tlp-red" ], - "Recommendation": "Ensure verification process includes a chained hash function. ", + "Recommendation": "Ensure verification process includes a chained hash function.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH06", - "CCC.TH07" + "CCC.Core.TH06", + "CCC.Core.TH07" ] } ], @@ -106,7 +1794,7 @@ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH06" + "CCC.Core.TH06" ] } ], @@ -130,9 +1818,7 @@ ], "Checks": [ "monitor_diagnostic_settings_exists", - "monitor_diagnostic_setting_with_appropriate_categories", - "monitor_storage_account_with_activity_logs_is_private", - "monitor_storage_account_with_activity_logs_cmk_encrypted" + "monitor_diagnostic_setting_with_appropriate_categories" ] }, { @@ -149,12 +1835,12 @@ "tlp-red", "tlp-amber" ], - "Recommendation": "Ensure alerting is correctly configured ", + "Recommendation": "Ensure alerting is correctly configured", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07" + "CCC.Core.TH07" ] } ], @@ -175,12 +1861,7 @@ ] } ], - "Checks": [ - "monitor_diagnostic_settings_exists", - "keyvault_logging_enabled", - "monitor_storage_account_with_activity_logs_cmk_encrypted", - "monitor_storage_account_with_activity_logs_is_private" - ] + "Checks": [] }, { "Id": "CCC.AuditLog.CN03.AR02", @@ -196,12 +1877,12 @@ "tlp-red", "tlp-amber" ], - "Recommendation": "Ensure alerting is correctly configured ", + "Recommendation": "Ensure alerting is correctly configured", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07" + "CCC.Core.TH07" ] } ], @@ -222,22 +1903,7 @@ ] } ], - "Checks": [ - "monitor_diagnostic_settings_exists", - "monitor_diagnostic_setting_with_appropriate_categories", - "monitor_storage_account_with_activity_logs_is_private", - "monitor_storage_account_with_activity_logs_cmk_encrypted", - "monitor_alert_service_health_exists", - "monitor_alert_create_update_sqlserver_fr", - "monitor_alert_delete_nsg", - "monitor_alert_delete_public_ip_address_rule", - "monitor_alert_delete_security_solution", - "monitor_alert_create_update_nsg", - "monitor_alert_create_update_public_ip_address_rule", - "monitor_alert_create_update_security_solution", - "monitor_alert_create_policy_assignment", - "monitor_alert_delete_policy_assignment" - ] + "Checks": [] }, { "Id": "CCC.AuditLog.CN04.AR01", @@ -253,13 +1919,13 @@ "tlp-red", "tlp-amber" ], - "Recommendation": "Configure the audit log bucket to enable server access logging. Ensure the target logging bucket is configured for appropriate security, including restricted access and immutability. ", + "Recommendation": "Configure the audit log bucket to enable server access logging. Ensure the target logging bucket is configured for appropriate security, including restricted access and immutability.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01", - "CCC.TH09" + "CCC.Core.TH01", + "CCC.Core.TH09" ] } ], @@ -281,11 +1947,7 @@ } ], "Checks": [ - "monitor_diagnostic_settings_exists", - "monitor_diagnostic_setting_with_appropriate_categories", - "monitor_storage_account_with_activity_logs_cmk_encrypted", - "monitor_storage_account_with_activity_logs_is_private", - "storage_blob_public_access_level_is_disabled" + "monitor_diagnostic_settings_exists" ] }, { @@ -302,12 +1964,12 @@ "tlp-red", "tlp-amber" ], - "Recommendation": "Configure audit log exporting. ", + "Recommendation": "Configure audit log exporting.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07" + "CCC.Core.TH07" ] } ], @@ -331,9 +1993,7 @@ ], "Checks": [ "monitor_diagnostic_settings_exists", - "monitor_diagnostic_setting_with_appropriate_categories", - "monitor_storage_account_with_activity_logs_is_private", - "monitor_storage_account_with_activity_logs_cmk_encrypted" + "monitor_diagnostic_setting_with_appropriate_categories" ] }, { @@ -351,13 +2011,13 @@ "tlp-amber", "tlp-green" ], - "Recommendation": "Configure the audit log bucket's lifecycle rules or object retention settings to enforce the required data retention period. ", + "Recommendation": "Configure the audit log bucket's lifecycle rules or object retention settings to enforce the required data retention period.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH06", - "CCC.TH07" + "CCC.Core.TH06", + "CCC.Core.TH07" ] } ], @@ -378,10 +2038,7 @@ ] } ], - "Checks": [ - "storage_ensure_soft_delete_is_enabled", - "monitor_diagnostic_settings_exists" - ] + "Checks": [] }, { "Id": "CCC.AuditLog.CN07.AR01", @@ -398,13 +2055,13 @@ "tlp-amber", "tlp-green" ], - "Recommendation": "Enable MFA Delete (or equivalent multi-factor authentication for delete operations) on the audit log bucket. ", + "Recommendation": "Enable MFA Delete (or equivalent multi-factor authentication for delete operations) on the audit log bucket.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH06", - "CCC.TH07" + "CCC.Core.TH06", + "CCC.Core.TH07" ] } ], @@ -441,12 +2098,12 @@ "tlp-red", "tlp-amber" ], - "Recommendation": "Configure object lock policy. ", + "Recommendation": "Configure object lock policy.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07" + "CCC.Core.TH07" ] } ], @@ -467,12 +2124,7 @@ ] } ], - "Checks": [ - "storage_ensure_soft_delete_is_enabled", - "monitor_diagnostic_settings_exists", - "monitor_storage_account_with_activity_logs_cmk_encrypted", - "monitor_storage_account_with_activity_logs_is_private" - ] + "Checks": [] }, { "Id": "CCC.AuditLog.CN09.AR01", @@ -488,12 +2140,12 @@ "tlp-red", "tlp-amber" ], - "Recommendation": "Review field level access controls on audit data. ", + "Recommendation": "Review field level access controls on audit data.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07" + "CCC.Core.TH07" ] } ], @@ -519,9 +2171,7 @@ } ], "Checks": [ - "monitor_storage_account_with_activity_logs_cmk_encrypted", - "monitor_storage_account_with_activity_logs_is_private", - "monitor_diagnostic_settings_exists" + "monitor_storage_account_with_activity_logs_is_private" ] }, { @@ -539,12 +2189,12 @@ "tlp-amber", "tlp-green" ], - "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted. ", + "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01" + "CCC.Core.TH01" ] } ], @@ -568,7 +2218,6 @@ "Checks": [ "storage_blob_public_access_level_is_disabled", "monitor_storage_account_with_activity_logs_is_private", - "monitor_storage_account_with_activity_logs_cmk_encrypted", "storage_ensure_private_endpoints_in_storage_accounts" ] }, @@ -587,12 +2236,12 @@ "tlp-amber", "tlp-green" ], - "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted. ", + "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01" + "CCC.Core.TH01" ] } ], @@ -616,944 +2265,16 @@ "Checks": [ "storage_blob_public_access_level_is_disabled", "storage_default_network_access_rule_is_denied", - "storage_ensure_private_endpoints_in_storage_accounts", - "monitor_storage_account_with_activity_logs_cmk_encrypted", - "monitor_storage_account_with_activity_logs_is_private" + "storage_ensure_private_endpoints_in_storage_accounts" ] }, - { - "Id": "CCC.Build.CN01.AR01", - "Description": "Attempt to initiate a build using an unauthorized build agent and verify that the build is rejected.", - "Attributes": [ - { - "FamilyName": "Access Control", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.Build.CN01 Restrict Allowed Build Agents", - "SubSection": "", - "SubSectionObjective": "Ensure that builds are executed only on authorized build agents to maintain control over the build environment and prevent unauthorized code execution.", - "Applicability": [ - "tlp-red", - "tlp-amber" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3", - "AC-6" - ] - } - ] - } - ], - "Checks": [] - }, - { - "Id": "CCC.Build.CN02.AR01", - "Description": "Attempt to trigger a build from an unauthorized external service or repository and verify that the build does not start.", - "Attributes": [ - { - "FamilyName": "Access Control", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.Build.CN02 Restrict Allowed External Services for Build Triggers", - "SubSection": "", - "SubSectionObjective": "Ensure that builds can only be triggered by authorized external services or repositories to prevent unauthorized code execution or tampering.", - "Applicability": [ - "tlp-red", - "tlp-amber" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3", - "AC-6" - ] - } - ] - } - ], - "Checks": [] - }, - { - "Id": "CCC.Build.CN03.AR01", - "Description": "Attempt to access the build environment from an external network and verify that access is denied.", - "Attributes": [ - { - "FamilyName": "Network Security", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.Build.CN03 Deny External Network Access for Build Environments", - "SubSection": "", - "SubSectionObjective": "Ensure that build environments do not have external network access to prevent unauthorized external access and data exfiltration.", - "Applicability": [ - "tlp-red", - "tlp-amber" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH02", - "CCC.TH05" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-5" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-7", - "SC-5" - ] - } - ] - } - ], - "Checks": [ - "aks_clusters_public_access_disabled", - "containerregistry_not_publicly_accessible", - "containerregistry_uses_private_link", - "app_function_not_publicly_accessible", - "network_http_internet_access_restricted", - "network_rdp_internet_access_restricted", - "network_ssh_internet_access_restricted", - "network_udp_internet_access_restricted" - ] - }, - { - "Id": "CCC.CntrReg.CN01.AR01", - "Description": "Attempt to push an artifact with known vulnerabilities to the registry and observe if it is flagged or rejected by the vulnerability scanning process.", - "Attributes": [ - { - "FamilyName": "Risk Management", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CntrReg.CN01 Implement Vulnerability Scanning for Artifacts", - "SubSection": "", - "SubSectionObjective": "Ensure that container images and artifacts stored in the container registry are scanned for vulnerabilities to identify and remediate security issues before deployment.", - "Applicability": [ - "tlp-red", - "tlp-amber" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.CntrReg.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "ID.RA-1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "RA-5", - "SI-5" - ] - } - ] - } - ], - "Checks": [ - "defender_container_images_scan_enabled", - "defender_container_images_resolved_vulnerabilities" - ] - }, - { - "Id": "CCC.DataWar.CN01.AR01", - "Description": "Attempt to access underlying database tables directly without using managed views and verify that access is denied.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.DataWar.CN01 Enforce Use of Managed Views for Data Access", - "SubSection": "", - "SubSectionObjective": "Ensure that data access is provided through managed views, restricting users from accessing underlying tables directly and enforcing consistent security policies.", - "Applicability": [ - "tlp-red", - "tlp-amber" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3", - "AC-6" - ] - } - ] - } - ], - "Checks": [] - }, - { - "Id": "CCC.DataWar.CN02.AR01", - "Description": "Attempt to query sensitive columns without the necessary permissions and verify that access is denied or data is masked.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.DataWar.CN02 Enforce Column-Level Security Policies", - "SubSection": "", - "SubSectionObjective": "Ensure that access to sensitive data columns is restricted based on user roles, preventing unauthorized access to sensitive information.", - "Applicability": [ - "tlp-red", - "tlp-amber" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3", - "AC-6" - ] - } - ] - } - ], - "Checks": [] - }, - { - "Id": "CCC.DataWar.CN03.AR01", - "Description": "Attempt to query data rows that the user should not have access to and verify that access is denied or data is not returned.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.DataWar.CN03 Enforce Row-Level Security Policies", - "SubSection": "", - "SubSectionObjective": "Ensure that access to data rows is restricted based on user roles or attributes, preventing unauthorized access to specific subsets of data.", - "Applicability": [ - "tlp-red", - "tlp-amber" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3", - "AC-6" - ] - } - ] - } - ], - "Checks": [ - "cosmosdb_account_use_aad_and_rbac", - "cosmosdb_account_firewall_use_selected_networks", - "cosmosdb_account_use_private_endpoints", - "sqlserver_unrestricted_inbound_access", - "postgresql_flexible_server_allow_access_services_disabled" - ] - }, - { - "Id": "CCC.KeyMgmt.CN01.AR01", - "Description": "When a key version is scheduled for deletion or disabled, an alert MUST be generated within five minutes.", - "Attributes": [ - { - "FamilyName": "Logging and Metrics Publication", - "FamilyDescription": "Controls that collect, alert, and retain key-management events.", - "Section": "CCC.KeyMgmt.CN01 Alert on Key-version Changes", - "SubSection": "", - "SubSectionObjective": "Generate near-real-time alerts when a KMS key version is disabled or scheduled for deletion, enabling rapid investigation and recovery.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Use native event services (e.g., CloudWatch Events, Azure Monitor, Cloud Audit Logs) to route notifications to an incident-response channel.", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.KeyMgmt.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "RS.AN-1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "IR-5" - ] - } - ] - } - ], - "Checks": [ - "keyvault_logging_enabled", - "monitor_diagnostic_settings_exists", - "monitor_alert_create_update_nsg", - "monitor_alert_create_update_public_ip_address_rule", - "monitor_alert_create_update_security_solution", - "monitor_alert_create_policy_assignment", - "monitor_alert_service_health_exists" - ] - }, - { - "Id": "CCC.KeyMgmt.CN02.AR01", - "Description": "When IAM roles and key policies are reviewed, Decrypt permission MUST be granted exclusively to documented authorised principals.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls that enforce least-privilege use of KMS operations.", - "Section": "CCC.KeyMgmt.CN02 Limit Decrypt Permissions", - "SubSection": "", - "SubSectionObjective": "Restrict the Decrypt operation to authorised principals only, applying the principle of least privilege to protect sensitive data.", - "Applicability": [ - "tlp-green" - ], - "Recommendation": "Periodically audit policy documents via automated tooling and report any deviations.", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.KeyMgmt.TH02" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-6" - ] - } - ] - } - ], - "Checks": [ - "keyvault_rbac_enabled", - "keyvault_rbac_key_expiration_set", - "keyvault_rbac_secret_expiration_set", - "keyvault_key_expiration_set_in_non_rbac" - ] - }, - { - "Id": "CCC.KeyMgmt.CN03.AR01", - "Description": "When rotation settings are examined, rotation MUST be enabled with an interval not exceeding 365 days.", - "Attributes": [ - { - "FamilyName": "Key Lifecycle Management", - "FamilyDescription": "Controls that govern creation, rotation, import, and retirement of cryptographic keys.", - "Section": "CCC.KeyMgmt.CN03 Enforce Automatic Rotation", - "SubSection": "", - "SubSectionObjective": "Ensure symmetric keys rotate automatically within policy intervals to reduce exposure of key material.", - "Applicability": [ - "tlp-green" - ], - "Recommendation": "Use cloud-provider rotation features and verify via configuration scanning.", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.KeyMgmt.TH03" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-12" - ] - } - ] - } - ], - "Checks": [ - "keyvault_key_rotation_enabled" - ] - }, - { - "Id": "CCC.KeyMgmt.CN04.AR01", - "Description": "When a key import request is processed, the key MUST use an approved algorithm (RSA-2048+, EC-P256+) and originate from a certified HSM.", - "Attributes": [ - { - "FamilyName": "Key Lifecycle Management", - "FamilyDescription": "Controls that govern creation, rotation, import, and retirement of cryptographic keys.", - "Section": "CCC.KeyMgmt.CN04 Validate Imported Keys", - "SubSection": "", - "SubSectionObjective": "Accept only externally generated keys that meet approved cryptographic strength and provenance requirements.", - "Applicability": [ - "tlp-green" - ], - "Recommendation": "Implement an approval workflow that validates attestation data before import.", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.KeyMgmt.TH04" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28" - ] - } - ] - } - ], - "Checks": [ - "keyvault_key_rotation_enabled", - "keyvault_rbac_enabled", - "keyvault_key_expiration_set_in_non_rbac", - "keyvault_rbac_key_expiration_set", - "keyvault_rbac_secret_expiration_set", - "keyvault_private_endpoints", - "keyvault_access_only_through_private_endpoints", - "keyvault_recoverable", - "keyvault_logging_enabled", - "keyvault_non_rbac_secret_expiration_set" - ] - }, - { - "Id": "CCC.LB.CN01.AR01", - "Description": "When a single client sends more than 2000 requests within any 5-minute sliding window, the load balancer MUST throttle all subsequent requests from that client for at least 60 seconds.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "Controls that detect anomalous traffic and record load-balancer activity. ", - "Section": "CCC.LB.CN01 Enforce and Detect Rate Limiting", - "SubSection": "", - "SubSectionObjective": "Detect and throttle malicious or excessive requests to prevent downstream resource exhaustion and brute-force activity.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Implement per-IP token-bucket limits with and verify via synthetic traffic tests. ", - "SectionThreatMappings": [ - { - "ReferenceId": "LB", - "Identifiers": [ - "CCC.LB.TH01", - "CCC.LB.TH09" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.CM-1", - "PR.AC-7", - "PR.PT-4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-6", - "SC-5", - "AC-7" - ] - } - ] - } - ], - "Checks": [] - }, - { - "Id": "CCC.LB.CN01.AR02", - "Description": "When throttling is invoked, the load balancer MUST record the event in the access log within 5 minutes for alerting and trend analysis.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "Controls that detect anomalous traffic and record load-balancer activity. ", - "Section": "CCC.LB.CN01 Enforce and Detect Rate Limiting", - "SubSection": "", - "SubSectionObjective": "Detect and throttle malicious or excessive requests to prevent downstream resource exhaustion and brute-force activity.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Enable access logging and configure metric filters on HTTP 429 counts to trigger alerts. ", - "SectionThreatMappings": [ - { - "ReferenceId": "LB", - "Identifiers": [ - "CCC.LB.TH01", - "CCC.LB.TH09" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.CM-1", - "PR.AC-7", - "PR.PT-4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-6", - "SC-5", - "AC-7" - ] - } - ] - } - ], - "Checks": [ - "monitor_diagnostic_settings_exists", - "monitor_diagnostic_setting_with_appropriate_categories", - "network_flow_log_captured_sent", - "network_watcher_enabled", - "monitor_storage_account_with_activity_logs_is_private", - "monitor_storage_account_with_activity_logs_cmk_encrypted" - ] - }, - { - "Id": "CCC.LB.CN06.AR01", - "Description": "When more than 10 percent of targets change from healthy to unhealthy within five minutes, an alert MUST be issued.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "Controls that detect anomalous traffic and record load-balancer activity. ", - "Section": "CCC.LB.CN06 Secure Health-Check Telemetry", - "SubSection": "", - "SubSectionObjective": "Monitor health-check endpoints for tampering and alert on abnormal status changes.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Instrument metrics for health check results and target removal events. Configure monitoring alarms to alert on abnormal spikes in unhealthy targets. ", - "SectionThreatMappings": [ - { - "ReferenceId": "LB", - "Identifiers": [ - "CCC.LB.TH05" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.AE-2" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SI-4" - ] - } - ] - } - ], - "Checks": [ - "monitor_diagnostic_settings_exists", - "monitor_diagnostic_setting_with_appropriate_categories", - "monitor_alert_service_health_exists", - "monitor_alert_create_update_nsg", - "monitor_alert_create_update_public_ip_address_rule", - "monitor_alert_create_update_security_solution", - "monitor_alert_create_policy_assignment", - "network_flow_log_captured_sent", - "network_watcher_enabled", - "vm_scaleset_associated_with_load_balancer" - ] - }, - { - "Id": "CCC.LB.CN04.AR01", - "Description": "When routing weights change, the request MUST originate from an explicitly defined and trusted identity and MUST be logged.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls that restrict who can change or query load-balancer resources. ", - "Section": "CCC.LB.CN04 Enforce Distribution Policies", - "SubSection": "", - "SubSectionObjective": "Ensure traffic-splitting weights and algorithms are modified only by trusted identities.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Define a list of trusted principals allowed to modify routing configurations. Enforce via conditional access policies, and log changes using audit logging. ", - "SectionThreatMappings": [ - { - "ReferenceId": "LB", - "Identifiers": [ - "CCC.LB.TH03" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "monitor_diagnostic_settings_exists", - "monitor_alert_create_update_public_ip_address_rule", - "monitor_alert_create_update_nsg", - "monitor_alert_create_policy_assignment", - "monitor_alert_create_update_security_solution", - "entra_global_admin_in_less_than_five_users", - "entra_non_privileged_user_has_mfa", - "iam_role_user_access_admin_restricted", - "iam_custom_role_has_permissions_to_administer_resource_locks" - ] - }, - { - "Id": "CCC.LB.CN05.AR01", - "Description": "When stickiness is enabled, session cookies MUST expire within 30 minutes of inactivity.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls that restrict who can change or query load-balancer resources. ", - "Section": "CCC.LB.CN05 Validate Session Affinity", - "SubSection": "", - "SubSectionObjective": "Configure session persistence to minimise fixation and hijacking risks.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Audit CCC.LB.F15 parameters via configuration scans.", - "SectionThreatMappings": [ - { - "ReferenceId": "LB", - "Identifiers": [ - "CCC.LB.TH04" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-7" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-23" - ] - } - ] - } - ], - "Checks": [ - "iam_role_user_access_admin_restricted", - "iam_subscription_roles_owner_custom_not_created", - "iam_custom_role_has_permissions_to_administer_resource_locks" - ] - }, - { - "Id": "CCC.LB.CN09.AR01", - "Description": "When an API call originates outside the approved CIDR set, the request MUST be denied.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls that restrict who can change or query load-balancer resources. ", - "Section": "CCC.LB.CN09 Restrict Management API Access", - "SubSection": "", - "SubSectionObjective": "Limit load-balancer API calls to authorised identities and trusted networks.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Combine VPC endpoints with IAM condition-key filters for protected APIs.", - "SectionThreatMappings": [ - { - "ReferenceId": "LB", - "Identifiers": [ - "CCC.LB.TH08" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-5" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-7" - ] - } - ] - } - ], - "Checks": [ - "entra_conditional_access_policy_require_mfa_for_management_api", - "entra_global_admin_in_less_than_five_users", - "entra_privileged_user_has_mfa", - "iam_role_user_access_admin_restricted", - "iam_custom_role_has_permissions_to_administer_resource_locks" - ] - }, - { - "Id": "CCC.LB.CN02.AR01", - "Description": "When concurrent connections reach 80 percent of capacity, the autoscaling group MUST add at least one instance within five minutes.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "Controls that preserve availability and confidentiality of traffic processed by the load balancer. ", - "Section": "CCC.LB.CN02 Auto-Scale Load Balancer Capacity", - "SubSection": "", - "SubSectionObjective": "Expand load-balancer capacity to maintain availability during traffic spikes.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Enable autoscaling policies.", - "SectionThreatMappings": [ - { - "ReferenceId": "LB", - "Identifiers": [ - "CCC.LB.TH09" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "ID.BE-5" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "CP-10" - ] - } - ] - } - ], - "Checks": [] - }, - { - "Id": "CCC.LB.CN07.AR01", - "Description": "When responses pass through the load balancer, the \"Server\" header MUST be replaced with \"lb\".", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "Controls that preserve availability and confidentiality of traffic processed by the load balancer. ", - "Section": "CCC.LB.CN07 Scrub Sensitive Headers", - "SubSection": "", - "SubSectionObjective": "Remove headers that disclose internal details or software versions from HTTP responses.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Configure header-transformation rules.", - "SectionThreatMappings": [ - { - "ReferenceId": "LB", - "Identifiers": [ - "CCC.TH15" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-2" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-13" - ] - } - ] - } - ], - "Checks": [] - }, - { - "Id": "CCC.LB.CN08.AR01", - "Description": "When a certificate is within 30 days of expiry, automated renewal MUST complete and deploy a new certificate within 24 hours.", - "Attributes": [ - { - "FamilyName": "Encryption", - "FamilyDescription": "Controls that ensure trustworthy TLS certificates and ciphers.", - "Section": "CCC.LB.CN08 Automate Certificate Renewal", - "SubSection": "", - "SubSectionObjective": "Maintain valid TLS certificates by automating renewal and deployment before expiry.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Use certificate-manager auto-renewal workflows.", - "SectionThreatMappings": [ - { - "ReferenceId": "LB", - "Identifiers": [ - "CCC.LB.TH07" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-6" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-17" - ] - } - ] - } - ], - "Checks": [] - }, { "Id": "CCC.Logging.CN01.AR01", "Description": "When a new cloud account is created, provider-level audit and network flow logging MUST be enabled by default and directed to the central sink.", "Attributes": [ { "FamilyName": "Data", - "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data. ", + "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data.", "Section": "CCC.Logging.CN01 Centralized and Comprehensive Log Aggregation", "SubSection": "", "SubSectionObjective": "Ensure all operational and security logs from across the cloud environment, including applications, operating systems, network traffic, and cloud service activity, are captured automatically and streamed to a central, secure log management service.", @@ -1591,10 +2312,7 @@ ], "Checks": [ "monitor_diagnostic_settings_exists", - "monitor_diagnostic_setting_with_appropriate_categories", - "network_flow_log_captured_sent", - "app_http_logs_enabled", - "appinsights_ensure_is_configured" + "network_flow_log_captured_sent" ] }, { @@ -1603,7 +2321,7 @@ "Attributes": [ { "FamilyName": "Data", - "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data. ", + "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data.", "Section": "CCC.Logging.CN01 Centralized and Comprehensive Log Aggregation", "SubSection": "", "SubSectionObjective": "Ensure all operational and security logs from across the cloud environment, including applications, operating systems, network traffic, and cloud service activity, are captured automatically and streamed to a central, secure log management service.", @@ -1643,8 +2361,7 @@ "monitor_diagnostic_settings_exists", "monitor_diagnostic_setting_with_appropriate_categories", "app_http_logs_enabled", - "keyvault_logging_enabled", - "network_flow_log_captured_sent" + "keyvault_logging_enabled" ] }, { @@ -1653,52 +2370,7 @@ "Attributes": [ { "FamilyName": "Data", - "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data. ", - "Section": "CCC.Logging.CN02 Enforce Data Retention Policy for Logs", - "SubSection": "", - "SubSectionObjective": "Ensure that the retention period configured for logs aligns with the organization's data retention policy.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Logging.TH05" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "GV.PO-01" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-11" - ] - } - ] - } - ], - "Checks": [ - "network_flow_log_more_than_90_days" - ] - }, - { - "Id": "CCC.Logging.CN02.AR02", - "Description": "When a query is performed to retrieve log events older than the number of days defined in the organisation's data retention policy, it MUST return an empty result.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data. ", + "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data.", "Section": "CCC.Logging.CN02 Enforce Data Retention Policy for Logs", "SubSection": "", "SubSectionObjective": "Ensure that the retention period configured for logs aligns with the organization's data retention policy.", @@ -1740,12 +2412,59 @@ ] }, { - "Id": "CCC.AuditLog.CN08.AR01", + "Id": "CCC.Logging.CN02.AR02", + "Description": "When a query is performed to retrieve log events older than the number of days defined in the organisation's data retention policy, it MUST return an empty result.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data.", + "Section": "CCC.Logging.CN02 Enforce Data Retention Policy for Logs", + "SubSection": "", + "SubSectionObjective": "Ensure that the retention period configured for logs aligns with the organization's data retention policy.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Logging.TH05" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "GV.PO-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AU-11" + ] + } + ] + } + ], + "Checks": [ + "network_flow_log_more_than_90_days", + "sqlserver_auditing_retention_90_days", + "postgresql_flexible_server_log_retention_days_greater_3" + ] + }, + { + "Id": "CCC.Logging.CN03.AR01", "Description": "When an attempt is made to modify or delete data before the object lock period expires, then the action MUST be denied.", "Attributes": [ { "FamilyName": "Data", - "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data. ", + "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data.", "Section": "CCC.Logging.CN03 Enable Object Lock On Log Bucket", "SubSection": "", "SubSectionObjective": "Ensure log immutability by enabling Write Once, Read Many (WORM) protection using object lock on log storage buckets. This prevents logs from being modified or deleted during the defined retention period, supporting compliance and forensic integrity.", @@ -1753,12 +2472,12 @@ "tlp-red", "tlp-amber" ], - "Recommendation": "Configure object lock policy. ", + "Recommendation": "Configure object lock policy.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07" + "CCC.Core.TH07" ] } ], @@ -1782,12 +2501,12 @@ "Checks": [] }, { - "Id": "CCC.AuditLog.CN04.AR01", + "Id": "CCC.Logging.CN04.AR01", "Description": "When restricted fields are accessed by unauthorized users, then those fields MUST remain masked.", "Attributes": [ { "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls that restrict who can access and modify logs. ", + "FamilyDescription": "Controls that restrict who can access and modify logs.", "Section": "CCC.Logging.CN04 Restrict Field And Log Type Access", "SubSection": "", "SubSectionObjective": "Configure access to logs to follow the principle of least privilege in particular where technically possible limit the log fields users have access to to prevent accidental exposure to sensitive information such as PII.", @@ -1795,7 +2514,7 @@ "tlp-red", "tlp-amber" ], - "Recommendation": "Review field level access controls on log data. ", + "Recommendation": "Review field level access controls on log data.", "SectionThreatMappings": [ { "ReferenceId": "CCC", @@ -1826,10 +2545,7 @@ } ], "Checks": [ - "monitor_diagnostic_settings_exists", - "keyvault_logging_enabled", - "monitor_storage_account_with_activity_logs_is_private", - "monitor_storage_account_with_activity_logs_cmk_encrypted" + "monitor_storage_account_with_activity_logs_is_private" ] }, { @@ -1838,7 +2554,7 @@ "Attributes": [ { "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls that restrict who can access and modify logs. ", + "FamilyDescription": "Controls that restrict who can access and modify logs.", "Section": "CCC.Logging.CN05 Ensure Log Bucket is Not Publicly Accessible", "SubSection": "", "SubSectionObjective": "Ensure that log storage buckets are not publicly accessible to prevent unauthorized access to sensitive log data. In addition, logs should be replicated to another cloud region to enhance availability, durability, and support disaster recovery requirements.", @@ -1847,12 +2563,12 @@ "tlp-amber", "tlp-green" ], - "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted. ", + "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01" + "CCC.Core.TH01" ] } ], @@ -1875,8 +2591,6 @@ ], "Checks": [ "monitor_storage_account_with_activity_logs_is_private", - "monitor_storage_account_with_activity_logs_cmk_encrypted", - "monitor_diagnostic_settings_exists", "storage_blob_public_access_level_is_disabled" ] }, @@ -1886,7 +2600,7 @@ "Attributes": [ { "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls that restrict who can access and modify logs. ", + "FamilyDescription": "Controls that restrict who can access and modify logs.", "Section": "CCC.Logging.CN05 Ensure Log Bucket is Not Publicly Accessible", "SubSection": "", "SubSectionObjective": "Ensure that log storage buckets are not publicly accessible to prevent unauthorized access to sensitive log data. In addition, logs should be replicated to another cloud region to enhance availability, durability, and support disaster recovery requirements.", @@ -1895,12 +2609,12 @@ "tlp-amber", "tlp-green" ], - "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted. ", + "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01" + "CCC.Core.TH01" ] } ], @@ -1923,10 +2637,7 @@ ], "Checks": [ "storage_blob_public_access_level_is_disabled", - "monitor_storage_account_with_activity_logs_is_private", - "monitor_storage_account_with_activity_logs_cmk_encrypted", - "monitor_diagnostic_settings_exists", - "storage_geo_redundant_enabled" + "monitor_storage_account_with_activity_logs_is_private" ] }, { @@ -1935,7 +2646,7 @@ "Attributes": [ { "FamilyName": "Logging and Monitoring", - "FamilyDescription": "Controls that collect, alert, and retain logging-related events. ", + "FamilyDescription": "Controls that collect, alert, and retain logging-related events.", "Section": "CCC.Logging.CN06 Detect and Alert on Potential Log Exfiltration", "SubSection": "", "SubSectionObjective": "Identify and alert on anomalous data access patterns that may indicate an attempt to exfiltrate log data.", @@ -1972,9 +2683,7 @@ ] } ], - "Checks": [ - "apim_threat_detection_llm_jacking" - ] + "Checks": [] }, { "Id": "CCC.Logging.CN07.AR01", @@ -1982,7 +2691,7 @@ "Attributes": [ { "FamilyName": "Logging and Monitoring", - "FamilyDescription": "Controls that collect, alert, and retain logging-related events. ", + "FamilyDescription": "Controls that collect, alert, and retain logging-related events.", "Section": "CCC.Logging.CN07 Detect and Alert on Log Service Tampering", "SubSection": "", "SubSectionObjective": "Alert when any component of the critical logging infrastructure is disabled, modified, or deleted, indicating a defense evasion attempt.", @@ -1997,7 +2706,7 @@ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH16" + "CCC.Core.TH16" ] } ], @@ -2020,921 +2729,14 @@ ] } ], - "Checks": [ - "monitor_diagnostic_settings_exists", - "monitor_alert_create_update_nsg", - "monitor_alert_delete_nsg", - "monitor_alert_create_update_public_ip_address_rule", - "monitor_alert_delete_public_ip_address_rule", - "monitor_alert_create_update_security_solution", - "monitor_alert_delete_security_solution", - "monitor_alert_create_policy_assignment", - "monitor_alert_delete_policy_assignment", - "monitor_alert_service_health_exists", - "monitor_alert_create_update_sqlserver_fr", - "monitor_alert_delete_sqlserver_fr" - ] - }, - { - "Id": "CCC.ObjStor.CN01.AR01", - "Description": "When a request is made to read a protected bucket, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN01 Prevent Unencrypted Requests", - "SubSection": "CCC.ObjStor.C01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", - "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption that can impact data availability and integrity.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01", - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DCS-04", - "DCS-06" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.10.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28" - ] - } - ] - } - ], - "Checks": [ - "storage_ensure_encryption_with_customer_managed_keys", - "databricks_workspace_cmk_encryption_enabled", - "vm_ensure_attached_disks_encrypted_with_cmk", - "vm_ensure_unattached_disks_encrypted_with_cmk" - ] - }, - { - "Id": "CCC.ObjStor.CN01.AR02", - "Description": "When a request is made to read a protected object, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN01 Prevent Unencrypted Requests", - "SubSection": "CCC.ObjStor.C01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", - "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption that can impact data availability and integrity.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01", - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DCS-04", - "DCS-06" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.10.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28" - ] - } - ] - } - ], - "Checks": [ - "storage_ensure_encryption_with_customer_managed_keys", - "monitor_storage_account_with_activity_logs_cmk_encrypted", - "keyvault_rbac_enabled", - "keyvault_private_endpoints", - "keyvault_rbac_key_expiration_set", - "keyvault_rbac_secret_expiration_set" - ] - }, - { - "Id": "CCC.ObjStor.CN01.AR03", - "Description": "When a request is made to write to a bucket, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN01 Prevent Unencrypted Requests", - "SubSection": "CCC.ObjStor.C01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", - "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption that can impact data availability and integrity.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01", - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DCS-04", - "DCS-06" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.10.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28" - ] - } - ] - } - ], - "Checks": [ - "storage_ensure_encryption_with_customer_managed_keys", - "storage_ensure_private_endpoints_in_storage_accounts", - "keyvault_rbac_enabled", - "keyvault_private_endpoints", - "keyvault_access_only_through_private_endpoints" - ] - }, - { - "Id": "CCC.ObjStor.CN01.AR04", - "Description": "When a request is made to write to an object, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN01 Prevent Unencrypted Requests", - "SubSection": "CCC.ObjStor.C01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", - "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption that can impact data availability and integrity.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01", - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DCS-04", - "DCS-06" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.10.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28" - ] - } - ] - } - ], - "Checks": [ - "storage_ensure_encryption_with_customer_managed_keys", - "databricks_workspace_cmk_encryption_enabled", - "monitor_storage_account_with_activity_logs_cmk_encrypted" - ] - }, - { - "Id": "CCC.ObjStor.CN03.AR01", - "Description": "When an object storage bucket deletion is attempted, the bucket MUST be fully recoverable for a set time-frame after deletion is requested.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN03 Implement Multi-factor Authentication (MFA) for Access", - "SubSection": "CCC.ObjStor.C03 Prevent Bucket Deletion Through Irrevocable Bucket Retention Policy", - "SubSectionObjective": "Ensure that object storage bucket is not deleted after creation, and that the preventative measure cannot be unset.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - } - ] - } - ], - "Checks": [ - "storage_blob_versioning_is_enabled", - "storage_ensure_soft_delete_is_enabled" - ] - }, - { - "Id": "CCC.ObjStor.CN03.AR02", - "Description": "When an attempt is made to modify the retention policy for an object storage bucket, the service MUST prevent the policy from being modified.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN03 Implement Multi-factor Authentication (MFA) for Access", - "SubSection": "CCC.ObjStor.C03 Prevent Bucket Deletion Through Irrevocable Bucket Retention Policy", - "SubSectionObjective": "Ensure that object storage bucket is not deleted after creation, and that the preventative measure cannot be unset.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - } - ] - } - ], "Checks": [] }, - { - "Id": "CCC.ObjStor.CN04.AR01", - "Description": "When an object is uploaded to the object storage system, the object MUST automatically receive a default retention policy that prevents premature deletion or modification.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN04 Log All Access and Changes", - "SubSection": "CCC.ObjStor.C04 Objects have an Effective Retention Policy by Default", - "SubSectionObjective": "Ensure that all objects stored in the object storage system have a retention policy applied by default, preventing premature deletion or modification of objects and ensuring compliance with data retention regulations.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - } - ] - } - ], - "Checks": [ - "storage_blob_versioning_is_enabled", - "storage_ensure_soft_delete_is_enabled", - "storage_ensure_file_shares_soft_delete_is_enabled" - ] - }, - { - "Id": "CCC.ObjStor.CN04.AR02", - "Description": "When an attempt is made to delete or modify an object that is subject to an active retention policy, the service MUST prevent the action from being completed.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN04 Log All Access and Changes", - "SubSection": "CCC.ObjStor.C04 Objects have an Effective Retention Policy by Default", - "SubSectionObjective": "Ensure that all objects stored in the object storage system have a retention policy applied by default, preventing premature deletion or modification of objects and ensuring compliance with data retention regulations.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - } - ] - } - ], - "Checks": [ - "storage_ensure_soft_delete_is_enabled", - "storage_ensure_file_shares_soft_delete_is_enabled" - ] - }, - { - "Id": "CCC.ObjStor.CN05.AR01", - "Description": "When an object is uploaded to the object storage bucket, the object MUST be stored with a unique identifier.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN05 Prevent Access from Untrusted Entities", - "SubSection": "CCC.ObjStor.C05 Versioning is Enabled for All Objects in the Bucket", - "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - } - ] - } - ], - "Checks": [ - "storage_blob_versioning_is_enabled" - ] - }, - { - "Id": "CCC.ObjStor.CN05.AR02", - "Description": "When an object is modified, the service MUST assign a new unique identifier to the modified object to differentiate it from the previous version.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN05 Prevent Access from Untrusted Entities", - "SubSection": "CCC.ObjStor.C05 Versioning is Enabled for All Objects in the Bucket", - "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - } - ] - } - ], - "Checks": [ - "storage_blob_versioning_is_enabled" - ] - }, - { - "Id": "CCC.ObjStor.CN05.AR03", - "Description": "When an object is modified, the service MUST allow for recovery of previous versions of the object.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN05 Prevent Access from Untrusted Entities", - "SubSection": "CCC.ObjStor.C05 Versioning is Enabled for All Objects in the Bucket", - "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - } - ] - } - ], - "Checks": [ - "storage_blob_versioning_is_enabled" - ] - }, - { - "Id": "CCC.ObjStor.CN05.AR04", - "Description": "When an object is deleted, the service MUST retain other versions of the object to allow for recovery of previous versions.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN05 Prevent Access from Untrusted Entities", - "SubSection": "CCC.ObjStor.C05 Versioning is Enabled for All Objects in the Bucket", - "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - } - ] - } - ], - "Checks": [ - "storage_blob_versioning_is_enabled" - ] - }, - { - "Id": "CCC.ObjStor.CN06.AR01", - "Description": "When an object storage bucket is accessed, the service MUST store access logs in a separate data store.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN06 Prevent Deployment in Restricted Regions", - "SubSection": "CCC.ObjStor.C06 Access Logs are Stored in a Separate Data Store", - "SubSectionObjective": "Ensure that access logs for object storage buckets are stored in a separate data store to protect against unauthorized access, tampering, or deletion of logs (Logbuckets are exempt from this requirement, but must be tlp-red).", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH07", - "CCC.TH09" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-6" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-07", - "DSP-17" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.15.0" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-9", - "SC-28" - ] - } - ] - } - ], - "Checks": [ - "monitor_diagnostic_settings_exists", - "monitor_diagnostic_setting_with_appropriate_categories", - "monitor_storage_account_with_activity_logs_is_private", - "monitor_storage_account_with_activity_logs_cmk_encrypted" - ] - }, - { - "Id": "CCC.ObjStor.CN02.AR01", - "Description": "When a permission set is allowed for an object in a bucket, the service MUST allow the same permission set to access all objects in the same bucket.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN02 Ensure Data Encryption at Rest for All Stored Data", - "SubSection": "CCC.ObjStor.C02 Enforce Uniform Bucket-level Access to Prevent Inconsistent Permissions", - "SubSectionObjective": "Ensure that uniform bucket-level access is enforced across all object storage buckets. This prevents the use of ad-hoc or inconsistent object-level permissions, ensuring centralized, consistent, and secure access management in accordance with the principle of least privilege.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-4" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.9.4.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3", - "AC-6" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DCS-09" - ] - } - ] - } - ], - "Checks": [ - "storage_blob_public_access_level_is_disabled", - "storage_default_network_access_rule_is_denied", - "storage_ensure_private_endpoints_in_storage_accounts" - ] - }, - { - "Id": "CCC.ObjStor.CN02.AR02", - "Description": "When a permission set is denied for an object in a bucket, the service MUST deny the same permission set to access all objects in the same bucket.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN02 Ensure Data Encryption at Rest for All Stored Data", - "SubSection": "CCC.ObjStor.C02 Enforce Uniform Bucket-level Access to Prevent Inconsistent Permissions", - "SubSectionObjective": "Ensure that uniform bucket-level access is enforced across all object storage buckets. This prevents the use of ad-hoc or inconsistent object-level permissions, ensuring centralized, consistent, and secure access management in accordance with the principle of least privilege.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-4" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.9.4.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3", - "AC-6" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DCS-09" - ] - } - ] - } - ], - "Checks": [ - "storage_blob_public_access_level_is_disabled", - "storage_account_key_access_disabled", - "storage_default_to_entra_authorization_enabled", - "storage_ensure_private_endpoints_in_storage_accounts" - ] - }, { "Id": "CCC.Monitor.CN01.AR01", "Description": "When an External Monitoring system exceeds the anticipated rate of monitoring checks then Rate Limiting MUST be applied and an Audit Alert MUST be generated.", "Attributes": [ { - "FamilyName": "Logging & Monitoring", + "FamilyName": "Logging and Monitoring", "FamilyDescription": "Controls that collect, alert, and retain events from other monitoring services.", "Section": "CCC.Monitor.CN01 Rate Limiting on External Monitoring", "SubSection": "", @@ -2972,25 +2774,14 @@ ] } ], - "Checks": [ - "monitor_alert_create_update_sqlserver_fr", - "monitor_alert_delete_nsg", - "monitor_alert_delete_public_ip_address_rule", - "monitor_alert_delete_security_solution", - "monitor_alert_create_policy_assignment", - "monitor_alert_create_update_public_ip_address_rule", - "monitor_alert_create_update_security_solution", - "monitor_alert_create_update_nsg", - "monitor_alert_service_health_exists", - "monitor_diagnostic_settings_exists" - ] + "Checks": [] }, { "Id": "CCC.Monitor.CN02.AR01", "Description": "When an Custom or User-Defined Metric starts to flood a collector, then a rate limit MUST be applied to reduce the network impact of traffic and an alert must triggered.", "Attributes": [ { - "FamilyName": "Logging & Monitoring", + "FamilyName": "Logging and Monitoring", "FamilyDescription": "Controls that collect, alert, and retain events from other monitoring services.", "Section": "CCC.Monitor.CN02 Rate Limiting on Metric Generation", "SubSection": "", @@ -3028,9 +2819,7 @@ ] } ], - "Checks": [ - "apim_threat_detection_llm_jacking" - ] + "Checks": [] }, { "Id": "CCC.Monitor.CN03.AR01", @@ -3075,10 +2864,7 @@ ] } ], - "Checks": [ - "monitor_storage_account_with_activity_logs_is_private", - "monitor_storage_account_with_activity_logs_cmk_encrypted" - ] + "Checks": [] }, { "Id": "CCC.Monitor.CN04.AR01", @@ -3146,7 +2932,7 @@ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH10" + "CCC.Core.TH10" ] } ], @@ -3212,11 +2998,1111 @@ ], "Checks": [ "app_function_identity_is_configured", - "app_function_identity_without_admin_privileges", - "app_function_access_keys_configured", - "app_function_not_publicly_accessible", - "app_register_with_identity", - "app_ensure_auth_is_set_up" + "app_function_access_keys_configured" + ] + }, + { + "Id": "CCC.ObjStor.CN01.AR01", + "Description": "When a request is made to read a bucket, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", + "SubSection": "", + "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption, or sensitive data decryption.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-01", + "IAM-03", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "storage_ensure_encryption_with_customer_managed_keys", + "keyvault_rbac_enabled", + "keyvault_private_endpoints" + ] + }, + { + "Id": "CCC.ObjStor.CN01.AR02", + "Description": "When a request is made to read an object, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", + "SubSection": "", + "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption, or sensitive data decryption.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-01", + "IAM-03", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "storage_ensure_encryption_with_customer_managed_keys", + "keyvault_rbac_enabled", + "keyvault_private_endpoints" + ] + }, + { + "Id": "CCC.ObjStor.CN01.AR03", + "Description": "When a request is made to write to a bucket, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", + "SubSection": "", + "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption, or sensitive data decryption.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-01", + "IAM-03", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "storage_ensure_encryption_with_customer_managed_keys", + "keyvault_rbac_enabled", + "keyvault_private_endpoints" + ] + }, + { + "Id": "CCC.ObjStor.CN01.AR04", + "Description": "When a request is made to write to an object, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", + "SubSection": "", + "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption, or sensitive data decryption.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-01", + "IAM-03", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "storage_ensure_encryption_with_customer_managed_keys", + "keyvault_rbac_enabled", + "keyvault_private_endpoints" + ] + }, + { + "Id": "CCC.ObjStor.CN03.AR01", + "Description": "When an object storage bucket deletion is attempted, the bucket MUST be fully recoverable for a set time-frame after deletion is requested.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN03 Prevent Bucket Deletion Through Irrevocable Bucket Retention Policy", + "SubSection": "", + "SubSectionObjective": "Ensure that object storage bucket is not deleted after creation, and that the preventative measure cannot be unset.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "storage_ensure_soft_delete_is_enabled", + "storage_blob_versioning_is_enabled" + ] + }, + { + "Id": "CCC.ObjStor.CN03.AR02", + "Description": "When an attempt is made to modify the retention policy for an object storage bucket, the service MUST prevent the policy from being modified.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN03 Prevent Bucket Deletion Through Irrevocable Bucket Retention Policy", + "SubSection": "", + "SubSectionObjective": "Ensure that object storage bucket is not deleted after creation, and that the preventative measure cannot be unset.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "storage_ensure_soft_delete_is_enabled" + ] + }, + { + "Id": "CCC.ObjStor.CN04.AR01", + "Description": "When an object is uploaded to the object storage system, the object MUST automatically receive a default retention policy that prevents premature deletion or modification.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN04 Objects have an Effective Retention Policy by Default", + "SubSection": "", + "SubSectionObjective": "Ensure that all objects stored in the object storage system have a retention policy applied by default, preventing premature deletion or modification of objects.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + }, + { + "ReferenceId": "CCC.ObjStor", + "Identifiers": [ + "CCC.ObjStor.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "storage_ensure_soft_delete_is_enabled", + "storage_ensure_file_shares_soft_delete_is_enabled" + ] + }, + { + "Id": "CCC.ObjStor.CN04.AR02", + "Description": "When an attempt is made to delete or modify an object that is subject to an active retention policy, the service MUST prevent the action from being completed.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN04 Objects have an Effective Retention Policy by Default", + "SubSection": "", + "SubSectionObjective": "Ensure that all objects stored in the object storage system have a retention policy applied by default, preventing premature deletion or modification of objects.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + }, + { + "ReferenceId": "CCC.ObjStor", + "Identifiers": [ + "CCC.ObjStor.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "storage_ensure_soft_delete_is_enabled" + ] + }, + { + "Id": "CCC.ObjStor.CN05.AR01", + "Description": "When an object is uploaded to the object storage bucket, the object MUST be stored with a unique identifier.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN05 Versioning is Enabled for All Objects in the Bucket", + "SubSection": "", + "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "storage_blob_versioning_is_enabled" + ] + }, + { + "Id": "CCC.ObjStor.CN05.AR02", + "Description": "When an object is modified, the service MUST assign a new unique identifier to the modified object to differentiate it from the previous version.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN05 Versioning is Enabled for All Objects in the Bucket", + "SubSection": "", + "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "storage_blob_versioning_is_enabled" + ] + }, + { + "Id": "CCC.ObjStor.CN05.AR03", + "Description": "When an object is modified, the service MUST allow for recovery of previous versions of the object.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN05 Versioning is Enabled for All Objects in the Bucket", + "SubSection": "", + "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "storage_blob_versioning_is_enabled" + ] + }, + { + "Id": "CCC.ObjStor.CN05.AR04", + "Description": "When an object is deleted, the service MUST retain other versions of the object to allow for recovery of previous versions.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN05 Versioning is Enabled for All Objects in the Bucket", + "SubSection": "", + "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "storage_blob_versioning_is_enabled" + ] + }, + { + "Id": "CCC.ObjStor.CN07.AR01", + "Description": "The object storage service MUST support a configuration option that requires MFA to be successfully completed before any object deletion can be attempted, regardless of the request interface.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN07 Multi-Factor Authentication Is Required for Object Deletion", + "SubSection": "", + "SubSectionObjective": "Ensure that deletion of objects stored in the object storage system is protected by multi-factor authentication (MFA), reducing the risk of accidental, unauthorized, or compromised-credential–based data destruction.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH06", + "CCC.Core.TH17" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "IAM-12" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.ObjStor.CN07.AR02", + "Description": "When MFA deletion protection is enabled on a bucket or object namespace, the service MUST deny any deletion request from an identity that has not satisfied the MFA requirement at the time of the request.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN07 Multi-Factor Authentication Is Required for Object Deletion", + "SubSection": "", + "SubSectionObjective": "Ensure that deletion of objects stored in the object storage system is protected by multi-factor authentication (MFA), reducing the risk of accidental, unauthorized, or compromised-credential–based data destruction.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH06", + "CCC.Core.TH17" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "IAM-12" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.ObjStor.CN07.AR03", + "Description": "When an attempt is made to delete an object, the service's audit logs MUST clearly record each deletion attempt, including whether MFA was required and whether validation was met.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN07 Multi-Factor Authentication Is Required for Object Deletion", + "SubSection": "", + "SubSectionObjective": "Ensure that deletion of objects stored in the object storage system is protected by multi-factor authentication (MFA), reducing the risk of accidental, unauthorized, or compromised-credential–based data destruction.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH06", + "CCC.Core.TH17" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "IAM-12" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.ObjStor.CN02.AR01", + "Description": "When a permission set is allowed for an object in a bucket, the service MUST allow the same permission set to access all objects in the same bucket.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources.", + "Section": "CCC.ObjStor.CN02 Enforce Uniform Bucket-level Access to Prevent Inconsistent Permissions", + "SubSection": "", + "SubSectionObjective": "Ensure that uniform bucket-level access is enforced across all object storage buckets. This prevents the use of ad-hoc or inconsistent object-level permissions, ensuring centralized, consistent, and secure access management in accordance with the principle of least privilege.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-08" + ] + } + ] + } + ], + "Checks": [ + "storage_account_key_access_disabled", + "storage_default_to_entra_authorization_enabled" + ] + }, + { + "Id": "CCC.ObjStor.CN02.AR02", + "Description": "When a permission set is denied for an object in a bucket, the service MUST deny the same permission set to access all objects in the same bucket.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources.", + "Section": "CCC.ObjStor.CN02 Enforce Uniform Bucket-level Access to Prevent Inconsistent Permissions", + "SubSection": "", + "SubSectionObjective": "Ensure that uniform bucket-level access is enforced across all object storage buckets. This prevents the use of ad-hoc or inconsistent object-level permissions, ensuring centralized, consistent, and secure access management in accordance with the principle of least privilege.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-08" + ] + } + ] + } + ], + "Checks": [ + "storage_account_key_access_disabled", + "storage_default_to_entra_authorization_enabled" + ] + }, + { + "Id": "CCC.LB.CN01.AR01", + "Description": "When a single client sends more than 2000 requests within any 5-minute sliding window, the load balancer MUST throttle all subsequent requests from that client for at least 60 seconds.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that detect anomalous traffic and record load-balancer activity.", + "Section": "CCC.LB.CN01 Enforce and Detect Rate Limiting", + "SubSection": "", + "SubSectionObjective": "Detect and throttle malicious or excessive requests to prevent downstream resource exhaustion and brute-force activity.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Implement per-IP token-bucket limits with and verify via synthetic traffic tests.", + "SectionThreatMappings": [ + { + "ReferenceId": "LB", + "Identifiers": [ + "CCC.LB.TH01", + "CCC.LB.TH09" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "DE.CM-1", + "PR.AC-7", + "PR.PT-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AU-6", + "SC-5", + "AC-7" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.LB.CN01.AR02", + "Description": "When throttling is invoked, the load balancer MUST record the event in the access log within 5 minutes for alerting and trend analysis.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that detect anomalous traffic and record load-balancer activity.", + "Section": "CCC.LB.CN01 Enforce and Detect Rate Limiting", + "SubSection": "", + "SubSectionObjective": "Detect and throttle malicious or excessive requests to prevent downstream resource exhaustion and brute-force activity.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Enable access logging and configure metric filters on HTTP 429 counts to trigger alerts.", + "SectionThreatMappings": [ + { + "ReferenceId": "LB", + "Identifiers": [ + "CCC.LB.TH01", + "CCC.LB.TH09" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "DE.CM-1", + "PR.AC-7", + "PR.PT-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AU-6", + "SC-5", + "AC-7" + ] + } + ] + } + ], + "Checks": [ + "monitor_diagnostic_settings_exists" + ] + }, + { + "Id": "CCC.LB.CN06.AR01", + "Description": "When more than 10 percent of targets change from healthy to unhealthy within five minutes, an alert MUST be issued.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that detect anomalous traffic and record load-balancer activity.", + "Section": "CCC.LB.CN06 Secure Health-Check Telemetry", + "SubSection": "", + "SubSectionObjective": "Monitor health-check endpoints for tampering and alert on abnormal status changes.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Instrument metrics for health check results and target removal events. Configure monitoring alarms to alert on abnormal spikes in unhealthy targets.", + "SectionThreatMappings": [ + { + "ReferenceId": "LB", + "Identifiers": [ + "CCC.LB.TH05" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "DE.AE-2" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-4" + ] + } + ] + } + ], + "Checks": [ + "monitor_alert_service_health_exists" + ] + }, + { + "Id": "CCC.LB.CN04.AR01", + "Description": "When routing weights change, the request MUST originate from an explicitly defined and trusted identity and MUST be logged.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can change or query load-balancer resources.", + "Section": "CCC.LB.CN04 Enforce Distribution Policies", + "SubSection": "", + "SubSectionObjective": "Ensure traffic-splitting weights and algorithms are modified only by trusted identities.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Define a list of trusted principals allowed to modify routing configurations. Enforce via conditional access policies, and log changes using audit logging.", + "SectionThreatMappings": [ + { + "ReferenceId": "LB", + "Identifiers": [ + "CCC.LB.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-3" + ] + } + ] + } + ], + "Checks": [ + "monitor_diagnostic_settings_exists" + ] + }, + { + "Id": "CCC.LB.CN05.AR01", + "Description": "When stickiness is enabled, session cookies MUST expire within 30 minutes of inactivity.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can change or query load-balancer resources.", + "Section": "CCC.LB.CN05 Validate Session Affinity", + "SubSection": "", + "SubSectionObjective": "Configure session persistence to minimise fixation and hijacking risks.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Audit CCC.LB.CP15 parameters via configuration scans.", + "SectionThreatMappings": [ + { + "ReferenceId": "LB", + "Identifiers": [ + "CCC.LB.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-7" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-23" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.LB.CN09.AR01", + "Description": "When an API call originates outside the approved CIDR set, the request MUST be denied.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can change or query load-balancer resources.", + "Section": "CCC.LB.CN09 Restrict Management API Access", + "SubSection": "", + "SubSectionObjective": "Limit load-balancer API calls to authorised identities and trusted networks.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Combine VPC endpoints with IAM condition-key filters for protected APIs.", + "SectionThreatMappings": [ + { + "ReferenceId": "LB", + "Identifiers": [ + "CCC.LB.TH08" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-5" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-7" + ] + } + ] + } + ], + "Checks": [ + "entra_conditional_access_policy_require_mfa_for_management_api" + ] + }, + { + "Id": "CCC.LB.CN02.AR01", + "Description": "When concurrent connections reach 80 percent of capacity, the autoscaling group MUST add at least one instance within five minutes.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "Controls that preserve availability and confidentiality of traffic processed by the load balancer.", + "Section": "CCC.LB.CN02 Auto-Scale Load Balancer Capacity", + "SubSection": "", + "SubSectionObjective": "Expand load-balancer capacity to maintain availability during traffic spikes.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Enable autoscaling policies.", + "SectionThreatMappings": [ + { + "ReferenceId": "LB", + "Identifiers": [ + "CCC.LB.TH09" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "ID.BE-5" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "CP-10" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.LB.CN07.AR01", + "Description": "When responses pass through the load balancer, the \"Server\" header MUST be replaced with \"lb\".", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "Controls that preserve availability and confidentiality of traffic processed by the load balancer.", + "Section": "CCC.LB.CN07 Scrub Sensitive Headers", + "SubSection": "", + "SubSectionObjective": "Remove headers that disclose internal details or software versions from HTTP responses.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Configure header-transformation rules.", + "SectionThreatMappings": [ + { + "ReferenceId": "LB", + "Identifiers": [ + "CCC.Core.TH15" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.DS-2" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-13" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.LB.CN08.AR01", + "Description": "When a certificate is within 30 days of expiry, automated renewal MUST complete and deploy a new certificate within 24 hours.", + "Attributes": [ + { + "FamilyName": "Encryption", + "FamilyDescription": "Controls that ensure trustworthy TLS certificates and ciphers.", + "Section": "CCC.LB.CN08 Automate Certificate Renewal", + "SubSection": "", + "SubSectionObjective": "Maintain valid TLS certificates by automating renewal and deployment before expiry.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Use certificate-manager auto-renewal workflows.", + "SectionThreatMappings": [ + { + "ReferenceId": "LB", + "Identifiers": [ + "CCC.LB.TH07" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.DS-6" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-17" + ] + } + ] + } + ], + "Checks": [ + "keyvault_key_rotation_enabled" ] }, { @@ -3272,6 +4158,58 @@ ], "Checks": [] }, + { + "Id": "CCC.VPC.CN02.AR01", + "Description": "When a resource is created in a public subnet, that resource MUST NOT be assigned an external IP address by default.", + "Attributes": [ + { + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.VPC.CN02 Limit Resource Creation in Public Subnet", + "SubSection": "", + "SubSectionObjective": "Restrict the creation of resources in the public subnet with direct access to the internet to minimize attack surfaces.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.VPC.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-3" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "SEF-05" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.13.1.1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-4" + ] + } + ] + } + ], + "Checks": [] + }, { "Id": "CCC.VPC.CN03.AR01", "Description": "When a VPC peering connection is requested, the service MUST prevent connections from VPCs that are not explicitly allowed.", @@ -3379,10 +4317,393 @@ ], "Checks": [ "network_flow_log_captured_sent", - "network_flow_log_more_than_90_days", "network_watcher_enabled" ] }, + { + "Id": "CCC.KeyMgmt.CN01.AR01", + "Description": "When a key version is scheduled for deletion or disabled, an alert MUST be generated within five minutes.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that collect, alert, and retain key-management events.", + "Section": "CCC.KeyMgmt.CN01 Alert on Key-version Changes", + "SubSection": "", + "SubSectionObjective": "Generate near-real-time alerts when a KMS key version is disabled or scheduled for deletion, enabling rapid investigation and recovery.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Use native event services (e.g., CloudWatch Events, Azure Monitor, Cloud Audit Logs) to route notifications to an incident-response channel.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.KeyMgmt.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "RS.AN-1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "IR-5" + ] + } + ] + } + ], + "Checks": [ + "keyvault_logging_enabled", + "keyvault_recoverable" + ] + }, + { + "Id": "CCC.KeyMgmt.CN02.AR01", + "Description": "When IAM roles and key policies are reviewed, Decrypt permission MUST be granted exclusively to documented authorised principals.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that enforce least-privilege use of KMS operations.", + "Section": "CCC.KeyMgmt.CN02 Limit Decrypt Permissions", + "SubSection": "", + "SubSectionObjective": "Restrict the Decrypt operation to authorised principals only, applying the principle of least privilege to protect sensitive data.", + "Applicability": [ + "tlp-green" + ], + "Recommendation": "Periodically audit policy documents via automated tooling and report any deviations.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.KeyMgmt.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6" + ] + } + ] + } + ], + "Checks": [ + "keyvault_rbac_enabled", + "keyvault_access_only_through_private_endpoints" + ] + }, + { + "Id": "CCC.KeyMgmt.CN03.AR01", + "Description": "When rotation settings are examined, rotation MUST be enabled with an interval not exceeding 365 days.", + "Attributes": [ + { + "FamilyName": "Key Lifecycle Management", + "FamilyDescription": "Controls that govern creation, rotation, import, and retirement of cryptographic keys.", + "Section": "CCC.KeyMgmt.CN03 Enforce Automatic Rotation", + "SubSection": "", + "SubSectionObjective": "Ensure symmetric keys rotate automatically within policy intervals to reduce exposure of key material.", + "Applicability": [ + "tlp-green" + ], + "Recommendation": "Use cloud-provider rotation features and verify via configuration scanning.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.KeyMgmt.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.DS-1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-12" + ] + } + ] + } + ], + "Checks": [ + "keyvault_key_rotation_enabled" + ] + }, + { + "Id": "CCC.KeyMgmt.CN04.AR01", + "Description": "When a key import request is processed, the key MUST use an approved algorithm (RSA-2048+, EC-P256+) and originate from a certified HSM.", + "Attributes": [ + { + "FamilyName": "Key Lifecycle Management", + "FamilyDescription": "Controls that govern creation, rotation, import, and retirement of cryptographic keys.", + "Section": "CCC.KeyMgmt.CN04 Validate Imported Keys", + "SubSection": "", + "SubSectionObjective": "Accept only externally generated keys that meet approved cryptographic strength and provenance requirements.", + "Applicability": [ + "tlp-green" + ], + "Recommendation": "Implement an approval workflow that validates attestation data before import.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.KeyMgmt.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.DS-1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-28" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.SecMgmt.CN01.AR01", + "Description": "Attempt to use an outdated version of a secret after its rotation period has passed and verify that access is denied.", + "Attributes": [ + { + "FamilyName": "Data Protection", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.SecMgmt.CN01 Enforce Automatic Secret Rotation", + "SubSection": "", + "SubSectionObjective": "Ensure that secrets are automatically rotated on a defined schedule to reduce the risk of secret compromise and unauthorized access.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH14" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.DS-6" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-12", + "SC-28" + ] + } + ] + } + ], + "Checks": [ + "keyvault_rbac_secret_expiration_set", + "keyvault_non_rbac_secret_expiration_set" + ] + }, + { + "Id": "CCC.SecMgmt.CN02.AR01", + "Description": "Attempt to retrieve a secret from an unauthorized region and verify that access is denied.", + "Attributes": [ + { + "FamilyName": "Data Protection", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.SecMgmt.CN02 Enforce Secret Replication Policies", + "SubSection": "", + "SubSectionObjective": "Ensure that secrets are replicated only to authorized locations as per organizational data residency and compliance requirements.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH03", + "CCC.Core.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.DS-5" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-3", + "SC-7" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.DataWar.CN01.AR01", + "Description": "Attempt to access underlying database tables directly without using managed views and verify that access is denied.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.DataWar.CN01 Enforce Use of Managed Views for Data Access", + "SubSection": "", + "SubSectionObjective": "Ensure that data access is provided through managed views, restricting users from accessing underlying tables directly and enforcing consistent security policies.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-3", + "AC-6" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.DataWar.CN02.AR01", + "Description": "Attempt to query sensitive columns without the necessary permissions and verify that access is denied or data is masked.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.DataWar.CN02 Enforce Column-Level Security Policies", + "SubSection": "", + "SubSectionObjective": "Ensure that access to sensitive data columns is restricted based on user roles, preventing unauthorized access to sensitive information.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-3", + "AC-6" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.DataWar.CN03.AR01", + "Description": "Attempt to query data rows that the user should not have access to and verify that access is denied or data is not returned.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.DataWar.CN03 Enforce Row-Level Security Policies", + "SubSection": "", + "SubSectionObjective": "Ensure that access to data rows is restricted based on user roles or attributes, preventing unauthorized access to specific subsets of data.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-3", + "AC-6" + ] + } + ] + } + ], + "Checks": [] + }, { "Id": "CCC.Vector.CN01.AR01", "Description": "When a vector embedding is submitted for indexing, the system MUST validate that it matches expected schema, dimension, and format profiles.", @@ -3406,7 +4727,7 @@ "Identifiers": [ "CCC.Vector.TH02", "CCC.Vector.TH05", - "CCC.TH12" + "CCC.Core.TH12" ] } ], @@ -3445,7 +4766,7 @@ "Identifiers": [ "CCC.Vector.TH02", "CCC.Vector.TH04", - "CCC.TH01" + "CCC.Core.TH01" ] } ], @@ -3459,13 +4780,7 @@ ] } ], - "Checks": [ - "iam_role_user_access_admin_restricted", - "iam_custom_role_has_permissions_to_administer_resource_locks", - "iam_subscription_roles_owner_custom_not_created", - "app_function_identity_without_admin_privileges", - "app_function_identity_is_configured" - ] + "Checks": [] }, { "Id": "CCC.Vector.CN03.AR01", @@ -3487,7 +4802,7 @@ "ReferenceId": "CCC", "Identifiers": [ "CCC.Vector.TH03", - "CCC.TH01" + "CCC.Core.TH01" ] } ], @@ -3526,7 +4841,7 @@ "ReferenceId": "CCC", "Identifiers": [ "CCC.Vector.TH02", - "CCC.TH12" + "CCC.Core.TH12" ] } ], @@ -3562,8 +4877,8 @@ "ReferenceId": "CCC", "Identifiers": [ "CCC.Vector.TH04", - "CCC.TH09", - "CCC.TH04" + "CCC.Core.TH09", + "CCC.Core.TH04" ] } ], @@ -3601,7 +4916,7 @@ "ReferenceId": "CCC", "Identifiers": [ "CCC.Vector.TH05", - "CCC.TH06" + "CCC.Core.TH06" ] } ], @@ -3646,451 +4961,25 @@ "Checks": [] }, { - "Id": "CCC.Core.CN01.AR01", - "Description": "When a port is exposed for non-SSH network traffic, all traffic MUST include a TLS handshake AND be encrypted using TLS 1.3 or higher.", + "Id": "CCC.RDMS.CN01.AR02", + "Description": "When an attempt is made to authenticate to the database using known default credentials, the authentication attempt must fail and no access should be granted.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN01 Encrypt Data for Transmission", + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.RDMS.CN01 Password Management", "SubSection": "", - "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Most cloud services enable TLS 1.3 by default. Where it is not already set, ensure that your services are configured or updated accordingly. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH02" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-03", - "CEK-04", - "IVS-03", - "IVS-07" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-02" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-8", - "SC-13" - ] - } - ] - } - ], - "Checks": [ - "app_minimum_tls_version_12", - "app_ensure_http_is_redirected_to_https", - "storage_secure_transfer_required_is_enabled", - "storage_ensure_minimum_tls_version_12" - ] - }, - { - "Id": "CCC.Core.CN01.AR02", - "Description": "When a port is exposed for SSH network traffic, all traffic MUST include a SSH handshake AND be encrypted using SSHv2 or higher.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN01 Encrypt Data for Transmission", - "SubSection": "", - "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Any time port 22 is exposed, ensure that it has a properly implemented SSH server with SSHv2 enabled and configured with strong ciphers. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH02" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-03", - "CEK-04", - "IVS-03", - "IVS-07" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-02" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-8", - "SC-13" - ] - } - ] - } - ], - "Checks": [ - "network_ssh_internet_access_restricted", - "vm_linux_enforce_ssh_authentication", - "app_minimum_tls_version_12", - "storage_secure_transfer_required_is_enabled", - "storage_ensure_minimum_tls_version_12", - "app_ensure_http_is_redirected_to_https" - ] - }, - { - "Id": "CCC.Core.CN01.AR03", - "Description": "When the service receives unencrypted traffic, then it MUST either block the request or automatically redirect it to the secure equivalent.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN01 Encrypt Data for Transmission", - "SubSection": "", - "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Review firewall, load balancer, and application configurations to ensure insecure protocols such as HTTP, FTP, and Telnet are not exposed. Where possible, implement automatic redirection to secure protocols such as HTTPS, SFTP, SSH, and regularly scan for protocol drift. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH02" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-03", - "CEK-04", - "IVS-03", - "IVS-07" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-02" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-8", - "SC-13" - ] - } - ] - } - ], - "Checks": [ - "app_ensure_http_is_redirected_to_https", - "app_minimum_tls_version_12", - "storage_secure_transfer_required_is_enabled", - "storage_ensure_minimum_tls_version_12", - "app_ftp_deployment_disabled" - ] - }, - { - "Id": "CCC.Core.CN01.AR07", - "Description": "When a port is exposed, the service MUST ensure that the protocol and service officially assigned to that port number by the IANA Service Name and Transport Protocol Port Number Registry, and no other, is run on that port.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN01 Encrypt Data for Transmission", - "SubSection": "", - "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Reference the IANA Service Name and Transport Protocol Port Number Registry for more information about correct protocol-to-port assignments. Avoid running non-standard services on well-known ports. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH02" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-03", - "CEK-04", - "IVS-03", - "IVS-07" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-02" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-8", - "SC-13" - ] - } - ] - } - ], - "Checks": [ - "app_minimum_tls_version_12", - "app_ensure_http_is_redirected_to_https", - "app_ensure_using_http20", - "storage_smb_channel_encryption_with_secure_algorithm", - "storage_secure_transfer_required_is_enabled", - "storage_ensure_minimum_tls_version_12", - "storage_smb_protocol_version_is_latest" - ] - }, - { - "Id": "CCC.Core.CN01.AR08", - "Description": "When a service transmits data using TLS, mutual TLS (mTLS) MUST be implemented to require both client and server certificate authentication for all connections.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN01 Encrypt Data for Transmission", - "SubSection": "", - "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Configure mTLS for all endpoints that process or transmit sensitive data. Ensure both client and server certificates are validated and managed securely. Regularly review certificate authorities and automate certificate rotation where possible. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH02" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-03", - "CEK-04", - "IVS-03", - "IVS-07" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-02" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-8", - "SC-13" - ] - } - ] - } - ], - "Checks": [ - "app_client_certificates_on", - "app_minimum_tls_version_12", - "app_ensure_http_is_redirected_to_https" - ] - }, - { - "Id": "CCC.Core.CN13.AR01", - "Description": "When a port is exposed that uses certificate-based encryption, the service MUST only use valid, unexpired certificates issued by a trusted certificate authority.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN13 Minimize Lifetime of Encryption and Authentication Certificates", - "SubSection": "", - "SubSectionObjective": "Ensure that encryption and authentication certificates have a limited lifetime to reduce the risk of compromise and ensure the use of up-to-date security practices.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Track certificate expiration dates and automate certificate renewal where possible. Use certificate management tools to ensure only certificates from trusted authorities are deployed. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH18" - ] - } - ], - "SectionGuidelineMappings": [] - } - ], - "Checks": [ - "app_client_certificates_on", - "app_minimum_tls_version_12", - "keyvault_key_expiration_set_in_non_rbac", - "keyvault_key_rotation_enabled", - "keyvault_rbac_key_expiration_set", - "keyvault_rbac_secret_expiration_set", - "keyvault_non_rbac_secret_expiration_set" - ] - }, - { - "Id": "CCC.Core.CN13.AR02", - "Description": "When a port is exposed that uses certificate-based encryption, the service MUST rotate active certificates within 180 days of issuance.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN13 Minimize Lifetime of Encryption and Authentication Certificates", - "SubSection": "", - "SubSectionObjective": "Ensure that encryption and authentication certificates have a limited lifetime to reduce the risk of compromise and ensure the use of up-to-date security practices.", + "SubSectionObjective": "Ensure default vendor-supplied DB administrator credentials are replaced with strong, unique passwords and that these credentials are properly managed using a secure password or secrets management solution.", "Applicability": [ + "tlp-red", "tlp-amber" ], - "Recommendation": "Track certificate expiration dates and automate certificate renewal where possible. Use certificate management tools to ensure only certificates from trusted authorities are deployed. ", + "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH18" - ] - } - ], - "SectionGuidelineMappings": [] - } - ], - "Checks": [ - "keyvault_key_rotation_enabled", - "keyvault_key_expiration_set_in_non_rbac", - "keyvault_rbac_key_expiration_set", - "keyvault_non_rbac_secret_expiration_set", - "keyvault_rbac_secret_expiration_set", - "storage_ensure_encryption_with_customer_managed_keys" - ] - }, - { - "Id": "CCC.Core.CN13.AR03", - "Description": "When a port is exposed that uses certificate-based encryption, the service MUST rotate active certificates within 90 days of issuance.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN13 Minimize Lifetime of Encryption and Authentication Certificates", - "SubSection": "", - "SubSectionObjective": "Ensure that encryption and authentication certificates have a limited lifetime to reduce the risk of compromise and ensure the use of up-to-date security practices.", - "Applicability": [ - "tlp-red" - ], - "Recommendation": "Track certificate expiration dates and automate certificate renewal where possible. Use certificate management tools to ensure only certificates from trusted authorities are deployed. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH18" - ] - } - ], - "SectionGuidelineMappings": [] - } - ], - "Checks": [ - "app_client_certificates_on", - "app_ensure_http_is_redirected_to_https", - "app_minimum_tls_version_12" - ] - }, - { - "Id": "CCC.Core.CN06.AR01", - "Description": "When the service is running, its region and availability zone MUST be included in a list of explicitly trusted or approved locations within the trust perimeter.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN06 Restrict Deployments to Trust Perimeter", - "SubSection": "", - "SubSectionObjective": "Ensure that the service and its child resources are only deployed on infrastructure in locations that are explicitly included within a defined trust perimeter.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Maintain an up-to-date list of trusted and approved regions based on organizational policies. Validate the service's deployment location is included in this list. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH03" + "CCC.RDMS.TH01" ] } ], @@ -4098,19 +4987,89 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-1" + "PR.AA-01" ] }, { - "ReferenceId": "CCM", + "ReferenceId": "NIST_800_53", "Identifiers": [ - "DSP-19" + "AC-2" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.RDMS.CN02.AR01", + "Description": "When repeated failed login attempts are made in a short timeframe, the account must be locked out or rate-limited to prevent further login attempts.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.RDMS.CN02 Account Lockout and Rate-Limiting", + "SubSection": "", + "SubSectionObjective": "Ensure the database enforces lockouts or rate-limiting after a specified number of failed authentication attempts. This prevents brute force or password-guessing attacks from succeeding.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.RDMS.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-1" ] }, { - "ReferenceId": "ISO_27001", + "ReferenceId": "NIST_800_53", "Identifiers": [ - "2013 A.11.1.1" + "AC-7" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.RDMS.CN04.AR01", + "Description": "When there is an attempt to perform a backup or restore, then the attempt must fail with an access denied message if credentials or roles that are not explicitly authorized for backup/restore functions.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.RDMS.CN04 Access Control for Backup and Restore Operations", + "SubSection": "", + "SubSectionObjective": "Restrict who can initiate, manage, and validate database backup or restore operations through strict role-based or least-privilege access. Prevents accidental or malicious restorations, protecting data integrity and availability.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.RDMS.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" ] }, { @@ -4122,44 +5081,28 @@ ] } ], - "Checks": [ - "aks_clusters_public_access_disabled", - "aks_clusters_created_with_private_nodes", - "containerregistry_not_publicly_accessible", - "containerregistry_uses_private_link", - "keyvault_private_endpoints", - "keyvault_access_only_through_private_endpoints", - "storage_ensure_private_endpoints_in_storage_accounts", - "network_http_internet_access_restricted", - "network_rdp_internet_access_restricted", - "network_ssh_internet_access_restricted", - "network_udp_internet_access_restricted", - "network_watcher_enabled", - "entra_trusted_named_locations_exists" - ] + "Checks": [] }, { - "Id": "CCC.Core.CN06.AR02", - "Description": "When a child resource is deployed, its region and availability zone MUST be included in a list of explicitly trusted or approved locations within the trust perimeter.", + "Id": "CCC.RDMS.CN05.AR01", + "Description": "When an attempt is made to share a snapshot with an unauthorized account, the sharing request must be denied.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN06 Restrict Deployments to Trust Perimeter", + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.RDMS.CN05 Restrict Snapshot Sharing to Authorized Accounts", "SubSection": "", - "SubSectionObjective": "Ensure that the service and its child resources are only deployed on infrastructure in locations that are explicitly included within a defined trust perimeter.", + "SubSectionObjective": "Ensure database snapshots can only be shared with explicitly authorized accounts, thereby minimizing the risk of data exposure or exfiltration.", "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" + "tlp-red", + "tlp-amber" ], - "Recommendation": "Maintain an up-to-date list of trusted and approved regions based on organizational policies. Validate that child resources can only be deployed to locations included in this list. ", + "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH03" + "CCC.RDMS.TH05" ] } ], @@ -4167,24 +5110,312 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-19" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.11.1.1" + "PR.DS-10" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ + "AC-4" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.RDMS.CN03.AR01", + "Description": "When backups are disabled, paused, or fail to run as scheduled, an alert must be triggered and logged.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.RDMS.CN03 Enforce and Monitor Automated Backups", + "SubSection": "", + "SubSectionObjective": "Ensure database backups are automatically scheduled, actively monitored, and promptly reported if any disruptions occur. This helps maintain data integrity, facilitates disaster recovery, and supports business continuity when a system failure or breach occurs.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.RDMS.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.IP-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "CP-9" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Build.CN01.AR01", + "Description": "Attempt to initiate a build using an unauthorized build agent and verify that the build is rejected.", + "Attributes": [ + { + "FamilyName": "Access Control", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.Build.CN01 Restrict Allowed Build Agents", + "SubSection": "", + "SubSectionObjective": "Ensure that builds are executed only on authorized build agents to maintain control over the build environment and prevent unauthorized code execution.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-3", + "AC-6" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Build.CN02.AR01", + "Description": "Attempt to trigger a build from an unauthorized external service or repository and verify that the build does not start.", + "Attributes": [ + { + "FamilyName": "Access Control", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.Build.CN02 Restrict Allowed External Services for Build Triggers", + "SubSection": "", + "SubSectionObjective": "Ensure that builds can only be triggered by authorized external services or repositories to prevent unauthorized code execution or tampering.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-3", + "AC-6" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Build.CN03.AR01", + "Description": "Attempt to access the build environment from an external network and verify that access is denied.", + "Attributes": [ + { + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.Build.CN03 Deny External Network Access for Build Environments", + "SubSection": "", + "SubSectionObjective": "Ensure that build environments do not have external network access to prevent unauthorized external access and data exfiltration.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH02", + "CCC.Core.TH05" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-5" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-7", + "SC-5" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.CntrReg.CN01.AR01", + "Description": "Attempt to push an artifact with known vulnerabilities to the registry and observe if it is flagged or rejected by the vulnerability scanning process.", + "Attributes": [ + { + "FamilyName": "Risk Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.CntrReg.CN01 Implement Vulnerability Scanning for Artifacts", + "SubSection": "", + "SubSectionObjective": "Ensure that container images and artifacts stored in the container registry are scanned for vulnerabilities to identify and remediate security issues before deployment.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.CntrReg.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "ID.RA-1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "RA-5", + "SI-5" + ] + } + ] + } + ], + "Checks": [ + "defender_container_images_scan_enabled", + "defender_container_images_resolved_vulnerabilities" + ] + }, + { + "Id": "CCC.CntrReg.CN02.AR01", + "Description": "Confirm that artifacts older than the specified retention period are automatically deleted from the registry.", + "Attributes": [ + { + "FamilyName": "Data Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.CntrReg.CN02 Implement Cleanup Policies for Artifacts", + "SubSection": "", + "SubSectionObjective": "Ensure that unused or outdated artifacts are cleaned up according to defined policies to manage storage effectively and reduce security risks associated with outdated versions.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH14" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.IP-6" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-12" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.IAM.CN01.AR01", + "Description": "When an identity policy for a non-administrative principal is evaluated, it MUST NOT grant permissions for creating credentials or generating temporary session tokens.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN01 Restrict IAM User Credentials Creation", + "SubSection": "", + "SubSectionObjective": "Prevent non-administrative principals from creating new long-lived credentials like access keys or generating temporary session tokens. This blocks a common privilege escalation and persistence vector.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-05" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2", + "AC-3", + "AC-5", "AC-6" ] } @@ -4192,127 +5423,20 @@ } ], "Checks": [ - "entra_trusted_named_locations_exists" + "entra_policy_default_users_cannot_create_security_groups", + "entra_policy_ensure_default_user_cannot_create_apps" ] }, { - "Id": "CCC.Core.CN08.AR01", - "Description": "When data is created or modified, the data MUST have a complete and recoverable duplicate that is stored in a physically separate data center.", + "Id": "CCC.IAM.CN01.AR02", + "Description": "When a non-administrative principal attempts to create new credentials or a temporary session token, the service MUST deny the action.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN08 Replicate Data to Multiple Locations", + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN01 Restrict IAM User Credentials Creation", "SubSection": "", - "SubSectionObjective": "Ensure that data is replicated across multiple physical locations to protect against data loss due to hardware failures, natural disasters, or other catastrophic events.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Implement automated data replication processes to ensure that data is consistently duplicated in another region or availability zone. Regularly test data recovery from the replicated location to ensure integrity and availability. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.PT-5" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "BCR-08", - "BCR-10", - "BCR-11" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "CP-2", - "CP-10" - ] - } - ] - } - ], - "Checks": [ - "storage_geo_redundant_enabled", - "vm_backup_enabled", - "vm_sufficient_daily_backup_retention_period" - ] - }, - { - "Id": "CCC.Core.CN08.AR02", - "Description": "When data is replicated into a second location, the service MUST be able to accurately represent the replication locations, replication status, and data synchronization status.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN08 Replicate Data to Multiple Locations", - "SubSection": "", - "SubSectionObjective": "Ensure that data is replicated across multiple physical locations to protect against data loss due to hardware failures, natural disasters, or other catastrophic events.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.PT-5" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "BCR-08", - "BCR-10", - "BCR-11" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "CP-2", - "CP-10" - ] - } - ] - } - ], - "Checks": [ - "storage_geo_redundant_enabled" - ] - }, - { - "Id": "CCC.Core.CN09.AR01", - "Description": "When the service is operational, its logs and any child resource logs MUST NOT be accessible from the resource they record access to.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN09 Ensure Integrity of Access Logs", - "SubSection": "", - "SubSectionObjective": "Ensure that access logs are always recorded to an external location that cannot be manipulated from the context of the service(s) it contains logs for.", + "SubSectionObjective": "Prevent non-administrative principals from creating new long-lived credentials like access keys or generating temporary session tokens. This blocks a common privilege escalation and persistence vector.", "Applicability": [ "tlp-clear", "tlp-green", @@ -4324,9 +5448,7 @@ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07", - "CCC.TH09", - "CCC.TH04" + "CCC.IAM.TH03" ] } ], @@ -4334,57 +5456,48 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-6" + "PR.AA-05" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "AU-9" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-02", - "LOG-04", - "LOG-09" + "AC-2", + "AC-3", + "AC-5", + "AC-6" ] } ] } ], "Checks": [ - "monitor_diagnostic_settings_exists", - "network_flow_log_captured_sent", - "monitor_storage_account_with_activity_logs_is_private", - "monitor_storage_account_with_activity_logs_cmk_encrypted" + "entra_policy_default_users_cannot_create_security_groups", + "entra_policy_ensure_default_user_cannot_create_apps" ] }, { - "Id": "CCC.Core.CN09.AR02", - "Description": "When the service is operational, disabling the logs for the service or its child resources MUST NOT be possible without also disabling the corresponding resource.", + "Id": "CCC.IAM.CN02.AR01", + "Description": "When an identity policy for a non-administrative principal is evaluated, it MUST NOT grant permissions for creating, updating, or attaching policies.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN09 Ensure Integrity of Access Logs", + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN02 Restrict IAM Policies Modification", "SubSection": "", - "SubSectionObjective": "Ensure that access logs are always recorded to an external location that cannot be manipulated from the context of the service(s) it contains logs for.", + "SubSectionObjective": "Ensure that only designated administrative accounts have the ability to create, modify, or attach policies that define permissions for other identities.", "Applicability": [ "tlp-clear", "tlp-green", "tlp-amber", "tlp-red" ], - "Recommendation": "No normal business operations should disable logs, as this could indicate an attempt to cover up unauthorized access. Ensure that logging mechanisms are tightly integrated with service operations, so that logging cannot be disabled without stopping the service itself. ", + "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07", - "CCC.TH09", - "CCC.TH04" + "CCC.IAM.TH06" ] } ], @@ -4392,57 +5505,49 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-6" + "PR.AA-05" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "AU-9" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-02", - "LOG-04", - "LOG-09" + "AC-2", + "AC-3", + "AC-5", + "AC-6" ] } ] } ], "Checks": [ - "monitor_diagnostic_settings_exists", - "monitor_diagnostic_setting_with_appropriate_categories", - "monitor_storage_account_with_activity_logs_cmk_encrypted", - "monitor_storage_account_with_activity_logs_is_private", - "keyvault_logging_enabled", - "app_http_logs_enabled" + "iam_role_user_access_admin_restricted", + "iam_subscription_roles_owner_custom_not_created", + "iam_custom_role_has_permissions_to_administer_resource_locks" ] }, { - "Id": "CCC.Core.CN09.AR03", - "Description": "When the service is operational, any attempt to redirect logs for the service or its child resources MUST NOT be possible without halting operation of the corresponding resource and publishing corresponding events to monitored channels.", + "Id": "CCC.IAM.CN02.AR02", + "Description": "When a non-administrative principal attempts to create, update, or attach policies, the service MUST deny the action.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN09 Ensure Integrity of Access Logs", + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN02 Restrict IAM Policies Modification", "SubSection": "", - "SubSectionObjective": "Ensure that access logs are always recorded to an external location that cannot be manipulated from the context of the service(s) it contains logs for.", + "SubSectionObjective": "Ensure that only designated administrative accounts have the ability to create, modify, or attach policies that define permissions for other identities.", "Applicability": [ + "tlp-clear", + "tlp-green", "tlp-amber", "tlp-red" ], - "Recommendation": "No normal business operations should result in the redirection of logs, as this could indicate an attempt to cover up unauthorized access. Ensure that logging configurations are immutable during service operation so that any changes require stopping the service and publishing corresponding events to monitored channels. ", + "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07", - "CCC.TH09", - "CCC.TH04" + "CCC.IAM.TH06" ] } ], @@ -4450,46 +5555,37 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-6" + "PR.AA-05" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "AU-9" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-02", - "LOG-04", - "LOG-09" + "AC-2", + "AC-3", + "AC-5", + "AC-6" ] } ] } ], "Checks": [ - "monitor_diagnostic_settings_exists", - "monitor_diagnostic_setting_with_appropriate_categories", - "monitor_storage_account_with_activity_logs_is_private", - "monitor_storage_account_with_activity_logs_cmk_encrypted", - "network_flow_log_captured_sent", - "app_http_logs_enabled", - "keyvault_logging_enabled" + "iam_role_user_access_admin_restricted", + "iam_subscription_roles_owner_custom_not_created", + "iam_custom_role_has_permissions_to_administer_resource_locks" ] }, { - "Id": "CCC.Core.CN10.AR01", - "Description": "When data is replicated, the service MUST ensure that replication only occurs to destinations that are explicitly included within the defined trust perimeter.", + "Id": "CCC.IAM.CN03.AR01", + "Description": "When a policy is created or updated that grants a principal permission to assume a role or impersonate a service identity, the principal MUST NOT contain a wildcard or be public/anonymous.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN10 Restrict Data Replication to Trust Perimeter", + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN03 Restrict Role Assumption / Delegation", "SubSection": "", - "SubSectionObjective": "Ensure that data is only replicated on infrastructure in locations that are explicitly included within a defined trust perimeter.", + "SubSectionObjective": "Limit which principals can assume a role or impersonate a service identity to only those required. This prevents unintended cross-account or public access by securing the \"who can act as this identity\" boundary.", "Applicability": [ "tlp-green", "tlp-amber", @@ -4500,7 +5596,1438 @@ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH04" + "CCC.IAM.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-3", + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2", + "AC-3", + "AC-6" + ] + } + ] + } + ], + "Checks": [ + "iam_role_user_access_admin_restricted" + ] + }, + { + "Id": "CCC.IAM.CN03.AR02", + "Description": "When an external or unauthenticated principal tries to assume a role or impersonate a service identity, the service MUST deny the action.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN03 Restrict Role Assumption / Delegation", + "SubSection": "", + "SubSectionObjective": "Limit which principals can assume a role or impersonate a service identity to only those required. This prevents unintended cross-account or public access by securing the \"who can act as this identity\" boundary.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-3", + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2", + "AC-3", + "AC-6" + ] + } + ] + } + ], + "Checks": [ + "iam_role_user_access_admin_restricted" + ] + }, + { + "Id": "CCC.IAM.CN04.AR01", + "Description": "When an IAM policy is created or updated, it MUST NOT contain allow statements with wildcard permissions, unless the statement is restricted by a condition.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN04 Restrict Wildcard Usage in IAM Policies", + "SubSection": "", + "SubSectionObjective": "Limit the use of wildcard permissions in IAM policies to prevent overly broad access from being granted by default.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH01", + "CCC.IAM.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-6" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2", + "AC-3", + "AC-6" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.IAM.CN05.AR01", + "Description": "When a new cloud account is provisioned, a password policy MUST be configured for IAM users following the minimum PCI DSS v4.0.1 configurations.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN05 Strong Password Policies for IAM Users", + "SubSection": "", + "SubSectionObjective": "Ensure that the password policies for IAM users have strong configurations.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "When a new cloud account is provisioned, a password policy must be configured for all IAM users to align with the minimum requirements defined in PCI DSS v4.0.1. This includes, at a minimum: strength: 0 # Not yet specified - reference-id: A password length of at least 12 characters. strength: 0 # Not yet specified - reference-id: A mix of upper- and lower-case letters, numbers, and special characters. strength: 0 # Not yet specified - reference-id: Prevention of the use of previously used passwords (password history). strength: 0 # Not yet specified - reference-id: Password expiration at a defined interval (e.g., every 90 days). strength: 0 # Not yet specified - reference-id: Account lockout after a defined number of failed login attempts.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-05" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "IA-5" + ] + }, + { + "ReferenceId": "PCI-DSS", + "Identifiers": [ + "8.3.9", + "8.6.3" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.IAM.CN06.AR01", + "Description": "When a static credential such as an access key has existed for 90 days or more, it MUST be rotated.", + "Attributes": [ + { + "FamilyName": "Identity Provisioning and Lifecycle", + "FamilyDescription": "Controls related to the provisioning and lifecycle of IAM identities.", + "Section": "CCC.IAM.CN06 Maximum Age for Long-Term Static Credentials", + "SubSection": "", + "SubSectionObjective": "Ensure that long-lived static credentials like access keys are programmatically rotated within a defined time period to limit the window of opportunity if compromised.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "When a static credential such as an access key has existed for 90 days or more, it must be automatically rotated to reduce the risk of compromise due to long-term exposure. Organizations should implement automated checks to identify aging credentials and enforce rotation policies. Additionally, access key usage should be regularly monitored, and credentials that are no longer in use should be deactivated or deleted promptly. Where possible, prefer temporary, short-lived credentials over long-lived static ones to further minimize risk.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH09", + "CCC.IAM.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.IAM.CN07.AR01", + "Description": "When a user account is disabled or deleted in the organization's IdP, the corresponding cloud identity and its access policies MUST be disabled or deleted within 24 hours.", + "Attributes": [ + { + "FamilyName": "Identity Provisioning and Lifecycle", + "FamilyDescription": "Controls related to the provisioning and lifecycle of IAM identities.", + "Section": "CCC.IAM.CN07 Automate Identity De-provisioning", + "SubSection": "", + "SubSectionObjective": "Ensure that when an identity is terminated in the central Identity Provider (IdP), ts corresponding access to cloud resources is revoked automatically.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH10", + "CCC.IAM.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.IAM.CN08.AR01", + "Description": "When an IAM user has credentials, such as passwords or access keys, that have not been used for 90 days or more, the unused credentials MUST be removed or deactivated.", + "Attributes": [ + { + "FamilyName": "Identity Provisioning and Lifecycle", + "FamilyDescription": "Controls related to the provisioning and lifecycle of IAM identities.", + "Section": "CCC.IAM.CN08 Maximum Age for Unused Credentials", + "SubSection": "", + "SubSectionObjective": "Ensure that unused IAM credentals are removed to reduce exposure in the event of potential compromise.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "IAM user credentials (such as passwords or access keys) that have not been used for 90 days or more must be automatically removed or deactivated.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH11", + "CCC.IAM.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.IAM.CN09.AR01", + "Description": "When a human user accesses the cloud environment, they MUST authenticate through the organization's federated IdP via a standard protocol (e.g., SAML, OIDC).", + "Attributes": [ + { + "FamilyName": "Identity Provisioning and Lifecycle", + "FamilyDescription": "Controls related to the provisioning and lifecycle of IAM identities.", + "Section": "CCC.IAM.CN09 Enforce Federated Single Sign-On (SSO) for Human Users", + "SubSection": "", + "SubSectionObjective": "Ensure that all human users must authenticate through a central, federated Identity Provider (IdP) to access the cloud environment. This eliminates cloud-native user accounts with long-lived passwords, centralizes authentication controls, and simplifies lifecycle management.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH01", + "CCC.IAM.TH09" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "IA-2" + ] + } + ] + } + ], + "Checks": [ + "entra_security_defaults_enabled" + ] + }, + { + "Id": "CCC.IAM.CN10.AR01", + "Description": "When suspicious API requests are detected, real time alerts MUST be generated to notify security personnel.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that collect, alert, and retain IAM-related events.", + "Section": "CCC.IAM.CN10 Alert On Anomalous Behaviour", + "SubSection": "", + "SubSectionObjective": "Ensure that logs and associated alerts are generated when anomalous API requests are made by a single identity, such as API requests commonly associated with privilege escalation tactics, originating from an external or malicious IP address or performed by a previously dormant identity, which may indicate that credentals may be compromised, as well as for password brute-force attempts and account lockouts.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "DE.CM-03", + "DE.CM-06", + "DE.CM-09" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-4", + "SI-5", + "AC-2" + ] + } + ] + } + ], + "Checks": [ + "monitor_diagnostic_settings_exists" + ] + }, + { + "Id": "CCC.IAM.CN10.AR02", + "Description": "When suspicious API requests are detected, the associated events MUST be logged, including the source details, time, and nature of the activity.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that collect, alert, and retain IAM-related events.", + "Section": "CCC.IAM.CN10 Alert On Anomalous Behaviour", + "SubSection": "", + "SubSectionObjective": "Ensure that logs and associated alerts are generated when anomalous API requests are made by a single identity, such as API requests commonly associated with privilege escalation tactics, originating from an external or malicious IP address or performed by a previously dormant identity, which may indicate that credentals may be compromised, as well as for password brute-force attempts and account lockouts.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "DE.CM-03", + "DE.CM-06", + "DE.CM-09" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-4", + "SI-5", + "AC-2" + ] + } + ] + } + ], + "Checks": [ + "monitor_diagnostic_settings_exists", + "monitor_diagnostic_setting_with_appropriate_categories" + ] + }, + { + "Id": "CCC.IAM.CN11.AR01", + "Description": "When a cloud account or organization is provisioned, the native automated access and usage analysis services MUST be enabled to continuously monitor for external or public access to resources, and unused access.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that collect, alert, and retain IAM-related events.", + "Section": "CCC.IAM.CN11 Enable Continuous IAM Access and Usage Analysis", + "SubSection": "", + "SubSectionObjective": "Enable and configure the cloud provider's native access and usage analysis services to continuously monitor for external access paths and internal unused access.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH02", + "CCC.IAM.TH10", + "CCC.IAM.TH11" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "ID.RA-01", + "ID.IM-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2", + "CA-7", + "RA-5" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN01.AR01", + "Description": "Untrusted input such as user queries, RAG data or tool output MUST be validated before it is passed to a GenAI model.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN01 Model Input Filtering and Sanitisation", + "SubSection": "", + "SubSectionObjective": "Inspect and validate input before it is passed to a GenAI model in order to filter or sanitise adversarial queries and prevent sensitive data leakage.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH01", + "CCC.GenAI.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-003", + "AIR-PREV-017", + "AIR-PREV-002", + "AIR-DET-001" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Input Validation and Sanitization" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0020", + "AML.M0021", + "AML.M0015" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN01.AR02", + "Description": "If malicious patterns such as prompt injection or sensitive data are detected during input validation, the input MUST be blocked or sanitised.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN01 Model Input Filtering and Sanitisation", + "SubSection": "", + "SubSectionObjective": "Inspect and validate input before it is passed to a GenAI model in order to filter or sanitise adversarial queries and prevent sensitive data leakage.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH01", + "CCC.GenAI.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-003", + "AIR-PREV-017", + "AIR-PREV-002", + "AIR-DET-001" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Input Validation and Sanitization" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0020", + "AML.M0021", + "AML.M0015" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN02.AR01", + "Description": "GenAI model output MUST be validated for format conformance, malicious patterns, sensitive data and inapropriate content before being passed to users, application or plugins.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN02 Model Output Filtering and Sanitisation", + "SubSection": "", + "SubSectionObjective": "Inspect and validate GenAI model output before passing it to users, applications or plugins in order to filter or sanitise insecure or unreliable output and prevent sensitive data leakage.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH01", + "CCC.GenAI.TH03", + "CCC.GenAI.TH04", + "CCC.GenAI.TH05", + "CCC.GenAI.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-003", + "AIR-PREV-017", + "AIR-PREV-002", + "AIR-DET-001" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Output Validation and Sanitization" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0020", + "AML.M0002" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN02.AR02", + "Description": "In the event of policy violations, the AI-generated content MUST be redacted, encoded or rejected.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN02 Model Output Filtering and Sanitisation", + "SubSection": "", + "SubSectionObjective": "Inspect and validate GenAI model output before passing it to users, applications or plugins in order to filter or sanitise insecure or unreliable output and prevent sensitive data leakage.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH01", + "CCC.GenAI.TH03", + "CCC.GenAI.TH04", + "CCC.GenAI.TH05", + "CCC.GenAI.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-003", + "AIR-PREV-017", + "AIR-PREV-002", + "AIR-DET-001" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Output Validation and Sanitization" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0020", + "AML.M0002" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN03.AR01", + "Description": "When data is designated for model training or RAG ingestion, then its source MUST be explicitly approved and its provenance documented.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN03 Data Provenance and Source Vetting", + "SubSection": "", + "SubSectionObjective": "Ensure that all data for training, fine-tuning or RAG comes from trusted, approved sources and is authorised for the intended purposes in order to prevent the initial introduction of malicious content or leaked sensitive data.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH02", + "CCC.GenAI.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-006" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Training Data Management" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0025" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN03.AR02", + "Description": "Data from unvetted sources MUST NOT be used in production systems.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN03 Data Provenance and Source Vetting", + "SubSection": "", + "SubSectionObjective": "Ensure that all data for training, fine-tuning or RAG comes from trusted, approved sources and is authorised for the intended purposes in order to prevent the initial introduction of malicious content or leaked sensitive data.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH02", + "CCC.GenAI.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-006" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Training Data Management" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0025" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN04.AR01", + "Description": "When data is ingested for training, fine-tuning or conversion to vector embeddings, it MUST be validated for sensitive information or malicious content.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN04 Sanitisation of Ingested Data", + "SubSection": "", + "SubSectionObjective": "Validate and sanitise all data ingested by GenAI systems from extenal sources or internal knowledge bases, whether for training, conversion to vector embeddings, or real-time retireval, in order to remove or redact poisoned or sensitive data before further processing.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH02", + "CCC.GenAI.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-002" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Training Data Sanitization" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0007" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN04.AR02", + "Description": "If sensitive data or malicious content is detected, it must be rejected, redacted or flagged for manual review.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN04 Sanitisation of Ingested Data", + "SubSection": "", + "SubSectionObjective": "Validate and sanitise all data ingested by GenAI systems from extenal sources or internal knowledge bases, whether for training, conversion to vector embeddings, or real-time retireval, in order to remove or redact poisoned or sensitive data before further processing.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH02", + "CCC.GenAI.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-002" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Training Data Sanitization" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0007" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN05.AR01", + "Description": "When a RAG-enabled system generates a response containing information retrieved from its knowledge base, then the response MUST include a verifiable citation that links back to the specific source document.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN05 Citations and Source Traceability", + "SubSection": "", + "SubSectionObjective": "Require the GenAI system to provide citations or direct links back to the source documents used to generate a response, in to enhance the transparency, trustworthiness, and verifiability of AI-generated content.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH09", + "CCC.GenAI.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-DET-013" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN06.AR01", + "Description": "When an LLM invokes an external tool (e.g., an API, a plugin), then the tool MUST operate with the least privileges required for performing its intended functionality.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.GenAI.CN06 Least Privilege for Plugins", + "SubSection": "", + "SubSectionObjective": "Restricts the permissions of any external tools the GenAI system can call to limit the potential damage if an agent is coerced to perform unintended actions or vulnerabilities in the tools are exploited.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH07", + "CCC.GenAI.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Agent Permissions" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN07.AR01", + "Description": "When an application makes an API call to a foundational model in a production environment, then it MUST specify an explicit version identifier.", + "Attributes": [ + { + "FamilyName": "Configuration Management", + "FamilyDescription": "The Configuration Management control family involves establishing, maintaining and monitoring the configuration of the service and related applications and infrastructure to ensure consistency, secure defaults and compliance.", + "Section": "CCC.GenAI.CN07 Model Version Pinning", + "SubSection": "", + "SubSectionObjective": "Mandate that applications are locked (\"pinned\") to a specific, tested version of a foundational model to prevent unexpected behaviour changes introduced by provider-side updates.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH10" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-010" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN08.AR01", + "Description": "When a new AI model is considered for production deployment, it MUST undergo a formal red teaming and quality assurance review.", + "Attributes": [ + { + "FamilyName": "Model Assurance and Evaluation", + "FamilyDescription": "The Model Assurance and Evaluation control family encompasses the proactiveand continuous processes of testing and validating the AI model's behavior to ensure it aligns with safety, ethical, and quality standards.", + "Section": "CCC.GenAI.CN08 Quality Control and Red Teaming", + "SubSection": "", + "SubSectionObjective": "Establish a formal program for quality evaluation and adversarial testing (red teaming) to ensure GenAI system meet all business, quality, security and compliance requirements before getting deployed into production environments.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH01", + "CCC.GenAI.TH02", + "CCC.GenAI.TH04", + "CCC.GenAI.TH08", + "CCC.GenAI.TH10" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-005" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Adversarial Training and Testing", + "Red Teaming", + "Product Governance" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0008" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN08.AR02", + "Description": "If model quality review or red teaming identifies an issue that exceeds the organization's risk tolerance, the model MUST NOT be deployed until the issue is remediated.", + "Attributes": [ + { + "FamilyName": "Model Assurance and Evaluation", + "FamilyDescription": "The Model Assurance and Evaluation control family encompasses the proactiveand continuous processes of testing and validating the AI model's behavior to ensure it aligns with safety, ethical, and quality standards.", + "Section": "CCC.GenAI.CN08 Quality Control and Red Teaming", + "SubSection": "", + "SubSectionObjective": "Establish a formal program for quality evaluation and adversarial testing (red teaming) to ensure GenAI system meet all business, quality, security and compliance requirements before getting deployed into production environments.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH01", + "CCC.GenAI.TH02", + "CCC.GenAI.TH04", + "CCC.GenAI.TH08", + "CCC.GenAI.TH10" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-005" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Adversarial Training and Testing", + "Red Teaming", + "Product Governance" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0008" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN01.AR01", + "Description": "Verify that only authorized users can access MLDE resources, and that access modes are properly defined and enforced.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN01 Define Access Mode for ML Development Environments", + "SubSection": "", + "SubSectionObjective": "Ensure that access to Machine Learning Development Environment (MLDE) resources is strictly defined and controlled. Only authorized users with appropriate permissions can access these environments, mitigating the risk of unauthorized access, data leakage, or service disruption.", + "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green", + "tlp-clear" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01", + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-3" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.1.1", + "2013 A.9.2.1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2", + "AC-3" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-01", + "IAM-02" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN03.AR01", + "Description": "Verify that root access is disabled on MLDE instances containing sensitive data.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN03 Disable Root Access on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Prevent users from obtaining root access on MLDE instances to reduce the risk of unauthorized system modifications and potential security breaches.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-08", + "IAM-12" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.2.3" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN03.AR02", + "Description": "For MLDE instances without sensitive data, ensure that root access is only enabled when necessary and properly authorized.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN03 Disable Root Access on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Prevent users from obtaining root access on MLDE instances to reduce the risk of unauthorized system modifications and potential security breaches.", + "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green", + "tlp-clear" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-08", + "IAM-12" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.2.3" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN04.AR01", + "Description": "Verify that terminal access is disabled on MLDE instances containing sensitive data.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN04 Disable Terminal Access on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Prevent users from accessing the terminal on MLDE instances to limit the risk of unauthorized commands and potential system compromise.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-08" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.2.3" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN04.AR02", + "Description": "For MLDE instances without sensitive data, ensure that terminal access is only enabled when necessary and properly authorized.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN04 Disable Terminal Access on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Prevent users from accessing the terminal on MLDE instances to limit the risk of unauthorized commands and potential system compromise.", + "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green", + "tlp-clear" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-08" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.2.3" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN02.AR01", + "Description": "Confirm that file download functionality is disabled on MLDE instances containing sensitive data.", + "Attributes": [ + { + "FamilyName": "Data Protection", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN02 Disable File Downloads on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Prevent unauthorized file downloads from MLDE instances to protect sensitive data from being exfiltrated.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH02", + "CCC.Core.TH02" ] } ], @@ -4514,168 +7041,98 @@ { "ReferenceId": "CCM", "Identifiers": [ - "DSP-10", - "DSP-19" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-4" - ] - } - ] - } - ], - "Checks": [ - "storage_cross_tenant_replication_disabled", - "cosmosdb_account_firewall_use_selected_networks", - "cosmosdb_account_use_private_endpoints", - "containerregistry_uses_private_link", - "keyvault_private_endpoints", - "storage_ensure_private_endpoints_in_storage_accounts" - ] - }, - { - "Id": "CCC.Core.CN02.AR01", - "Description": "When data is stored, it MUST be encrypted using the latest industry-standard encryption methods.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN02 Encrypt Data for Storage", - "SubSection": "", - "SubSectionObjective": "Ensure that all data stored is encrypted at rest using strong encryption algorithms.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-03", - "CEK-04", - "UEM-08", - "DSP-17" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-13", - "SC-28" - ] - } - ] - } - ], - "Checks": [ - "storage_infrastructure_encryption_is_enabled", - "storage_ensure_encryption_with_customer_managed_keys", - "monitor_storage_account_with_activity_logs_cmk_encrypted", - "vm_ensure_attached_disks_encrypted_with_cmk", - "vm_ensure_unattached_disks_encrypted_with_cmk", - "sqlserver_tde_encrypted_with_cmk", - "sqlserver_tde_encryption_enabled", - "databricks_workspace_cmk_encryption_enabled" - ] - }, - { - "Id": "CCC.Core.CN11.AR01", - "Description": "When encryption keys are used, the service MUST verify that all encryption keys use the latest industry-standard cryptographic algorithms.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN11 Protect Encryption Keys", - "SubSection": "", - "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH16" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-08", - "CEK-10", - "CEK-12" + "DSI-05", + "DSI-07" ] }, { "ReferenceId": "ISO_27001", "Identifiers": [ - "2013 A.10.1.2" + "2013 A.13.2.1" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "SC-12", - "SC-17" + "SC-7", + "SC-8" ] } ] } ], - "Checks": [ - "storage_ensure_encryption_with_customer_managed_keys", - "databricks_workspace_cmk_encryption_enabled", - "keyvault_key_rotation_enabled", - "keyvault_key_expiration_set_in_non_rbac", - "keyvault_rbac_key_expiration_set", - "keyvault_rbac_secret_expiration_set", - "keyvault_non_rbac_secret_expiration_set", - "monitor_storage_account_with_activity_logs_cmk_encrypted", - "storage_smb_channel_encryption_with_secure_algorithm", - "storage_secure_transfer_required_is_enabled", - "storage_ensure_minimum_tls_version_12" - ] + "Checks": [] }, { - "Id": "CCC.Core.CN11.AR02", - "Description": "When encryption keys are used, the service MUST rotate active keys within 180 days of issuance.", + "Id": "CCC.MLDE.CN02.AR02", + "Description": "For MLDE instances without sensitive data, ensure that file downloads are monitored and logged.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN11 Protect Encryption Keys", + "FamilyName": "Data Protection", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN02 Disable File Downloads on MLDE Instances", "SubSection": "", - "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "SubSectionObjective": "Prevent unauthorized file downloads from MLDE instances to protect sensitive data from being exfiltrated.", "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green", + "tlp-clear" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH02", + "CCC.Core.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.DS-5" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSI-05", + "DSI-07" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.13.2.1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-7", + "SC-8" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN05.AR01", + "Description": "Verify that only approved VM and container images can be selected when creating MLDE instances.", + "Attributes": [ + { + "FamilyName": "Configuration Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN05 Restrict Environment Options on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Limit the virtual machine and container image options available when creating new MLDE instances to approved and secure configurations.", + "Applicability": [ + "tlp-red", "tlp-amber" ], "Recommendation": "", @@ -4683,7 +7140,7 @@ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH16" + "CCC.MLDE.TH04" ] } ], @@ -4691,51 +7148,43 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-1" + "PR.IP-1" ] }, { "ReferenceId": "CCM", "Identifiers": [ - "CEK-08", - "CEK-10", - "CEK-12" + "TVM-02" ] }, { "ReferenceId": "ISO_27001", "Identifiers": [ - "2013 A.10.1.2" + "2013 A.12.5.1" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "SC-12", - "SC-17" + "CM-2" ] } ] } ], - "Checks": [ - "keyvault_key_rotation_enabled", - "storage_key_rotation_90_days", - "databricks_workspace_cmk_encryption_enabled" - ] + "Checks": [] }, { - "Id": "CCC.Core.CN11.AR03", - "Description": "When encrypting data, the service MUST verify that customer-managed encryption keys (CMEKs) are used.", + "Id": "CCC.MLDE.CN05.AR02", + "Description": "Attempt to create an MLDE instance with an unapproved image and confirm that it is denied.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "", - "SubSection": "CCC.Core.CN11 Protect Encryption Keys", - "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "FamilyName": "Configuration Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN05 Restrict Environment Options on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Limit the virtual machine and container image options available when creating new MLDE instances to approved and secure configurations.", "Applicability": [ - "tlp-amber", "tlp-red" ], "Recommendation": "", @@ -4743,7 +7192,7 @@ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH16" + "CCC.MLDE.TH04" ] } ], @@ -4751,52 +7200,371 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-1" + "PR.IP-1" ] }, { "ReferenceId": "CCM", "Identifiers": [ - "CEK-08", - "CEK-10", - "CEK-12" + "TVM-02" ] }, { "ReferenceId": "ISO_27001", "Identifiers": [ - "2013 A.10.1.2" + "2013 A.12.5.1" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "SC-12", - "SC-17" + "CM-2" ] } ] } ], - "Checks": [ - "databricks_workspace_cmk_encryption_enabled", - "storage_ensure_encryption_with_customer_managed_keys", - "vm_ensure_attached_disks_encrypted_with_cmk", - "vm_ensure_unattached_disks_encrypted_with_cmk", - "sqlserver_tde_encrypted_with_cmk", - "monitor_storage_account_with_activity_logs_cmk_encrypted" - ] + "Checks": [] }, { - "Id": "CCC.Core.CN11.AR04", - "Description": "When encryption keys are accessed, the service MUST verify that access to encryption keys is restricted to authorized personnel and services, following the principle of least privilege.", + "Id": "CCC.MLDE.CN06.AR01", + "Description": "Verify that automatic scheduled upgrades are enabled on user-managed MLDE instances containing sensitive data.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN11 Protect Encryption Keys", + "FamilyName": "Vulnerability Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN06 Require Automatic Scheduled Upgrades on User-Managed MLDE Instances", "SubSection": "", - "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "SubSectionObjective": "Ensure that MLDE instances are kept up-to-date with the latest security patches by enforcing automatic scheduled upgrades.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH04", + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.IP-12" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "TVM-01", + "TVM-02" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.12.6.1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-2" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN06.AR02", + "Description": "Ensure that the upgrade schedule is appropriately configured and does not interfere with critical operations.", + "Attributes": [ + { + "FamilyName": "Vulnerability Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN06 Require Automatic Scheduled Upgrades on User-Managed MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Ensure that MLDE instances are kept up-to-date with the latest security patches by enforcing automatic scheduled upgrades.", + "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green", + "tlp-clear" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH04", + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.IP-12" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "TVM-01", + "TVM-02" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.12.6.1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-2" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN07.AR01", + "Description": "Verify that MLDE instances containing sensitive data cannot be accessed via public IP addresses.", + "Attributes": [ + { + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN07 Restrict Public IP Access on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Prevent public IP access to MLDE instances to reduce exposure to the internet and enhance security.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH02", + "CCC.VPC.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-3" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "SEF-05" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.13.1.1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-7" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN07.AR02", + "Description": "For MLDE instances without sensitive data requiring public access, ensure that appropriate security controls are in place and access is approved.", + "Attributes": [ + { + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN07 Restrict Public IP Access on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Prevent public IP access to MLDE instances to reduce exposure to the internet and enhance security.", + "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green", + "tlp-clear" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH02", + "CCC.VPC.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-3" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "SEF-05" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.13.1.1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-7" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN08.AR01", + "Description": "Verify that MLDE instances containing sensitive data can only be deployed in approved virtual networks with appropriate security controls.", + "Attributes": [ + { + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN08 Restrict Virtual Networks for MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Limit the virtual networks that can be used when creating new MLDE instances to ensure they are deployed within approved and secure network environments.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01", + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-12" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.1.2" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN08.AR02", + "Description": "Ensure that MLDE instances without sensitive data are deployed in networks that meet organizational security standards.", + "Attributes": [ + { + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN08 Restrict Virtual Networks for MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Limit the virtual networks that can be used when creating new MLDE instances to ensure they are deployed within approved and secure network environments.", + "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green", + "tlp-clear" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01", + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-12" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.1.2" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Message.CN01.AR01", + "Description": "Attempt to publish a message without using a customer-managed encryption key and verify that the message is rejected or not stored.", + "Attributes": [ + { + "FamilyName": "Encryption", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.Message.CN01 Use Customer-Managed Encryption Keys (CMEK) for Messages", + "SubSection": "", + "SubSectionObjective": "Ensure that messages are encrypted using customer-managed encryption keys (CMEK) to provide enhanced control over encryption processes and keys, meeting compliance and security requirements.", "Applicability": [ "tlp-clear", "tlp-green", @@ -4808,7 +7576,7 @@ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH16" + "CCC.Core.TH01" ] } ], @@ -4819,546 +7587,82 @@ "PR.DS-1" ] }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-08", - "CEK-10", - "CEK-12" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.10.1.2" - ] - }, { "ReferenceId": "NIST_800_53", "Identifiers": [ "SC-12", - "SC-17" + "SC-13" ] } ] } ], - "Checks": [ - "databricks_workspace_cmk_encryption_enabled", - "keyvault_rbac_enabled", - "keyvault_key_rotation_enabled", - "keyvault_key_expiration_set_in_non_rbac", - "keyvault_rbac_key_expiration_set", - "keyvault_access_only_through_private_endpoints", - "keyvault_private_endpoints", - "keyvault_logging_enabled", - "keyvault_recoverable", - "storage_ensure_encryption_with_customer_managed_keys" - ] + "Checks": [] }, { - "Id": "CCC.Core.CN11.AR05", - "Description": "When encryption keys are used, the service MUST rotate active keys within 365 days of issuance.", + "Id": "CCC.SvlsComp.CN01.AR01", + "Description": "Attempt to access the serverless function over the public internet and verify that access is denied.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN11 Protect Encryption Keys", + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.SvlsComp.CN01 Enforce Use of Private Endpoints for Serverless Function", "SubSection": "", - "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "SubSectionObjective": "Ensure that the serverless function is accessible only through a private endpoint, allowing it to communicate securely within a virtual private network and preventing unauthorized external access.", "Applicability": [ - "tlp-clear", - "tlp-green" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH16" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-08", - "CEK-10", - "CEK-12" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.10.1.2" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-12", - "SC-17" - ] - } - ] - } - ], - "Checks": [ - "keyvault_key_rotation_enabled", - "storage_ensure_encryption_with_customer_managed_keys", - "databricks_workspace_cmk_encryption_enabled", - "storage_key_rotation_90_days", - "keyvault_rbac_key_expiration_set", - "keyvault_rbac_secret_expiration_set", - "keyvault_key_expiration_set_in_non_rbac" - ] - }, - { - "Id": "CCC.Core.CN11.AR06", - "Description": "When encryption keys are used, the service MUST rotate active keys within 90 days of issuance.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN11 Protect Encryption Keys", - "SubSection": "", - "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", - "Applicability": [ - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH16" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-08", - "CEK-10", - "CEK-12" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.10.1.2" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-12", - "SC-17" - ] - } - ] - } - ], - "Checks": [ - "keyvault_key_rotation_enabled" - ] - }, - { - "Id": "CCC.Core.CN14.AR01", - "Description": "When backups are created for disaster recovery purposes, the storage mechanism MUST NOT allow modification or deletion within 30 days of creation.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN14 Maintain Recent Backups", - "SubSection": "", - "SubSectionObjective": "Ensure that all backups used for disaster recovery are recent and subject to a retention policy that limits deletion.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Use immutable storage solutions where possible. Implement backup retention policies that enforce a minimum retention period of 30 days. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [] - } - ], - "Checks": [ - "vm_backup_enabled", - "vm_sufficient_daily_backup_retention_period" - ] - }, - { - "Id": "CCC.Core.CN14.AR02", - "Description": "When backups are created for disaster recovery purposes, the most recent backup MUST have a creation date within the past 30 days.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN14 Maintain Recent Backups", - "SubSection": "", - "SubSectionObjective": "Ensure that all backups used for disaster recovery are recent and subject to a retention policy that limits deletion.", - "Applicability": [ - "tlp-clear", - "tlp-green", + "tlp-red", "tlp-amber" ], - "Recommendation": "Implement automated backup processes to ensure that backups are created regularly. Monitor backup schedules and verify that the most recent backup creation date is within the last 30 days. ", + "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH06" + "CCC.Core.TH01" ] } ], - "SectionGuidelineMappings": [] - } - ], - "Checks": [ - "vm_backup_enabled", - "vm_sufficient_daily_backup_retention_period" - ] - }, - { - "Id": "CCC.Core.CN14.AR02", - "Description": "When backups are created for disaster recovery purposes, the most recent backup MUST have a creation date within the past 14 days.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN14 Maintain Recent Backups", - "SubSection": "", - "SubSectionObjective": "Ensure that all backups used for disaster recovery are recent and subject to a retention policy that limits deletion.", - "Applicability": [ - "tlp-red" - ], - "Recommendation": "Implement automated backup processes to ensure that backups are created regularly. Monitor backup schedules and verify that the most recent backup creation date is within the last 14 days. ", - "SectionThreatMappings": [ + "SectionGuidelineMappings": [ { - "ReferenceId": "CCC", + "ReferenceId": "NIST-CSF", "Identifiers": [ - "CCC.TH06" + "PR.AC-5" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-7", + "SC-8" ] } - ], - "SectionGuidelineMappings": [] + ] } ], "Checks": [ - "vm_backup_enabled", - "vm_sufficient_daily_backup_retention_period" + "app_function_not_publicly_accessible" ] }, { - "Id": "CCC.Core.CN03.AR01", - "Description": "When an entity attempts to modify the service through a user interface, the authentication process MUST require multiple identifying factors for authentication.", + "Id": "CCC.SvlsComp.CN02.AR01", + "Description": "Send requests to invoke the function up to the allowed threshold and confirm they are successful; then send additional requests exceeding the threshold from the same entity and verify that they are denied.", "Attributes": [ { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", + "FamilyName": "Availability", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.SvlsComp.CN02 Implement Function Invocation Rate Limits", "SubSection": "", - "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", + "SubSectionObjective": "Ensure that function invocation is limited to a specified threshold from any single entity, preventing resource exhaustion and denial of service attacks.", "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" + "tlp-red", + "tlp-amber" ], "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-14" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-7" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-03", - "IAM-08" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.9.4.2" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "IA-2" - ] - } - ] - } - ], - "Checks": [ - "entra_non_privileged_user_has_mfa", - "entra_privileged_user_has_mfa", - "entra_conditional_access_policy_require_mfa_for_management_api", - "entra_security_defaults_enabled", - "entra_user_with_vm_access_has_mfa" - ] - }, - { - "Id": "CCC.Core.CN03.AR02", - "Description": "When an entity attempts to modify the service through an API endpoint, the authentication process MUST require a credential such as an API key or token AND originate from within the trust perimeter.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", - "SubSection": "", - "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-14" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-7" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-03", - "IAM-08" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.9.4.2" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "IA-2" - ] - } - ] - } - ], - "Checks": [ - "entra_conditional_access_policy_require_mfa_for_management_api", - "entra_non_privileged_user_has_mfa", - "entra_privileged_user_has_mfa", - "entra_security_defaults_enabled" - ] - }, - { - "Id": "CCC.Core.CN03.AR03", - "Description": "When an entity attempts to view information on the service through a user interface, the authentication process MUST require multiple identifying factors from the user.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", - "SubSection": "", - "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-14" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-7" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-03", - "IAM-08" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.9.4.2" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "IA-2" - ] - } - ] - } - ], - "Checks": [ - "entra_conditional_access_policy_require_mfa_for_management_api", - "entra_non_privileged_user_has_mfa", - "entra_privileged_user_has_mfa", - "entra_security_defaults_enabled" - ] - }, - { - "Id": "CCC.Core.CN03.AR04", - "Description": "When an entity attempts to view information on the service through an API endpoint, the authentication process MUST require a credential such as an API key or token AND originate from within the trust perimeter.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", - "SubSection": "", - "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-14" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-7" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-03", - "IAM-08" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.9.4.2" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "IA-2" - ] - } - ] - } - ], - "Checks": [ - "entra_conditional_access_policy_require_mfa_for_management_api", - "entra_non_privileged_user_has_mfa", - "entra_privileged_user_has_mfa", - "entra_user_with_vm_access_has_mfa" - ] - }, - { - "Id": "CCC.Core.CN05.AR01", - "Description": "When an attempt is made to modify data on the service or a child resource, the service MUST block requests from unauthorized entities.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", - "SubSection": "", - "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" + "CCC.Core.TH12" ] } ], @@ -5366,782 +7670,19 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.AC-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-01", - "DSP-07", - "DSP-08", - "DSP-10", - "DSP-17" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.3" + "PR.DS-4" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "AC-3" + "SC-5" ] } ] } ], - "Checks": [ - "aks_cluster_rbac_enabled", - "aks_network_policy_enabled", - "aks_clusters_public_access_disabled", - "app_client_certificates_on", - "app_ensure_auth_is_set_up", - "app_register_with_identity", - "app_function_identity_is_configured", - "app_function_identity_without_admin_privileges", - "app_function_not_publicly_accessible", - "app_function_access_keys_configured", - "iam_custom_role_has_permissions_to_administer_resource_locks", - "iam_subscription_roles_owner_custom_not_created", - "iam_role_user_access_admin_restricted", - "keyvault_rbac_enabled", - "keyvault_private_endpoints", - "keyvault_access_only_through_private_endpoints", - "entra_conditional_access_policy_require_mfa_for_management_api", - "entra_global_admin_in_less_than_five_users", - "entra_non_privileged_user_has_mfa", - "entra_security_defaults_enabled", - "entra_trusted_named_locations_exists", - "entra_user_with_vm_access_has_mfa", - "vm_jit_access_enabled", - "vm_linux_enforce_ssh_authentication" - ] - }, - { - "Id": "CCC.Core.CN05.AR02", - "Description": "When administrative access or configuration change is attempted on the service or a child resource, the service MUST refuse requests from unauthorized entities.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", - "SubSection": "", - "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-01", - "DSP-07", - "DSP-08", - "DSP-10", - "DSP-17" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.3" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "aks_cluster_rbac_enabled", - "app_register_with_identity", - "app_function_identity_is_configured", - "app_function_identity_without_admin_privileges", - "iam_custom_role_has_permissions_to_administer_resource_locks", - "iam_subscription_roles_owner_custom_not_created", - "iam_role_user_access_admin_restricted", - "containerregistry_not_publicly_accessible", - "containerregistry_uses_private_link", - "containerregistry_admin_user_disabled", - "cosmosdb_account_use_private_endpoints", - "cosmosdb_account_firewall_use_selected_networks", - "cosmosdb_account_use_aad_and_rbac", - "keyvault_rbac_enabled", - "keyvault_private_endpoints", - "keyvault_access_only_through_private_endpoints", - "storage_account_key_access_disabled", - "storage_ensure_private_endpoints_in_storage_accounts", - "storage_ensure_azure_services_are_trusted_to_access_is_enabled", - "storage_default_to_entra_authorization_enabled", - "network_http_internet_access_restricted", - "network_rdp_internet_access_restricted", - "network_ssh_internet_access_restricted", - "network_udp_internet_access_restricted", - "aks_clusters_public_access_disabled", - "app_function_not_publicly_accessible", - "entra_global_admin_in_less_than_five_users", - "entra_conditional_access_policy_require_mfa_for_management_api", - "entra_non_privileged_user_has_mfa", - "entra_privileged_user_has_mfa", - "entra_trusted_named_locations_exists", - "entra_user_with_vm_access_has_mfa" - ] - }, - { - "Id": "CCC.Core.CN05.AR03", - "Description": "When administrative access or configuration change is attempted on the service or a child resource in a multi-tenant environment, the service MUST refuse requests across tenant boundaries unless the origin is explicitly included in a pre-approved allowlist.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", - "SubSection": "", - "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-01", - "DSP-07", - "DSP-08", - "DSP-10", - "DSP-17" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.3" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "entra_conditional_access_policy_require_mfa_for_management_api", - "entra_global_admin_in_less_than_five_users", - "entra_non_privileged_user_has_mfa", - "iam_role_user_access_admin_restricted", - "entra_trusted_named_locations_exists", - "keyvault_private_endpoints", - "keyvault_access_only_through_private_endpoints", - "keyvault_rbac_enabled", - "cosmosdb_account_firewall_use_selected_networks", - "cosmosdb_account_use_private_endpoints", - "containerregistry_not_publicly_accessible", - "containerregistry_uses_private_link", - "containerregistry_admin_user_disabled", - "network_http_internet_access_restricted", - "network_rdp_internet_access_restricted", - "network_ssh_internet_access_restricted", - "network_udp_internet_access_restricted", - "app_function_not_publicly_accessible", - "storage_default_network_access_rule_is_denied", - "storage_ensure_private_endpoints_in_storage_accounts", - "storage_blob_public_access_level_is_disabled", - "storage_cross_tenant_replication_disabled" - ] - }, - { - "Id": "CCC.Core.CN05.AR04", - "Description": "When data is requested from outside the trust perimeter, the service MUST refuse requests from unauthorized entities.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "", - "SubSection": "CCC.Core.CN05 Prevent Access from Untrusted Entities", - "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-01", - "DSP-07", - "DSP-08", - "DSP-10", - "DSP-17" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.3" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "aks_cluster_rbac_enabled", - "aks_network_policy_enabled", - "aks_clusters_public_access_disabled", - "app_register_with_identity", - "app_function_access_keys_configured", - "app_function_identity_is_configured", - "app_function_identity_without_admin_privileges", - "app_ensure_auth_is_set_up", - "app_function_not_publicly_accessible", - "containerregistry_not_publicly_accessible", - "containerregistry_uses_private_link", - "containerregistry_admin_user_disabled", - "keyvault_private_endpoints", - "keyvault_access_only_through_private_endpoints", - "keyvault_rbac_enabled", - "storage_account_key_access_disabled", - "storage_default_to_entra_authorization_enabled", - "storage_ensure_azure_services_are_trusted_to_access_is_enabled", - "storage_default_network_access_rule_is_denied", - "storage_secure_transfer_required_is_enabled", - "iam_role_user_access_admin_restricted", - "iam_subscription_roles_owner_custom_not_created", - "cosmosdb_account_use_aad_and_rbac", - "sqlserver_azuread_administrator_enabled", - "sqlserver_unrestricted_inbound_access", - "network_http_internet_access_restricted", - "network_rdp_internet_access_restricted", - "network_ssh_internet_access_restricted", - "network_udp_internet_access_restricted", - "vm_jit_access_enabled", - "entra_conditional_access_policy_require_mfa_for_management_api", - "entra_global_admin_in_less_than_five_users", - "entra_non_privileged_user_has_mfa", - "entra_privileged_user_has_mfa", - "entra_trusted_named_locations_exists", - "entra_user_with_vm_access_has_mfa" - ] - }, - { - "Id": "CCC.Core.CN05.AR05", - "Description": "When any request is made from outside the trust perimeter, the service MUST NOT provide any response that may indicate the service exists.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", - "SubSection": "", - "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", - "Applicability": [ - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-01", - "DSP-07", - "DSP-08", - "DSP-10", - "DSP-17" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.3" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "aks_clusters_created_with_private_nodes", - "aks_clusters_public_access_disabled", - "aks_network_policy_enabled", - "app_function_not_publicly_accessible", - "app_register_with_identity", - "app_function_identity_is_configured", - "app_function_identity_without_admin_privileges", - "app_client_certificates_on", - "app_ensure_auth_is_set_up", - "containerregistry_not_publicly_accessible", - "containerregistry_uses_private_link", - "containerregistry_admin_user_disabled", - "keyvault_private_endpoints", - "keyvault_access_only_through_private_endpoints", - "keyvault_rbac_enabled", - "keyvault_logging_enabled", - "storage_account_key_access_disabled", - "storage_default_to_entra_authorization_enabled", - "storage_ensure_private_endpoints_in_storage_accounts", - "storage_ensure_azure_services_are_trusted_to_access_is_enabled", - "iam_custom_role_has_permissions_to_administer_resource_locks", - "iam_role_user_access_admin_restricted", - "iam_subscription_roles_owner_custom_not_created", - "vm_jit_access_enabled", - "vm_linux_enforce_ssh_authentication", - "network_http_internet_access_restricted", - "network_rdp_internet_access_restricted", - "network_ssh_internet_access_restricted", - "network_udp_internet_access_restricted" - ] - }, - { - "Id": "CCC.Core.CN05.AR06", - "Description": "When any request is made to the service or a child resource, the service MUST refuse requests from unauthorized entities.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", - "SubSection": "", - "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-01", - "DSP-07", - "DSP-08", - "DSP-10", - "DSP-17" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.3" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "aks_cluster_rbac_enabled", - "app_register_with_identity", - "app_function_identity_is_configured", - "app_function_identity_without_admin_privileges", - "app_function_access_keys_configured", - "app_function_not_publicly_accessible", - "iam_custom_role_has_permissions_to_administer_resource_locks", - "iam_role_user_access_admin_restricted", - "iam_subscription_roles_owner_custom_not_created", - "cosmosdb_account_use_aad_and_rbac", - "entra_global_admin_in_less_than_five_users", - "entra_non_privileged_user_has_mfa", - "entra_privileged_user_has_mfa", - "entra_conditional_access_policy_require_mfa_for_management_api", - "keyvault_rbac_enabled", - "vm_jit_access_enabled", - "vm_linux_enforce_ssh_authentication" - ] - }, - { - "Id": "CCC.Core.CN04.AR01", - "Description": "When administrative access or configuration change is attempted on the service or a child resource, the service MUST log the client identity, time, and result of the attempt.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements. ", - "Section": "CCC.Core.CN04 Log All Access and Changes", - "SubSection": "", - "SubSectionObjective": "Ensure that all access attempts are logged to maintain a detailed audit trail for security and compliance purposes.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.AE-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-08" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-2", - "AU-3", - "AU-12" - ] - } - ] - } - ], - "Checks": [ - "monitor_diagnostic_settings_exists", - "monitor_diagnostic_setting_with_appropriate_categories", - "keyvault_logging_enabled", - "network_flow_log_captured_sent", - "monitor_storage_account_with_activity_logs_is_private", - "monitor_storage_account_with_activity_logs_cmk_encrypted", - "monitor_alert_create_update_sqlserver_fr", - "monitor_alert_delete_nsg", - "monitor_alert_delete_public_ip_address_rule", - "monitor_alert_delete_security_solution", - "monitor_alert_create_policy_assignment", - "monitor_alert_create_update_public_ip_address_rule", - "monitor_alert_create_update_security_solution", - "monitor_alert_service_health_exists" - ] - }, - { - "Id": "CCC.Core.CN04.AR02", - "Description": "When any attempt is made to modify data on the service or a child resource, the service MUST log the client identity, time, and result of the attempt.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements. ", - "Section": "CCC.Core.CN04 Log All Access and Changes", - "SubSection": "", - "SubSectionObjective": "Ensure that all access attempts are logged to maintain a detailed audit trail for security and compliance purposes.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.AE-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-08" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-2", - "AU-3", - "AU-12" - ] - } - ] - } - ], - "Checks": [ - "monitor_diagnostic_settings_exists", - "monitor_diagnostic_setting_with_appropriate_categories", - "keyvault_logging_enabled", - "monitor_storage_account_with_activity_logs_is_private", - "monitor_storage_account_with_activity_logs_cmk_encrypted", - "network_flow_log_captured_sent" - ] - }, - { - "Id": "CCC.Core.CN04.AR03", - "Description": "When any attempt is made to read data on the service or a child resource, the service MUST log the client identity, time, and result of the attempt.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements. ", - "Section": "CCC.Core.CN04 Log All Access and Changes", - "SubSection": "", - "SubSectionObjective": "Ensure that all access attempts are logged to maintain a detailed audit trail for security and compliance purposes.", - "Applicability": [ - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.AE-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-08" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-2", - "AU-3", - "AU-12" - ] - } - ] - } - ], - "Checks": [ - "monitor_diagnostic_settings_exists", - "monitor_diagnostic_setting_with_appropriate_categories", - "keyvault_logging_enabled", - "app_http_logs_enabled", - "network_flow_log_captured_sent", - "monitor_storage_account_with_activity_logs_is_private", - "monitor_storage_account_with_activity_logs_cmk_encrypted" - ] - }, - { - "Id": "CCC.Core.CN07.AR01", - "Description": "When enumeration activities are detected, the service MUST publish an event to a monitored channel which includes the client identity, time, and nature of the activity.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements. ", - "Section": "CCC.Core.CN07 Alert on Unusual Enumeration Activity", - "SubSection": "", - "SubSectionObjective": "Ensure that logs and associated alerts are generated when unusual enumeration activity is detected that may indicate reconnaissance activities.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Implement event publication mechanisms and alerts for patterns indicative of enumeration activities, such as repeated access attempts, requests, or liveness probes. Configure alerts to notify security teams of any activities that merit further investigation. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH15" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.AE-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-05", - "SEF-05" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-6" - ] - } - ] - } - ], - "Checks": [ - "apim_threat_detection_llm_jacking" - ] - }, - { - "Id": "CCC.Core.CN07.AR02", - "Description": "When enumeration activities are detected, the service MUST log the client identity, time, and nature of the activity.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements. ", - "Section": "CCC.Core.CN07 Alert on Unusual Enumeration Activity", - "SubSection": "", - "SubSectionObjective": "Ensure that logs and associated alerts are generated when unusual enumeration activity is detected that may indicate reconnaissance activities.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Implement logging mechanisms to capture details of enumeration activities, including client identity, timestamps, and activity nature. Retain logs according to organizational policies, and occasionally review them for patterns that may indicate reconnaissance activities. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH15" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.AE-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-05", - "SEF-05" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-6" - ] - } - ] - } - ], - "Checks": [ - "monitor_diagnostic_settings_exists", - "monitor_diagnostic_setting_with_appropriate_categories", - "monitor_alert_create_update_sqlserver_fr", - "monitor_alert_delete_nsg", - "monitor_alert_delete_public_ip_address_rule", - "monitor_alert_delete_security_solution", - "monitor_alert_create_policy_assignment", - "monitor_alert_create_update_nsg", - "monitor_alert_create_update_public_ip_address_rule", - "monitor_alert_create_update_security_solution", - "monitor_alert_service_health_exists", - "monitor_storage_account_with_activity_logs_cmk_encrypted", - "monitor_storage_account_with_activity_logs_is_private" - ] + "Checks": [] } ] } diff --git a/prowler/compliance/gcp/ccc_gcp.json b/prowler/compliance/gcp/ccc_gcp.json index e3060646c5..df6b15bc5c 100644 --- a/prowler/compliance/gcp/ccc_gcp.json +++ b/prowler/compliance/gcp/ccc_gcp.json @@ -1,10 +1,1630 @@ { "Framework": "CCC", - "Version": "", + "Version": "v2025.10", "Provider": "GCP", "Name": "Common Cloud Controls Catalog (CCC)", "Description": "Common Cloud Controls Catalog (CCC) for GCP", "Requirements": [ + { + "Id": "CCC.Core.CN01.AR01", + "Description": "When a port is exposed for non-SSH network traffic, all traffic MUST include a TLS handshake AND be encrypted using TLS 1.3 or higher.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN01 Encrypt Data for Transmission", + "SubSection": "", + "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Most cloud services enable TLS 1.3 by default. Where it is not already set, ensure that your services are configured or updated accordingly.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-03", + "CEK-04", + "IVS-03", + "IVS-07" + ] + } + ] + } + ], + "Checks": [ + "cloudsql_instance_ssl_connections" + ] + }, + { + "Id": "CCC.Core.CN01.AR02", + "Description": "When a port is exposed for SSH network traffic, all traffic MUST include a SSH handshake AND be encrypted using SSHv2 or higher.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN01 Encrypt Data for Transmission", + "SubSection": "", + "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Any time port 22 is exposed, ensure that it has a properly implemented SSH server with SSHv2 enabled and configured with strong ciphers.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-03", + "CEK-04", + "IVS-03", + "IVS-07" + ] + } + ] + } + ], + "Checks": [ + "compute_firewall_ssh_access_from_the_internet_allowed", + "compute_instance_block_project_wide_ssh_keys_disabled", + "compute_project_os_login_enabled", + "compute_project_os_login_2fa_enabled" + ] + }, + { + "Id": "CCC.Core.CN01.AR03", + "Description": "When the service receives unencrypted traffic, then it MUST either block the request or automatically redirect it to the secure equivalent.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN01 Encrypt Data for Transmission", + "SubSection": "", + "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Review firewall, load balancer, and application configurations to ensure insecure protocols such as HTTP, FTP, and Telnet are not exposed. Where possible, implement automatic redirection to secure protocols such as HTTPS, SFTP, SSH, and regularly scan for protocol drift.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-03", + "CEK-04", + "IVS-03", + "IVS-07" + ] + } + ] + } + ], + "Checks": [ + "cloudsql_instance_ssl_connections" + ] + }, + { + "Id": "CCC.Core.CN01.AR07", + "Description": "When a port is exposed, the service MUST ensure that the protocol and service officially assigned to that port number by the IANA Service Name and Transport Protocol Port Number Registry, and no other, is run on that port.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN01 Encrypt Data for Transmission", + "SubSection": "", + "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Reference the IANA Service Name and Transport Protocol Port Number Registry for more information about correct protocol-to-port assignments. Avoid running non-standard services on well-known ports.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-03", + "CEK-04", + "IVS-03", + "IVS-07" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Core.CN01.AR08", + "Description": "When a service transmits data using TLS, mutual TLS (mTLS) MUST be implemented to require both client and server certificate authentication for all connections.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN01 Encrypt Data for Transmission", + "SubSection": "", + "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Configure mTLS for all endpoints that process or transmit sensitive data. Ensure both client and server certificates are validated and managed securely. Regularly review certificate authorities and automate certificate rotation where possible.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-03", + "CEK-04", + "IVS-03", + "IVS-07" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Core.CN13.AR01", + "Description": "When a port is exposed that uses certificate-based encryption, the service MUST only use valid, unexpired certificates issued by a trusted certificate authority.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN13 Minimize Lifetime of Encryption and Authentication Certificates", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption and authentication certificates have a limited lifetime to reduce the risk of compromise and ensure the use of up-to-date security practices.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Track certificate expiration dates and automate certificate renewal where possible. Use certificate management tools to ensure only certificates from trusted authorities are deployed.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH18" + ] + } + ], + "SectionGuidelineMappings": [] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Core.CN13.AR02", + "Description": "When a port is exposed that uses certificate-based encryption, the service MUST rotate active certificates within 180 days of issuance.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN13 Minimize Lifetime of Encryption and Authentication Certificates", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption and authentication certificates have a limited lifetime to reduce the risk of compromise and ensure the use of up-to-date security practices.", + "Applicability": [ + "tlp-amber" + ], + "Recommendation": "Track certificate expiration dates and automate certificate renewal where possible. Use certificate management tools to ensure only certificates from trusted authorities are deployed.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH18" + ] + } + ], + "SectionGuidelineMappings": [] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Core.CN13.AR03", + "Description": "When a port is exposed that uses certificate-based encryption, the service MUST rotate active certificates within 90 days of issuance.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN13 Minimize Lifetime of Encryption and Authentication Certificates", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption and authentication certificates have a limited lifetime to reduce the risk of compromise and ensure the use of up-to-date security practices.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "Track certificate expiration dates and automate certificate renewal where possible. Use certificate management tools to ensure only certificates from trusted authorities are deployed.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH18" + ] + } + ], + "SectionGuidelineMappings": [] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Core.CN06.AR01", + "Description": "When the service is running, its region and availability zone MUST be included in a list of explicitly trusted or approved locations within the trust perimeter.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN06 Restrict Deployments to Trust Perimeter", + "SubSection": "", + "SubSectionObjective": "Ensure that the service and its child resources are only deployed on infrastructure in locations that are explicitly included within a defined trust perimeter.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Maintain an up-to-date list of trusted and approved regions based on organizational policies. Validate the service's deployment location is included in this list.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-19" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Core.CN06.AR02", + "Description": "When a child resource is deployed, its region and availability zone MUST be included in a list of explicitly trusted or approved locations within the trust perimeter.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN06 Restrict Deployments to Trust Perimeter", + "SubSection": "", + "SubSectionObjective": "Ensure that the service and its child resources are only deployed on infrastructure in locations that are explicitly included within a defined trust perimeter.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Maintain an up-to-date list of trusted and approved regions based on organizational policies. Validate that child resources can only be deployed to locations included in this list.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-19" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Core.CN08.AR01", + "Description": "When data is created or modified, the data MUST have a complete and recoverable duplicate that is stored in a physically separate data center.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN08 Replicate Data to Multiple Locations", + "SubSection": "", + "SubSectionObjective": "Ensure that data is replicated across multiple physical locations to protect against data loss due to hardware failures, natural disasters, or other catastrophic events.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Implement automated data replication processes to ensure that data is consistently duplicated in another region or availability zone. Regularly test data recovery from the replicated location to ensure integrity and availability.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "BCR-08", + "BCR-10", + "BCR-11" + ] + } + ] + } + ], + "Checks": [ + "cloudsql_instance_automated_backups" + ] + }, + { + "Id": "CCC.Core.CN08.AR02", + "Description": "When data is replicated into a second location, the service MUST be able to accurately represent the replication locations, replication status, and data synchronization status.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN08 Replicate Data to Multiple Locations", + "SubSection": "", + "SubSectionObjective": "Ensure that data is replicated across multiple physical locations to protect against data loss due to hardware failures, natural disasters, or other catastrophic events.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "BCR-08", + "BCR-10", + "BCR-11" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Core.CN09.AR01", + "Description": "When the service is operational, its logs and any child resource logs MUST NOT be accessible from the resource they record access to.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN09 Ensure Integrity of Access Logs", + "SubSection": "", + "SubSectionObjective": "Ensure that access logs are always recorded to an external location that cannot be manipulated from the context of the service(s) it contains logs for.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH07", + "CCC.Core.TH09", + "CCC.Core.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-02", + "LOG-04", + "LOG-09" + ] + } + ] + } + ], + "Checks": [ + "cloudstorage_bucket_log_retention_policy_lock" + ] + }, + { + "Id": "CCC.Core.CN09.AR02", + "Description": "When the service is operational, disabling the logs for the service or its child resources MUST NOT be possible without also disabling the corresponding resource.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN09 Ensure Integrity of Access Logs", + "SubSection": "", + "SubSectionObjective": "Ensure that access logs are always recorded to an external location that cannot be manipulated from the context of the service(s) it contains logs for.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "No normal business operations should disable logs, as this could indicate an attempt to cover up unauthorized access. Ensure that logging mechanisms are tightly integrated with service operations, so that logging cannot be disabled without stopping the service itself.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH07", + "CCC.Core.TH09", + "CCC.Core.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-02", + "LOG-04", + "LOG-09" + ] + } + ] + } + ], + "Checks": [ + "iam_audit_logs_enabled", + "logging_sink_created", + "cloudstorage_bucket_log_retention_policy_lock" + ] + }, + { + "Id": "CCC.Core.CN09.AR03", + "Description": "When the service is operational, any attempt to redirect logs for the service or its child resources MUST NOT be possible without halting operation of the corresponding resource and publishing corresponding events to monitored channels.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN09 Ensure Integrity of Access Logs", + "SubSection": "", + "SubSectionObjective": "Ensure that access logs are always recorded to an external location that cannot be manipulated from the context of the service(s) it contains logs for.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "No normal business operations should result in the redirection of logs, as this could indicate an attempt to cover up unauthorized access. Ensure that logging configurations are immutable during service operation so that any changes require stopping the service and publishing corresponding events to monitored channels.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH07", + "CCC.Core.TH09", + "CCC.Core.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-02", + "LOG-04", + "LOG-09" + ] + } + ] + } + ], + "Checks": [ + "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled" + ] + }, + { + "Id": "CCC.Core.CN10.AR01", + "Description": "When data is replicated, the service MUST ensure that replication only occurs to destinations that are explicitly included within the defined trust perimeter.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN10 Restrict Data Replication to Trust Perimeter", + "SubSection": "", + "SubSectionObjective": "Ensure that data is only replicated on infrastructure in locations that are explicitly included within a defined trust perimeter.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-10", + "DSP-19" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Core.CN02.AR01", + "Description": "When data is stored, it MUST be encrypted using the latest industry-standard encryption methods.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN02 Encrypt Data for Storage", + "SubSection": "", + "SubSectionObjective": "Ensure that all data stored is encrypted at rest using strong encryption algorithms.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-03", + "CEK-04", + "UEM-08", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "compute_instance_encryption_with_csek_enabled", + "dataproc_encrypted_with_cmks_disabled", + "bigquery_dataset_cmk_encryption", + "bigquery_table_cmk_encryption" + ] + }, + { + "Id": "CCC.Core.CN11.AR01", + "Description": "When encryption keys are used, the service MUST verify that all encryption keys use the latest industry-standard cryptographic algorithms.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN11 Protect Encryption Keys", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH16" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-08", + "CEK-10", + "CEK-12" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Core.CN11.AR02", + "Description": "When encryption keys are used, the service MUST rotate active keys within 180 days of issuance.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN11 Protect Encryption Keys", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "Applicability": [ + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH16" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-08", + "CEK-10", + "CEK-12" + ] + } + ] + } + ], + "Checks": [ + "kms_key_rotation_enabled" + ] + }, + { + "Id": "CCC.Core.CN11.AR03", + "Description": "When encrypting data, the service MUST verify that customer-managed encryption keys (CMEKs) are used.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN11 Protect Encryption Keys", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH16" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-08", + "CEK-10", + "CEK-12" + ] + } + ] + } + ], + "Checks": [ + "bigquery_dataset_cmk_encryption", + "bigquery_table_cmk_encryption", + "dataproc_encrypted_with_cmks_disabled", + "compute_instance_encryption_with_csek_enabled" + ] + }, + { + "Id": "CCC.Core.CN11.AR04", + "Description": "When encryption keys are accessed, the service MUST verify that access to encryption keys is restricted to authorized personnel and services, following the principle of least privilege.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN11 Protect Encryption Keys", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH16" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-08", + "CEK-10", + "CEK-12" + ] + } + ] + } + ], + "Checks": [ + "kms_key_not_publicly_accessible", + "iam_role_kms_enforce_separation_of_duties" + ] + }, + { + "Id": "CCC.Core.CN11.AR05", + "Description": "When encryption keys are used, the service MUST rotate active keys within 365 days of issuance.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN11 Protect Encryption Keys", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "Applicability": [ + "tlp-clear", + "tlp-green" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH16" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-08", + "CEK-10", + "CEK-12" + ] + } + ] + } + ], + "Checks": [ + "kms_key_rotation_enabled" + ] + }, + { + "Id": "CCC.Core.CN11.AR06", + "Description": "When encryption keys are used, the service MUST rotate active keys within 90 days of issuance.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN11 Protect Encryption Keys", + "SubSection": "", + "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH16" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "CEK-08", + "CEK-10", + "CEK-12" + ] + } + ] + } + ], + "Checks": [ + "kms_key_rotation_enabled" + ] + }, + { + "Id": "CCC.Core.CN14.AR01", + "Description": "When backups are created for disaster recovery purposes, the storage mechanism MUST NOT allow modification or deletion within 30 days of creation.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN14 Maintain Recent Backups", + "SubSection": "", + "SubSectionObjective": "Ensure that all backups used for disaster recovery are recent and subject to a retention policy that limits deletion.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Use immutable storage solutions where possible. Implement backup retention policies that enforce a minimum retention period of 30 days.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [] + } + ], + "Checks": [ + "cloudsql_instance_automated_backups", + "cloudstorage_bucket_log_retention_policy_lock", + "cloudstorage_bucket_sufficient_retention_period" + ] + }, + { + "Id": "CCC.Core.CN14.AR02", + "Description": "When backups are created for disaster recovery purposes, the most recent backup MUST have a creation date within the past 30 days.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN14 Maintain Recent Backups", + "SubSection": "", + "SubSectionObjective": "Ensure that all backups used for disaster recovery are recent and subject to a retention policy that limits deletion.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber" + ], + "Recommendation": "Implement automated backup processes to ensure that backups are created regularly. Monitor backup schedules and verify that the most recent backup creation date is within the last 30 days.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [] + } + ], + "Checks": [ + "cloudsql_instance_automated_backups" + ] + }, + { + "Id": "CCC.Core.CN14.AR03", + "Description": "When backups are created for disaster recovery purposes, the most recent backup MUST have a creation date within the past 14 days.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.Core.CN14 Maintain Recent Backups", + "SubSection": "", + "SubSectionObjective": "Ensure that all backups used for disaster recovery are recent and subject to a retention policy that limits deletion.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "Implement automated backup processes to ensure that backups are created regularly. Monitor backup schedules and verify that the most recent backup creation date is within the last 14 days.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [] + } + ], + "Checks": [ + "cloudsql_instance_automated_backups" + ] + }, + { + "Id": "CCC.Core.CN03.AR01", + "Description": "When an entity attempts to modify the service through a user interface, the authentication process MUST require multiple identifying factors for authentication.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", + "SubSection": "", + "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-14" + ] + } + ] + } + ], + "Checks": [ + "compute_project_os_login_2fa_enabled" + ] + }, + { + "Id": "CCC.Core.CN03.AR02", + "Description": "When an entity attempts to modify the service through an API endpoint, the authentication process MUST require a credential such as an API key or token AND originate from within the trust perimeter.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", + "SubSection": "", + "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-14" + ] + } + ] + } + ], + "Checks": [ + "apikeys_api_restrictions_configured", + "apikeys_key_exists", + "apikeys_key_rotated_in_90_days" + ] + }, + { + "Id": "CCC.Core.CN03.AR03", + "Description": "When an entity attempts to view information on the service through a user interface, the authentication process MUST require multiple identifying factors from the user.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", + "SubSection": "", + "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-14" + ] + } + ] + } + ], + "Checks": [ + "compute_project_os_login_2fa_enabled" + ] + }, + { + "Id": "CCC.Core.CN03.AR04", + "Description": "When an entity attempts to view information on the service through an API endpoint, the authentication process MUST require a credential such as an API key or token AND originate from within the trust perimeter.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", + "SubSection": "", + "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-14" + ] + } + ] + } + ], + "Checks": [ + "apikeys_api_restrictions_configured", + "apikeys_key_exists", + "apikeys_key_rotated_in_90_days" + ] + }, + { + "Id": "CCC.Core.CN05.AR01", + "Description": "When an attempt is made to modify data on the service or a child resource, the service MUST block requests from unauthorized entities.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", + "SubSection": "", + "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-01", + "DSP-07", + "DSP-08", + "DSP-10", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "cloudstorage_bucket_public_access", + "compute_instance_public_ip", + "cloudsql_instance_public_ip", + "compute_instance_default_service_account_in_use", + "compute_instance_default_service_account_in_use_with_full_api_access", + "gke_cluster_no_default_service_account", + "iam_no_service_roles_at_project_level", + "iam_role_kms_enforce_separation_of_duties", + "iam_role_sa_enforce_separation_of_duties", + "iam_sa_no_administrative_privileges" + ] + }, + { + "Id": "CCC.Core.CN05.AR02", + "Description": "When administrative access or configuration change is attempted on the service or a child resource, the service MUST refuse requests from unauthorized entities.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", + "SubSection": "", + "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-01", + "DSP-07", + "DSP-08", + "DSP-10", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "iam_no_service_roles_at_project_level", + "iam_sa_no_administrative_privileges", + "iam_role_kms_enforce_separation_of_duties", + "iam_role_sa_enforce_separation_of_duties", + "iam_account_access_approval_enabled" + ] + }, + { + "Id": "CCC.Core.CN05.AR03", + "Description": "When administrative access or configuration change is attempted on the service or a child resource in a multi-tenant environment, the service MUST refuse requests across tenant boundaries unless the origin is explicitly included in a pre-approved allowlist.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", + "SubSection": "", + "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-01", + "DSP-07", + "DSP-08", + "DSP-10", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "cloudstorage_uses_vpc_service_controls" + ] + }, + { + "Id": "CCC.Core.CN05.AR04", + "Description": "When data is requested from outside the trust perimeter, the service MUST refuse requests from unauthorized entities.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", + "SubSection": "", + "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-01", + "DSP-07", + "DSP-08", + "DSP-10", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "cloudstorage_bucket_public_access", + "cloudstorage_uses_vpc_service_controls", + "cloudsql_instance_public_ip", + "cloudsql_instance_public_access", + "compute_instance_public_ip", + "kms_key_not_publicly_accessible", + "bigquery_dataset_public_access" + ] + }, + { + "Id": "CCC.Core.CN05.AR05", + "Description": "When any request is made from outside the trust perimeter, the service MUST NOT provide any response that may indicate the service exists.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", + "SubSection": "", + "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-01", + "DSP-07", + "DSP-08", + "DSP-10", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "compute_instance_public_ip", + "cloudstorage_bucket_public_access", + "cloudsql_instance_public_ip", + "kms_key_not_publicly_accessible", + "compute_image_not_publicly_shared", + "bigquery_dataset_public_access" + ] + }, + { + "Id": "CCC.Core.CN05.AR06", + "Description": "When any request is made to the service or a child resource, the service MUST refuse requests from unauthorized entities.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", + "SubSection": "", + "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-01", + "DSP-07", + "DSP-08", + "DSP-10", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "iam_no_service_roles_at_project_level", + "iam_sa_no_administrative_privileges", + "iam_sa_no_user_managed_keys", + "iam_sa_user_managed_key_rotate_90_days", + "iam_sa_user_managed_key_unused", + "iam_service_account_unused", + "iam_role_kms_enforce_separation_of_duties", + "iam_role_sa_enforce_separation_of_duties" + ] + }, + { + "Id": "CCC.Core.CN04.AR01", + "Description": "When administrative access or configuration change is attempted on the service or a child resource, the service MUST log the client identity, time, and result of the attempt.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements.", + "Section": "CCC.Core.CN04 Log All Access and Changes", + "SubSection": "", + "SubSectionObjective": "Ensure that all access attempts are logged to maintain a detailed audit trail for security and compliance purposes.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-08" + ] + } + ] + } + ], + "Checks": [ + "iam_audit_logs_enabled", + "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled", + "logging_log_metric_filter_and_alert_for_bucket_permission_changes_enabled", + "logging_log_metric_filter_and_alert_for_custom_role_changes_enabled", + "logging_log_metric_filter_and_alert_for_sql_instance_configuration_changes_enabled", + "logging_log_metric_filter_and_alert_for_vpc_firewall_rule_changes_enabled", + "logging_log_metric_filter_and_alert_for_vpc_network_changes_enabled", + "logging_log_metric_filter_and_alert_for_vpc_network_route_changes_enabled", + "logging_log_metric_filter_and_alert_for_project_ownership_changes_enabled" + ] + }, + { + "Id": "CCC.Core.CN04.AR02", + "Description": "When any attempt is made to modify data on the service or a child resource, the service MUST log the client identity, time, and result of the attempt.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements.", + "Section": "CCC.Core.CN04 Log All Access and Changes", + "SubSection": "", + "SubSectionObjective": "Ensure that all access attempts are logged to maintain a detailed audit trail for security and compliance purposes.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-08" + ] + } + ] + } + ], + "Checks": [ + "iam_audit_logs_enabled", + "cloudstorage_audit_logs_enabled", + "cloudstorage_bucket_logging_enabled", + "logging_sink_created" + ] + }, + { + "Id": "CCC.Core.CN04.AR03", + "Description": "When any attempt is made to read data on the service or a child resource, the service MUST log the client identity, time, and result of the attempt.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements.", + "Section": "CCC.Core.CN04 Log All Access and Changes", + "SubSection": "", + "SubSectionObjective": "Ensure that all access attempts are logged to maintain a detailed audit trail for security and compliance purposes.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-08" + ] + } + ] + } + ], + "Checks": [ + "iam_audit_logs_enabled", + "cloudstorage_audit_logs_enabled", + "cloudstorage_bucket_logging_enabled", + "logging_sink_created" + ] + }, + { + "Id": "CCC.Core.CN07.AR01", + "Description": "When enumeration activities are detected, the service MUST publish an event to a monitored channel which includes the client identity, time, and nature of the activity.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements.", + "Section": "CCC.Core.CN07 Alert on Unusual Enumeration Activity", + "SubSection": "", + "SubSectionObjective": "Ensure that logs and associated alerts are generated when unusual enumeration activity is detected that may indicate reconnaissance activities.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Implement event publication mechanisms and alerts for patterns indicative of enumeration activities, such as repeated access attempts, requests, or liveness probes. Configure alerts to notify security teams of any activities that merit further investigation.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH15" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-05", + "SEF-05" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Core.CN07.AR02", + "Description": "When enumeration activities are detected, the service MUST log the client identity, time, and nature of the activity.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements.", + "Section": "CCC.Core.CN07 Alert on Unusual Enumeration Activity", + "SubSection": "", + "SubSectionObjective": "Ensure that logs and associated alerts are generated when unusual enumeration activity is detected that may indicate reconnaissance activities.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Implement logging mechanisms to capture details of enumeration activities, including client identity, timestamps, and activity nature. Retain logs according to organizational policies, and occasionally review them for patterns that may indicate reconnaissance activities.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH15" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "LOG-05", + "SEF-05" + ] + } + ] + } + ], + "Checks": [] + }, { "Id": "CCC.AuditLog.CN01.AR01", "Description": "When the signature validation process is performed, then it MUST detect any modification of data.", @@ -18,13 +1638,13 @@ "Applicability": [ "tlp-red" ], - "Recommendation": "Ensure hash of data is included in digital signature. ", + "Recommendation": "Ensure hash of data is included in digital signature.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH06", - "CCC.TH07" + "CCC.Core.TH06", + "CCC.Core.TH07" ] } ], @@ -44,12 +1664,7 @@ ] } ], - "Checks": [ - "iam_audit_logs_enabled", - "cloudstorage_bucket_log_retention_policy_lock", - "logging_sink_created", - "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled" - ] + "Checks": [] }, { "Id": "CCC.AuditLog.CN01.AR02", @@ -64,13 +1679,13 @@ "Applicability": [ "tlp-red" ], - "Recommendation": "Ensure verification process includes a chained hash function. ", + "Recommendation": "Ensure verification process includes a chained hash function.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH06", - "CCC.TH07" + "CCC.Core.TH06", + "CCC.Core.TH07" ] } ], @@ -90,11 +1705,7 @@ ] } ], - "Checks": [ - "iam_audit_logs_enabled", - "logging_sink_created", - "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled" - ] + "Checks": [] }, { "Id": "CCC.AuditLog.CN02.AR01", @@ -115,7 +1726,7 @@ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH06" + "CCC.Core.TH06" ] } ], @@ -139,15 +1750,7 @@ ], "Checks": [ "iam_audit_logs_enabled", - "logging_sink_created", - "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_project_ownership_changes_enabled", - "logging_log_metric_filter_and_alert_for_bucket_permission_changes_enabled", - "logging_log_metric_filter_and_alert_for_custom_role_changes_enabled", - "logging_log_metric_filter_and_alert_for_sql_instance_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_firewall_rule_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_route_changes_enabled" + "logging_sink_created" ] }, { @@ -164,12 +1767,12 @@ "tlp-red", "tlp-amber" ], - "Recommendation": "Ensure alerting is correctly configured ", + "Recommendation": "Ensure alerting is correctly configured", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07" + "CCC.Core.TH07" ] } ], @@ -191,17 +1794,7 @@ } ], "Checks": [ - "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled", - "cloudstorage_bucket_log_retention_policy_lock", - "logging_sink_created", - "logging_log_metric_filter_and_alert_for_bucket_permission_changes_enabled", - "logging_log_metric_filter_and_alert_for_custom_role_changes_enabled", - "logging_log_metric_filter_and_alert_for_sql_instance_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_firewall_rule_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_route_changes_enabled", - "logging_log_metric_filter_and_alert_for_project_ownership_changes_enabled", - "iam_audit_logs_enabled" + "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled" ] }, { @@ -218,12 +1811,12 @@ "tlp-red", "tlp-amber" ], - "Recommendation": "Ensure alerting is correctly configured ", + "Recommendation": "Ensure alerting is correctly configured", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07" + "CCC.Core.TH07" ] } ], @@ -245,9 +1838,7 @@ } ], "Checks": [ - "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled", - "cloudstorage_bucket_log_retention_policy_lock", - "logging_sink_created" + "logging_log_metric_filter_and_alert_for_bucket_permission_changes_enabled" ] }, { @@ -264,13 +1855,13 @@ "tlp-red", "tlp-amber" ], - "Recommendation": "Configure the audit log bucket to enable server access logging. Ensure the target logging bucket is configured for appropriate security, including restricted access and immutability. ", + "Recommendation": "Configure the audit log bucket to enable server access logging. Ensure the target logging bucket is configured for appropriate security, including restricted access and immutability.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01", - "CCC.TH09" + "CCC.Core.TH01", + "CCC.Core.TH09" ] } ], @@ -292,10 +1883,8 @@ } ], "Checks": [ - "cloudstorage_bucket_log_retention_policy_lock", - "cloudstorage_bucket_public_access", - "cloudstorage_bucket_uniform_bucket_level_access", - "logging_sink_created" + "cloudstorage_bucket_logging_enabled", + "cloudstorage_audit_logs_enabled" ] }, { @@ -312,12 +1901,12 @@ "tlp-red", "tlp-amber" ], - "Recommendation": "Configure audit log exporting. ", + "Recommendation": "Configure audit log exporting.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07" + "CCC.Core.TH07" ] } ], @@ -340,11 +1929,8 @@ } ], "Checks": [ - "logging_sink_created", "iam_audit_logs_enabled", - "cloudstorage_bucket_log_retention_policy_lock", - "cloudstorage_bucket_uniform_bucket_level_access", - "cloudstorage_bucket_public_access" + "logging_sink_created" ] }, { @@ -362,13 +1948,13 @@ "tlp-amber", "tlp-green" ], - "Recommendation": "Configure the audit log bucket's lifecycle rules or object retention settings to enforce the required data retention period. ", + "Recommendation": "Configure the audit log bucket's lifecycle rules or object retention settings to enforce the required data retention period.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH06", - "CCC.TH07" + "CCC.Core.TH06", + "CCC.Core.TH07" ] } ], @@ -390,8 +1976,7 @@ } ], "Checks": [ - "cloudstorage_bucket_log_retention_policy_lock", - "logging_sink_created" + "cloudstorage_bucket_log_retention_policy_lock" ] }, { @@ -409,13 +1994,13 @@ "tlp-amber", "tlp-green" ], - "Recommendation": "Enable MFA Delete (or equivalent multi-factor authentication for delete operations) on the audit log bucket. ", + "Recommendation": "Enable MFA Delete (or equivalent multi-factor authentication for delete operations) on the audit log bucket.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH06", - "CCC.TH07" + "CCC.Core.TH06", + "CCC.Core.TH07" ] } ], @@ -436,11 +2021,7 @@ ] } ], - "Checks": [ - "cloudstorage_bucket_log_retention_policy_lock", - "iam_audit_logs_enabled", - "logging_sink_created" - ] + "Checks": [] }, { "Id": "CCC.AuditLog.CN08.AR01", @@ -456,12 +2037,12 @@ "tlp-red", "tlp-amber" ], - "Recommendation": "Configure object lock policy. ", + "Recommendation": "Configure object lock policy.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07" + "CCC.Core.TH07" ] } ], @@ -500,12 +2081,12 @@ "tlp-red", "tlp-amber" ], - "Recommendation": "Review field level access controls on audit data. ", + "Recommendation": "Review field level access controls on audit data.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07" + "CCC.Core.TH07" ] } ], @@ -547,59 +2128,12 @@ "tlp-amber", "tlp-green" ], - "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted. ", + "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AA-05" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3", - "SC-7" - ] - } - ] - } - ], - "Checks": [ - "cloudstorage_bucket_public_access", - "cloudstorage_bucket_uniform_bucket_level_access", - "cloudstorage_bucket_log_retention_policy_lock" - ] - }, - { - "Id": "CCC.AuditLog.CN10.AR02", - "Description": "When the URL of a audit log storage bucket's object is accessed publicly then, it should be denied by bucket policy.", - "Attributes": [ - { - "FamilyName": "Confidentiality", - "FamilyDescription": "Controls designed to protected the confidentiality of Audit Log data.", - "Section": "CCC.AuditLog.CN10 Ensure Audit Bucket is Not Publicly Accessible", - "SubSection": "", - "SubSectionObjective": "Ensure that audit log storage buckets are not publicly accessible to prevent unauthorized exposure of sensitive log data.", - "Applicability": [ - "tlp-red", - "tlp-amber", - "tlp-green" - ], - "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" + "CCC.Core.TH01" ] } ], @@ -626,25 +2160,26 @@ ] }, { - "Id": "CCC.Build.CN01.AR01", - "Description": "Attempt to initiate a build using an unauthorized build agent and verify that the build is rejected.", + "Id": "CCC.AuditLog.CN10.AR02", + "Description": "When the URL of a audit log storage bucket's object is accessed publicly then, it should be denied by bucket policy.", "Attributes": [ { - "FamilyName": "Access Control", - "FamilyDescription": "TODO: Describe this control family", - "Section": "", - "SubSection": "CCC.Build.C01 Restrict Allowed Build Agents", - "SubSectionObjective": "Ensure that builds are executed only on authorized build agents to maintain control over the build environment and prevent unauthorized code execution.", + "FamilyName": "Confidentiality", + "FamilyDescription": "Controls designed to protected the confidentiality of Audit Log data.", + "Section": "CCC.AuditLog.CN10 Ensure Audit Bucket is Not Publicly Accessible", + "SubSection": "", + "SubSectionObjective": "Ensure that audit log storage buckets are not publicly accessible to prevent unauthorized exposure of sensitive log data.", "Applicability": [ "tlp-red", - "tlp-amber" + "tlp-amber", + "tlp-green" ], - "Recommendation": "", + "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01" + "CCC.Core.TH01" ] } ], @@ -652,14 +2187,296 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.AC-4" + "PR.AA-05" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ "AC-3", - "AC-6" + "SC-7" + ] + } + ] + } + ], + "Checks": [ + "cloudstorage_bucket_public_access", + "cloudstorage_bucket_uniform_bucket_level_access" + ] + }, + { + "Id": "CCC.Logging.CN01.AR01", + "Description": "When a new cloud account is created, provider-level audit and network flow logging MUST be enabled by default and directed to the central sink.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data.", + "Section": "CCC.Logging.CN01 Centralized and Comprehensive Log Aggregation", + "SubSection": "", + "SubSectionObjective": "Ensure all operational and security logs from across the cloud environment, including applications, operating systems, network traffic, and cloud service activity, are captured automatically and streamed to a central, secure log management service.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Logging.TH07" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.PS-04" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AU-2", + "AU-3" + ] + } + ] + } + ], + "Checks": [ + "iam_audit_logs_enabled", + "compute_subnet_flow_logs_enabled", + "logging_sink_created" + ] + }, + { + "Id": "CCC.Logging.CN01.AR02", + "Description": "When a new cloud compute resource is deployed, it MUST be configured to forward all relevant logs (e.g., OS, application, service logs) to the central log sink.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data.", + "Section": "CCC.Logging.CN01 Centralized and Comprehensive Log Aggregation", + "SubSection": "", + "SubSectionObjective": "Ensure all operational and security logs from across the cloud environment, including applications, operating systems, network traffic, and cloud service activity, are captured automatically and streamed to a central, secure log management service.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Logging.TH07" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.PS-04" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AU-2", + "AU-3" + ] + } + ] + } + ], + "Checks": [ + "logging_sink_created", + "compute_subnet_flow_logs_enabled", + "compute_loadbalancer_logging_enabled", + "compute_network_dns_logging_enabled" + ] + }, + { + "Id": "CCC.Logging.CN02.AR01", + "Description": "When a new log bucket or stream is created, its retention policy MUST be configured in accordance with organisation's data retention policy.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data.", + "Section": "CCC.Logging.CN02 Enforce Data Retention Policy for Logs", + "SubSection": "", + "SubSectionObjective": "Ensure that the retention period configured for logs aligns with the organization's data retention policy.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Logging.TH05" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "GV.PO-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AU-11" + ] + } + ] + } + ], + "Checks": [ + "cloudstorage_bucket_log_retention_policy_lock", + "cloudstorage_bucket_sufficient_retention_period" + ] + }, + { + "Id": "CCC.Logging.CN02.AR02", + "Description": "When a query is performed to retrieve log events older than the number of days defined in the organisation's data retention policy, it MUST return an empty result.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data.", + "Section": "CCC.Logging.CN02 Enforce Data Retention Policy for Logs", + "SubSection": "", + "SubSectionObjective": "Ensure that the retention period configured for logs aligns with the organization's data retention policy.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Logging.TH05" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "GV.PO-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AU-11" + ] + } + ] + } + ], + "Checks": [ + "cloudstorage_bucket_log_retention_policy_lock", + "cloudstorage_bucket_sufficient_retention_period" + ] + }, + { + "Id": "CCC.Logging.CN03.AR01", + "Description": "When an attempt is made to modify or delete data before the object lock period expires, then the action MUST be denied.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data.", + "Section": "CCC.Logging.CN03 Enable Object Lock On Log Bucket", + "SubSection": "", + "SubSectionObjective": "Ensure log immutability by enabling Write Once, Read Many (WORM) protection using object lock on log storage buckets. This prevents logs from being modified or deleted during the defined retention period, supporting compliance and forensic integrity.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "Configure object lock policy.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH07" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.PS-04" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AU-9", + "AU-11" + ] + } + ] + } + ], + "Checks": [ + "cloudstorage_bucket_log_retention_policy_lock" + ] + }, + { + "Id": "CCC.Logging.CN04.AR01", + "Description": "When restricted fields are accessed by unauthorized users, then those fields MUST remain masked.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify logs.", + "Section": "CCC.Logging.CN04 Restrict Field And Log Type Access", + "SubSection": "", + "SubSectionObjective": "Configure access to logs to follow the principle of least privilege in particular where technically possible limit the log fields users have access to to prevent accidental exposure to sensitive information such as PII.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "Review field level access controls on log data.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Logging.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.PS-04" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6", + "AU-9", + "AC-3", + "PT-2", + "PT-3", + "PT-3" ] } ] @@ -668,25 +2485,26 @@ "Checks": [] }, { - "Id": "CCC.Build.CN02.AR01", - "Description": "Attempt to trigger a build from an unauthorized external service or repository and verify that the build does not start.", + "Id": "CCC.Logging.CN05.AR01", + "Description": "When a log storage bucket is created, the bucket's access control settings MUST explicitly deny public read and write access.", "Attributes": [ { - "FamilyName": "Access Control", - "FamilyDescription": "TODO: Describe this control family", - "Section": "", - "SubSection": "CCC.Build.C02 Restrict Allowed External Services for Build Triggers", - "SubSectionObjective": "Ensure that builds can only be triggered by authorized external services or repositories to prevent unauthorized code execution or tampering.", + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify logs.", + "Section": "CCC.Logging.CN05 Ensure Log Bucket is Not Publicly Accessible", + "SubSection": "", + "SubSectionObjective": "Ensure that log storage buckets are not publicly accessible to prevent unauthorized access to sensitive log data. In addition, logs should be replicated to another cloud region to enhance availability, durability, and support disaster recovery requirements.", "Applicability": [ "tlp-red", - "tlp-amber" + "tlp-amber", + "tlp-green" ], - "Recommendation": "", + "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted.", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01" + "CCC.Core.TH01" ] } ], @@ -694,14 +2512,108 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.AC-4" + "PR.AA-05" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ "AC-3", - "AC-6" + "SC-7" + ] + } + ] + } + ], + "Checks": [ + "cloudstorage_bucket_public_access", + "cloudstorage_bucket_uniform_bucket_level_access" + ] + }, + { + "Id": "CCC.Logging.CN05.AR02", + "Description": "When the URL of a log storage bucket's object is accessed publicly, the action MUST be denied by bucket policy.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify logs.", + "Section": "CCC.Logging.CN05 Ensure Log Bucket is Not Publicly Accessible", + "SubSection": "", + "SubSectionObjective": "Ensure that log storage buckets are not publicly accessible to prevent unauthorized access to sensitive log data. In addition, logs should be replicated to another cloud region to enhance availability, durability, and support disaster recovery requirements.", + "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green" + ], + "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-05" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-3", + "SC-7" + ] + } + ] + } + ], + "Checks": [ + "cloudstorage_bucket_public_access", + "cloudstorage_bucket_uniform_bucket_level_access" + ] + }, + { + "Id": "CCC.Logging.CN06.AR01", + "Description": "When a single principal executes an anomalously high number of log queries, an alert MUST be generated.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that collect, alert, and retain logging-related events.", + "Section": "CCC.Logging.CN06 Detect and Alert on Potential Log Exfiltration", + "SubSection": "", + "SubSectionObjective": "Identify and alert on anomalous data access patterns that may indicate an attempt to exfiltrate log data.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Logging.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "DE.CM-03", + "DE.CM-09" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-4", + "CA-7", + "AU-6" ] } ] @@ -710,26 +2622,1273 @@ "Checks": [] }, { - "Id": "CCC.Build.CN03.AR01", - "Description": "Attempt to access the build environment from an external network and verify that access is denied.", + "Id": "CCC.Logging.CN07.AR01", + "Description": "When an audit log event is recorded that corresponds to a modification of the logging service configuration such as disabling a log trail, deleting a log sink, or altering a log forwarding rule, an alert MUST be generated.", "Attributes": [ { - "FamilyName": "Network Security", - "FamilyDescription": "TODO: Describe this control family", - "Section": "", - "SubSection": "CCC.Build.C03 Deny External Network Access for Build Environments", - "SubSectionObjective": "Ensure that build environments do not have external network access to prevent unauthorized external access and data exfiltration.", + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that collect, alert, and retain logging-related events.", + "Section": "CCC.Logging.CN07 Detect and Alert on Log Service Tampering", + "SubSection": "", + "SubSectionObjective": "Alert when any component of the critical logging infrastructure is disabled, modified, or deleted, indicating a defense evasion attempt.", "Applicability": [ - "tlp-red", - "tlp-amber" + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" ], "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH02", - "CCC.TH05" + "CCC.Core.TH16" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "DE.CM-03", + "DE.CM-09" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-4", + "CA-7", + "AU-6" + ] + } + ] + } + ], + "Checks": [ + "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled" + ] + }, + { + "Id": "CCC.Monitor.CN01.AR01", + "Description": "When an External Monitoring system exceeds the anticipated rate of monitoring checks then Rate Limiting MUST be applied and an Audit Alert MUST be generated.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that collect, alert, and retain events from other monitoring services.", + "Section": "CCC.Monitor.CN01 Rate Limiting on External Monitoring", + "SubSection": "", + "SubSectionObjective": "Prevent DoS attacks using External Monitoring tools.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Monitor.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.IR-01", + "DE.CM-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-5", + "SC-7" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Monitor.CN02.AR01", + "Description": "When an Custom or User-Defined Metric starts to flood a collector, then a rate limit MUST be applied to reduce the network impact of traffic and an alert must triggered.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that collect, alert, and retain events from other monitoring services.", + "Section": "CCC.Monitor.CN02 Rate Limiting on Metric Generation", + "SubSection": "", + "SubSectionObjective": "Prevent Malicious Actor or misconfiguration from flooding services with metric data.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Monitor.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "DE.CM-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-5(2)", + "CA-7", + "SI-4" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Monitor.CN03.AR01", + "Description": "When external systems have approved access to internal systems not normally available for public access then they MUST be secured to prevent unauthorised access jumping through to the internal systems and only allow access to specific internal services.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls designed to prevent unauthorised access to monitoring features.", + "Section": "CCC.Monitor.CN03 Access External Monitoring", + "SubSection": "", + "SubSectionObjective": "Control access to Synthetic monitoring solutions using API keys or Certificate based authentication to ensure they don't become an attack path, preventing monitoring systems from forging network requests to gain access to internal systems.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Monitor.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "DE.CM-06", + "PR.IR-01", + "PR.AA-05" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-3" + ] + } + ] + } + ], + "Checks": [ + "apikeys_api_restrictions_configured", + "apikeys_key_exists", + "apikeys_key_rotated_in_90_days" + ] + }, + { + "Id": "CCC.Monitor.CN04.AR01", + "Description": "When monitoring dashboards display degraded services which may become potential targets then the dashboard MUST be protected from unauthorised access.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls designed to prevent unauthorised access to monitoring features.", + "Section": "CCC.Monitor.CN04 Restrict access to Monitoring Dashboards", + "SubSection": "", + "SubSectionObjective": "Control access to Monitoring Dashboards and reports to ensure they don't highlight an attack path.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Monitor.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "DE.CM-09", + "DE.AE-03" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-4", + "AC-3" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Monitor.CN05.AR01", + "Description": "When monitoring services have generated an alert, the service MUST ensure only authorised responders silence or acknowledge the alert.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls designed to prevent unauthorised access to monitoring features.", + "Section": "CCC.Monitor.CN05 Restrict access to silence or acknowledge an alert", + "SubSection": "", + "SubSectionObjective": "Ensure only a subset of users can silence or acknowledge alerts to prevent attackers hiding their activity.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH10" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.IR-01", + "PR.AA-05" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-3" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Monitor.CN06.AR01", + "Description": "When systems push metrics or traces they MUST be authenticated for that particular type of metric or trace", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls designed to prevent unauthorised access to monitoring features.", + "Section": "CCC.Monitor.CN06 Metrics pushed for authorised services only", + "SubSection": "", + "SubSectionObjective": "Use IAM to control which types of metrics or traces can be pushed by different system to avoid a compromised system pushing fabricated metrics about a different service", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Monitor.TH05" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-05" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-5" + ] + } + ] + } + ], + "Checks": [ + "iam_sa_no_administrative_privileges", + "iam_sa_no_user_managed_keys", + "compute_instance_default_service_account_in_use", + "compute_instance_default_service_account_in_use_with_full_api_access" + ] + }, + { + "Id": "CCC.ObjStor.CN01.AR01", + "Description": "When a request is made to read a bucket, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", + "SubSection": "", + "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption, or sensitive data decryption.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-01", + "IAM-03", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "kms_key_not_publicly_accessible" + ] + }, + { + "Id": "CCC.ObjStor.CN01.AR02", + "Description": "When a request is made to read an object, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", + "SubSection": "", + "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption, or sensitive data decryption.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-01", + "IAM-03", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "kms_key_not_publicly_accessible" + ] + }, + { + "Id": "CCC.ObjStor.CN01.AR03", + "Description": "When a request is made to write to a bucket, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", + "SubSection": "", + "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption, or sensitive data decryption.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-01", + "IAM-03", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "kms_key_not_publicly_accessible" + ] + }, + { + "Id": "CCC.ObjStor.CN01.AR04", + "Description": "When a request is made to write to an object, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", + "SubSection": "", + "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption, or sensitive data decryption.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-01", + "IAM-03", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "kms_key_not_publicly_accessible" + ] + }, + { + "Id": "CCC.ObjStor.CN03.AR01", + "Description": "When an object storage bucket deletion is attempted, the bucket MUST be fully recoverable for a set time-frame after deletion is requested.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN03 Prevent Bucket Deletion Through Irrevocable Bucket Retention Policy", + "SubSection": "", + "SubSectionObjective": "Ensure that object storage bucket is not deleted after creation, and that the preventative measure cannot be unset.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "cloudstorage_bucket_soft_delete_enabled", + "cloudstorage_bucket_log_retention_policy_lock" + ] + }, + { + "Id": "CCC.ObjStor.CN03.AR02", + "Description": "When an attempt is made to modify the retention policy for an object storage bucket, the service MUST prevent the policy from being modified.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN03 Prevent Bucket Deletion Through Irrevocable Bucket Retention Policy", + "SubSection": "", + "SubSectionObjective": "Ensure that object storage bucket is not deleted after creation, and that the preventative measure cannot be unset.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "cloudstorage_bucket_log_retention_policy_lock" + ] + }, + { + "Id": "CCC.ObjStor.CN04.AR01", + "Description": "When an object is uploaded to the object storage system, the object MUST automatically receive a default retention policy that prevents premature deletion or modification.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN04 Objects have an Effective Retention Policy by Default", + "SubSection": "", + "SubSectionObjective": "Ensure that all objects stored in the object storage system have a retention policy applied by default, preventing premature deletion or modification of objects.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + }, + { + "ReferenceId": "CCC.ObjStor", + "Identifiers": [ + "CCC.ObjStor.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "cloudstorage_bucket_sufficient_retention_period", + "cloudstorage_bucket_log_retention_policy_lock" + ] + }, + { + "Id": "CCC.ObjStor.CN04.AR02", + "Description": "When an attempt is made to delete or modify an object that is subject to an active retention policy, the service MUST prevent the action from being completed.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN04 Objects have an Effective Retention Policy by Default", + "SubSection": "", + "SubSectionObjective": "Ensure that all objects stored in the object storage system have a retention policy applied by default, preventing premature deletion or modification of objects.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + }, + { + "ReferenceId": "CCC.ObjStor", + "Identifiers": [ + "CCC.ObjStor.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "cloudstorage_bucket_log_retention_policy_lock" + ] + }, + { + "Id": "CCC.ObjStor.CN05.AR01", + "Description": "When an object is uploaded to the object storage bucket, the object MUST be stored with a unique identifier.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN05 Versioning is Enabled for All Objects in the Bucket", + "SubSection": "", + "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "cloudstorage_bucket_versioning_enabled" + ] + }, + { + "Id": "CCC.ObjStor.CN05.AR02", + "Description": "When an object is modified, the service MUST assign a new unique identifier to the modified object to differentiate it from the previous version.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN05 Versioning is Enabled for All Objects in the Bucket", + "SubSection": "", + "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "cloudstorage_bucket_versioning_enabled" + ] + }, + { + "Id": "CCC.ObjStor.CN05.AR03", + "Description": "When an object is modified, the service MUST allow for recovery of previous versions of the object.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN05 Versioning is Enabled for All Objects in the Bucket", + "SubSection": "", + "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "cloudstorage_bucket_versioning_enabled" + ] + }, + { + "Id": "CCC.ObjStor.CN05.AR04", + "Description": "When an object is deleted, the service MUST retain other versions of the object to allow for recovery of previous versions.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN05 Versioning is Enabled for All Objects in the Bucket", + "SubSection": "", + "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "DSP-17" + ] + } + ] + } + ], + "Checks": [ + "cloudstorage_bucket_versioning_enabled" + ] + }, + { + "Id": "CCC.ObjStor.CN07.AR01", + "Description": "The object storage service MUST support a configuration option that requires MFA to be successfully completed before any object deletion can be attempted, regardless of the request interface.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN07 Multi-Factor Authentication Is Required for Object Deletion", + "SubSection": "", + "SubSectionObjective": "Ensure that deletion of objects stored in the object storage system is protected by multi-factor authentication (MFA), reducing the risk of accidental, unauthorized, or compromised-credential–based data destruction.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH06", + "CCC.Core.TH17" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "IAM-12" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.ObjStor.CN07.AR02", + "Description": "When MFA deletion protection is enabled on a bucket or object namespace, the service MUST deny any deletion request from an identity that has not satisfied the MFA requirement at the time of the request.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN07 Multi-Factor Authentication Is Required for Object Deletion", + "SubSection": "", + "SubSectionObjective": "Ensure that deletion of objects stored in the object storage system is protected by multi-factor authentication (MFA), reducing the risk of accidental, unauthorized, or compromised-credential–based data destruction.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH06", + "CCC.Core.TH17" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "IAM-12" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.ObjStor.CN07.AR03", + "Description": "When an attempt is made to delete an object, the service's audit logs MUST clearly record each deletion attempt, including whether MFA was required and whether validation was met.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + "Section": "CCC.ObjStor.CN07 Multi-Factor Authentication Is Required for Object Deletion", + "SubSection": "", + "SubSectionObjective": "Ensure that deletion of objects stored in the object storage system is protected by multi-factor authentication (MFA), reducing the risk of accidental, unauthorized, or compromised-credential–based data destruction.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01", + "CCC.Core.TH06", + "CCC.Core.TH17" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "DSP-16", + "IAM-12" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.ObjStor.CN02.AR01", + "Description": "When a permission set is allowed for an object in a bucket, the service MUST allow the same permission set to access all objects in the same bucket.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources.", + "Section": "CCC.ObjStor.CN02 Enforce Uniform Bucket-level Access to Prevent Inconsistent Permissions", + "SubSection": "", + "SubSectionObjective": "Ensure that uniform bucket-level access is enforced across all object storage buckets. This prevents the use of ad-hoc or inconsistent object-level permissions, ensuring centralized, consistent, and secure access management in accordance with the principle of least privilege.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-08" + ] + } + ] + } + ], + "Checks": [ + "cloudstorage_bucket_uniform_bucket_level_access" + ] + }, + { + "Id": "CCC.ObjStor.CN02.AR02", + "Description": "When a permission set is denied for an object in a bucket, the service MUST deny the same permission set to access all objects in the same bucket.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources.", + "Section": "CCC.ObjStor.CN02 Enforce Uniform Bucket-level Access to Prevent Inconsistent Permissions", + "SubSection": "", + "SubSectionObjective": "Ensure that uniform bucket-level access is enforced across all object storage buckets. This prevents the use of ad-hoc or inconsistent object-level permissions, ensuring centralized, consistent, and secure access management in accordance with the principle of least privilege.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-08" + ] + } + ] + } + ], + "Checks": [ + "cloudstorage_bucket_uniform_bucket_level_access" + ] + }, + { + "Id": "CCC.LB.CN01.AR01", + "Description": "When a single client sends more than 2000 requests within any 5-minute sliding window, the load balancer MUST throttle all subsequent requests from that client for at least 60 seconds.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that detect anomalous traffic and record load-balancer activity.", + "Section": "CCC.LB.CN01 Enforce and Detect Rate Limiting", + "SubSection": "", + "SubSectionObjective": "Detect and throttle malicious or excessive requests to prevent downstream resource exhaustion and brute-force activity.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Implement per-IP token-bucket limits with and verify via synthetic traffic tests.", + "SectionThreatMappings": [ + { + "ReferenceId": "LB", + "Identifiers": [ + "CCC.LB.TH01", + "CCC.LB.TH09" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "DE.CM-1", + "PR.AC-7", + "PR.PT-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AU-6", + "SC-5", + "AC-7" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.LB.CN01.AR02", + "Description": "When throttling is invoked, the load balancer MUST record the event in the access log within 5 minutes for alerting and trend analysis.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that detect anomalous traffic and record load-balancer activity.", + "Section": "CCC.LB.CN01 Enforce and Detect Rate Limiting", + "SubSection": "", + "SubSectionObjective": "Detect and throttle malicious or excessive requests to prevent downstream resource exhaustion and brute-force activity.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Enable access logging and configure metric filters on HTTP 429 counts to trigger alerts.", + "SectionThreatMappings": [ + { + "ReferenceId": "LB", + "Identifiers": [ + "CCC.LB.TH01", + "CCC.LB.TH09" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "DE.CM-1", + "PR.AC-7", + "PR.PT-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AU-6", + "SC-5", + "AC-7" + ] + } + ] + } + ], + "Checks": [ + "compute_loadbalancer_logging_enabled" + ] + }, + { + "Id": "CCC.LB.CN06.AR01", + "Description": "When more than 10 percent of targets change from healthy to unhealthy within five minutes, an alert MUST be issued.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that detect anomalous traffic and record load-balancer activity.", + "Section": "CCC.LB.CN06 Secure Health-Check Telemetry", + "SubSection": "", + "SubSectionObjective": "Monitor health-check endpoints for tampering and alert on abnormal status changes.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Instrument metrics for health check results and target removal events. Configure monitoring alarms to alert on abnormal spikes in unhealthy targets.", + "SectionThreatMappings": [ + { + "ReferenceId": "LB", + "Identifiers": [ + "CCC.LB.TH05" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "DE.AE-2" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-4" + ] + } + ] + } + ], + "Checks": [ + "compute_loadbalancer_logging_enabled" + ] + }, + { + "Id": "CCC.LB.CN04.AR01", + "Description": "When routing weights change, the request MUST originate from an explicitly defined and trusted identity and MUST be logged.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can change or query load-balancer resources.", + "Section": "CCC.LB.CN04 Enforce Distribution Policies", + "SubSection": "", + "SubSectionObjective": "Ensure traffic-splitting weights and algorithms are modified only by trusted identities.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Define a list of trusted principals allowed to modify routing configurations. Enforce via conditional access policies, and log changes using audit logging.", + "SectionThreatMappings": [ + { + "ReferenceId": "LB", + "Identifiers": [ + "CCC.LB.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-3" + ] + } + ] + } + ], + "Checks": [ + "iam_audit_logs_enabled", + "compute_loadbalancer_logging_enabled" + ] + }, + { + "Id": "CCC.LB.CN05.AR01", + "Description": "When stickiness is enabled, session cookies MUST expire within 30 minutes of inactivity.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can change or query load-balancer resources.", + "Section": "CCC.LB.CN05 Validate Session Affinity", + "SubSection": "", + "SubSectionObjective": "Configure session persistence to minimise fixation and hijacking risks.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Audit CCC.LB.CP15 parameters via configuration scans.", + "SectionThreatMappings": [ + { + "ReferenceId": "LB", + "Identifiers": [ + "CCC.LB.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-7" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-23" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.LB.CN09.AR01", + "Description": "When an API call originates outside the approved CIDR set, the request MUST be denied.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can change or query load-balancer resources.", + "Section": "CCC.LB.CN09 Restrict Management API Access", + "SubSection": "", + "SubSectionObjective": "Limit load-balancer API calls to authorised identities and trusted networks.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Combine VPC endpoints with IAM condition-key filters for protected APIs.", + "SectionThreatMappings": [ + { + "ReferenceId": "LB", + "Identifiers": [ + "CCC.LB.TH08" ] } ], @@ -743,102 +3902,7 @@ { "ReferenceId": "NIST_800_53", "Identifiers": [ - "SC-7", - "SC-5" - ] - } - ] - } - ], - "Checks": [ - "compute_firewall_rdp_access_from_the_internet_allowed", - "compute_firewall_ssh_access_from_the_internet_allowed", - "compute_instance_public_ip", - "cloudstorage_bucket_public_access", - "cloudsql_instance_public_ip", - "cloudsql_instance_public_access" - ] - }, - { - "Id": "CCC.CntrReg.CN01.AR01", - "Description": "Attempt to push an artifact with known vulnerabilities to the registry and observe if it is flagged or rejected by the vulnerability scanning process.", - "Attributes": [ - { - "FamilyName": "Risk Management", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CntrReg.CN01 Implement Vulnerability Scanning for Artifacts", - "SubSection": "", - "SubSectionObjective": "Ensure that container images and artifacts stored in the container registry are scanned for vulnerabilities to identify and remediate security issues before deployment.", - "Applicability": [ - "tlp-red", - "tlp-amber" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.CntrReg.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "ID.RA-1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "RA-5", - "SI-5" - ] - } - ] - } - ], - "Checks": [ - "artifacts_container_analysis_enabled", - "gcr_container_scanning_enabled" - ] - }, - { - "Id": "CCC.DataWar.CN01.AR01", - "Description": "Attempt to access underlying database tables directly without using managed views and verify that access is denied.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.DataWar.CN01 Enforce Use of Managed Views for Data Access", - "SubSection": "", - "SubSectionObjective": "Ensure that data access is provided through managed views, restricting users from accessing underlying tables directly and enforcing consistent security policies.", - "Applicability": [ - "tlp-red", - "tlp-amber" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3", - "AC-6" + "SC-7" ] } ] @@ -847,25 +3911,26 @@ "Checks": [] }, { - "Id": "CCC.DataWar.CN02.AR01", - "Description": "Attempt to query sensitive columns without the necessary permissions and verify that access is denied or data is masked.", + "Id": "CCC.LB.CN02.AR01", + "Description": "When concurrent connections reach 80 percent of capacity, the autoscaling group MUST add at least one instance within five minutes.", "Attributes": [ { "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.DataWar.CN02 Enforce Column-Level Security Policies", + "FamilyDescription": "Controls that preserve availability and confidentiality of traffic processed by the load balancer.", + "Section": "CCC.LB.CN02 Auto-Scale Load Balancer Capacity", "SubSection": "", - "SubSectionObjective": "Ensure that access to sensitive data columns is restricted based on user roles, preventing unauthorized access to sensitive information.", + "SubSectionObjective": "Expand load-balancer capacity to maintain availability during traffic spikes.", "Applicability": [ - "tlp-red", - "tlp-amber" + "tlp-green", + "tlp-amber", + "tlp-red" ], - "Recommendation": "", + "Recommendation": "Enable autoscaling policies.", "SectionThreatMappings": [ { - "ReferenceId": "CCC", + "ReferenceId": "LB", "Identifiers": [ - "CCC.TH01" + "CCC.LB.TH09" ] } ], @@ -873,14 +3938,13 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.AC-4" + "ID.BE-5" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "AC-3", - "AC-6" + "CP-10" ] } ] @@ -889,25 +3953,26 @@ "Checks": [] }, { - "Id": "CCC.DataWar.CN03.AR01", - "Description": "Attempt to query data rows that the user should not have access to and verify that access is denied or data is not returned.", + "Id": "CCC.LB.CN07.AR01", + "Description": "When responses pass through the load balancer, the \"Server\" header MUST be replaced with \"lb\".", "Attributes": [ { "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.DataWar.CN03 Enforce Row-Level Security Policies", + "FamilyDescription": "Controls that preserve availability and confidentiality of traffic processed by the load balancer.", + "Section": "CCC.LB.CN07 Scrub Sensitive Headers", "SubSection": "", - "SubSectionObjective": "Ensure that access to data rows is restricted based on user roles or attributes, preventing unauthorized access to specific subsets of data.", + "SubSectionObjective": "Remove headers that disclose internal details or software versions from HTTP responses.", "Applicability": [ - "tlp-red", - "tlp-amber" + "tlp-green", + "tlp-amber", + "tlp-red" ], - "Recommendation": "", + "Recommendation": "Configure header-transformation rules.", "SectionThreatMappings": [ { - "ReferenceId": "CCC", + "ReferenceId": "LB", "Identifiers": [ - "CCC.TH01" + "CCC.Core.TH15" ] } ], @@ -915,50 +3980,286 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.AC-4" + "PR.DS-2" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "AC-3", - "AC-6" + "SC-13" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.LB.CN08.AR01", + "Description": "When a certificate is within 30 days of expiry, automated renewal MUST complete and deploy a new certificate within 24 hours.", + "Attributes": [ + { + "FamilyName": "Encryption", + "FamilyDescription": "Controls that ensure trustworthy TLS certificates and ciphers.", + "Section": "CCC.LB.CN08 Automate Certificate Renewal", + "SubSection": "", + "SubSectionObjective": "Maintain valid TLS certificates by automating renewal and deployment before expiry.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "Use certificate-manager auto-renewal workflows.", + "SectionThreatMappings": [ + { + "ReferenceId": "LB", + "Identifiers": [ + "CCC.LB.TH07" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.DS-6" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-17" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.VPC.CN01.AR01", + "Description": "When a subscription is created, the subscription MUST NOT contain default network resources.", + "Attributes": [ + { + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.VPC.CN01 Restrict Default Network Creation", + "SubSection": "", + "SubSectionObjective": "Restrict the automatic creation of default virtual networks and related resources during subscription initialization to avoid insecure default configurations and enforce custom network policies.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.VPC.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-5" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "TVM-02" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.12.3.1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-7" + ] + } + ] + } + ], + "Checks": [ + "compute_network_default_in_use" + ] + }, + { + "Id": "CCC.VPC.CN02.AR01", + "Description": "When a resource is created in a public subnet, that resource MUST NOT be assigned an external IP address by default.", + "Attributes": [ + { + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.VPC.CN02 Limit Resource Creation in Public Subnet", + "SubSection": "", + "SubSectionObjective": "Restrict the creation of resources in the public subnet with direct access to the internet to minimize attack surfaces.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.VPC.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-3" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "SEF-05" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.13.1.1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-4" ] } ] } ], "Checks": [ - "iam_no_service_roles_at_project_level", - "iam_sa_no_administrative_privileges", - "iam_role_sa_enforce_separation_of_duties", - "iam_role_kms_enforce_separation_of_duties", - "iam_account_access_approval_enabled", - "iam_audit_logs_enabled", - "logging_sink_created", - "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled", - "cloudsql_instance_postgres_enable_pgaudit_flag", - "cloudsql_instance_postgres_log_connections_flag", - "cloudsql_instance_postgres_log_disconnections_flag", - "cloudsql_instance_postgres_log_min_messages_flag", - "cloudsql_instance_postgres_log_min_duration_statement_flag", - "cloudsql_instance_postgres_log_error_verbosity_flag", - "cloudsql_instance_postgres_log_min_error_statement_flag", - "cloudsql_instance_public_ip", - "cloudsql_instance_private_ip_assignment", - "compute_firewall_ssh_access_from_the_internet_allowed", - "compute_firewall_rdp_access_from_the_internet_allowed", - "compute_instance_default_service_account_in_use", - "compute_instance_default_service_account_in_use_with_full_api_access", "compute_instance_public_ip" ] }, + { + "Id": "CCC.VPC.CN03.AR01", + "Description": "When a VPC peering connection is requested, the service MUST prevent connections from VPCs that are not explicitly allowed.", + "Attributes": [ + { + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.VPC.CN03 Restrict VPC Peering to Authorized Accounts", + "SubSection": "", + "SubSectionObjective": "Ensure VPC peering connections are only established with explicitly authorized destinations to limit network exposure and enforce boundary controls.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.VPC.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-3" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IVS-01" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.13.1.3" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-4" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.VPC.CN04.AR01", + "Description": "When any network traffic goes to or from an interface in the VPC, the service MUST capture and log all relevant information.", + "Attributes": [ + { + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.VPC.CN04 Enforce VPC Flow Logs on VPCs", + "SubSection": "", + "SubSectionObjective": "Ensure VPCs are configured with flow logs enabled to capture traffic information.", + "Applicability": [ + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.VPC.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.PT-1" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.12.4.1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AU-2" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IVS-06" + ] + } + ] + } + ], + "Checks": [ + "compute_subnet_flow_logs_enabled" + ] + }, { "Id": "CCC.KeyMgmt.CN01.AR01", "Description": "When a key version is scheduled for deletion or disabled, an alert MUST be generated within five minutes.", "Attributes": [ { - "FamilyName": "Logging and Metrics Publication", + "FamilyName": "Logging and Monitoring", "FamilyDescription": "Controls that collect, alert, and retain key-management events.", "Section": "CCC.KeyMgmt.CN01 Alert on Key-version Changes", "SubSection": "", @@ -1117,429 +4418,29 @@ ] } ], - "Checks": [ - "kms_key_not_publicly_accessible", - "kms_key_rotation_enabled" - ] - }, - { - "Id": "CCC.LB.CN01.AR01", - "Description": "When a single client sends more than 2000 requests within any 5-minute sliding window, the load balancer MUST throttle all subsequent requests from that client for at least 60 seconds.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "Controls that detect anomalous traffic and record load-balancer activity. ", - "Section": "CCC.LB.CN01 Enforce and Detect Rate Limiting", - "SubSection": "", - "SubSectionObjective": "Detect and throttle malicious or excessive requests to prevent downstream resource exhaustion and brute-force activity.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Implement per-IP token-bucket limits with and verify via synthetic traffic tests. ", - "SectionThreatMappings": [ - { - "ReferenceId": "LB", - "Identifiers": [ - "CCC.LB.TH01", - "CCC.LB.TH09" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.CM-1", - "PR.AC-7", - "PR.PT-4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-6", - "SC-5", - "AC-7" - ] - } - ] - } - ], - "Checks": [ - "compute_loadbalancer_logging_enabled", - "compute_subnet_flow_logs_enabled", - "logging_sink_created", - "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_firewall_rule_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_route_changes_enabled" - ] - }, - { - "Id": "CCC.LB.CN01.AR02", - "Description": "When throttling is invoked, the load balancer MUST record the event in the access log within 5 minutes for alerting and trend analysis.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "Controls that detect anomalous traffic and record load-balancer activity. ", - "Section": "CCC.LB.CN01 Enforce and Detect Rate Limiting", - "SubSection": "", - "SubSectionObjective": "Detect and throttle malicious or excessive requests to prevent downstream resource exhaustion and brute-force activity.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Enable access logging and configure metric filters on HTTP 429 counts to trigger alerts. ", - "SectionThreatMappings": [ - { - "ReferenceId": "LB", - "Identifiers": [ - "CCC.LB.TH01", - "CCC.LB.TH09" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.CM-1", - "PR.AC-7", - "PR.PT-4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-6", - "SC-5", - "AC-7" - ] - } - ] - } - ], - "Checks": [ - "compute_loadbalancer_logging_enabled", - "logging_sink_created" - ] - }, - { - "Id": "CCC.LB.CN06.AR01", - "Description": "When more than 10 percent of targets change from healthy to unhealthy within five minutes, an alert MUST be issued.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "Controls that detect anomalous traffic and record load-balancer activity. ", - "Section": "CCC.LB.CN06 Secure Health-Check Telemetry", - "SubSection": "", - "SubSectionObjective": "Monitor health-check endpoints for tampering and alert on abnormal status changes.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Instrument metrics for health check results and target removal events. Configure monitoring alarms to alert on abnormal spikes in unhealthy targets. ", - "SectionThreatMappings": [ - { - "ReferenceId": "LB", - "Identifiers": [ - "CCC.LB.TH05" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.AE-2" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SI-4" - ] - } - ] - } - ], - "Checks": [ - "compute_loadbalancer_logging_enabled", - "logging_sink_created", - "compute_subnet_flow_logs_enabled" - ] - }, - { - "Id": "CCC.LB.CN04.AR01", - "Description": "When routing weights change, the request MUST originate from an explicitly defined and trusted identity and MUST be logged.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls that restrict who can change or query load-balancer resources. ", - "Section": "CCC.LB.CN04 Enforce Distribution Policies", - "SubSection": "", - "SubSectionObjective": "Ensure traffic-splitting weights and algorithms are modified only by trusted identities.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Define a list of trusted principals allowed to modify routing configurations. Enforce via conditional access policies, and log changes using audit logging. ", - "SectionThreatMappings": [ - { - "ReferenceId": "LB", - "Identifiers": [ - "CCC.LB.TH03" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "iam_audit_logs_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_route_changes_enabled", - "compute_loadbalancer_logging_enabled", - "logging_sink_created", - "iam_no_service_roles_at_project_level", - "iam_role_sa_enforce_separation_of_duties", - "iam_sa_no_administrative_privileges" - ] - }, - { - "Id": "CCC.LB.CN05.AR01", - "Description": "When stickiness is enabled, session cookies MUST expire within 30 minutes of inactivity.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls that restrict who can change or query load-balancer resources. ", - "Section": "CCC.LB.CN05 Validate Session Affinity", - "SubSection": "", - "SubSectionObjective": "Configure session persistence to minimise fixation and hijacking risks.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Audit CCC.LB.F15 parameters via configuration scans.", - "SectionThreatMappings": [ - { - "ReferenceId": "LB", - "Identifiers": [ - "CCC.LB.TH04" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-7" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-23" - ] - } - ] - } - ], - "Checks": [ - "iam_no_service_roles_at_project_level", - "iam_sa_no_administrative_privileges", - "iam_role_kms_enforce_separation_of_duties", - "iam_role_sa_enforce_separation_of_duties", - "iam_account_access_approval_enabled", - "iam_audit_logs_enabled", - "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_custom_role_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_route_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_firewall_rule_changes_enabled", - "logging_sink_created", - "logging_log_metric_filter_and_alert_for_project_ownership_changes_enabled" - ] - }, - { - "Id": "CCC.LB.CN09.AR01", - "Description": "When an API call originates outside the approved CIDR set, the request MUST be denied.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls that restrict who can change or query load-balancer resources. ", - "Section": "CCC.LB.CN09 Restrict Management API Access", - "SubSection": "", - "SubSectionObjective": "Limit load-balancer API calls to authorised identities and trusted networks.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Combine VPC endpoints with IAM condition-key filters for protected APIs.", - "SectionThreatMappings": [ - { - "ReferenceId": "LB", - "Identifiers": [ - "CCC.LB.TH08" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-5" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-7" - ] - } - ] - } - ], - "Checks": [ - "iam_account_access_approval_enabled", - "iam_audit_logs_enabled", - "iam_no_service_roles_at_project_level", - "iam_role_kms_enforce_separation_of_duties", - "iam_role_sa_enforce_separation_of_duties", - "iam_sa_no_administrative_privileges", - "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_custom_role_changes_enabled", - "logging_log_metric_filter_and_alert_for_sql_instance_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_firewall_rule_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_route_changes_enabled" - ] - }, - { - "Id": "CCC.LB.CN02.AR01", - "Description": "When concurrent connections reach 80 percent of capacity, the autoscaling group MUST add at least one instance within five minutes.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "Controls that preserve availability and confidentiality of traffic processed by the load balancer. ", - "Section": "CCC.LB.CN02 Auto-Scale Load Balancer Capacity", - "SubSection": "", - "SubSectionObjective": "Expand load-balancer capacity to maintain availability during traffic spikes.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Enable autoscaling policies.", - "SectionThreatMappings": [ - { - "ReferenceId": "LB", - "Identifiers": [ - "CCC.LB.TH09" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "ID.BE-5" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "CP-10" - ] - } - ] - } - ], "Checks": [] }, { - "Id": "CCC.LB.CN07.AR01", - "Description": "When responses pass through the load balancer, the \"Server\" header MUST be replaced with \"lb\".", + "Id": "CCC.SecMgmt.CN01.AR01", + "Description": "Attempt to use an outdated version of a secret after its rotation period has passed and verify that access is denied.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "Controls that preserve availability and confidentiality of traffic processed by the load balancer. ", - "Section": "CCC.LB.CN07 Scrub Sensitive Headers", + "FamilyName": "Data Protection", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.SecMgmt.CN01 Enforce Automatic Secret Rotation", "SubSection": "", - "SubSectionObjective": "Remove headers that disclose internal details or software versions from HTTP responses.", + "SubSectionObjective": "Ensure that secrets are automatically rotated on a defined schedule to reduce the risk of secret compromise and unauthorized access.", "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" + "tlp-red", + "tlp-amber" ], - "Recommendation": "Configure header-transformation rules.", + "Recommendation": "", "SectionThreatMappings": [ { - "ReferenceId": "LB", + "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH15" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-2" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-13" - ] - } - ] - } - ], - "Checks": [] - }, - { - "Id": "CCC.LB.CN08.AR01", - "Description": "When a certificate is within 30 days of expiry, automated renewal MUST complete and deploy a new certificate within 24 hours.", - "Attributes": [ - { - "FamilyName": "Encryption", - "FamilyDescription": "Controls that ensure trustworthy TLS certificates and ciphers.", - "Section": "CCC.LB.CN08 Automate Certificate Renewal", - "SubSection": "", - "SubSectionObjective": "Maintain valid TLS certificates by automating renewal and deployment before expiry.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Use certificate-manager auto-renewal workflows.", - "SectionThreatMappings": [ - { - "ReferenceId": "LB", - "Identifiers": [ - "CCC.LB.TH07" + "CCC.Core.TH01", + "CCC.Core.TH14" ] } ], @@ -1553,7 +4454,8 @@ { "ReferenceId": "NIST_800_53", "Identifiers": [ - "SC-17" + "SC-12", + "SC-28" ] } ] @@ -1562,214 +4464,26 @@ "Checks": [] }, { - "Id": "CCC.Logging.CN01.AR01", - "Description": "When a new cloud account is created, provider-level audit and network flow logging MUST be enabled by default and directed to the central sink.", + "Id": "CCC.SecMgmt.CN02.AR01", + "Description": "Attempt to retrieve a secret from an unauthorized region and verify that access is denied.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data. ", - "Section": "CCC.Logging.CN01 Centralized and Comprehensive Log Aggregation", + "FamilyName": "Data Protection", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.SecMgmt.CN02 Enforce Secret Replication Policies", "SubSection": "", - "SubSectionObjective": "Ensure all operational and security logs from across the cloud environment, including applications, operating systems, network traffic, and cloud service activity, are captured automatically and streamed to a central, secure log management service.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Logging.TH07" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.PS-04" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-2", - "AU-3" - ] - } - ] - } - ], - "Checks": [ - "iam_audit_logs_enabled", - "compute_subnet_flow_logs_enabled", - "logging_sink_created" - ] - }, - { - "Id": "CCC.Logging.CN01.AR02", - "Description": "When a new cloud compute resource is deployed, it MUST be configured to forward all relevant logs (e.g., OS, application, service logs) to the central log sink.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data. ", - "Section": "CCC.Logging.CN01 Centralized and Comprehensive Log Aggregation", - "SubSection": "", - "SubSectionObjective": "Ensure all operational and security logs from across the cloud environment, including applications, operating systems, network traffic, and cloud service activity, are captured automatically and streamed to a central, secure log management service.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Logging.TH07" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.PS-04" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-2", - "AU-3" - ] - } - ] - } - ], - "Checks": [ - "logging_sink_created", - "cloudstorage_bucket_log_retention_policy_lock", - "iam_audit_logs_enabled", - "compute_subnet_flow_logs_enabled", - "compute_network_dns_logging_enabled", - "compute_loadbalancer_logging_enabled" - ] - }, - { - "Id": "CCC.Logging.CN02.AR01", - "Description": "When a new log bucket or stream is created, its retention policy MUST be configured in accordance with organisation's data retention policy.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data. ", - "Section": "CCC.Logging.CN02 Enforce Data Retention Policy for Logs", - "SubSection": "", - "SubSectionObjective": "Ensure that the retention period configured for logs aligns with the organization's data retention policy.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Logging.TH05" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "GV.PO-01" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-11" - ] - } - ] - } - ], - "Checks": [ - "cloudstorage_bucket_log_retention_policy_lock" - ] - }, - { - "Id": "CCC.Logging.CN02.AR02", - "Description": "When a query is performed to retrieve log events older than the number of days defined in the organisation's data retention policy, it MUST return an empty result.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data. ", - "Section": "CCC.Logging.CN02 Enforce Data Retention Policy for Logs", - "SubSection": "", - "SubSectionObjective": "Ensure that the retention period configured for logs aligns with the organization's data retention policy.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Logging.TH05" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "GV.PO-01" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-11" - ] - } - ] - } - ], - "Checks": [ - "cloudstorage_bucket_log_retention_policy_lock" - ] - }, - { - "Id": "CCC.AuditLog.CN08.AR01", - "Description": "When an attempt is made to modify or delete data before the object lock period expires, then the action MUST be denied.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "Controls related to the confidentiality, integrity and availability of log data. ", - "Section": "CCC.Logging.CN03 Enable Object Lock On Log Bucket", - "SubSection": "", - "SubSectionObjective": "Ensure log immutability by enabling Write Once, Read Many (WORM) protection using object lock on log storage buckets. This prevents logs from being modified or deleted during the defined retention period, supporting compliance and forensic integrity.", + "SubSectionObjective": "Ensure that secrets are replicated only to authorized locations as per organizational data residency and compliance requirements.", "Applicability": [ "tlp-red", "tlp-amber" ], - "Recommendation": "Configure object lock policy. ", + "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07" + "CCC.Core.TH03", + "CCC.Core.TH04" ] } ], @@ -1777,98 +4491,7 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.PS-04" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-9", - "AU-11" - ] - } - ] - } - ], - "Checks": [ - "cloudstorage_bucket_log_retention_policy_lock" - ] - }, - { - "Id": "CCC.AuditLog.CN04.AR01", - "Description": "When restricted fields are accessed by unauthorized users, then those fields MUST remain masked.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls that restrict who can access and modify logs. ", - "Section": "CCC.Logging.CN04 Restrict Field And Log Type Access", - "SubSection": "", - "SubSectionObjective": "Configure access to logs to follow the principle of least privilege in particular where technically possible limit the log fields users have access to to prevent accidental exposure to sensitive information such as PII.", - "Applicability": [ - "tlp-red", - "tlp-amber" - ], - "Recommendation": "Review field level access controls on log data. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Logging.TH04" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.PS-04" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-6", - "AU-9", - "AC-3", - "PT-2", - "PT-3", - "PT-3" - ] - } - ] - } - ], - "Checks": [] - }, - { - "Id": "CCC.Logging.CN05.AR01", - "Description": "When a log storage bucket is created, the bucket's access control settings MUST explicitly deny public read and write access.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls that restrict who can access and modify logs. ", - "Section": "CCC.Logging.CN05 Ensure Log Bucket is Not Publicly Accessible", - "SubSection": "", - "SubSectionObjective": "Ensure that log storage buckets are not publicly accessible to prevent unauthorized access to sensitive log data. In addition, logs should be replicated to another cloud region to enhance availability, durability, and support disaster recovery requirements.", - "Applicability": [ - "tlp-red", - "tlp-amber", - "tlp-green" - ], - "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AA-05" + "PR.DS-5" ] }, { @@ -1881,946 +4504,28 @@ ] } ], - "Checks": [ - "cloudstorage_bucket_public_access", - "cloudstorage_bucket_uniform_bucket_level_access", - "cloudstorage_bucket_log_retention_policy_lock", - "logging_sink_created" - ] + "Checks": [] }, { - "Id": "CCC.Logging.CN05.AR02", - "Description": "When the URL of a log storage bucket's object is accessed publicly, the action MUST be denied by bucket policy.", + "Id": "CCC.DataWar.CN01.AR01", + "Description": "Attempt to access underlying database tables directly without using managed views and verify that access is denied.", "Attributes": [ { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls that restrict who can access and modify logs. ", - "Section": "CCC.Logging.CN05 Ensure Log Bucket is Not Publicly Accessible", + "FamilyName": "Data", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.DataWar.CN01 Enforce Use of Managed Views for Data Access", "SubSection": "", - "SubSectionObjective": "Ensure that log storage buckets are not publicly accessible to prevent unauthorized access to sensitive log data. In addition, logs should be replicated to another cloud region to enhance availability, durability, and support disaster recovery requirements.", + "SubSectionObjective": "Ensure that data access is provided through managed views, restricting users from accessing underlying tables directly and enforcing consistent security policies.", "Applicability": [ "tlp-red", - "tlp-amber", - "tlp-green" - ], - "Recommendation": "Configure bucket policies and access control lists (ACLs) to restrict public access. Regularly review bucket permissions to ensure no public access has been inadvertently granted. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AA-05" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3", - "SC-7" - ] - } - ] - } - ], - "Checks": [ - "cloudstorage_bucket_public_access", - "cloudstorage_bucket_uniform_bucket_level_access" - ] - }, - { - "Id": "CCC.Logging.CN06.AR01", - "Description": "When a single principal executes an anomalously high number of log queries, an alert MUST be generated.", - "Attributes": [ - { - "FamilyName": "Logging and Monitoring", - "FamilyDescription": "Controls that collect, alert, and retain logging-related events. ", - "Section": "CCC.Logging.CN06 Detect and Alert on Potential Log Exfiltration", - "SubSection": "", - "SubSectionObjective": "Identify and alert on anomalous data access patterns that may indicate an attempt to exfiltrate log data.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" + "tlp-amber" ], "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.Logging.TH02" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.CM-03", - "DE.CM-09" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SI-4", - "CA-7", - "AU-6" - ] - } - ] - } - ], - "Checks": [ - "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_bucket_permission_changes_enabled", - "logging_log_metric_filter_and_alert_for_custom_role_changes_enabled", - "logging_log_metric_filter_and_alert_for_sql_instance_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_firewall_rule_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_route_changes_enabled", - "logging_log_metric_filter_and_alert_for_project_ownership_changes_enabled", - "logging_sink_created" - ] - }, - { - "Id": "CCC.Logging.CN07.AR01", - "Description": "When an audit log event is recorded that corresponds to a modification of the logging service configuration such as disabling a log trail, deleting a log sink, or altering a log forwarding rule, an alert MUST be generated.", - "Attributes": [ - { - "FamilyName": "Logging and Monitoring", - "FamilyDescription": "Controls that collect, alert, and retain logging-related events. ", - "Section": "CCC.Logging.CN07 Detect and Alert on Log Service Tampering", - "SubSection": "", - "SubSectionObjective": "Alert when any component of the critical logging infrastructure is disabled, modified, or deleted, indicating a defense evasion attempt.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH16" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.CM-03", - "DE.CM-09" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SI-4", - "CA-7", - "AU-6" - ] - } - ] - } - ], - "Checks": [ - "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_bucket_permission_changes_enabled", - "logging_log_metric_filter_and_alert_for_custom_role_changes_enabled", - "logging_log_metric_filter_and_alert_for_sql_instance_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_firewall_rule_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_route_changes_enabled", - "logging_log_metric_filter_and_alert_for_project_ownership_changes_enabled" - ] - }, - { - "Id": "CCC.ObjStor.CN01.AR01", - "Description": "When a request is made to read a protected bucket, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN01 Prevent Unencrypted Requests", - "SubSection": "CCC.ObjStor.C01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", - "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption that can impact data availability and integrity.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01", - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DCS-04", - "DCS-06" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.10.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28" - ] - } - ] - } - ], - "Checks": [ - "kms_key_not_publicly_accessible", - "kms_key_rotation_enabled" - ] - }, - { - "Id": "CCC.ObjStor.CN01.AR02", - "Description": "When a request is made to read a protected object, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN01 Prevent Unencrypted Requests", - "SubSection": "CCC.ObjStor.C01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", - "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption that can impact data availability and integrity.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01", - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DCS-04", - "DCS-06" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.10.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28" - ] - } - ] - } - ], - "Checks": [] - }, - { - "Id": "CCC.ObjStor.CN01.AR03", - "Description": "When a request is made to write to a bucket, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN01 Prevent Unencrypted Requests", - "SubSection": "CCC.ObjStor.C01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", - "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption that can impact data availability and integrity.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01", - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DCS-04", - "DCS-06" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.10.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28" - ] - } - ] - } - ], - "Checks": [ - "kms_key_not_publicly_accessible", - "kms_key_rotation_enabled", - "bigquery_dataset_cmk_encryption", - "bigquery_table_cmk_encryption", - "dataproc_encrypted_with_cmks_disabled" - ] - }, - { - "Id": "CCC.ObjStor.CN01.AR04", - "Description": "When a request is made to write to an object, the service MUST prevent any request using KMS keys not listed as trusted by the organization.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN01 Prevent Unencrypted Requests", - "SubSection": "CCC.ObjStor.C01 Prevent Requests to Buckets or Objects with Untrusted KMS Keys", - "SubSectionObjective": "Prevent any requests to object storage buckets or objects using untrusted KMS keys to protect against unauthorized data encryption that can impact data availability and integrity.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01", - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DCS-04", - "DCS-06" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.10.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28" - ] - } - ] - } - ], - "Checks": [ - "kms_key_not_publicly_accessible", - "kms_key_rotation_enabled", - "dataproc_encrypted_with_cmks_disabled", - "compute_instance_encryption_with_csek_enabled", - "bigquery_dataset_cmk_encryption", - "bigquery_table_cmk_encryption" - ] - }, - { - "Id": "CCC.ObjStor.CN03.AR01", - "Description": "When an object storage bucket deletion is attempted, the bucket MUST be fully recoverable for a set time-frame after deletion is requested.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN03 Implement Multi-factor Authentication (MFA) for Access", - "SubSection": "CCC.ObjStor.C03 Prevent Bucket Deletion Through Irrevocable Bucket Retention Policy", - "SubSectionObjective": "Ensure that object storage bucket is not deleted after creation, and that the preventative measure cannot be unset.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - } - ] - } - ], - "Checks": [ - "cloudstorage_bucket_log_retention_policy_lock" - ] - }, - { - "Id": "CCC.ObjStor.CN03.AR02", - "Description": "When an attempt is made to modify the retention policy for an object storage bucket, the service MUST prevent the policy from being modified.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN03 Implement Multi-factor Authentication (MFA) for Access", - "SubSection": "CCC.ObjStor.C03 Prevent Bucket Deletion Through Irrevocable Bucket Retention Policy", - "SubSectionObjective": "Ensure that object storage bucket is not deleted after creation, and that the preventative measure cannot be unset.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - } - ] - } - ], - "Checks": [ - "cloudstorage_bucket_log_retention_policy_lock" - ] - }, - { - "Id": "CCC.ObjStor.CN04.AR01", - "Description": "When an object is uploaded to the object storage system, the object MUST automatically receive a default retention policy that prevents premature deletion or modification.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN04 Log All Access and Changes", - "SubSection": "CCC.ObjStor.C04 Objects have an Effective Retention Policy by Default", - "SubSectionObjective": "Ensure that all objects stored in the object storage system have a retention policy applied by default, preventing premature deletion or modification of objects and ensuring compliance with data retention regulations.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - } - ] - } - ], - "Checks": [ - "cloudstorage_bucket_log_retention_policy_lock" - ] - }, - { - "Id": "CCC.ObjStor.CN04.AR02", - "Description": "When an attempt is made to delete or modify an object that is subject to an active retention policy, the service MUST prevent the action from being completed.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN04 Log All Access and Changes", - "SubSection": "CCC.ObjStor.C04 Objects have an Effective Retention Policy by Default", - "SubSectionObjective": "Ensure that all objects stored in the object storage system have a retention policy applied by default, preventing premature deletion or modification of objects and ensuring compliance with data retention regulations.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - } - ] - } - ], - "Checks": [ - "cloudstorage_bucket_log_retention_policy_lock" - ] - }, - { - "Id": "CCC.ObjStor.CN05.AR01", - "Description": "When an object is uploaded to the object storage bucket, the object MUST be stored with a unique identifier.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN05 Prevent Access from Untrusted Entities", - "SubSection": "CCC.ObjStor.C05 Versioning is Enabled for All Objects in the Bucket", - "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - } - ] - } - ], - "Checks": [] - }, - { - "Id": "CCC.ObjStor.CN05.AR02", - "Description": "When an object is modified, the service MUST assign a new unique identifier to the modified object to differentiate it from the previous version.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN05 Prevent Access from Untrusted Entities", - "SubSection": "CCC.ObjStor.C05 Versioning is Enabled for All Objects in the Bucket", - "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - } - ] - } - ], - "Checks": [] - }, - { - "Id": "CCC.ObjStor.CN05.AR03", - "Description": "When an object is modified, the service MUST allow for recovery of previous versions of the object.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN05 Prevent Access from Untrusted Entities", - "SubSection": "CCC.ObjStor.C05 Versioning is Enabled for All Objects in the Bucket", - "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - } - ] - } - ], - "Checks": [] - }, - { - "Id": "CCC.ObjStor.CN05.AR04", - "Description": "When an object is deleted, the service MUST retain other versions of the object to allow for recovery of previous versions.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN05 Prevent Access from Untrusted Entities", - "SubSection": "CCC.ObjStor.C05 Versioning is Enabled for All Objects in the Bucket", - "SubSectionObjective": "Ensure that versioning is enabled for all objects stored in the object storage bucket to enable recovery of previous versions of objects in case of loss or corruption.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.1.4" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-28", - "CP-10" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-16" - ] - } - ] - } - ], - "Checks": [] - }, - { - "Id": "CCC.ObjStor.CN06.AR01", - "Description": "When an object storage bucket is accessed, the service MUST store access logs in a separate data store.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN06 Prevent Deployment in Restricted Regions", - "SubSection": "CCC.ObjStor.C06 Access Logs are Stored in a Separate Data Store", - "SubSectionObjective": "Ensure that access logs for object storage buckets are stored in a separate data store to protect against unauthorized access, tampering, or deletion of logs (Logbuckets are exempt from this requirement, but must be tlp-red).", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH07", - "CCC.TH09" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-6" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-07", - "DSP-17" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2022 A.8.15.0" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-9", - "SC-28" - ] - } - ] - } - ], - "Checks": [ - "cloudstorage_bucket_log_retention_policy_lock", - "logging_sink_created" - ] - }, - { - "Id": "CCC.ObjStor.CN02.AR01", - "Description": "When a permission set is allowed for an object in a bucket, the service MUST allow the same permission set to access all objects in the same bucket.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN02 Ensure Data Encryption at Rest for All Stored Data", - "SubSection": "CCC.ObjStor.C02 Enforce Uniform Bucket-level Access to Prevent Inconsistent Permissions", - "SubSectionObjective": "Ensure that uniform bucket-level access is enforced across all object storage buckets. This prevents the use of ad-hoc or inconsistent object-level permissions, ensuring centralized, consistent, and secure access management in accordance with the principle of least privilege.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" + "CCC.Core.TH01" ] } ], @@ -2831,54 +4536,38 @@ "PR.AC-4" ] }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.9.4.1" - ] - }, { "ReferenceId": "NIST_800_53", "Identifiers": [ "AC-3", "AC-6" ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DCS-09" - ] } ] } ], - "Checks": [ - "cloudstorage_bucket_uniform_bucket_level_access" - ] + "Checks": [] }, { - "Id": "CCC.ObjStor.CN02.AR02", - "Description": "When a permission set is denied for an object in a bucket, the service MUST deny the same permission set to access all objects in the same bucket.", + "Id": "CCC.DataWar.CN02.AR01", + "Description": "Attempt to query sensitive columns without the necessary permissions and verify that access is denied or data is masked.", "Attributes": [ { - "FamilyName": "Identity and Access Management", + "FamilyName": "Data", "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.CN02 Ensure Data Encryption at Rest for All Stored Data", - "SubSection": "CCC.ObjStor.C02 Enforce Uniform Bucket-level Access to Prevent Inconsistent Permissions", - "SubSectionObjective": "Ensure that uniform bucket-level access is enforced across all object storage buckets. This prevents the use of ad-hoc or inconsistent object-level permissions, ensuring centralized, consistent, and secure access management in accordance with the principle of least privilege.", + "Section": "CCC.DataWar.CN02 Enforce Column-Level Security Policies", + "SubSection": "", + "SubSectionObjective": "Ensure that access to sensitive data columns is restricted based on user roles, preventing unauthorized access to sensitive information.", "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" + "tlp-red", + "tlp-amber" ], "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01" + "CCC.Core.TH01" ] } ], @@ -2890,9 +4579,45 @@ ] }, { - "ReferenceId": "ISO_27001", + "ReferenceId": "NIST_800_53", "Identifiers": [ - "2013 A.9.4.1" + "AC-3", + "AC-6" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.DataWar.CN03.AR01", + "Description": "Attempt to query data rows that the user should not have access to and verify that access is denied or data is not returned.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.DataWar.CN03 Enforce Row-Level Security Policies", + "SubSection": "", + "SubSectionObjective": "Ensure that access to data rows is restricted based on user roles or attributes, preventing unauthorized access to specific subsets of data.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" ] }, { @@ -2901,502 +4626,12 @@ "AC-3", "AC-6" ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DCS-09" - ] - } - ] - } - ], - "Checks": [ - "cloudstorage_bucket_uniform_bucket_level_access" - ] - }, - { - "Id": "CCC.Monitor.CN01.AR01", - "Description": "When an External Monitoring system exceeds the anticipated rate of monitoring checks then Rate Limiting MUST be applied and an Audit Alert MUST be generated.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "Controls that collect, alert, and retain events from other monitoring services.", - "Section": "CCC.Monitor.CN01 Rate Limiting on External Monitoring", - "SubSection": "", - "SubSectionObjective": "Prevent DoS attacks using External Monitoring tools.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Monitor.TH03" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.IR-01", - "DE.CM-01" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-5", - "SC-7" - ] - } - ] - } - ], - "Checks": [ - "iam_audit_logs_enabled", - "logging_sink_created", - "cloudstorage_bucket_log_retention_policy_lock", - "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_bucket_permission_changes_enabled", - "logging_log_metric_filter_and_alert_for_custom_role_changes_enabled", - "logging_log_metric_filter_and_alert_for_sql_instance_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_firewall_rule_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_route_changes_enabled", - "logging_log_metric_filter_and_alert_for_project_ownership_changes_enabled" - ] - }, - { - "Id": "CCC.Monitor.CN02.AR01", - "Description": "When an Custom or User-Defined Metric starts to flood a collector, then a rate limit MUST be applied to reduce the network impact of traffic and an alert must triggered.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "Controls that collect, alert, and retain events from other monitoring services.", - "Section": "CCC.Monitor.CN02 Rate Limiting on Metric Generation", - "SubSection": "", - "SubSectionObjective": "Prevent Malicious Actor or misconfiguration from flooding services with metric data.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Monitor.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.CM-01" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-5(2)", - "CA-7", - "SI-4" - ] - } - ] - } - ], - "Checks": [ - "logging_sink_created", - "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_bucket_permission_changes_enabled", - "logging_log_metric_filter_and_alert_for_custom_role_changes_enabled", - "logging_log_metric_filter_and_alert_for_sql_instance_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_firewall_rule_changes_enabled", - "logging_log_metric_filter_and_alert_for_project_ownership_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_route_changes_enabled", - "iam_audit_logs_enabled", - "compute_loadbalancer_logging_enabled", - "compute_network_dns_logging_enabled", - "compute_subnet_flow_logs_enabled" - ] - }, - { - "Id": "CCC.Monitor.CN03.AR01", - "Description": "When external systems have approved access to internal systems not normally available for public access then they MUST be secured to prevent unauthorised access jumping through to the internal systems and only allow access to specific internal services.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls designed to prevent unauthorised access to monitoring features.", - "Section": "CCC.Monitor.CN03 Access External Monitoring", - "SubSection": "", - "SubSectionObjective": "Control access to Synthetic monitoring solutions using API keys or Certificate based authentication to ensure they don't become an attack path, preventing monitoring systems from forging network requests to gain access to internal systems.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Monitor.TH04" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.CM-06", - "PR.IR-01", - "PR.AA-05" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "apikeys_api_restrictions_configured", - "apikeys_key_exists", - "apikeys_key_rotated_in_90_days" - ] - }, - { - "Id": "CCC.Monitor.CN04.AR01", - "Description": "When monitoring dashboards display degraded services which may become potential targets then the dashboard MUST be protected from unauthorised access.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls designed to prevent unauthorised access to monitoring features.", - "Section": "CCC.Monitor.CN04 Restrict access to Monitoring Dashboards", - "SubSection": "", - "SubSectionObjective": "Control access to Monitoring Dashboards and reports to ensure they don't highlight an attack path.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Monitor.TH02" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.CM-09", - "DE.AE-03" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SI-4", - "AC-3" - ] } ] } ], "Checks": [] }, - { - "Id": "CCC.Monitor.CN05.AR01", - "Description": "When monitoring services have generated an alert, the service MUST ensure only authorised responders silence or acknowledge the alert.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls designed to prevent unauthorised access to monitoring features.", - "Section": "CCC.Monitor.CN05 Restrict access to silence or acknowledge an alert", - "SubSection": "", - "SubSectionObjective": "Ensure only a subset of users can silence or acknowledge alerts to prevent attackers hiding their activity.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH10" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.IR-01", - "PR.AA-05" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_bucket_permission_changes_enabled", - "logging_log_metric_filter_and_alert_for_custom_role_changes_enabled", - "logging_log_metric_filter_and_alert_for_sql_instance_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_firewall_rule_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_route_changes_enabled", - "logging_log_metric_filter_and_alert_for_project_ownership_changes_enabled", - "logging_sink_created", - "iam_organization_essential_contacts_configured" - ] - }, - { - "Id": "CCC.Monitor.CN06.AR01", - "Description": "When systems push metrics or traces they MUST be authenticated for that particular type of metric or trace", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "Controls designed to prevent unauthorised access to monitoring features.", - "Section": "CCC.Monitor.CN06 Metrics pushed for authorised services only", - "SubSection": "", - "SubSectionObjective": "Use IAM to control which types of metrics or traces can be pushed by different system to avoid a compromised system pushing fabricated metrics about a different service", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.Monitor.TH05" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AA-05" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-5" - ] - } - ] - } - ], - "Checks": [ - "compute_instance_default_service_account_in_use", - "compute_instance_default_service_account_in_use_with_full_api_access", - "gke_cluster_no_default_service_account", - "iam_no_service_roles_at_project_level", - "iam_sa_no_administrative_privileges", - "iam_sa_no_user_managed_keys", - "iam_sa_user_managed_key_rotate_90_days", - "iam_sa_user_managed_key_unused", - "iam_service_account_unused" - ] - }, - { - "Id": "CCC.VPC.CN01.AR01", - "Description": "When a subscription is created, the subscription MUST NOT contain default network resources.", - "Attributes": [ - { - "FamilyName": "Network Security", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.VPC.CN01 Restrict Default Network Creation", - "SubSection": "", - "SubSectionObjective": "Restrict the automatic creation of default virtual networks and related resources during subscription initialization to avoid insecure default configurations and enforce custom network policies.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.VPC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-5" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "TVM-02" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.12.3.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-7" - ] - } - ] - } - ], - "Checks": [ - "compute_network_default_in_use" - ] - }, - { - "Id": "CCC.VPC.CN03.AR01", - "Description": "When a VPC peering connection is requested, the service MUST prevent connections from VPCs that are not explicitly allowed.", - "Attributes": [ - { - "FamilyName": "Network Security", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.VPC.CN03 Restrict VPC Peering to Authorized Accounts", - "SubSection": "", - "SubSectionObjective": "Ensure VPC peering connections are only established with explicitly authorized destinations to limit network exposure and enforce boundary controls.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.VPC.TH03" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "IVS-01" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.3" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-4" - ] - } - ] - } - ], - "Checks": [] - }, - { - "Id": "CCC.VPC.CN04.AR01", - "Description": "When any network traffic goes to or from an interface in the VPC, the service MUST capture and log all relevant information.", - "Attributes": [ - { - "FamilyName": "Network Security", - "FamilyDescription": "TODO: Describe this control family", - "Section": "CCC.VPC.CN04 Enforce VPC Flow Logs on VPCs", - "SubSection": "", - "SubSectionObjective": "Ensure VPCs are configured with flow logs enabled to capture traffic information.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.VPC.TH04" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.PT-1" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.12.4.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-2" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "IVS-06" - ] - } - ] - } - ], - "Checks": [ - "compute_subnet_flow_logs_enabled" - ] - }, { "Id": "CCC.Vector.CN01.AR01", "Description": "When a vector embedding is submitted for indexing, the system MUST validate that it matches expected schema, dimension, and format profiles.", @@ -3420,7 +4655,7 @@ "Identifiers": [ "CCC.Vector.TH02", "CCC.Vector.TH05", - "CCC.TH12" + "CCC.Core.TH12" ] } ], @@ -3459,7 +4694,7 @@ "Identifiers": [ "CCC.Vector.TH02", "CCC.Vector.TH04", - "CCC.TH01" + "CCC.Core.TH01" ] } ], @@ -3474,11 +4709,7 @@ } ], "Checks": [ - "iam_role_kms_enforce_separation_of_duties", - "iam_role_sa_enforce_separation_of_duties", - "iam_sa_no_administrative_privileges", - "iam_no_service_roles_at_project_level", - "kms_key_not_publicly_accessible" + "iam_sa_no_administrative_privileges" ] }, { @@ -3501,7 +4732,7 @@ "ReferenceId": "CCC", "Identifiers": [ "CCC.Vector.TH03", - "CCC.TH01" + "CCC.Core.TH01" ] } ], @@ -3540,7 +4771,7 @@ "ReferenceId": "CCC", "Identifiers": [ "CCC.Vector.TH02", - "CCC.TH12" + "CCC.Core.TH12" ] } ], @@ -3576,8 +4807,8 @@ "ReferenceId": "CCC", "Identifiers": [ "CCC.Vector.TH04", - "CCC.TH09", - "CCC.TH04" + "CCC.Core.TH09", + "CCC.Core.TH04" ] } ], @@ -3591,18 +4822,7 @@ ] } ], - "Checks": [ - "iam_audit_logs_enabled", - "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled", - "logging_sink_created", - "logging_log_metric_filter_and_alert_for_bucket_permission_changes_enabled", - "logging_log_metric_filter_and_alert_for_project_ownership_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_route_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_firewall_rule_changes_enabled", - "logging_log_metric_filter_and_alert_for_sql_instance_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_custom_role_changes_enabled" - ] + "Checks": [] }, { "Id": "CCC.Vector.CN06.AR01", @@ -3626,7 +4846,7 @@ "ReferenceId": "CCC", "Identifiers": [ "CCC.Vector.TH05", - "CCC.TH06" + "CCC.Core.TH06" ] } ], @@ -3671,422 +4891,25 @@ "Checks": [] }, { - "Id": "CCC.Core.CN01.AR01", - "Description": "When a port is exposed for non-SSH network traffic, all traffic MUST include a TLS handshake AND be encrypted using TLS 1.3 or higher.", + "Id": "CCC.RDMS.CN01.AR02", + "Description": "When an attempt is made to authenticate to the database using known default credentials, the authentication attempt must fail and no access should be granted.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN01 Encrypt Data for Transmission", + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.RDMS.CN01 Password Management", "SubSection": "", - "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Most cloud services enable TLS 1.3 by default. Where it is not already set, ensure that your services are configured or updated accordingly. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH02" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-03", - "CEK-04", - "IVS-03", - "IVS-07" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-02" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-8", - "SC-13" - ] - } - ] - } - ], - "Checks": [ - "cloudsql_instance_ssl_connections" - ] - }, - { - "Id": "CCC.Core.CN01.AR02", - "Description": "When a port is exposed for SSH network traffic, all traffic MUST include a SSH handshake AND be encrypted using SSHv2 or higher.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN01 Encrypt Data for Transmission", - "SubSection": "", - "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Any time port 22 is exposed, ensure that it has a properly implemented SSH server with SSHv2 enabled and configured with strong ciphers. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH02" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-03", - "CEK-04", - "IVS-03", - "IVS-07" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-02" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-8", - "SC-13" - ] - } - ] - } - ], - "Checks": [ - "compute_firewall_ssh_access_from_the_internet_allowed" - ] - }, - { - "Id": "CCC.Core.CN01.AR03", - "Description": "When the service receives unencrypted traffic, then it MUST either block the request or automatically redirect it to the secure equivalent.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN01 Encrypt Data for Transmission", - "SubSection": "", - "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Review firewall, load balancer, and application configurations to ensure insecure protocols such as HTTP, FTP, and Telnet are not exposed. Where possible, implement automatic redirection to secure protocols such as HTTPS, SFTP, SSH, and regularly scan for protocol drift. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH02" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-03", - "CEK-04", - "IVS-03", - "IVS-07" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-02" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-8", - "SC-13" - ] - } - ] - } - ], - "Checks": [ - "cloudsql_instance_ssl_connections", - "cloudsql_instance_public_access", - "cloudsql_instance_public_ip", - "compute_firewall_rdp_access_from_the_internet_allowed", - "compute_firewall_ssh_access_from_the_internet_allowed", - "compute_instance_public_ip", - "cloudstorage_bucket_public_access" - ] - }, - { - "Id": "CCC.Core.CN01.AR07", - "Description": "When a port is exposed, the service MUST ensure that the protocol and service officially assigned to that port number by the IANA Service Name and Transport Protocol Port Number Registry, and no other, is run on that port.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN01 Encrypt Data for Transmission", - "SubSection": "", - "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Reference the IANA Service Name and Transport Protocol Port Number Registry for more information about correct protocol-to-port assignments. Avoid running non-standard services on well-known ports. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH02" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-03", - "CEK-04", - "IVS-03", - "IVS-07" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-02" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-8", - "SC-13" - ] - } - ] - } - ], - "Checks": [ - "compute_firewall_rdp_access_from_the_internet_allowed", - "compute_firewall_ssh_access_from_the_internet_allowed" - ] - }, - { - "Id": "CCC.Core.CN01.AR08", - "Description": "When a service transmits data using TLS, mutual TLS (mTLS) MUST be implemented to require both client and server certificate authentication for all connections.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN01 Encrypt Data for Transmission", - "SubSection": "", - "SubSectionObjective": "Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Configure mTLS for all endpoints that process or transmit sensitive data. Ensure both client and server certificates are validated and managed securely. Regularly review certificate authorities and automate certificate rotation where possible. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH02" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-03", - "CEK-04", - "IVS-03", - "IVS-07" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-02" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.1" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-8", - "SC-13" - ] - } - ] - } - ], - "Checks": [ - "cloudsql_instance_ssl_connections" - ] - }, - { - "Id": "CCC.Core.CN13.AR01", - "Description": "When a port is exposed that uses certificate-based encryption, the service MUST only use valid, unexpired certificates issued by a trusted certificate authority.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN13 Minimize Lifetime of Encryption and Authentication Certificates", - "SubSection": "", - "SubSectionObjective": "Ensure that encryption and authentication certificates have a limited lifetime to reduce the risk of compromise and ensure the use of up-to-date security practices.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Track certificate expiration dates and automate certificate renewal where possible. Use certificate management tools to ensure only certificates from trusted authorities are deployed. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH18" - ] - } - ], - "SectionGuidelineMappings": [] - } - ], - "Checks": [] - }, - { - "Id": "CCC.Core.CN13.AR02", - "Description": "When a port is exposed that uses certificate-based encryption, the service MUST rotate active certificates within 180 days of issuance.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN13 Minimize Lifetime of Encryption and Authentication Certificates", - "SubSection": "", - "SubSectionObjective": "Ensure that encryption and authentication certificates have a limited lifetime to reduce the risk of compromise and ensure the use of up-to-date security practices.", + "SubSectionObjective": "Ensure default vendor-supplied DB administrator credentials are replaced with strong, unique passwords and that these credentials are properly managed using a secure password or secrets management solution.", "Applicability": [ + "tlp-red", "tlp-amber" ], - "Recommendation": "Track certificate expiration dates and automate certificate renewal where possible. Use certificate management tools to ensure only certificates from trusted authorities are deployed. ", + "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH18" - ] - } - ], - "SectionGuidelineMappings": [] - } - ], - "Checks": [] - }, - { - "Id": "CCC.Core.CN13.AR03", - "Description": "When a port is exposed that uses certificate-based encryption, the service MUST rotate active certificates within 90 days of issuance.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN13 Minimize Lifetime of Encryption and Authentication Certificates", - "SubSection": "", - "SubSectionObjective": "Ensure that encryption and authentication certificates have a limited lifetime to reduce the risk of compromise and ensure the use of up-to-date security practices.", - "Applicability": [ - "tlp-red" - ], - "Recommendation": "Track certificate expiration dates and automate certificate renewal where possible. Use certificate management tools to ensure only certificates from trusted authorities are deployed. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH18" - ] - } - ], - "SectionGuidelineMappings": [] - } - ], - "Checks": [ - "apikeys_key_rotated_in_90_days", - "kms_key_rotation_enabled" - ] - }, - { - "Id": "CCC.Core.CN06.AR01", - "Description": "When the service is running, its region and availability zone MUST be included in a list of explicitly trusted or approved locations within the trust perimeter.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN06 Restrict Deployments to Trust Perimeter", - "SubSection": "", - "SubSectionObjective": "Ensure that the service and its child resources are only deployed on infrastructure in locations that are explicitly included within a defined trust perimeter.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Maintain an up-to-date list of trusted and approved regions based on organizational policies. Validate the service's deployment location is included in this list. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH03" + "CCC.RDMS.TH01" ] } ], @@ -4094,19 +4917,89 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-1" + "PR.AA-01" ] }, { - "ReferenceId": "CCM", + "ReferenceId": "NIST_800_53", "Identifiers": [ - "DSP-19" + "AC-2" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.RDMS.CN02.AR01", + "Description": "When repeated failed login attempts are made in a short timeframe, the account must be locked out or rate-limited to prevent further login attempts.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.RDMS.CN02 Account Lockout and Rate-Limiting", + "SubSection": "", + "SubSectionObjective": "Ensure the database enforces lockouts or rate-limiting after a specified number of failed authentication attempts. This prevents brute force or password-guessing attacks from succeeding.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.RDMS.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-1" ] }, { - "ReferenceId": "ISO_27001", + "ReferenceId": "NIST_800_53", "Identifiers": [ - "2013 A.11.1.1" + "AC-7" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.RDMS.CN04.AR01", + "Description": "When there is an attempt to perform a backup or restore, then the attempt must fail with an access denied message if credentials or roles that are not explicitly authorized for backup/restore functions.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.RDMS.CN04 Access Control for Backup and Restore Operations", + "SubSection": "", + "SubSectionObjective": "Restrict who can initiate, manage, and validate database backup or restore operations through strict role-based or least-privilege access. Prevents accidental or malicious restorations, protecting data integrity and availability.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.RDMS.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" ] }, { @@ -4118,30 +5011,30 @@ ] } ], - "Checks": [] + "Checks": [ + "iam_no_service_roles_at_project_level" + ] }, { - "Id": "CCC.Core.CN06.AR02", - "Description": "When a child resource is deployed, its region and availability zone MUST be included in a list of explicitly trusted or approved locations within the trust perimeter.", + "Id": "CCC.RDMS.CN05.AR01", + "Description": "When an attempt is made to share a snapshot with an unauthorized account, the sharing request must be denied.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN06 Restrict Deployments to Trust Perimeter", + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.RDMS.CN05 Restrict Snapshot Sharing to Authorized Accounts", "SubSection": "", - "SubSectionObjective": "Ensure that the service and its child resources are only deployed on infrastructure in locations that are explicitly included within a defined trust perimeter.", + "SubSectionObjective": "Ensure database snapshots can only be shared with explicitly authorized accounts, thereby minimizing the risk of data exposure or exfiltration.", "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" + "tlp-red", + "tlp-amber" ], - "Recommendation": "Maintain an up-to-date list of trusted and approved regions based on organizational policies. Validate that child resources can only be deployed to locations included in this list. ", + "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH03" + "CCC.RDMS.TH05" ] } ], @@ -4149,24 +5042,99 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-19" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.11.1.1" + "PR.DS-10" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ + "AC-4" + ] + } + ] + } + ], + "Checks": [ + "compute_image_not_publicly_shared" + ] + }, + { + "Id": "CCC.RDMS.CN03.AR01", + "Description": "When backups are disabled, paused, or fail to run as scheduled, an alert must be triggered and logged.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.RDMS.CN03 Enforce and Monitor Automated Backups", + "SubSection": "", + "SubSectionObjective": "Ensure database backups are automatically scheduled, actively monitored, and promptly reported if any disruptions occur. This helps maintain data integrity, facilitates disaster recovery, and supports business continuity when a system failure or breach occurs.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.RDMS.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.IP-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "CP-9" + ] + } + ] + } + ], + "Checks": [ + "cloudsql_instance_automated_backups" + ] + }, + { + "Id": "CCC.Build.CN01.AR01", + "Description": "Attempt to initiate a build using an unauthorized build agent and verify that the build is rejected.", + "Attributes": [ + { + "FamilyName": "Access Control", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.Build.CN01 Restrict Allowed Build Agents", + "SubSection": "", + "SubSectionObjective": "Ensure that builds are executed only on authorized build agents to maintain control over the build environment and prevent unauthorized code execution.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-3", "AC-6" ] } @@ -4176,80 +5144,25 @@ "Checks": [] }, { - "Id": "CCC.Core.CN08.AR01", - "Description": "When data is created or modified, the data MUST have a complete and recoverable duplicate that is stored in a physically separate data center.", + "Id": "CCC.Build.CN02.AR01", + "Description": "Attempt to trigger a build from an unauthorized external service or repository and verify that the build does not start.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN08 Replicate Data to Multiple Locations", + "FamilyName": "Access Control", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.Build.CN02 Restrict Allowed External Services for Build Triggers", "SubSection": "", - "SubSectionObjective": "Ensure that data is replicated across multiple physical locations to protect against data loss due to hardware failures, natural disasters, or other catastrophic events.", + "SubSectionObjective": "Ensure that builds can only be triggered by authorized external services or repositories to prevent unauthorized code execution or tampering.", "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Implement automated data replication processes to ensure that data is consistently duplicated in another region or availability zone. Regularly test data recovery from the replicated location to ensure integrity and availability. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.PT-5" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "BCR-08", - "BCR-10", - "BCR-11" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "CP-2", - "CP-10" - ] - } - ] - } - ], - "Checks": [ - "cloudsql_instance_automated_backups", - "cloudstorage_bucket_log_retention_policy_lock" - ] - }, - { - "Id": "CCC.Core.CN08.AR02", - "Description": "When data is replicated into a second location, the service MUST be able to accurately represent the replication locations, replication status, and data synchronization status.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN08 Replicate Data to Multiple Locations", - "SubSection": "", - "SubSectionObjective": "Ensure that data is replicated across multiple physical locations to protect against data loss due to hardware failures, natural disasters, or other catastrophic events.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" + "tlp-red", + "tlp-amber" ], "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH06" + "CCC.Core.TH01" ] } ], @@ -4257,22 +5170,14 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.PT-5" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "BCR-08", - "BCR-10", - "BCR-11" + "PR.AC-4" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "CP-2", - "CP-10" + "AC-3", + "AC-6" ] } ] @@ -4281,15 +5186,144 @@ "Checks": [] }, { - "Id": "CCC.Core.CN09.AR01", - "Description": "When the service is operational, its logs and any child resource logs MUST NOT be accessible from the resource they record access to.", + "Id": "CCC.Build.CN03.AR01", + "Description": "Attempt to access the build environment from an external network and verify that access is denied.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN09 Ensure Integrity of Access Logs", + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.Build.CN03 Deny External Network Access for Build Environments", "SubSection": "", - "SubSectionObjective": "Ensure that access logs are always recorded to an external location that cannot be manipulated from the context of the service(s) it contains logs for.", + "SubSectionObjective": "Ensure that build environments do not have external network access to prevent unauthorized external access and data exfiltration.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH02", + "CCC.Core.TH05" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-5" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-7", + "SC-5" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.CntrReg.CN01.AR01", + "Description": "Attempt to push an artifact with known vulnerabilities to the registry and observe if it is flagged or rejected by the vulnerability scanning process.", + "Attributes": [ + { + "FamilyName": "Risk Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.CntrReg.CN01 Implement Vulnerability Scanning for Artifacts", + "SubSection": "", + "SubSectionObjective": "Ensure that container images and artifacts stored in the container registry are scanned for vulnerabilities to identify and remediate security issues before deployment.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.CntrReg.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "ID.RA-1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "RA-5", + "SI-5" + ] + } + ] + } + ], + "Checks": [ + "artifacts_container_analysis_enabled", + "gcr_container_scanning_enabled" + ] + }, + { + "Id": "CCC.CntrReg.CN02.AR01", + "Description": "Confirm that artifacts older than the specified retention period are automatically deleted from the registry.", + "Attributes": [ + { + "FamilyName": "Data Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.CntrReg.CN02 Implement Cleanup Policies for Artifacts", + "SubSection": "", + "SubSectionObjective": "Ensure that unused or outdated artifacts are cleaned up according to defined policies to manage storage effectively and reduce security risks associated with outdated versions.", + "Applicability": [ + "tlp-red", + "tlp-amber" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.Core.TH14" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.IP-6" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-12" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.IAM.CN01.AR01", + "Description": "When an identity policy for a non-administrative principal is evaluated, it MUST NOT grant permissions for creating credentials or generating temporary session tokens.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN01 Restrict IAM User Credentials Creation", + "SubSection": "", + "SubSectionObjective": "Prevent non-administrative principals from creating new long-lived credentials like access keys or generating temporary session tokens. This blocks a common privilege escalation and persistence vector.", "Applicability": [ "tlp-clear", "tlp-green", @@ -4301,9 +5335,7 @@ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07", - "CCC.TH09", - "CCC.TH04" + "CCC.IAM.TH03" ] } ], @@ -4311,56 +5343,48 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-6" + "PR.AA-05" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "AU-9" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-02", - "LOG-04", - "LOG-09" + "AC-2", + "AC-3", + "AC-5", + "AC-6" ] } ] } ], "Checks": [ - "logging_sink_created", - "cloudstorage_bucket_log_retention_policy_lock", - "iam_audit_logs_enabled" + "iam_sa_no_user_managed_keys", + "iam_no_service_roles_at_project_level" ] }, { - "Id": "CCC.Core.CN09.AR02", - "Description": "When the service is operational, disabling the logs for the service or its child resources MUST NOT be possible without also disabling the corresponding resource.", + "Id": "CCC.IAM.CN01.AR02", + "Description": "When a non-administrative principal attempts to create new credentials or a temporary session token, the service MUST deny the action.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN09 Ensure Integrity of Access Logs", + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN01 Restrict IAM User Credentials Creation", "SubSection": "", - "SubSectionObjective": "Ensure that access logs are always recorded to an external location that cannot be manipulated from the context of the service(s) it contains logs for.", + "SubSectionObjective": "Prevent non-administrative principals from creating new long-lived credentials like access keys or generating temporary session tokens. This blocks a common privilege escalation and persistence vector.", "Applicability": [ "tlp-clear", "tlp-green", "tlp-amber", "tlp-red" ], - "Recommendation": "No normal business operations should disable logs, as this could indicate an attempt to cover up unauthorized access. Ensure that logging mechanisms are tightly integrated with service operations, so that logging cannot be disabled without stopping the service itself. ", + "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH07", - "CCC.TH09", - "CCC.TH04" + "CCC.IAM.TH03" ] } ], @@ -4368,21 +5392,589 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-6" + "PR.AA-05" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "AU-9" + "AC-2", + "AC-3", + "AC-5", + "AC-6" + ] + } + ] + } + ], + "Checks": [ + "iam_sa_no_user_managed_keys", + "iam_no_service_roles_at_project_level" + ] + }, + { + "Id": "CCC.IAM.CN02.AR01", + "Description": "When an identity policy for a non-administrative principal is evaluated, it MUST NOT grant permissions for creating, updating, or attaching policies.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN02 Restrict IAM Policies Modification", + "SubSection": "", + "SubSectionObjective": "Ensure that only designated administrative accounts have the ability to create, modify, or attach policies that define permissions for other identities.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-05" ] }, { - "ReferenceId": "CCM", + "ReferenceId": "NIST_800_53", "Identifiers": [ - "LOG-02", - "LOG-04", - "LOG-09" + "AC-2", + "AC-3", + "AC-5", + "AC-6" + ] + } + ] + } + ], + "Checks": [ + "iam_no_service_roles_at_project_level", + "iam_sa_no_administrative_privileges" + ] + }, + { + "Id": "CCC.IAM.CN02.AR02", + "Description": "When a non-administrative principal attempts to create, update, or attach policies, the service MUST deny the action.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN02 Restrict IAM Policies Modification", + "SubSection": "", + "SubSectionObjective": "Ensure that only designated administrative accounts have the ability to create, modify, or attach policies that define permissions for other identities.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-05" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2", + "AC-3", + "AC-5", + "AC-6" + ] + } + ] + } + ], + "Checks": [ + "iam_no_service_roles_at_project_level", + "iam_sa_no_administrative_privileges" + ] + }, + { + "Id": "CCC.IAM.CN03.AR01", + "Description": "When a policy is created or updated that grants a principal permission to assume a role or impersonate a service identity, the principal MUST NOT contain a wildcard or be public/anonymous.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN03 Restrict Role Assumption / Delegation", + "SubSection": "", + "SubSectionObjective": "Limit which principals can assume a role or impersonate a service identity to only those required. This prevents unintended cross-account or public access by securing the \"who can act as this identity\" boundary.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-3", + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2", + "AC-3", + "AC-6" + ] + } + ] + } + ], + "Checks": [ + "iam_role_sa_enforce_separation_of_duties", + "iam_no_service_roles_at_project_level" + ] + }, + { + "Id": "CCC.IAM.CN03.AR02", + "Description": "When an external or unauthenticated principal tries to assume a role or impersonate a service identity, the service MUST deny the action.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN03 Restrict Role Assumption / Delegation", + "SubSection": "", + "SubSectionObjective": "Limit which principals can assume a role or impersonate a service identity to only those required. This prevents unintended cross-account or public access by securing the \"who can act as this identity\" boundary.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-3", + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2", + "AC-3", + "AC-6" + ] + } + ] + } + ], + "Checks": [ + "iam_role_sa_enforce_separation_of_duties", + "iam_no_service_roles_at_project_level" + ] + }, + { + "Id": "CCC.IAM.CN04.AR01", + "Description": "When an IAM policy is created or updated, it MUST NOT contain allow statements with wildcard permissions, unless the statement is restricted by a condition.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN04 Restrict Wildcard Usage in IAM Policies", + "SubSection": "", + "SubSectionObjective": "Limit the use of wildcard permissions in IAM policies to prevent overly broad access from being granted by default.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH01", + "CCC.IAM.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-6" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2", + "AC-3", + "AC-6" + ] + } + ] + } + ], + "Checks": [ + "iam_sa_no_administrative_privileges" + ] + }, + { + "Id": "CCC.IAM.CN05.AR01", + "Description": "When a new cloud account is provisioned, a password policy MUST be configured for IAM users following the minimum PCI DSS v4.0.1 configurations.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "Controls that restrict who can access and modify IAM resources.", + "Section": "CCC.IAM.CN05 Strong Password Policies for IAM Users", + "SubSection": "", + "SubSectionObjective": "Ensure that the password policies for IAM users have strong configurations.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "When a new cloud account is provisioned, a password policy must be configured for all IAM users to align with the minimum requirements defined in PCI DSS v4.0.1. This includes, at a minimum: strength: 0 # Not yet specified - reference-id: A password length of at least 12 characters. strength: 0 # Not yet specified - reference-id: A mix of upper- and lower-case letters, numbers, and special characters. strength: 0 # Not yet specified - reference-id: Prevention of the use of previously used passwords (password history). strength: 0 # Not yet specified - reference-id: Password expiration at a defined interval (e.g., every 90 days). strength: 0 # Not yet specified - reference-id: Account lockout after a defined number of failed login attempts.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-05" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "IA-5" + ] + }, + { + "ReferenceId": "PCI-DSS", + "Identifiers": [ + "8.3.9", + "8.6.3" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.IAM.CN06.AR01", + "Description": "When a static credential such as an access key has existed for 90 days or more, it MUST be rotated.", + "Attributes": [ + { + "FamilyName": "Identity Provisioning and Lifecycle", + "FamilyDescription": "Controls related to the provisioning and lifecycle of IAM identities.", + "Section": "CCC.IAM.CN06 Maximum Age for Long-Term Static Credentials", + "SubSection": "", + "SubSectionObjective": "Ensure that long-lived static credentials like access keys are programmatically rotated within a defined time period to limit the window of opportunity if compromised.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "When a static credential such as an access key has existed for 90 days or more, it must be automatically rotated to reduce the risk of compromise due to long-term exposure. Organizations should implement automated checks to identify aging credentials and enforce rotation policies. Additionally, access key usage should be regularly monitored, and credentials that are no longer in use should be deactivated or deleted promptly. Where possible, prefer temporary, short-lived credentials over long-lived static ones to further minimize risk.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH09", + "CCC.IAM.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2" + ] + } + ] + } + ], + "Checks": [ + "iam_sa_user_managed_key_rotate_90_days" + ] + }, + { + "Id": "CCC.IAM.CN07.AR01", + "Description": "When a user account is disabled or deleted in the organization's IdP, the corresponding cloud identity and its access policies MUST be disabled or deleted within 24 hours.", + "Attributes": [ + { + "FamilyName": "Identity Provisioning and Lifecycle", + "FamilyDescription": "Controls related to the provisioning and lifecycle of IAM identities.", + "Section": "CCC.IAM.CN07 Automate Identity De-provisioning", + "SubSection": "", + "SubSectionObjective": "Ensure that when an identity is terminated in the central Identity Provider (IdP), ts corresponding access to cloud resources is revoked automatically.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH10", + "CCC.IAM.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2" + ] + } + ] + } + ], + "Checks": [ + "iam_service_account_unused", + "iam_sa_user_managed_key_unused" + ] + }, + { + "Id": "CCC.IAM.CN08.AR01", + "Description": "When an IAM user has credentials, such as passwords or access keys, that have not been used for 90 days or more, the unused credentials MUST be removed or deactivated.", + "Attributes": [ + { + "FamilyName": "Identity Provisioning and Lifecycle", + "FamilyDescription": "Controls related to the provisioning and lifecycle of IAM identities.", + "Section": "CCC.IAM.CN08 Maximum Age for Unused Credentials", + "SubSection": "", + "SubSectionObjective": "Ensure that unused IAM credentals are removed to reduce exposure in the event of potential compromise.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "IAM user credentials (such as passwords or access keys) that have not been used for 90 days or more must be automatically removed or deactivated.", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH11", + "CCC.IAM.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2" + ] + } + ] + } + ], + "Checks": [ + "iam_sa_user_managed_key_unused", + "iam_service_account_unused" + ] + }, + { + "Id": "CCC.IAM.CN09.AR01", + "Description": "When a human user accesses the cloud environment, they MUST authenticate through the organization's federated IdP via a standard protocol (e.g., SAML, OIDC).", + "Attributes": [ + { + "FamilyName": "Identity Provisioning and Lifecycle", + "FamilyDescription": "Controls related to the provisioning and lifecycle of IAM identities.", + "Section": "CCC.IAM.CN09 Enforce Federated Single Sign-On (SSO) for Human Users", + "SubSection": "", + "SubSectionObjective": "Ensure that all human users must authenticate through a central, federated Identity Provider (IdP) to access the cloud environment. This eliminates cloud-native user accounts with long-lived passwords, centralizes authentication controls, and simplifies lifecycle management.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH01", + "CCC.IAM.TH09" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AA-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "IA-2" + ] + } + ] + } + ], + "Checks": [ + "compute_project_os_login_enabled" + ] + }, + { + "Id": "CCC.IAM.CN10.AR01", + "Description": "When suspicious API requests are detected, real time alerts MUST be generated to notify security personnel.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that collect, alert, and retain IAM-related events.", + "Section": "CCC.IAM.CN10 Alert On Anomalous Behaviour", + "SubSection": "", + "SubSectionObjective": "Ensure that logs and associated alerts are generated when anomalous API requests are made by a single identity, such as API requests commonly associated with privilege escalation tactics, originating from an external or malicious IP address or performed by a previously dormant identity, which may indicate that credentals may be compromised, as well as for password brute-force attempts and account lockouts.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "DE.CM-03", + "DE.CM-06", + "DE.CM-09" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-4", + "SI-5", + "AC-2" + ] + } + ] + } + ], + "Checks": [ + "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled", + "logging_log_metric_filter_and_alert_for_custom_role_changes_enabled" + ] + }, + { + "Id": "CCC.IAM.CN10.AR02", + "Description": "When suspicious API requests are detected, the associated events MUST be logged, including the source details, time, and nature of the activity.", + "Attributes": [ + { + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that collect, alert, and retain IAM-related events.", + "Section": "CCC.IAM.CN10 Alert On Anomalous Behaviour", + "SubSection": "", + "SubSectionObjective": "Ensure that logs and associated alerts are generated when anomalous API requests are made by a single identity, such as API requests commonly associated with privilege escalation tactics, originating from an external or malicious IP address or performed by a previously dormant identity, which may indicate that credentals may be compromised, as well as for password brute-force attempts and account lockouts.", + "Applicability": [ + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.IAM.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "DE.CM-03", + "DE.CM-06", + "DE.CM-09" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-4", + "SI-5", + "AC-2" ] } ] @@ -4390,78 +5982,21 @@ ], "Checks": [ "iam_audit_logs_enabled", - "logging_sink_created", - "cloudstorage_bucket_log_retention_policy_lock", - "compute_subnet_flow_logs_enabled", - "compute_loadbalancer_logging_enabled", - "compute_network_dns_logging_enabled" + "logging_sink_created" ] }, { - "Id": "CCC.Core.CN09.AR03", - "Description": "When the service is operational, any attempt to redirect logs for the service or its child resources MUST NOT be possible without halting operation of the corresponding resource and publishing corresponding events to monitored channels.", + "Id": "CCC.IAM.CN11.AR01", + "Description": "When a cloud account or organization is provisioned, the native automated access and usage analysis services MUST be enabled to continuously monitor for external or public access to resources, and unused access.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN09 Ensure Integrity of Access Logs", + "FamilyName": "Logging and Monitoring", + "FamilyDescription": "Controls that collect, alert, and retain IAM-related events.", + "Section": "CCC.IAM.CN11 Enable Continuous IAM Access and Usage Analysis", "SubSection": "", - "SubSectionObjective": "Ensure that access logs are always recorded to an external location that cannot be manipulated from the context of the service(s) it contains logs for.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "No normal business operations should result in the redirection of logs, as this could indicate an attempt to cover up unauthorized access. Ensure that logging configurations are immutable during service operation so that any changes require stopping the service and publishing corresponding events to monitored channels. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH07", - "CCC.TH09", - "CCC.TH04" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-6" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-9" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-02", - "LOG-04", - "LOG-09" - ] - } - ] - } - ], - "Checks": [ - "logging_sink_created", - "cloudstorage_bucket_log_retention_policy_lock" - ] - }, - { - "Id": "CCC.Core.CN10.AR01", - "Description": "When data is replicated, the service MUST ensure that replication only occurs to destinations that are explicitly included within the defined trust perimeter.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN10 Restrict Data Replication to Trust Perimeter", - "SubSection": "", - "SubSectionObjective": "Ensure that data is only replicated on infrastructure in locations that are explicitly included within a defined trust perimeter.", + "SubSectionObjective": "Enable and configure the cloud provider's native access and usage analysis services to continuously monitor for external access paths and internal unused access.", "Applicability": [ + "tlp-clear", "tlp-green", "tlp-amber", "tlp-red" @@ -4471,7 +6006,978 @@ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH04" + "CCC.IAM.TH02", + "CCC.IAM.TH10", + "CCC.IAM.TH11" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "ID.RA-01", + "ID.IM-01" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2", + "CA-7", + "RA-5" + ] + } + ] + } + ], + "Checks": [ + "iam_account_access_approval_enabled", + "iam_cloud_asset_inventory_enabled" + ] + }, + { + "Id": "CCC.GenAI.CN01.AR01", + "Description": "Untrusted input such as user queries, RAG data or tool output MUST be validated before it is passed to a GenAI model.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN01 Model Input Filtering and Sanitisation", + "SubSection": "", + "SubSectionObjective": "Inspect and validate input before it is passed to a GenAI model in order to filter or sanitise adversarial queries and prevent sensitive data leakage.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH01", + "CCC.GenAI.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-003", + "AIR-PREV-017", + "AIR-PREV-002", + "AIR-DET-001" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Input Validation and Sanitization" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0020", + "AML.M0021", + "AML.M0015" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN01.AR02", + "Description": "If malicious patterns such as prompt injection or sensitive data are detected during input validation, the input MUST be blocked or sanitised.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN01 Model Input Filtering and Sanitisation", + "SubSection": "", + "SubSectionObjective": "Inspect and validate input before it is passed to a GenAI model in order to filter or sanitise adversarial queries and prevent sensitive data leakage.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH01", + "CCC.GenAI.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-003", + "AIR-PREV-017", + "AIR-PREV-002", + "AIR-DET-001" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Input Validation and Sanitization" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0020", + "AML.M0021", + "AML.M0015" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN02.AR01", + "Description": "GenAI model output MUST be validated for format conformance, malicious patterns, sensitive data and inapropriate content before being passed to users, application or plugins.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN02 Model Output Filtering and Sanitisation", + "SubSection": "", + "SubSectionObjective": "Inspect and validate GenAI model output before passing it to users, applications or plugins in order to filter or sanitise insecure or unreliable output and prevent sensitive data leakage.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH01", + "CCC.GenAI.TH03", + "CCC.GenAI.TH04", + "CCC.GenAI.TH05", + "CCC.GenAI.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-003", + "AIR-PREV-017", + "AIR-PREV-002", + "AIR-DET-001" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Output Validation and Sanitization" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0020", + "AML.M0002" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN02.AR02", + "Description": "In the event of policy violations, the AI-generated content MUST be redacted, encoded or rejected.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN02 Model Output Filtering and Sanitisation", + "SubSection": "", + "SubSectionObjective": "Inspect and validate GenAI model output before passing it to users, applications or plugins in order to filter or sanitise insecure or unreliable output and prevent sensitive data leakage.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH01", + "CCC.GenAI.TH03", + "CCC.GenAI.TH04", + "CCC.GenAI.TH05", + "CCC.GenAI.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-003", + "AIR-PREV-017", + "AIR-PREV-002", + "AIR-DET-001" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Output Validation and Sanitization" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0020", + "AML.M0002" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN03.AR01", + "Description": "When data is designated for model training or RAG ingestion, then its source MUST be explicitly approved and its provenance documented.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN03 Data Provenance and Source Vetting", + "SubSection": "", + "SubSectionObjective": "Ensure that all data for training, fine-tuning or RAG comes from trusted, approved sources and is authorised for the intended purposes in order to prevent the initial introduction of malicious content or leaked sensitive data.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH02", + "CCC.GenAI.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-006" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Training Data Management" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0025" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN03.AR02", + "Description": "Data from unvetted sources MUST NOT be used in production systems.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN03 Data Provenance and Source Vetting", + "SubSection": "", + "SubSectionObjective": "Ensure that all data for training, fine-tuning or RAG comes from trusted, approved sources and is authorised for the intended purposes in order to prevent the initial introduction of malicious content or leaked sensitive data.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH02", + "CCC.GenAI.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-006" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Training Data Management" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0025" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN04.AR01", + "Description": "When data is ingested for training, fine-tuning or conversion to vector embeddings, it MUST be validated for sensitive information or malicious content.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN04 Sanitisation of Ingested Data", + "SubSection": "", + "SubSectionObjective": "Validate and sanitise all data ingested by GenAI systems from extenal sources or internal knowledge bases, whether for training, conversion to vector embeddings, or real-time retireval, in order to remove or redact poisoned or sensitive data before further processing.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH02", + "CCC.GenAI.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-002" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Training Data Sanitization" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0007" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN04.AR02", + "Description": "If sensitive data or malicious content is detected, it must be rejected, redacted or flagged for manual review.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN04 Sanitisation of Ingested Data", + "SubSection": "", + "SubSectionObjective": "Validate and sanitise all data ingested by GenAI systems from extenal sources or internal knowledge bases, whether for training, conversion to vector embeddings, or real-time retireval, in order to remove or redact poisoned or sensitive data before further processing.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH02", + "CCC.GenAI.TH03" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-002" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Training Data Sanitization" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0007" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN05.AR01", + "Description": "When a RAG-enabled system generates a response containing information retrieved from its knowledge base, then the response MUST include a verifiable citation that links back to the specific source document.", + "Attributes": [ + { + "FamilyName": "Data", + "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters.", + "Section": "CCC.GenAI.CN05 Citations and Source Traceability", + "SubSection": "", + "SubSectionObjective": "Require the GenAI system to provide citations or direct links back to the source documents used to generate a response, in to enhance the transparency, trustworthiness, and verifiability of AI-generated content.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH09", + "CCC.GenAI.TH04" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-DET-013" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN06.AR01", + "Description": "When an LLM invokes an external tool (e.g., an API, a plugin), then the tool MUST operate with the least privileges required for performing its intended functionality.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration.", + "Section": "CCC.GenAI.CN06 Least Privilege for Plugins", + "SubSection": "", + "SubSectionObjective": "Restricts the permissions of any external tools the GenAI system can call to limit the potential damage if an agent is coerced to perform unintended actions or vulnerabilities in the tools are exploited.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH07", + "CCC.GenAI.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Agent Permissions" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN07.AR01", + "Description": "When an application makes an API call to a foundational model in a production environment, then it MUST specify an explicit version identifier.", + "Attributes": [ + { + "FamilyName": "Configuration Management", + "FamilyDescription": "The Configuration Management control family involves establishing, maintaining and monitoring the configuration of the service and related applications and infrastructure to ensure consistency, secure defaults and compliance.", + "Section": "CCC.GenAI.CN07 Model Version Pinning", + "SubSection": "", + "SubSectionObjective": "Mandate that applications are locked (\"pinned\") to a specific, tested version of a foundational model to prevent unexpected behaviour changes introduced by provider-side updates.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH10" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-010" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN08.AR01", + "Description": "When a new AI model is considered for production deployment, it MUST undergo a formal red teaming and quality assurance review.", + "Attributes": [ + { + "FamilyName": "Model Assurance and Evaluation", + "FamilyDescription": "The Model Assurance and Evaluation control family encompasses the proactiveand continuous processes of testing and validating the AI model's behavior to ensure it aligns with safety, ethical, and quality standards.", + "Section": "CCC.GenAI.CN08 Quality Control and Red Teaming", + "SubSection": "", + "SubSectionObjective": "Establish a formal program for quality evaluation and adversarial testing (red teaming) to ensure GenAI system meet all business, quality, security and compliance requirements before getting deployed into production environments.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH01", + "CCC.GenAI.TH02", + "CCC.GenAI.TH04", + "CCC.GenAI.TH08", + "CCC.GenAI.TH10" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-005" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Adversarial Training and Testing", + "Red Teaming", + "Product Governance" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0008" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.GenAI.CN08.AR02", + "Description": "If model quality review or red teaming identifies an issue that exceeds the organization's risk tolerance, the model MUST NOT be deployed until the issue is remediated.", + "Attributes": [ + { + "FamilyName": "Model Assurance and Evaluation", + "FamilyDescription": "The Model Assurance and Evaluation control family encompasses the proactiveand continuous processes of testing and validating the AI model's behavior to ensure it aligns with safety, ethical, and quality standards.", + "Section": "CCC.GenAI.CN08 Quality Control and Red Teaming", + "SubSection": "", + "SubSectionObjective": "Establish a formal program for quality evaluation and adversarial testing (red teaming) to ensure GenAI system meet all business, quality, security and compliance requirements before getting deployed into production environments.", + "Applicability": [ + "tlp-clear", + "tlp-green", + "tlp-amber", + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.GenAI.TH01", + "CCC.GenAI.TH02", + "CCC.GenAI.TH04", + "CCC.GenAI.TH08", + "CCC.GenAI.TH10" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "FINOS-AIGF", + "Identifiers": [ + "AIR-PREV-005" + ] + }, + { + "ReferenceId": "SAIF", + "Identifiers": [ + "Adversarial Training and Testing", + "Red Teaming", + "Product Governance" + ] + }, + { + "ReferenceId": "MITRE-ATLAS", + "Identifiers": [ + "AML.M0008" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN01.AR01", + "Description": "Verify that only authorized users can access MLDE resources, and that access modes are properly defined and enforced.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN01 Define Access Mode for ML Development Environments", + "SubSection": "", + "SubSectionObjective": "Ensure that access to Machine Learning Development Environment (MLDE) resources is strictly defined and controlled. Only authorized users with appropriate permissions can access these environments, mitigating the risk of unauthorized access, data leakage, or service disruption.", + "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green", + "tlp-clear" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01", + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-3" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.1.1", + "2013 A.9.2.1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-2", + "AC-3" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-01", + "IAM-02" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN03.AR01", + "Description": "Verify that root access is disabled on MLDE instances containing sensitive data.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN03 Disable Root Access on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Prevent users from obtaining root access on MLDE instances to reduce the risk of unauthorized system modifications and potential security breaches.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-08", + "IAM-12" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.2.3" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN03.AR02", + "Description": "For MLDE instances without sensitive data, ensure that root access is only enabled when necessary and properly authorized.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN03 Disable Root Access on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Prevent users from obtaining root access on MLDE instances to reduce the risk of unauthorized system modifications and potential security breaches.", + "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green", + "tlp-clear" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-08", + "IAM-12" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.2.3" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN04.AR01", + "Description": "Verify that terminal access is disabled on MLDE instances containing sensitive data.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN04 Disable Terminal Access on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Prevent users from accessing the terminal on MLDE instances to limit the risk of unauthorized commands and potential system compromise.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-08" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.2.3" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN04.AR02", + "Description": "For MLDE instances without sensitive data, ensure that terminal access is only enabled when necessary and properly authorized.", + "Attributes": [ + { + "FamilyName": "Identity and Access Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN04 Disable Terminal Access on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Prevent users from accessing the terminal on MLDE instances to limit the risk of unauthorized commands and potential system compromise.", + "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green", + "tlp-clear" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-08" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.2.3" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN02.AR01", + "Description": "Confirm that file download functionality is disabled on MLDE instances containing sensitive data.", + "Attributes": [ + { + "FamilyName": "Data Protection", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN02 Disable File Downloads on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Prevent unauthorized file downloads from MLDE instances to protect sensitive data from being exfiltrated.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH02", + "CCC.Core.TH02" ] } ], @@ -4485,14 +6991,21 @@ { "ReferenceId": "CCM", "Identifiers": [ - "DSP-10", - "DSP-19" + "DSI-05", + "DSI-07" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.13.2.1" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "AC-4" + "SC-7", + "SC-8" ] } ] @@ -4501,26 +7014,28 @@ "Checks": [] }, { - "Id": "CCC.Core.CN02.AR01", - "Description": "When data is stored, it MUST be encrypted using the latest industry-standard encryption methods.", + "Id": "CCC.MLDE.CN02.AR02", + "Description": "For MLDE instances without sensitive data, ensure that file downloads are monitored and logged.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN02 Encrypt Data for Storage", + "FamilyName": "Data Protection", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN02 Disable File Downloads on MLDE Instances", "SubSection": "", - "SubSectionObjective": "Ensure that all data stored is encrypted at rest using strong encryption algorithms.", + "SubSectionObjective": "Prevent unauthorized file downloads from MLDE instances to protect sensitive data from being exfiltrated.", "Applicability": [ + "tlp-red", + "tlp-amber", "tlp-green", - "tlp-amber", - "tlp-red" + "tlp-clear" ], "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01" + "CCC.MLDE.TH02", + "CCC.Core.TH02" ] } ], @@ -4528,110 +7043,46 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-1" + "PR.DS-5" ] }, { "ReferenceId": "CCM", "Identifiers": [ - "CEK-03", - "CEK-04", - "UEM-08", - "DSP-17" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-13", - "SC-28" - ] - } - ] - } - ], - "Checks": [ - "compute_instance_encryption_with_csek_enabled", - "dataproc_encrypted_with_cmks_disabled", - "bigquery_dataset_cmk_encryption", - "bigquery_table_cmk_encryption", - "kms_key_not_publicly_accessible", - "kms_key_rotation_enabled" - ] - }, - { - "Id": "CCC.Core.CN11.AR01", - "Description": "When encryption keys are used, the service MUST verify that all encryption keys use the latest industry-standard cryptographic algorithms.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN11 Protect Encryption Keys", - "SubSection": "", - "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH16" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-08", - "CEK-10", - "CEK-12" + "DSI-05", + "DSI-07" ] }, { "ReferenceId": "ISO_27001", "Identifiers": [ - "2013 A.10.1.2" + "2013 A.13.2.1" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "SC-12", - "SC-17" + "SC-7", + "SC-8" ] } ] } ], - "Checks": [ - "kms_key_rotation_enabled", - "dataproc_encrypted_with_cmks_disabled", - "bigquery_dataset_cmk_encryption", - "bigquery_table_cmk_encryption", - "kms_key_not_publicly_accessible" - ] + "Checks": [] }, { - "Id": "CCC.Core.CN11.AR02", - "Description": "When encryption keys are used, the service MUST rotate active keys within 180 days of issuance.", + "Id": "CCC.MLDE.CN05.AR01", + "Description": "Verify that only approved VM and container images can be selected when creating MLDE instances.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN11 Protect Encryption Keys", + "FamilyName": "Configuration Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN05 Restrict Environment Options on MLDE Instances", "SubSection": "", - "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "SubSectionObjective": "Limit the virtual machine and container image options available when creating new MLDE instances to approved and secure configurations.", "Applicability": [ + "tlp-red", "tlp-amber" ], "Recommendation": "", @@ -4639,7 +7090,7 @@ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH16" + "CCC.MLDE.TH04" ] } ], @@ -4647,53 +7098,43 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-1" + "PR.IP-1" ] }, { "ReferenceId": "CCM", "Identifiers": [ - "CEK-08", - "CEK-10", - "CEK-12" + "TVM-02" ] }, { "ReferenceId": "ISO_27001", "Identifiers": [ - "2013 A.10.1.2" + "2013 A.12.5.1" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "SC-12", - "SC-17" + "CM-2" ] } ] } ], - "Checks": [ - "kms_key_rotation_enabled", - "kms_key_not_publicly_accessible", - "dataproc_encrypted_with_cmks_disabled", - "bigquery_dataset_cmk_encryption", - "bigquery_table_cmk_encryption" - ] + "Checks": [] }, { - "Id": "CCC.Core.CN11.AR03", - "Description": "When encrypting data, the service MUST verify that customer-managed encryption keys (CMEKs) are used.", + "Id": "CCC.MLDE.CN05.AR02", + "Description": "Attempt to create an MLDE instance with an unapproved image and confirm that it is denied.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "", - "SubSection": "CCC.Core.CN11 Protect Encryption Keys", - "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "FamilyName": "Configuration Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN05 Restrict Environment Options on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Limit the virtual machine and container image options available when creating new MLDE instances to approved and secure configurations.", "Applicability": [ - "tlp-amber", "tlp-red" ], "Recommendation": "", @@ -4701,7 +7142,7 @@ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH16" + "CCC.MLDE.TH04" ] } ], @@ -4709,51 +7150,371 @@ { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.DS-1" + "PR.IP-1" ] }, { "ReferenceId": "CCM", "Identifiers": [ - "CEK-08", - "CEK-10", - "CEK-12" + "TVM-02" ] }, { "ReferenceId": "ISO_27001", "Identifiers": [ - "2013 A.10.1.2" + "2013 A.12.5.1" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "SC-12", - "SC-17" + "CM-2" ] } ] } ], - "Checks": [ - "bigquery_dataset_cmk_encryption", - "bigquery_table_cmk_encryption", - "dataproc_encrypted_with_cmks_disabled", - "kms_key_not_publicly_accessible", - "kms_key_rotation_enabled" - ] + "Checks": [] }, { - "Id": "CCC.Core.CN11.AR04", - "Description": "When encryption keys are accessed, the service MUST verify that access to encryption keys is restricted to authorized personnel and services, following the principle of least privilege.", + "Id": "CCC.MLDE.CN06.AR01", + "Description": "Verify that automatic scheduled upgrades are enabled on user-managed MLDE instances containing sensitive data.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN11 Protect Encryption Keys", + "FamilyName": "Vulnerability Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN06 Require Automatic Scheduled Upgrades on User-Managed MLDE Instances", "SubSection": "", - "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "SubSectionObjective": "Ensure that MLDE instances are kept up-to-date with the latest security patches by enforcing automatic scheduled upgrades.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH04", + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.IP-12" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "TVM-01", + "TVM-02" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.12.6.1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-2" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN06.AR02", + "Description": "Ensure that the upgrade schedule is appropriately configured and does not interfere with critical operations.", + "Attributes": [ + { + "FamilyName": "Vulnerability Management", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN06 Require Automatic Scheduled Upgrades on User-Managed MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Ensure that MLDE instances are kept up-to-date with the latest security patches by enforcing automatic scheduled upgrades.", + "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green", + "tlp-clear" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH04", + "CCC.Core.TH06" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.IP-12" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "TVM-01", + "TVM-02" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.12.6.1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SI-2" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN07.AR01", + "Description": "Verify that MLDE instances containing sensitive data cannot be accessed via public IP addresses.", + "Attributes": [ + { + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN07 Restrict Public IP Access on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Prevent public IP access to MLDE instances to reduce exposure to the internet and enhance security.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH02", + "CCC.VPC.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-3" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "SEF-05" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.13.1.1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-7" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN07.AR02", + "Description": "For MLDE instances without sensitive data requiring public access, ensure that appropriate security controls are in place and access is approved.", + "Attributes": [ + { + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN07 Restrict Public IP Access on MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Prevent public IP access to MLDE instances to reduce exposure to the internet and enhance security.", + "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green", + "tlp-clear" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH02", + "CCC.VPC.TH02" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-3" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "SEF-05" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.13.1.1" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "SC-7" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN08.AR01", + "Description": "Verify that MLDE instances containing sensitive data can only be deployed in approved virtual networks with appropriate security controls.", + "Attributes": [ + { + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN08 Restrict Virtual Networks for MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Limit the virtual networks that can be used when creating new MLDE instances to ensure they are deployed within approved and secure network environments.", + "Applicability": [ + "tlp-red" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01", + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-12" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.1.2" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.MLDE.CN08.AR02", + "Description": "Ensure that MLDE instances without sensitive data are deployed in networks that meet organizational security standards.", + "Attributes": [ + { + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.MLDE.CN08 Restrict Virtual Networks for MLDE Instances", + "SubSection": "", + "SubSectionObjective": "Limit the virtual networks that can be used when creating new MLDE instances to ensure they are deployed within approved and secure network environments.", + "Applicability": [ + "tlp-red", + "tlp-amber", + "tlp-green", + "tlp-clear" + ], + "Recommendation": "", + "SectionThreatMappings": [ + { + "ReferenceId": "CCC", + "Identifiers": [ + "CCC.MLDE.TH01", + "CCC.Core.TH01" + ] + } + ], + "SectionGuidelineMappings": [ + { + "ReferenceId": "NIST-CSF", + "Identifiers": [ + "PR.AC-4" + ] + }, + { + "ReferenceId": "CCM", + "Identifiers": [ + "IAM-12" + ] + }, + { + "ReferenceId": "ISO_27001", + "Identifiers": [ + "2013 A.9.1.2" + ] + }, + { + "ReferenceId": "NIST_800_53", + "Identifiers": [ + "AC-6" + ] + } + ] + } + ], + "Checks": [] + }, + { + "Id": "CCC.Message.CN01.AR01", + "Description": "Attempt to publish a message without using a customer-managed encryption key and verify that the message is rejected or not stored.", + "Attributes": [ + { + "FamilyName": "Encryption", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.Message.CN01 Use Customer-Managed Encryption Keys (CMEK) for Messages", + "SubSection": "", + "SubSectionObjective": "Ensure that messages are encrypted using customer-managed encryption keys (CMEK) to provide enhanced control over encryption processes and keys, meeting compliance and security requirements.", "Applicability": [ "tlp-clear", "tlp-green", @@ -4765,7 +7526,7 @@ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH16" + "CCC.Core.TH01" ] } ], @@ -4776,310 +7537,53 @@ "PR.DS-1" ] }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-08", - "CEK-10", - "CEK-12" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.10.1.2" - ] - }, { "ReferenceId": "NIST_800_53", "Identifiers": [ "SC-12", - "SC-17" + "SC-13" ] } ] } ], - "Checks": [ - "kms_key_not_publicly_accessible", - "kms_key_rotation_enabled", - "bigquery_dataset_cmk_encryption", - "bigquery_table_cmk_encryption", - "dataproc_encrypted_with_cmks_disabled" - ] + "Checks": [] }, { - "Id": "CCC.Core.CN11.AR05", - "Description": "When encryption keys are used, the service MUST rotate active keys within 365 days of issuance.", + "Id": "CCC.SvlsComp.CN01.AR01", + "Description": "Attempt to access the serverless function over the public internet and verify that access is denied.", "Attributes": [ { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN11 Protect Encryption Keys", + "FamilyName": "Network Security", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.SvlsComp.CN01 Enforce Use of Private Endpoints for Serverless Function", "SubSection": "", - "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", + "SubSectionObjective": "Ensure that the serverless function is accessible only through a private endpoint, allowing it to communicate securely within a virtual private network and preventing unauthorized external access.", "Applicability": [ - "tlp-clear", - "tlp-green" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH16" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-08", - "CEK-10", - "CEK-12" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.10.1.2" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-12", - "SC-17" - ] - } - ] - } - ], - "Checks": [ - "kms_key_rotation_enabled", - "kms_key_not_publicly_accessible", - "dataproc_encrypted_with_cmks_disabled", - "bigquery_dataset_cmk_encryption", - "bigquery_table_cmk_encryption" - ] - }, - { - "Id": "CCC.Core.CN11.AR06", - "Description": "When encryption keys are used, the service MUST rotate active keys within 90 days of issuance.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN11 Protect Encryption Keys", - "SubSection": "", - "SubSectionObjective": "Ensure that encryption keys are managed securely by enforcing the use of approved algorithms, regular key rotation, and customer-managed encryption keys (CMEKs).", - "Applicability": [ - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH16" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.DS-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "CEK-08", - "CEK-10", - "CEK-12" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.10.1.2" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "SC-12", - "SC-17" - ] - } - ] - } - ], - "Checks": [ - "kms_key_rotation_enabled", - "kms_key_not_publicly_accessible", - "dataproc_encrypted_with_cmks_disabled", - "bigquery_dataset_cmk_encryption", - "bigquery_table_cmk_encryption" - ] - }, - { - "Id": "CCC.Core.CN14.AR01", - "Description": "When backups are created for disaster recovery purposes, the storage mechanism MUST NOT allow modification or deletion within 30 days of creation.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN14 Maintain Recent Backups", - "SubSection": "", - "SubSectionObjective": "Ensure that all backups used for disaster recovery are recent and subject to a retention policy that limits deletion.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Use immutable storage solutions where possible. Implement backup retention policies that enforce a minimum retention period of 30 days. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [] - } - ], - "Checks": [ - "cloudstorage_bucket_log_retention_policy_lock", - "cloudsql_instance_automated_backups" - ] - }, - { - "Id": "CCC.Core.CN14.AR02", - "Description": "When backups are created for disaster recovery purposes, the most recent backup MUST have a creation date within the past 30 days.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN14 Maintain Recent Backups", - "SubSection": "", - "SubSectionObjective": "Ensure that all backups used for disaster recovery are recent and subject to a retention policy that limits deletion.", - "Applicability": [ - "tlp-clear", - "tlp-green", + "tlp-red", "tlp-amber" ], - "Recommendation": "Implement automated backup processes to ensure that backups are created regularly. Monitor backup schedules and verify that the most recent backup creation date is within the last 30 days. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [] - } - ], - "Checks": [ - "cloudsql_instance_automated_backups" - ] - }, - { - "Id": "CCC.Core.CN14.AR02", - "Description": "When backups are created for disaster recovery purposes, the most recent backup MUST have a creation date within the past 14 days.", - "Attributes": [ - { - "FamilyName": "Data", - "FamilyDescription": "The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle. These controls govern how data is transmitted, stored, replicated, and protected from unauthorized access, tampering, or exposure beyond defined trust perimeters. ", - "Section": "CCC.Core.CN14 Maintain Recent Backups", - "SubSection": "", - "SubSectionObjective": "Ensure that all backups used for disaster recovery are recent and subject to a retention policy that limits deletion.", - "Applicability": [ - "tlp-red" - ], - "Recommendation": "Implement automated backup processes to ensure that backups are created regularly. Monitor backup schedules and verify that the most recent backup creation date is within the last 14 days. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH06" - ] - } - ], - "SectionGuidelineMappings": [] - } - ], - "Checks": [ - "cloudsql_instance_automated_backups", - "cloudstorage_bucket_log_retention_policy_lock" - ] - }, - { - "Id": "CCC.Core.CN03.AR01", - "Description": "When an entity attempts to modify the service through a user interface, the authentication process MUST require multiple identifying factors for authentication.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", - "SubSection": "", - "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01" + "CCC.Core.TH01" ] } ], "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-14" - ] - }, { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.AC-7" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-03", - "IAM-08" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.9.4.2" + "PR.AC-5" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "IA-2" + "SC-7", + "SC-8" ] } ] @@ -5088,990 +7592,45 @@ "Checks": [] }, { - "Id": "CCC.Core.CN03.AR02", - "Description": "When an entity attempts to modify the service through an API endpoint, the authentication process MUST require a credential such as an API key or token AND originate from within the trust perimeter.", + "Id": "CCC.SvlsComp.CN02.AR01", + "Description": "Send requests to invoke the function up to the allowed threshold and confirm they are successful; then send additional requests exceeding the threshold from the same entity and verify that they are denied.", "Attributes": [ { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", + "FamilyName": "Availability", + "FamilyDescription": "TODO: Describe this control family", + "Section": "CCC.SvlsComp.CN02 Implement Function Invocation Rate Limits", "SubSection": "", - "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", + "SubSectionObjective": "Ensure that function invocation is limited to a specified threshold from any single entity, preventing resource exhaustion and denial of service attacks.", "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" + "tlp-red", + "tlp-amber" ], "Recommendation": "", "SectionThreatMappings": [ { "ReferenceId": "CCC", "Identifiers": [ - "CCC.TH01" + "CCC.Core.TH12" ] } ], "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-14" - ] - }, { "ReferenceId": "NIST-CSF", "Identifiers": [ - "PR.AC-7" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-03", - "IAM-08" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.9.4.2" + "PR.DS-4" ] }, { "ReferenceId": "NIST_800_53", "Identifiers": [ - "IA-2" - ] - } - ] - } - ], - "Checks": [ - "apikeys_api_restrictions_configured", - "apikeys_key_exists", - "apikeys_key_rotated_in_90_days", - "compute_firewall_rdp_access_from_the_internet_allowed", - "compute_firewall_ssh_access_from_the_internet_allowed", - "compute_instance_public_ip", - "cloudsql_instance_public_ip", - "cloudstorage_bucket_public_access" - ] - }, - { - "Id": "CCC.Core.CN03.AR03", - "Description": "When an entity attempts to view information on the service through a user interface, the authentication process MUST require multiple identifying factors from the user.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", - "SubSection": "", - "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-14" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-7" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-03", - "IAM-08" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.9.4.2" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "IA-2" + "SC-5" ] } ] } ], "Checks": [] - }, - { - "Id": "CCC.Core.CN03.AR04", - "Description": "When an entity attempts to view information on the service through an API endpoint, the authentication process MUST require a credential such as an API key or token AND originate from within the trust perimeter.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN03 Implement Multi-factor Authentication (MFA) for Access", - "SubSection": "", - "SubSectionObjective": "Ensure that all sensitive activities require two or more identity factors during authentication to prevent unauthorized access.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-14" - ] - }, - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-7" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "IAM-03", - "IAM-08" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.9.4.2" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "IA-2" - ] - } - ] - } - ], - "Checks": [ - "compute_firewall_rdp_access_from_the_internet_allowed", - "compute_firewall_ssh_access_from_the_internet_allowed", - "cloudsql_instance_public_ip", - "cloudsql_instance_public_access", - "cloudstorage_bucket_public_access", - "apikeys_api_restrictions_configured", - "iam_account_access_approval_enabled", - "iam_audit_logs_enabled", - "compute_project_os_login_enabled" - ] - }, - { - "Id": "CCC.Core.CN05.AR01", - "Description": "When an attempt is made to modify data on the service or a child resource, the service MUST block requests from unauthorized entities.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", - "SubSection": "", - "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-01", - "DSP-07", - "DSP-08", - "DSP-10", - "DSP-17" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.3" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "compute_firewall_rdp_access_from_the_internet_allowed", - "compute_firewall_ssh_access_from_the_internet_allowed", - "cloudstorage_bucket_public_access", - "compute_instance_public_ip", - "cloudsql_instance_public_ip", - "compute_instance_ip_forwarding_is_enabled", - "compute_instance_default_service_account_in_use", - "compute_instance_default_service_account_in_use_with_full_api_access", - "gke_cluster_no_default_service_account", - "iam_no_service_roles_at_project_level", - "iam_role_kms_enforce_separation_of_duties", - "iam_role_sa_enforce_separation_of_duties", - "iam_sa_no_administrative_privileges", - "iam_sa_no_user_managed_keys", - "iam_sa_user_managed_key_rotate_90_days", - "iam_sa_user_managed_key_unused", - "iam_service_account_unused", - "iam_audit_logs_enabled", - "iam_account_access_approval_enabled" - ] - }, - { - "Id": "CCC.Core.CN05.AR02", - "Description": "When administrative access or configuration change is attempted on the service or a child resource, the service MUST refuse requests from unauthorized entities.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", - "SubSection": "", - "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-01", - "DSP-07", - "DSP-08", - "DSP-10", - "DSP-17" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.3" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "iam_account_access_approval_enabled", - "iam_audit_logs_enabled", - "iam_no_service_roles_at_project_level", - "iam_sa_no_administrative_privileges", - "iam_role_kms_enforce_separation_of_duties", - "iam_role_sa_enforce_separation_of_duties", - "iam_sa_no_user_managed_keys", - "iam_sa_user_managed_key_rotate_90_days", - "iam_sa_user_managed_key_unused", - "iam_service_account_unused", - "compute_project_os_login_enabled", - "compute_instance_default_service_account_in_use", - "compute_instance_default_service_account_in_use_with_full_api_access", - "compute_instance_public_ip", - "compute_firewall_rdp_access_from_the_internet_allowed", - "compute_firewall_ssh_access_from_the_internet_allowed", - "cloudsql_instance_public_ip", - "cloudstorage_bucket_public_access", - "apikeys_api_restrictions_configured", - "apikeys_key_exists", - "apikeys_key_rotated_in_90_days", - "iam_cloud_asset_inventory_enabled" - ] - }, - { - "Id": "CCC.Core.CN05.AR03", - "Description": "When administrative access or configuration change is attempted on the service or a child resource in a multi-tenant environment, the service MUST refuse requests across tenant boundaries unless the origin is explicitly included in a pre-approved allowlist.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", - "SubSection": "", - "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-01", - "DSP-07", - "DSP-08", - "DSP-10", - "DSP-17" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.3" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "iam_no_service_roles_at_project_level", - "iam_account_access_approval_enabled", - "iam_audit_logs_enabled", - "iam_role_kms_enforce_separation_of_duties", - "iam_role_sa_enforce_separation_of_duties", - "iam_sa_no_administrative_privileges", - "iam_sa_no_user_managed_keys", - "iam_sa_user_managed_key_rotate_90_days", - "iam_sa_user_managed_key_unused", - "iam_service_account_unused", - "compute_firewall_rdp_access_from_the_internet_allowed", - "compute_firewall_ssh_access_from_the_internet_allowed", - "compute_instance_public_ip", - "cloudsql_instance_public_ip", - "cloudsql_instance_public_access", - "cloudstorage_bucket_public_access", - "compute_instance_default_service_account_in_use", - "compute_instance_default_service_account_in_use_with_full_api_access", - "gke_cluster_no_default_service_account" - ] - }, - { - "Id": "CCC.Core.CN05.AR04", - "Description": "When data is requested from outside the trust perimeter, the service MUST refuse requests from unauthorized entities.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "", - "SubSection": "CCC.Core.CN05 Prevent Access from Untrusted Entities", - "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-01", - "DSP-07", - "DSP-08", - "DSP-10", - "DSP-17" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.3" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "iam_no_service_roles_at_project_level", - "iam_account_access_approval_enabled", - "iam_audit_logs_enabled", - "iam_role_kms_enforce_separation_of_duties", - "iam_role_sa_enforce_separation_of_duties", - "iam_sa_no_administrative_privileges", - "iam_sa_no_user_managed_keys", - "iam_sa_user_managed_key_rotate_90_days", - "iam_sa_user_managed_key_unused", - "iam_service_account_unused", - "gke_cluster_no_default_service_account", - "compute_instance_default_service_account_in_use", - "compute_instance_default_service_account_in_use_with_full_api_access", - "compute_instance_block_project_wide_ssh_keys_disabled", - "compute_instance_public_ip", - "compute_firewall_ssh_access_from_the_internet_allowed", - "compute_firewall_rdp_access_from_the_internet_allowed", - "cloudsql_instance_public_ip", - "cloudsql_instance_public_access", - "cloudstorage_bucket_public_access", - "kms_key_not_publicly_accessible", - "kms_key_rotation_enabled", - "apikeys_api_restrictions_configured", - "apikeys_key_exists", - "apikeys_key_rotated_in_90_days" - ] - }, - { - "Id": "CCC.Core.CN05.AR05", - "Description": "When any request is made from outside the trust perimeter, the service MUST NOT provide any response that may indicate the service exists.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", - "SubSection": "", - "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", - "Applicability": [ - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-01", - "DSP-07", - "DSP-08", - "DSP-10", - "DSP-17" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.3" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "compute_firewall_rdp_access_from_the_internet_allowed", - "compute_firewall_ssh_access_from_the_internet_allowed", - "compute_instance_public_ip", - "cloudsql_instance_public_access", - "cloudstorage_bucket_public_access", - "compute_instance_default_service_account_in_use", - "compute_instance_default_service_account_in_use_with_full_api_access", - "gke_cluster_no_default_service_account", - "iam_no_service_roles_at_project_level", - "iam_account_access_approval_enabled", - "iam_audit_logs_enabled", - "iam_sa_no_administrative_privileges", - "iam_sa_no_user_managed_keys", - "iam_role_kms_enforce_separation_of_duties", - "iam_role_sa_enforce_separation_of_duties", - "iam_sa_user_managed_key_rotate_90_days", - "iam_sa_user_managed_key_unused", - "iam_service_account_unused", - "apikeys_api_restrictions_configured", - "kms_key_not_publicly_accessible", - "compute_instance_ip_forwarding_is_enabled" - ] - }, - { - "Id": "CCC.Core.CN05.AR06", - "Description": "When any request is made to the service or a child resource, the service MUST refuse requests from unauthorized entities.", - "Attributes": [ - { - "FamilyName": "Identity and Access Management", - "FamilyDescription": "The Identity and Access Management control family ensures that only trusted and authenticated entities can access resources. These controls establish strong authentication, enforce multi-factor verification, and restrict access to approved sources to prevent unauthorized use or data exfiltration. ", - "Section": "CCC.Core.CN05 Prevent Access from Untrusted Entities", - "SubSection": "", - "SubSectionObjective": "Ensure that secure access controls enforce the principle of least privilege to restrict access to authorized entities from explicitly trusted sources only.", - "Applicability": [ - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "PR.AC-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "DSP-01", - "DSP-07", - "DSP-08", - "DSP-10", - "DSP-17" - ] - }, - { - "ReferenceId": "ISO_27001", - "Identifiers": [ - "2013 A.13.1.3" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AC-3" - ] - } - ] - } - ], - "Checks": [ - "compute_firewall_rdp_access_from_the_internet_allowed", - "compute_firewall_ssh_access_from_the_internet_allowed", - "compute_instance_public_ip", - "compute_instance_default_service_account_in_use", - "compute_instance_default_service_account_in_use_with_full_api_access", - "gke_cluster_no_default_service_account", - "cloudstorage_bucket_public_access", - "cloudsql_instance_public_access", - "cloudsql_instance_public_ip", - "cloudsql_instance_private_ip_assignment", - "apikeys_api_restrictions_configured", - "apikeys_key_exists", - "apikeys_key_rotated_in_90_days", - "kms_key_not_publicly_accessible", - "kms_key_rotation_enabled", - "iam_no_service_roles_at_project_level", - "iam_account_access_approval_enabled", - "iam_audit_logs_enabled", - "iam_role_kms_enforce_separation_of_duties", - "iam_role_sa_enforce_separation_of_duties", - "iam_sa_no_administrative_privileges", - "iam_sa_no_user_managed_keys", - "iam_sa_user_managed_key_rotate_90_days", - "iam_sa_user_managed_key_unused", - "iam_service_account_unused" - ] - }, - { - "Id": "CCC.Core.CN04.AR01", - "Description": "When administrative access or configuration change is attempted on the service or a child resource, the service MUST log the client identity, time, and result of the attempt.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements. ", - "Section": "CCC.Core.CN04 Log All Access and Changes", - "SubSection": "", - "SubSectionObjective": "Ensure that all access attempts are logged to maintain a detailed audit trail for security and compliance purposes.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.AE-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-08" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-2", - "AU-3", - "AU-12" - ] - } - ] - } - ], - "Checks": [ - "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_bucket_permission_changes_enabled", - "logging_log_metric_filter_and_alert_for_custom_role_changes_enabled", - "logging_log_metric_filter_and_alert_for_sql_instance_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_firewall_rule_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_route_changes_enabled", - "iam_audit_logs_enabled", - "logging_sink_created", - "logging_log_metric_filter_and_alert_for_project_ownership_changes_enabled" - ] - }, - { - "Id": "CCC.Core.CN04.AR02", - "Description": "When any attempt is made to modify data on the service or a child resource, the service MUST log the client identity, time, and result of the attempt.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements. ", - "Section": "CCC.Core.CN04 Log All Access and Changes", - "SubSection": "", - "SubSectionObjective": "Ensure that all access attempts are logged to maintain a detailed audit trail for security and compliance purposes.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.AE-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-08" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-2", - "AU-3", - "AU-12" - ] - } - ] - } - ], - "Checks": [ - "iam_audit_logs_enabled", - "logging_sink_created", - "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_bucket_permission_changes_enabled", - "logging_log_metric_filter_and_alert_for_custom_role_changes_enabled", - "logging_log_metric_filter_and_alert_for_sql_instance_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_firewall_rule_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_route_changes_enabled", - "logging_log_metric_filter_and_alert_for_project_ownership_changes_enabled" - ] - }, - { - "Id": "CCC.Core.CN04.AR03", - "Description": "When any attempt is made to read data on the service or a child resource, the service MUST log the client identity, time, and result of the attempt.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements. ", - "Section": "CCC.Core.CN04 Log All Access and Changes", - "SubSection": "", - "SubSectionObjective": "Ensure that all access attempts are logged to maintain a detailed audit trail for security and compliance purposes.", - "Applicability": [ - "tlp-red" - ], - "Recommendation": "", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH01" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.AE-3" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-08" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-2", - "AU-3", - "AU-12" - ] - } - ] - } - ], - "Checks": [ - "iam_audit_logs_enabled", - "logging_sink_created", - "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_bucket_permission_changes_enabled", - "logging_log_metric_filter_and_alert_for_custom_role_changes_enabled", - "logging_log_metric_filter_and_alert_for_sql_instance_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_firewall_rule_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_route_changes_enabled", - "logging_log_metric_filter_and_alert_for_project_ownership_changes_enabled" - ] - }, - { - "Id": "CCC.Core.CN07.AR01", - "Description": "When enumeration activities are detected, the service MUST publish an event to a monitored channel which includes the client identity, time, and nature of the activity.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements. ", - "Section": "CCC.Core.CN07 Alert on Unusual Enumeration Activity", - "SubSection": "", - "SubSectionObjective": "Ensure that logs and associated alerts are generated when unusual enumeration activity is detected that may indicate reconnaissance activities.", - "Applicability": [ - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Implement event publication mechanisms and alerts for patterns indicative of enumeration activities, such as repeated access attempts, requests, or liveness probes. Configure alerts to notify security teams of any activities that merit further investigation. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH15" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.AE-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-05", - "SEF-05" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-6" - ] - } - ] - } - ], - "Checks": [ - "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_bucket_permission_changes_enabled", - "logging_log_metric_filter_and_alert_for_custom_role_changes_enabled", - "logging_log_metric_filter_and_alert_for_sql_instance_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_firewall_rule_changes_enabled", - "logging_log_metric_filter_and_alert_for_project_ownership_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_route_changes_enabled", - "logging_sink_created" - ] - }, - { - "Id": "CCC.Core.CN07.AR02", - "Description": "When enumeration activities are detected, the service MUST log the client identity, time, and nature of the activity.", - "Attributes": [ - { - "FamilyName": "Logging & Monitoring", - "FamilyDescription": "The Logging & Monitoring control family ensures that access, changes, and security-relevant events are captured, monitored, and alerted on in order to provide visibility, support incident response, and meet compliance requirements. ", - "Section": "CCC.Core.CN07 Alert on Unusual Enumeration Activity", - "SubSection": "", - "SubSectionObjective": "Ensure that logs and associated alerts are generated when unusual enumeration activity is detected that may indicate reconnaissance activities.", - "Applicability": [ - "tlp-clear", - "tlp-green", - "tlp-amber", - "tlp-red" - ], - "Recommendation": "Implement logging mechanisms to capture details of enumeration activities, including client identity, timestamps, and activity nature. Retain logs according to organizational policies, and occasionally review them for patterns that may indicate reconnaissance activities. ", - "SectionThreatMappings": [ - { - "ReferenceId": "CCC", - "Identifiers": [ - "CCC.TH15" - ] - } - ], - "SectionGuidelineMappings": [ - { - "ReferenceId": "NIST-CSF", - "Identifiers": [ - "DE.AE-1" - ] - }, - { - "ReferenceId": "CCM", - "Identifiers": [ - "LOG-05", - "SEF-05" - ] - }, - { - "ReferenceId": "NIST_800_53", - "Identifiers": [ - "AU-6" - ] - } - ] - } - ], - "Checks": [ - "iam_audit_logs_enabled", - "logging_sink_created", - "logging_log_metric_filter_and_alert_for_audit_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_custom_role_changes_enabled", - "logging_log_metric_filter_and_alert_for_bucket_permission_changes_enabled", - "logging_log_metric_filter_and_alert_for_project_ownership_changes_enabled", - "logging_log_metric_filter_and_alert_for_sql_instance_configuration_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_network_route_changes_enabled", - "logging_log_metric_filter_and_alert_for_vpc_firewall_rule_changes_enabled" - ] } ] } diff --git a/prowler/compliance/googleworkspace/cis_1.3_googleworkspace.json b/prowler/compliance/googleworkspace/cis_1.3_googleworkspace.json index fa32397826..5d99d82c73 100644 --- a/prowler/compliance/googleworkspace/cis_1.3_googleworkspace.json +++ b/prowler/compliance/googleworkspace/cis_1.3_googleworkspace.json @@ -54,7 +54,9 @@ { "Id": "1.1.3", "Description": "Ensure super admin accounts are used only for super admin activities", - "Checks": [], + "Checks": [ + "directory_super_admin_only_admin_roles" + ], "Attributes": [ { "Section": "1 Directory", @@ -96,7 +98,9 @@ { "Id": "3.1.1.1.1", "Description": "Ensure external sharing options for primary calendars are configured", - "Checks": [], + "Checks": [ + "calendar_external_sharing_primary_calendar" + ], "Attributes": [ { "Section": "3 Apps", @@ -138,7 +142,9 @@ { "Id": "3.1.1.1.3", "Description": "Ensure external invitation warnings for Google Calendar are configured", - "Checks": [], + "Checks": [ + "calendar_external_invitations_warning" + ], "Attributes": [ { "Section": "3 Apps", @@ -159,7 +165,9 @@ { "Id": "3.1.1.2.1", "Description": "Ensure external sharing options for secondary calendars are configured", - "Checks": [], + "Checks": [ + "calendar_external_sharing_secondary_calendar" + ], "Attributes": [ { "Section": "3 Apps", diff --git a/prowler/compliance/googleworkspace/cisa_scuba_0.6_googleworkspace.json b/prowler/compliance/googleworkspace/cisa_scuba_0.6_googleworkspace.json index 17c2b88b2c..e81f4c70a3 100644 --- a/prowler/compliance/googleworkspace/cisa_scuba_0.6_googleworkspace.json +++ b/prowler/compliance/googleworkspace/cisa_scuba_0.6_googleworkspace.json @@ -1310,7 +1310,9 @@ { "Id": "GWS.CALENDAR.1.1", "Description": "External Sharing Options for Primary Calendars SHALL be configured to Only free/busy information (hide event details)", - "Checks": [], + "Checks": [ + "calendar_external_sharing_primary_calendar" + ], "Attributes": [ { "Section": "Calendar", @@ -1323,7 +1325,9 @@ { "Id": "GWS.CALENDAR.1.2", "Description": "External sharing options for secondary calendars SHALL be configured to Only free/busy information (hide event details)", - "Checks": [], + "Checks": [ + "calendar_external_sharing_secondary_calendar" + ], "Attributes": [ { "Section": "Calendar", @@ -1336,7 +1340,9 @@ { "Id": "GWS.CALENDAR.2.1", "Description": "External invitations warnings SHALL be enabled to prompt users before sending invitations", - "Checks": [], + "Checks": [ + "calendar_external_invitations_warning" + ], "Attributes": [ { "Section": "Calendar", diff --git a/prowler/lib/cli/parser.py b/prowler/lib/cli/parser.py index 47275b12cf..d2afa70e15 100644 --- a/prowler/lib/cli/parser.py +++ b/prowler/lib/cli/parser.py @@ -12,6 +12,7 @@ from prowler.config.config import ( default_output_directory, ) from prowler.lib.check.models import Severity +from prowler.lib.cli.redact import warn_sensitive_argument_values from prowler.lib.outputs.common import Status from prowler.providers.common.arguments import ( init_providers_parser, @@ -19,8 +20,6 @@ from prowler.providers.common.arguments import ( validate_provider_arguments, ) -SENSITIVE_ARGUMENTS = frozenset({"--shodan"}) - class ProwlerArgumentParser: # Set the default parser @@ -126,6 +125,10 @@ Detailed documentation at https://docs.prowler.com elif sys.argv[1] == "oci": sys.argv[1] = "oraclecloud" + # Warn about sensitive flags passed with explicit values + # Snapshot argv before parse_args() which may exit on errors + warn_sensitive_argument_values(list(sys.argv[1:])) + # Parse arguments args = self.parser.parse_args() @@ -434,7 +437,7 @@ Detailed documentation at https://docs.prowler.com nargs="?", default=None, metavar="SHODAN_API_KEY", - help="Check if any public IPs in your Cloud environments are exposed in Shodan.", + help="Check if any public IPs in your Cloud environments are exposed in Shodan. We recommend to use the SHODAN_API_KEY environment variable to provide the API key.", ) third_party_subparser.add_argument( "--slack", diff --git a/prowler/lib/cli/redact.py b/prowler/lib/cli/redact.py index 2dfd9e2bfa..3984139bae 100644 --- a/prowler/lib/cli/redact.py +++ b/prowler/lib/cli/redact.py @@ -1,6 +1,9 @@ from functools import lru_cache from importlib import import_module +from colorama import Fore, Style + +from prowler.lib.cli.sensitive import SENSITIVE_ARGUMENTS as COMMON_SENSITIVE_ARGUMENTS from prowler.lib.logger import logger from prowler.providers.common.provider import Provider, providers_path @@ -13,11 +16,7 @@ def get_sensitive_arguments() -> frozenset: sensitive: set[str] = set() # Common parser sensitive arguments (e.g., --shodan) - try: - parser_module = import_module("prowler.lib.cli.parser") - sensitive.update(getattr(parser_module, "SENSITIVE_ARGUMENTS", frozenset())) - except Exception as error: - logger.debug(f"Could not load SENSITIVE_ARGUMENTS from parser: {error}") + sensitive.update(COMMON_SENSITIVE_ARGUMENTS) # Provider-specific sensitive arguments for provider in Provider.get_available_providers(): @@ -66,3 +65,49 @@ def redact_argv(argv: list[str]) -> str: result.append(arg) return " ".join(result) + + +def warn_sensitive_argument_values(argv: list[str]) -> None: + """Log a warning for each sensitive CLI flag that was passed with an explicit value. + + Scans the raw argv list (not parsed args) to detect when users pass + secret values directly on the command line instead of using environment + variables. Handles both ``--flag value`` and ``--flag=value`` syntax. + + Args: + argv: The argument list to check (typically ``sys.argv[1:]``). + """ + sensitive = get_sensitive_arguments() + if not sensitive: + return + + use_color = "--no-color" not in argv + flags_with_values: list[str] = [] + + for i, arg in enumerate(argv): + # --flag=value syntax + if "=" in arg: + flag = arg.split("=", 1)[0] + if flag in sensitive: + flags_with_values.append(flag) + continue + + # --flag value syntax + if arg in sensitive: + if i + 1 < len(argv) and not argv[i + 1].startswith("-"): + flags_with_values.append(arg) + + for flag in flags_with_values: + if use_color: + logger.warning( + f"{Fore.YELLOW}{Style.BRIGHT}WARNING:{Style.RESET_ALL}{Fore.YELLOW} " + f"Passing a value directly to {flag} is not recommended. " + f"Use the corresponding environment variable instead to avoid " + f"exposing secrets in process listings and shell history.{Style.RESET_ALL}" + ) + else: + logger.warning( + f"Passing a value directly to {flag} is not recommended. " + f"Use the corresponding environment variable instead to avoid " + f"exposing secrets in process listings and shell history." + ) diff --git a/prowler/lib/cli/sensitive.py b/prowler/lib/cli/sensitive.py new file mode 100644 index 0000000000..4f5ad004d7 --- /dev/null +++ b/prowler/lib/cli/sensitive.py @@ -0,0 +1,8 @@ +"""Common parser sensitive arguments. + +This module is kept dependency-free (no prowler-internal imports) so that +``prowler.lib.cli.redact`` and any provider argument module can import it +without circular-import risk. +""" + +SENSITIVE_ARGUMENTS = frozenset({"--shodan"}) diff --git a/prowler/lib/outputs/compliance/ccc/ccc.py b/prowler/lib/outputs/compliance/ccc/ccc.py new file mode 100644 index 0000000000..99a6c91cd9 --- /dev/null +++ b/prowler/lib/outputs/compliance/ccc/ccc.py @@ -0,0 +1,98 @@ +from colorama import Fore, Style +from tabulate import tabulate + +from prowler.config.config import orange_color + + +def get_ccc_table( + findings: list, + bulk_checks_metadata: dict, + compliance_framework: str, + output_filename: str, + output_directory: str, + compliance_overview: bool, +): + section_table = { + "Provider": [], + "Section": [], + "Status": [], + "Muted": [], + } + pass_count = [] + fail_count = [] + muted_count = [] + sections = {} + for index, finding in enumerate(findings): + check = bulk_checks_metadata[finding.check_metadata.CheckID] + check_compliances = check.Compliance + for compliance in check_compliances: + if compliance.Framework == "CCC": + for requirement in compliance.Requirements: + for attribute in requirement.Attributes: + section = attribute.Section + + if section not in sections: + sections[section] = {"FAIL": 0, "PASS": 0, "Muted": 0} + + if finding.muted: + if index not in muted_count: + muted_count.append(index) + sections[section]["Muted"] += 1 + else: + if finding.status == "FAIL" and index not in fail_count: + fail_count.append(index) + sections[section]["FAIL"] += 1 + elif finding.status == "PASS" and index not in pass_count: + pass_count.append(index) + sections[section]["PASS"] += 1 + + sections = dict(sorted(sections.items())) + for section in sections: + section_table["Provider"].append(compliance.Provider) + section_table["Section"].append(section) + if sections[section]["FAIL"] > 0: + section_table["Status"].append( + f"{Fore.RED}FAIL({sections[section]['FAIL']}){Style.RESET_ALL}" + ) + else: + if sections[section]["PASS"] > 0: + section_table["Status"].append( + f"{Fore.GREEN}PASS({sections[section]['PASS']}){Style.RESET_ALL}" + ) + else: + section_table["Status"].append(f"{Fore.GREEN}PASS{Style.RESET_ALL}") + section_table["Muted"].append( + f"{orange_color}{sections[section]['Muted']}{Style.RESET_ALL}" + ) + + if ( + len(fail_count) + len(pass_count) + len(muted_count) > 1 + ): # If there are no resources, don't print the compliance table + print( + f"\nCompliance Status of {Fore.YELLOW}{compliance_framework.upper()}{Style.RESET_ALL} Framework:" + ) + total_findings_count = len(fail_count) + len(pass_count) + len(muted_count) + overview_table = [ + [ + f"{Fore.RED}{round(len(fail_count) / total_findings_count * 100, 2)}% ({len(fail_count)}) FAIL{Style.RESET_ALL}", + f"{Fore.GREEN}{round(len(pass_count) / total_findings_count * 100, 2)}% ({len(pass_count)}) PASS{Style.RESET_ALL}", + f"{orange_color}{round(len(muted_count) / total_findings_count * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}", + ] + ] + print(tabulate(overview_table, tablefmt="rounded_grid")) + if not compliance_overview: + if len(fail_count) > 0 and len(section_table["Section"]) > 0: + print( + f"\nFramework {Fore.YELLOW}{compliance_framework.upper()}{Style.RESET_ALL} Results:" + ) + print( + tabulate( + section_table, + tablefmt="rounded_grid", + headers="keys", + ) + ) + print(f"\nDetailed results of {compliance_framework.upper()} are in:") + print( + f" - CSV: {output_directory}/compliance/{output_filename}_{compliance_framework}.csv\n" + ) diff --git a/prowler/lib/outputs/compliance/compliance.py b/prowler/lib/outputs/compliance/compliance.py index bb7fbf1146..d399900837 100644 --- a/prowler/lib/outputs/compliance/compliance.py +++ b/prowler/lib/outputs/compliance/compliance.py @@ -3,6 +3,7 @@ import sys from prowler.lib.check.models import Check_Report from prowler.lib.logger import logger from prowler.lib.outputs.compliance.c5.c5 import get_c5_table +from prowler.lib.outputs.compliance.ccc.ccc import get_ccc_table from prowler.lib.outputs.compliance.cis.cis import get_cis_table from prowler.lib.outputs.compliance.csa.csa import get_csa_table from prowler.lib.outputs.compliance.ens.ens import get_ens_table @@ -104,6 +105,15 @@ def display_compliance_table( output_directory, compliance_overview, ) + elif compliance_framework.startswith("ccc_"): + get_ccc_table( + findings, + bulk_checks_metadata, + compliance_framework, + output_filename, + output_directory, + compliance_overview, + ) else: get_generic_compliance_table( findings, diff --git a/prowler/providers/aws/services/iam/lib/policy.py b/prowler/providers/aws/services/iam/lib/policy.py index d8806f280b..ecf09d312f 100644 --- a/prowler/providers/aws/services/iam/lib/policy.py +++ b/prowler/providers/aws/services/iam/lib/policy.py @@ -617,6 +617,11 @@ def is_condition_block_restrictive( "aws:sourceorgpaths", "aws:userid", "aws:username", + "aws:calledvia", + "aws:calledviafirst", + "aws:calledvialast", + "kms:calleraccount", + "kms:viaservice", "s3:resourceaccount", "lambda:eventsourcetoken", # For Alexa Home functions, a token that the invoker must supply. ], @@ -635,6 +640,11 @@ def is_condition_block_restrictive( "aws:sourceorgpaths", "aws:userid", "aws:username", + "aws:calledvia", + "aws:calledviafirst", + "aws:calledvialast", + "kms:calleraccount", + "kms:viaservice", "s3:resourceaccount", "lambda:eventsourcetoken", ], diff --git a/prowler/providers/aws/services/stepfunctions/stepfunctions_statemachine_no_secrets_in_definition/__init__.py b/prowler/providers/aws/services/stepfunctions/stepfunctions_statemachine_no_secrets_in_definition/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/aws/services/stepfunctions/stepfunctions_statemachine_no_secrets_in_definition/stepfunctions_statemachine_no_secrets_in_definition.metadata.json b/prowler/providers/aws/services/stepfunctions/stepfunctions_statemachine_no_secrets_in_definition/stepfunctions_statemachine_no_secrets_in_definition.metadata.json new file mode 100644 index 0000000000..746b53d8fd --- /dev/null +++ b/prowler/providers/aws/services/stepfunctions/stepfunctions_statemachine_no_secrets_in_definition/stepfunctions_statemachine_no_secrets_in_definition.metadata.json @@ -0,0 +1,44 @@ +{ + "Provider": "aws", + "CheckID": "stepfunctions_statemachine_no_secrets_in_definition", + "CheckTitle": "Step Functions state machine has no sensitive credentials in its definition", + "CheckType": [ + "Software and Configuration Checks/AWS Security Best Practices", + "TTPs/Credential Access", + "Effects/Data Exposure", + "Sensitive Data Identifications/Security" + ], + "ServiceName": "stepfunctions", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "critical", + "ResourceType": "AwsStepFunctionStateMachine", + "ResourceGroup": "serverless", + "Description": "**AWS Step Functions state machines** are inspected for **hardcoded secrets** (keys, tokens, passwords) embedded directly in the state machine **definition** (Amazon States Language JSON).\n\nSuch values indicate sensitive data is stored directly in task parameters instead of being sourced securely.", + "Risk": "Plaintext secrets in state machine definitions reduce confidentiality: values can be viewed in the AWS Console, CLI, and may leak into execution logs or public outputs. Compromised credentials enable unauthorized AWS actions, lateral movement, and data exfiltration.", + "RelatedUrl": "", + "AdditionalURLs": [ + "https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html", + "https://docs.aws.amazon.com/step-functions/latest/dg/security-best-practices.html", + "https://docs.aws.amazon.com/secretsmanager/latest/userguide/integrating_how-services-use-secrets_step-functions.html", + "https://docs.aws.amazon.com/systems-manager/latest/userguide/integration-ps-secretsmanager.html" + ], + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "```yaml\nResources:\n :\n Type: AWS::StepFunctions::StateMachine\n Properties:\n StateMachineName: \n RoleArn: \n DefinitionString: |\n {\n \"Comment\": \"Example state machine\",\n \"StartAt\": \"MyTask\",\n \"States\": {\n \"MyTask\": {\n \"Type\": \"Task\",\n \"Resource\": \"arn:aws:states:::aws-sdk:secretsmanager:getSecretValue\",\n \"Parameters\": {\n \"SecretId\": \"\"\n },\n \"End\": true\n }\n }\n }\n```", + "Other": "1. In AWS Console, go to Step Functions and open your state machine\n2. Click Edit\n3. Remove any hardcoded secrets from the definition\n4. Use AWS Secrets Manager or Parameter Store to retrieve secrets at runtime\n5. Grant the state machine IAM role permission to access the secret\n6. Save the updated definition", + "Terraform": "```hcl\nresource \"aws_sfn_state_machine\" \"\" {\n name = \"\"\n role_arn = \"\"\n\n definition = jsonencode({\n Comment = \"Example state machine\"\n StartAt = \"MyTask\"\n States = {\n MyTask = {\n Type = \"Task\"\n Resource = \"arn:aws:states:::aws-sdk:secretsmanager:getSecretValue\"\n Parameters = {\n SecretId = \"\" # Reference secret by name, never hardcode value\n }\n End = true\n }\n }\n })\n}\n```" + }, + "Recommendation": { + "Text": "Store secrets outside the state machine definition and retrieve them securely at runtime using **AWS Secrets Manager** or **AWS Systems Manager Parameter Store**.\n- Use the `aws-sdk:secretsmanager:getSecretValue` integration to fetch secrets dynamically\n- Enforce **least privilege** on the state machine IAM role\n- Rotate secrets regularly and never embed them in the definition", + "Url": "https://hub.prowler.com/check/stepfunctions_statemachine_no_secrets_in_definition" + } + }, + "Categories": [ + "secrets" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/stepfunctions/stepfunctions_statemachine_no_secrets_in_definition/stepfunctions_statemachine_no_secrets_in_definition.py b/prowler/providers/aws/services/stepfunctions/stepfunctions_statemachine_no_secrets_in_definition/stepfunctions_statemachine_no_secrets_in_definition.py new file mode 100644 index 0000000000..db04710029 --- /dev/null +++ b/prowler/providers/aws/services/stepfunctions/stepfunctions_statemachine_no_secrets_in_definition/stepfunctions_statemachine_no_secrets_in_definition.py @@ -0,0 +1,45 @@ +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.lib.utils.utils import detect_secrets_scan +from prowler.providers.aws.services.stepfunctions.stepfunctions_client import ( + stepfunctions_client, +) + + +class stepfunctions_statemachine_no_secrets_in_definition(Check): + """Check that AWS Step Functions state machine definitions contain no hardcoded secrets.""" + + def execute(self) -> list[Check_Report_AWS]: + findings = [] + secrets_ignore_patterns = stepfunctions_client.audit_config.get( + "secrets_ignore_patterns", [] + ) + for state_machine in stepfunctions_client.state_machines.values(): + report = Check_Report_AWS(metadata=self.metadata(), resource=state_machine) + report.status = "PASS" + report.status_extended = f"No secrets found in Step Functions state machine {state_machine.name} definition." + + if state_machine.definition: + detect_secrets_output = detect_secrets_scan( + data=state_machine.definition, + excluded_secrets=secrets_ignore_patterns, + detect_secrets_plugins=stepfunctions_client.audit_config.get( + "detect_secrets_plugins", + ), + ) + + if detect_secrets_output: + secrets_string = ", ".join( + [ + f"{secret['type']} on line {secret['line_number']}" + for secret in detect_secrets_output + ] + ) + report.status = "FAIL" + report.status_extended = ( + f"Potential {'secrets' if len(detect_secrets_output) > 1 else 'secret'} " + f"found in Step Functions state machine {state_machine.name} definition " + f"-> {secrets_string}." + ) + + findings.append(report) + return findings diff --git a/prowler/providers/googleworkspace/googleworkspace_provider.py b/prowler/providers/googleworkspace/googleworkspace_provider.py index 83beee0d3e..563078f1e3 100644 --- a/prowler/providers/googleworkspace/googleworkspace_provider.py +++ b/prowler/providers/googleworkspace/googleworkspace_provider.py @@ -59,11 +59,14 @@ class GoogleworkspaceProvider(Provider): _mutelist: GoogleWorkspaceMutelist audit_metadata: Audit_Metadata - # Google Workspace Admin SDK OAuth2 scopes - DIRECTORY_SCOPES = [ + # Google Workspace OAuth2 scopes + SCOPES = [ "https://www.googleapis.com/auth/admin.directory.user.readonly", "https://www.googleapis.com/auth/admin.directory.domain.readonly", "https://www.googleapis.com/auth/admin.directory.customer.readonly", + # Cloud Identity Policy API (calendar and other app policies) + "https://www.googleapis.com/auth/cloud-identity.policies.readonly", + "https://www.googleapis.com/auth/admin.directory.rolemanagement.readonly", ] def __init__( @@ -214,7 +217,7 @@ class GoogleworkspaceProvider(Provider): try: credentials = service_account.Credentials.from_service_account_file( credentials_file, - scopes=GoogleworkspaceProvider.DIRECTORY_SCOPES, + scopes=GoogleworkspaceProvider.SCOPES, ) except FileNotFoundError as error: raise GoogleWorkspaceInvalidCredentialsError( @@ -241,7 +244,7 @@ class GoogleworkspaceProvider(Provider): try: credentials = service_account.Credentials.from_service_account_info( credentials_data, - scopes=GoogleworkspaceProvider.DIRECTORY_SCOPES, + scopes=GoogleworkspaceProvider.SCOPES, ) except ValueError as error: raise GoogleWorkspaceInvalidCredentialsError( @@ -264,7 +267,7 @@ class GoogleworkspaceProvider(Provider): try: credentials = service_account.Credentials.from_service_account_file( env_file, - scopes=GoogleworkspaceProvider.DIRECTORY_SCOPES, + scopes=GoogleworkspaceProvider.SCOPES, ) except FileNotFoundError as error: raise GoogleWorkspaceInvalidCredentialsError( @@ -293,7 +296,7 @@ class GoogleworkspaceProvider(Provider): try: credentials = service_account.Credentials.from_service_account_info( credentials_data, - scopes=GoogleworkspaceProvider.DIRECTORY_SCOPES, + scopes=GoogleworkspaceProvider.SCOPES, ) except ValueError as error: raise GoogleWorkspaceInvalidCredentialsError( @@ -414,7 +417,7 @@ class GoogleworkspaceProvider(Provider): ) # Fetch all domains (primary + aliases) to support domain aliases - # The scope admin.directory.domain.readonly is already in DIRECTORY_SCOPES + # The scope admin.directory.domain.readonly is already in SCOPES above try: domains_response = service.domains().list(customer="my_customer").execute() valid_domains = [ diff --git a/prowler/providers/googleworkspace/services/calendar/__init__.py b/prowler/providers/googleworkspace/services/calendar/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/googleworkspace/services/calendar/calendar_client.py b/prowler/providers/googleworkspace/services/calendar/calendar_client.py new file mode 100644 index 0000000000..9162bb3207 --- /dev/null +++ b/prowler/providers/googleworkspace/services/calendar/calendar_client.py @@ -0,0 +1,6 @@ +from prowler.providers.common.provider import Provider +from prowler.providers.googleworkspace.services.calendar.calendar_service import ( + Calendar, +) + +calendar_client = Calendar(Provider.get_global_provider()) diff --git a/prowler/providers/googleworkspace/services/calendar/calendar_external_invitations_warning/__init__.py b/prowler/providers/googleworkspace/services/calendar/calendar_external_invitations_warning/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/googleworkspace/services/calendar/calendar_external_invitations_warning/calendar_external_invitations_warning.metadata.json b/prowler/providers/googleworkspace/services/calendar/calendar_external_invitations_warning/calendar_external_invitations_warning.metadata.json new file mode 100644 index 0000000000..3a47f981d0 --- /dev/null +++ b/prowler/providers/googleworkspace/services/calendar/calendar_external_invitations_warning/calendar_external_invitations_warning.metadata.json @@ -0,0 +1,41 @@ +{ + "Provider": "googleworkspace", + "CheckID": "calendar_external_invitations_warning", + "CheckTitle": "External invitation warnings are enabled for Google Calendar", + "CheckType": [], + "ServiceName": "calendar", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "NotDefined", + "ResourceGroup": "collaboration", + "Description": "Google Calendar **warns users** when they invite guests from outside the organization to an event. This prompt gives users a chance to reconsider before sharing meeting details with external parties, reducing the likelihood of **accidental information disclosure** through calendar invitations.", + "Risk": "Without external invitation warnings, users may unintentionally include **external guests** in internal meetings, exposing **confidential meeting details**, agendas, and internal attendee lists to unauthorized parties. This is a common vector for inadvertent data leakage through everyday calendar actions.", + "RelatedUrl": "", + "AdditionalURLs": [ + "https://support.google.com/a/answer/6329284", + "https://knowledge.workspace.google.com/admin/calendar/set-google-calendar-sharing-options", + "https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings" + ], + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "1. Sign in to the Google **Admin console** at https://admin.google.com\n2. Navigate to **Apps** > **Google Workspace** > **Calendar**\n3. Click **Sharing settings**\n4. Under **External invitations**, check **Warn users when inviting guests outside of the domain**\n5. Click **Save**", + "Terraform": "" + }, + "Recommendation": { + "Text": "Enable external invitation warnings so users are notified whenever a meeting invitation includes guests outside the organization. This simple prompt helps prevent accidental disclosure of meeting details to unintended recipients.", + "Url": "https://hub.prowler.com/check/calendar_external_invitations_warning" + } + }, + "Categories": [ + "internet-exposed" + ], + "DependsOn": [], + "RelatedTo": [ + "calendar_external_sharing_primary_calendar", + "calendar_external_sharing_secondary_calendar" + ], + "Notes": "" +} diff --git a/prowler/providers/googleworkspace/services/calendar/calendar_external_invitations_warning/calendar_external_invitations_warning.py b/prowler/providers/googleworkspace/services/calendar/calendar_external_invitations_warning/calendar_external_invitations_warning.py new file mode 100644 index 0000000000..7af51cbfba --- /dev/null +++ b/prowler/providers/googleworkspace/services/calendar/calendar_external_invitations_warning/calendar_external_invitations_warning.py @@ -0,0 +1,56 @@ +from typing import List + +from prowler.lib.check.models import Check, CheckReportGoogleWorkspace +from prowler.providers.googleworkspace.services.calendar.calendar_client import ( + calendar_client, +) + + +class calendar_external_invitations_warning(Check): + """Check that external invitation warnings are enabled for Google Calendar + + This check verifies that the domain-level policy warns users when they + invite guests from outside the organization, reducing the risk of accidental + information disclosure through calendar events. + """ + + def execute(self) -> List[CheckReportGoogleWorkspace]: + findings = [] + + if calendar_client.policies_fetched: + report = CheckReportGoogleWorkspace( + metadata=self.metadata(), + resource=calendar_client.provider.identity, + resource_name=calendar_client.provider.identity.domain, + resource_id=calendar_client.provider.identity.customer_id, + customer_id=calendar_client.provider.identity.customer_id, + location="global", + ) + + warning_enabled = calendar_client.policies.external_invitations_warning + + if warning_enabled is True: + report.status = "PASS" + report.status_extended = ( + f"External invitation warnings for Google Calendar are enabled " + f"in domain {calendar_client.provider.identity.domain}." + ) + else: + report.status = "FAIL" + if warning_enabled is None: + report.status_extended = ( + f"External invitation warnings for Google Calendar are not " + f"explicitly configured in domain " + f"{calendar_client.provider.identity.domain}. " + f"Users should be warned when inviting guests outside the organization." + ) + else: + report.status_extended = ( + f"External invitation warnings for Google Calendar are disabled " + f"in domain {calendar_client.provider.identity.domain}. " + f"Users should be warned when inviting guests outside the organization." + ) + + findings.append(report) + + return findings diff --git a/prowler/providers/googleworkspace/services/calendar/calendar_external_sharing_primary_calendar/__init__.py b/prowler/providers/googleworkspace/services/calendar/calendar_external_sharing_primary_calendar/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/googleworkspace/services/calendar/calendar_external_sharing_primary_calendar/calendar_external_sharing_primary_calendar.metadata.json b/prowler/providers/googleworkspace/services/calendar/calendar_external_sharing_primary_calendar/calendar_external_sharing_primary_calendar.metadata.json new file mode 100644 index 0000000000..536e8413f2 --- /dev/null +++ b/prowler/providers/googleworkspace/services/calendar/calendar_external_sharing_primary_calendar/calendar_external_sharing_primary_calendar.metadata.json @@ -0,0 +1,41 @@ +{ + "Provider": "googleworkspace", + "CheckID": "calendar_external_sharing_primary_calendar", + "CheckTitle": "External sharing for primary calendars is restricted to free/busy only", + "CheckType": [], + "ServiceName": "calendar", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "NotDefined", + "ResourceGroup": "collaboration", + "Description": "Primary calendars in the Google Workspace domain share **only free/busy information** with external users. When external sharing is set to share full event details, sensitive information such as meeting titles, attendees, locations, and descriptions is exposed to users outside the organization.", + "Risk": "Overly permissive external sharing of primary calendars exposes **sensitive meeting metadata** — titles, attendees, locations, and descriptions — to users outside the organization. This increases the risk of **information disclosure**, **social engineering**, and **targeted phishing** based on insights into organizational activities.", + "RelatedUrl": "", + "AdditionalURLs": [ + "https://support.google.com/a/answer/60765", + "https://knowledge.workspace.google.com/admin/calendar/set-google-calendar-sharing-options", + "https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings" + ], + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "1. Sign in to the Google **Admin console** at https://admin.google.com\n2. Navigate to **Apps** > **Google Workspace** > **Calendar**\n3. Click **Sharing settings**\n4. Under **External sharing options for primary calendars**, select **Only free/busy information (hide event details)**\n5. Click **Save**", + "Terraform": "" + }, + "Recommendation": { + "Text": "Restrict external sharing of primary calendars to free/busy information only. This preserves scheduling functionality with external users while preventing exposure of sensitive meeting details.", + "Url": "https://hub.prowler.com/check/calendar_external_sharing_primary_calendar" + } + }, + "Categories": [ + "internet-exposed" + ], + "DependsOn": [], + "RelatedTo": [ + "calendar_external_sharing_secondary_calendar", + "calendar_external_invitations_warning" + ], + "Notes": "" +} diff --git a/prowler/providers/googleworkspace/services/calendar/calendar_external_sharing_primary_calendar/calendar_external_sharing_primary_calendar.py b/prowler/providers/googleworkspace/services/calendar/calendar_external_sharing_primary_calendar/calendar_external_sharing_primary_calendar.py new file mode 100644 index 0000000000..b019acf4de --- /dev/null +++ b/prowler/providers/googleworkspace/services/calendar/calendar_external_sharing_primary_calendar/calendar_external_sharing_primary_calendar.py @@ -0,0 +1,56 @@ +from typing import List + +from prowler.lib.check.models import Check, CheckReportGoogleWorkspace +from prowler.providers.googleworkspace.services.calendar.calendar_client import ( + calendar_client, +) + + +class calendar_external_sharing_primary_calendar(Check): + """Check that external sharing for primary calendars is restricted to free/busy only + + This check verifies that the domain-level policy for primary calendar external + sharing is set to share only free/busy information, preventing exposure of + event details to external users. + """ + + def execute(self) -> List[CheckReportGoogleWorkspace]: + findings = [] + + if calendar_client.policies_fetched: + report = CheckReportGoogleWorkspace( + metadata=self.metadata(), + resource=calendar_client.provider.identity, + resource_name=calendar_client.provider.identity.domain, + resource_id=calendar_client.provider.identity.customer_id, + customer_id=calendar_client.provider.identity.customer_id, + location="global", + ) + + sharing = calendar_client.policies.primary_calendar_external_sharing + + if sharing == "EXTERNAL_FREE_BUSY_ONLY": + report.status = "PASS" + report.status_extended = ( + f"Primary calendar external sharing in domain " + f"{calendar_client.provider.identity.domain} is restricted to " + f"free/busy information only." + ) + else: + report.status = "FAIL" + if sharing is None: + report.status_extended = ( + f"Primary calendar external sharing is not explicitly configured " + f"in domain {calendar_client.provider.identity.domain}. " + f"External sharing should be restricted to free/busy information only." + ) + else: + report.status_extended = ( + f"Primary calendar external sharing in domain " + f"{calendar_client.provider.identity.domain} is set to {sharing}. " + f"External sharing should be restricted to free/busy information only." + ) + + findings.append(report) + + return findings diff --git a/prowler/providers/googleworkspace/services/calendar/calendar_external_sharing_secondary_calendar/__init__.py b/prowler/providers/googleworkspace/services/calendar/calendar_external_sharing_secondary_calendar/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/googleworkspace/services/calendar/calendar_external_sharing_secondary_calendar/calendar_external_sharing_secondary_calendar.metadata.json b/prowler/providers/googleworkspace/services/calendar/calendar_external_sharing_secondary_calendar/calendar_external_sharing_secondary_calendar.metadata.json new file mode 100644 index 0000000000..1dadf4965c --- /dev/null +++ b/prowler/providers/googleworkspace/services/calendar/calendar_external_sharing_secondary_calendar/calendar_external_sharing_secondary_calendar.metadata.json @@ -0,0 +1,41 @@ +{ + "Provider": "googleworkspace", + "CheckID": "calendar_external_sharing_secondary_calendar", + "CheckTitle": "External sharing for secondary calendars is restricted to free/busy only", + "CheckType": [], + "ServiceName": "calendar", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "NotDefined", + "ResourceGroup": "collaboration", + "Description": "Secondary calendars in the Google Workspace domain share **only free/busy information** with external users. Secondary calendars are additional calendars users create beyond their primary calendar (e.g., for projects, teams, or personal events), and are commonly used to organize sensitive or focused activities that should not be visible to external parties.", + "Risk": "Overly permissive external sharing of secondary calendars exposes **project-specific or team-specific event details** to users outside the organization. Because secondary calendars often hold more targeted activities (e.g., product launches, internal reviews), unrestricted external sharing increases the risk of **information disclosure** and **competitive intelligence leakage**.", + "RelatedUrl": "", + "AdditionalURLs": [ + "https://support.google.com/a/answer/60765", + "https://knowledge.workspace.google.com/admin/calendar/set-google-calendar-sharing-options", + "https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings" + ], + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "1. Sign in to the Google **Admin console** at https://admin.google.com\n2. Navigate to **Apps** > **Google Workspace** > **Calendar**\n3. Click **Sharing settings**\n4. Under **External sharing options for secondary calendars**, select **Only free/busy information (hide event details)**\n5. Click **Save**", + "Terraform": "" + }, + "Recommendation": { + "Text": "Restrict external sharing of secondary calendars to free/busy information only. This preserves scheduling interoperability with external collaborators while preventing exposure of sensitive event details in user-created calendars.", + "Url": "https://hub.prowler.com/check/calendar_external_sharing_secondary_calendar" + } + }, + "Categories": [ + "internet-exposed" + ], + "DependsOn": [], + "RelatedTo": [ + "calendar_external_sharing_primary_calendar", + "calendar_external_invitations_warning" + ], + "Notes": "" +} diff --git a/prowler/providers/googleworkspace/services/calendar/calendar_external_sharing_secondary_calendar/calendar_external_sharing_secondary_calendar.py b/prowler/providers/googleworkspace/services/calendar/calendar_external_sharing_secondary_calendar/calendar_external_sharing_secondary_calendar.py new file mode 100644 index 0000000000..f7a418b48e --- /dev/null +++ b/prowler/providers/googleworkspace/services/calendar/calendar_external_sharing_secondary_calendar/calendar_external_sharing_secondary_calendar.py @@ -0,0 +1,56 @@ +from typing import List + +from prowler.lib.check.models import Check, CheckReportGoogleWorkspace +from prowler.providers.googleworkspace.services.calendar.calendar_client import ( + calendar_client, +) + + +class calendar_external_sharing_secondary_calendar(Check): + """Check that external sharing for secondary calendars is restricted to free/busy only + + This check verifies that the domain-level policy for secondary calendar external + sharing is set to share only free/busy information, preventing exposure of + event details in user-created calendars to external users. + """ + + def execute(self) -> List[CheckReportGoogleWorkspace]: + findings = [] + + if calendar_client.policies_fetched: + report = CheckReportGoogleWorkspace( + metadata=self.metadata(), + resource=calendar_client.provider.identity, + resource_name=calendar_client.provider.identity.domain, + resource_id=calendar_client.provider.identity.customer_id, + customer_id=calendar_client.provider.identity.customer_id, + location="global", + ) + + sharing = calendar_client.policies.secondary_calendar_external_sharing + + if sharing == "EXTERNAL_FREE_BUSY_ONLY": + report.status = "PASS" + report.status_extended = ( + f"Secondary calendar external sharing in domain " + f"{calendar_client.provider.identity.domain} is restricted to " + f"free/busy information only." + ) + else: + report.status = "FAIL" + if sharing is None: + report.status_extended = ( + f"Secondary calendar external sharing is not explicitly configured " + f"in domain {calendar_client.provider.identity.domain}. " + f"External sharing should be restricted to free/busy information only." + ) + else: + report.status_extended = ( + f"Secondary calendar external sharing in domain " + f"{calendar_client.provider.identity.domain} is set to {sharing}. " + f"External sharing should be restricted to free/busy information only." + ) + + findings.append(report) + + return findings diff --git a/prowler/providers/googleworkspace/services/calendar/calendar_service.py b/prowler/providers/googleworkspace/services/calendar/calendar_service.py new file mode 100644 index 0000000000..ae71c0fdf1 --- /dev/null +++ b/prowler/providers/googleworkspace/services/calendar/calendar_service.py @@ -0,0 +1,112 @@ +from typing import Optional + +from pydantic import BaseModel + +from prowler.lib.logger import logger +from prowler.providers.googleworkspace.lib.service.service import GoogleWorkspaceService + + +class Calendar(GoogleWorkspaceService): + """Google Workspace Calendar service for auditing domain-level calendar policies. + + Uses the Cloud Identity Policy API v1 to read calendar sharing + and invitation settings configured in the Admin Console. + """ + + def __init__(self, provider): + super().__init__(provider) + self.policies = CalendarPolicies() + self.policies_fetched = False + self._fetch_calendar_policies() + + def _fetch_calendar_policies(self): + """Fetch calendar policies from the Cloud Identity Policy API v1.""" + logger.info("Calendar - Fetching calendar policies...") + + try: + service = self._build_service("cloudidentity", "v1") + + if not service: + logger.error("Failed to build Cloud Identity service") + return + + request = service.policies().list(pageSize=100) + fetch_succeeded = True + + while request is not None: + try: + response = request.execute() + + for policy in response.get("policies", []): + setting = policy.get("setting", {}) + setting_type = setting.get("type", "").removeprefix("settings/") + value = setting.get("value", {}) + + if ( + setting_type + == "calendar.primary_calendar_max_allowed_external_sharing" + ): + self.policies.primary_calendar_external_sharing = value.get( + "maxAllowedExternalSharing" + ) + logger.debug( + "Primary calendar external sharing: " + f"{self.policies.primary_calendar_external_sharing}" + ) + + elif ( + setting_type + == "calendar.secondary_calendar_max_allowed_external_sharing" + ): + self.policies.secondary_calendar_external_sharing = ( + value.get("maxAllowedExternalSharing") + ) + logger.debug( + "Secondary calendar external sharing: " + f"{self.policies.secondary_calendar_external_sharing}" + ) + + elif setting_type == "calendar.external_invitations": + self.policies.external_invitations_warning = value.get( + "warnOnInvite" + ) + logger.debug( + "External invitations warning: " + f"{self.policies.external_invitations_warning}" + ) + + request = service.policies().list_next(request, response) + + except Exception as error: + self._handle_api_error( + error, + "fetching calendar policies", + self.provider.identity.customer_id, + ) + fetch_succeeded = False + break + + self.policies_fetched = fetch_succeeded + + logger.info( + f"Calendar policies fetched - " + f"Primary sharing: {self.policies.primary_calendar_external_sharing}, " + f"Secondary sharing: {self.policies.secondary_calendar_external_sharing}, " + f"Invitation warnings: {self.policies.external_invitations_warning}" + ) + + except Exception as error: + self._handle_api_error( + error, + "fetching calendar policies", + self.provider.identity.customer_id, + ) + self.policies_fetched = False + + +class CalendarPolicies(BaseModel): + """Model for domain-level Calendar policy settings.""" + + primary_calendar_external_sharing: Optional[str] = None + secondary_calendar_external_sharing: Optional[str] = None + external_invitations_warning: Optional[bool] = None diff --git a/prowler/providers/googleworkspace/services/directory/directory_service.py b/prowler/providers/googleworkspace/services/directory/directory_service.py index ef0b54c18c..6afa8e4521 100644 --- a/prowler/providers/googleworkspace/services/directory/directory_service.py +++ b/prowler/providers/googleworkspace/services/directory/directory_service.py @@ -8,23 +8,21 @@ class Directory(GoogleWorkspaceService): def __init__(self, provider): super().__init__(provider) + self._service = self._build_service("admin", "directory_v1") self.users = self._list_users() + self._roles = self._list_roles() + self._populate_role_assignments() def _list_users(self): logger.info("Directory - Listing Users...") users = {} try: - # Build the Admin SDK Directory service - service = self._build_service("admin", "directory_v1") - - if not service: + if not self._service: logger.error("Failed to build Directory service") return users - # Fetch users using the Directory API - # Reference: https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/list - request = service.users().list( + request = self._service.users().list( customer=self.provider.identity.customer_id, maxResults=500, # Max allowed by API orderBy="email", @@ -38,14 +36,11 @@ class Directory(GoogleWorkspaceService): user = User( id=user_data.get("id"), email=user_data.get("primaryEmail"), - is_admin=user_data.get("isAdmin", False), ) users[user.id] = user - logger.debug( - f"Processed user: {user.email} (Admin: {user.is_admin})" - ) + logger.debug(f"Processed user: {user.email}") - request = service.users().list_next(request, response) + request = self._service.users().list_next(request, response) except Exception as error: self._handle_api_error( @@ -62,9 +57,108 @@ class Directory(GoogleWorkspaceService): return users + def _list_roles(self): + logger.info("Directory - Listing Roles...") + roles = {} + + try: + if not self._service: + return roles + + request = self._service.roles().list( + customer=self.provider.identity.customer_id, + ) + + while request is not None: + try: + response = request.execute() + + for role_data in response.get("items", []): + role_id = str(role_data.get("roleId", "")) + role_name = role_data.get("roleName", "") + if role_id and role_name: + roles[role_id] = Role( + id=role_id, + name=role_name, + description=role_data.get("roleDescription", ""), + is_super_admin_role=role_data.get( + "isSuperAdminRole", False + ), + ) + + request = self._service.roles().list_next(request, response) + + except Exception as error: + self._handle_api_error( + error, + "listing roles", + self.provider.identity.customer_id, + ) + break + + logger.info(f"Found {len(roles)} roles in the domain") + + except Exception as error: + logger.error( + f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + return roles + + def _populate_role_assignments(self): + logger.info("Directory - Fetching Role Assignments...") + + if not self._service: + return + + try: + request = self._service.roleAssignments().list( + customer=self.provider.identity.customer_id, + ) + + while request is not None: + try: + response = request.execute() + + for assignment in response.get("items", []): + user_id = str(assignment.get("assignedTo", "")) + role_id = str(assignment.get("roleId", "")) + user = self.users.get(user_id) + role = self._roles.get(role_id) + if user and role: + user.role_assignments.append(role) + if role.is_super_admin_role: + user.is_admin = True + + request = self._service.roleAssignments().list_next( + request, response + ) + + except Exception as error: + self._handle_api_error( + error, + "listing role assignments", + self.provider.identity.customer_id, + ) + break + + except Exception as error: + logger.error( + f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + +class Role(BaseModel): + + id: str + name: str + description: str = "" + is_super_admin_role: bool = False + class User(BaseModel): id: str email: str is_admin: bool = False + role_assignments: list[Role] = [] diff --git a/prowler/providers/googleworkspace/services/directory/directory_super_admin_only_admin_roles/__init__.py b/prowler/providers/googleworkspace/services/directory/directory_super_admin_only_admin_roles/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/googleworkspace/services/directory/directory_super_admin_only_admin_roles/directory_super_admin_only_admin_roles.metadata.json b/prowler/providers/googleworkspace/services/directory/directory_super_admin_only_admin_roles/directory_super_admin_only_admin_roles.metadata.json new file mode 100644 index 0000000000..0264078982 --- /dev/null +++ b/prowler/providers/googleworkspace/services/directory/directory_super_admin_only_admin_roles/directory_super_admin_only_admin_roles.metadata.json @@ -0,0 +1,39 @@ +{ + "Provider": "googleworkspace", + "CheckID": "directory_super_admin_only_admin_roles", + "CheckTitle": "All super admin accounts are used only for super admin activities", + "CheckType": [], + "ServiceName": "directory", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "high", + "ResourceType": "NotDefined", + "ResourceGroup": "IAM", + "Description": "Super admin accounts do not also hold **additional admin roles** such as Groups Admin, User Management Admin, etc. Each super administrator has a separate, non-admin account for daily activities, following the **principle of least privilege**.", + "Risk": "A super admin account that also holds additional admin roles increases the **attack surface** for phishing and credential theft. Compromising a single dual-role account grants full administrative access, bypassing **separation of duties** and enabling unauthorized changes to users, billing, and security settings.", + "RelatedUrl": "", + "AdditionalURLs": [ + "https://knowledge.workspace.google.com/admin/users/prebuilt-administrator-roles", + "https://support.google.com/a/answer/9011373" + ], + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "1. Sign in to the Google **Admin console** at https://admin.google.com\n2. Navigate to **Directory** > **Users**\n3. Click on the super admin user who also has additional admin roles\n4. Click **Admin roles and privileges**\n5. Remove the additional admin roles from the super admin account\n6. Create a separate account for daily admin tasks", + "Terraform": "" + }, + "Recommendation": { + "Text": "Apply the principle of separation of duties by maintaining dedicated super admin accounts exclusively for privileged tasks. Daily administrative activities should be performed from separate accounts with only the delegated roles required.", + "Url": "https://hub.prowler.com/check/directory_super_admin_only_admin_roles" + } + }, + "Categories": [ + "identity-access" + ], + "DependsOn": [], + "RelatedTo": [ + "directory_super_admin_count" + ], + "Notes": "" +} diff --git a/prowler/providers/googleworkspace/services/directory/directory_super_admin_only_admin_roles/directory_super_admin_only_admin_roles.py b/prowler/providers/googleworkspace/services/directory/directory_super_admin_only_admin_roles/directory_super_admin_only_admin_roles.py new file mode 100644 index 0000000000..702e3445ce --- /dev/null +++ b/prowler/providers/googleworkspace/services/directory/directory_super_admin_only_admin_roles/directory_super_admin_only_admin_roles.py @@ -0,0 +1,60 @@ +from typing import List + +from prowler.lib.check.models import Check, CheckReportGoogleWorkspace +from prowler.providers.googleworkspace.services.directory.directory_client import ( + directory_client, +) + + +class directory_super_admin_only_admin_roles(Check): + """Check that super admin accounts are used only for super admin activities + + This check verifies that no super admin user has additional admin roles assigned + beyond the Super Admin role. Super admins should have separate accounts for daily + activities to follow least privilege. + """ + + def execute(self) -> List[CheckReportGoogleWorkspace]: + findings = [] + + if directory_client.users: + dual_role_admins = {} + for user in directory_client.users.values(): + if user.is_admin: + extra_roles = [ + r.description or r.name + for r in user.role_assignments + if not r.is_super_admin_role + ] + if extra_roles: + dual_role_admins[user.email] = extra_roles + + report = CheckReportGoogleWorkspace( + metadata=self.metadata(), + resource=directory_client.provider.identity, + resource_name=directory_client.provider.identity.domain, + resource_id=directory_client.provider.identity.customer_id, + customer_id=directory_client.provider.identity.customer_id, + location="global", + ) + + if dual_role_admins: + details = ", ".join( + f"{email} ({', '.join(roles)})" + for email, roles in dual_role_admins.items() + ) + report.status = "FAIL" + report.status_extended = ( + f"Super admin accounts also holding additional admin roles: {details}. " + f"Super admin accounts should be used only for super admin activities." + ) + else: + report.status = "PASS" + report.status_extended = ( + f"All super admin accounts in domain {directory_client.provider.identity.domain} " + f"are used only for super admin activities." + ) + + findings.append(report) + + return findings diff --git a/prowler/providers/nhn/lib/arguments/arguments.py b/prowler/providers/nhn/lib/arguments/arguments.py index 8f7c71fd7c..a102665a26 100644 --- a/prowler/providers/nhn/lib/arguments/arguments.py +++ b/prowler/providers/nhn/lib/arguments/arguments.py @@ -13,7 +13,11 @@ def init_parser(self): "--nhn-username", nargs="?", default=None, help="NHN API Username" ) nhn_auth_subparser.add_argument( - "--nhn-password", nargs="?", default=None, help="NHN API Password" + "--nhn-password", + nargs="?", + default=None, + metavar="NHN_PASSWORD", + help="NHN API Password", ) nhn_auth_subparser.add_argument( "--nhn-tenant-id", nargs="?", default=None, help="NHN Tenant ID" diff --git a/prowler/providers/openstack/lib/arguments/arguments.py b/prowler/providers/openstack/lib/arguments/arguments.py index 459012c4ec..68674528b6 100644 --- a/prowler/providers/openstack/lib/arguments/arguments.py +++ b/prowler/providers/openstack/lib/arguments/arguments.py @@ -46,6 +46,7 @@ def init_parser(self): "--os-password", nargs="?", default=None, + metavar="OS_PASSWORD", help="OpenStack password for authentication. Can also be set via OS_PASSWORD environment variable", ) openstack_explicit_subparser.add_argument( diff --git a/prowler/providers/vercel/services/authentication/authentication_no_stale_tokens/authentication_no_stale_tokens.metadata.json b/prowler/providers/vercel/services/authentication/authentication_no_stale_tokens/authentication_no_stale_tokens.metadata.json index d94e12f0b9..f4863ada5f 100644 --- a/prowler/providers/vercel/services/authentication/authentication_no_stale_tokens/authentication_no_stale_tokens.metadata.json +++ b/prowler/providers/vercel/services/authentication/authentication_no_stale_tokens/authentication_no_stale_tokens.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Regularly audit API tokens and revoke any that have not been used within 90 days. Implement a token lifecycle management process that includes periodic reviews, automatic expiration dates, and documentation of each token's purpose and owner.", - "Url": "https://hub.prowler.com/checks/vercel/authentication_no_stale_tokens" + "Url": "https://hub.prowler.com/check/authentication_no_stale_tokens" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/authentication/authentication_token_not_expired/authentication_token_not_expired.metadata.json b/prowler/providers/vercel/services/authentication/authentication_token_not_expired/authentication_token_not_expired.metadata.json index dca48e73bf..47e5087cf5 100644 --- a/prowler/providers/vercel/services/authentication/authentication_token_not_expired/authentication_token_not_expired.metadata.json +++ b/prowler/providers/vercel/services/authentication/authentication_token_not_expired/authentication_token_not_expired.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Remove expired tokens and create new ones with appropriate expiration dates. Implement a token rotation schedule to ensure tokens are refreshed before they expire. Update all integrations and automation that depend on the replaced tokens.", - "Url": "https://hub.prowler.com/checks/vercel/authentication_token_not_expired" + "Url": "https://hub.prowler.com/check/authentication_token_not_expired" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/deployment/deployment_production_uses_stable_target/deployment_production_uses_stable_target.metadata.json b/prowler/providers/vercel/services/deployment/deployment_production_uses_stable_target/deployment_production_uses_stable_target.metadata.json index 8a7cc70d40..25416e6882 100644 --- a/prowler/providers/vercel/services/deployment/deployment_production_uses_stable_target/deployment_production_uses_stable_target.metadata.json +++ b/prowler/providers/vercel/services/deployment/deployment_production_uses_stable_target/deployment_production_uses_stable_target.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Configure the production branch to main or master and ensure all production deployments go through the standard merge workflow. Use branch protection rules in your Git provider to prevent direct pushes to the production branch.", - "Url": "https://hub.prowler.com/checks/vercel/deployment_production_uses_stable_target" + "Url": "https://hub.prowler.com/check/deployment_production_uses_stable_target" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/domain/domain_dns_properly_configured/domain_dns_properly_configured.metadata.json b/prowler/providers/vercel/services/domain/domain_dns_properly_configured/domain_dns_properly_configured.metadata.json index f9104ee960..e2450da8f1 100644 --- a/prowler/providers/vercel/services/domain/domain_dns_properly_configured/domain_dns_properly_configured.metadata.json +++ b/prowler/providers/vercel/services/domain/domain_dns_properly_configured/domain_dns_properly_configured.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Update DNS records at your domain registrar to correctly point to Vercel. Use a CNAME record for subdomains or an A record for apex domains. Verify the configuration in the Vercel dashboard after making changes.", - "Url": "https://hub.prowler.com/checks/vercel/domain_dns_properly_configured" + "Url": "https://hub.prowler.com/check/domain_dns_properly_configured" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/domain/domain_ssl_certificate_valid/domain_ssl_certificate_valid.metadata.json b/prowler/providers/vercel/services/domain/domain_ssl_certificate_valid/domain_ssl_certificate_valid.metadata.json index f15892df61..ac683cd71f 100644 --- a/prowler/providers/vercel/services/domain/domain_ssl_certificate_valid/domain_ssl_certificate_valid.metadata.json +++ b/prowler/providers/vercel/services/domain/domain_ssl_certificate_valid/domain_ssl_certificate_valid.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Ensure domain DNS records are properly configured to point to Vercel. Once DNS is validated, Vercel automatically provisions and renews SSL/TLS certificates. Check the domain configuration in the Vercel dashboard if the certificate is not being issued.", - "Url": "https://hub.prowler.com/checks/vercel/domain_ssl_certificate_valid" + "Url": "https://hub.prowler.com/check/domain_ssl_certificate_valid" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/domain/domain_verified/domain_verified.metadata.json b/prowler/providers/vercel/services/domain/domain_verified/domain_verified.metadata.json index f520fb00d8..1e79a433ba 100644 --- a/prowler/providers/vercel/services/domain/domain_verified/domain_verified.metadata.json +++ b/prowler/providers/vercel/services/domain/domain_verified/domain_verified.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Complete domain verification by configuring the required DNS records at your domain registrar. Remove any domains that are no longer needed to reduce the attack surface. Regularly audit domain configurations to ensure all domains remain verified.", - "Url": "https://hub.prowler.com/checks/vercel/domain_verified" + "Url": "https://hub.prowler.com/check/domain_verified" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/project/project_auto_expose_system_env_disabled/project_auto_expose_system_env_disabled.metadata.json b/prowler/providers/vercel/services/project/project_auto_expose_system_env_disabled/project_auto_expose_system_env_disabled.metadata.json index bf7adc2832..21c3f118e1 100644 --- a/prowler/providers/vercel/services/project/project_auto_expose_system_env_disabled/project_auto_expose_system_env_disabled.metadata.json +++ b/prowler/providers/vercel/services/project/project_auto_expose_system_env_disabled/project_auto_expose_system_env_disabled.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Disable automatic exposure of system environment variables and explicitly define only the variables required by your application. This follows the principle of least privilege and reduces the risk of leaking internal infrastructure details through client-side code.", - "Url": "https://hub.prowler.com/checks/vercel/project_auto_expose_system_env_disabled" + "Url": "https://hub.prowler.com/check/project_auto_expose_system_env_disabled" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/project/project_deployment_protection_enabled/project_deployment_protection_enabled.metadata.json b/prowler/providers/vercel/services/project/project_deployment_protection_enabled/project_deployment_protection_enabled.metadata.json index c704b42784..521610c617 100644 --- a/prowler/providers/vercel/services/project/project_deployment_protection_enabled/project_deployment_protection_enabled.metadata.json +++ b/prowler/providers/vercel/services/project/project_deployment_protection_enabled/project_deployment_protection_enabled.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Enable deployment protection on preview deployments to require authentication before visitors can access preview URLs. Use 'Standard Protection' for Vercel Authentication or configure trusted IP ranges for more granular control.", - "Url": "https://hub.prowler.com/checks/vercel/project_deployment_protection_enabled" + "Url": "https://hub.prowler.com/check/project_deployment_protection_enabled" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/project/project_directory_listing_disabled/project_directory_listing_disabled.metadata.json b/prowler/providers/vercel/services/project/project_directory_listing_disabled/project_directory_listing_disabled.metadata.json index b9de49aa0e..b477a5984f 100644 --- a/prowler/providers/vercel/services/project/project_directory_listing_disabled/project_directory_listing_disabled.metadata.json +++ b/prowler/providers/vercel/services/project/project_directory_listing_disabled/project_directory_listing_disabled.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Disable directory listing to prevent visitors from browsing the file structure of your deployments. Ensure that all directories either contain an index file or return a 404 response when accessed directly.", - "Url": "https://hub.prowler.com/checks/vercel/project_directory_listing_disabled" + "Url": "https://hub.prowler.com/check/project_directory_listing_disabled" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/project/project_environment_no_overly_broad_target/project_environment_no_overly_broad_target.metadata.json b/prowler/providers/vercel/services/project/project_environment_no_overly_broad_target/project_environment_no_overly_broad_target.metadata.json index 2467c1b104..c9a418a503 100644 --- a/prowler/providers/vercel/services/project/project_environment_no_overly_broad_target/project_environment_no_overly_broad_target.metadata.json +++ b/prowler/providers/vercel/services/project/project_environment_no_overly_broad_target/project_environment_no_overly_broad_target.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Follow the **principle of least privilege** for environment variable targeting.\n- Assign each variable to only the environments where it is actually needed\n- Use different credentials for production, preview, and development environments\n- Non-sensitive configuration (e.g. feature flags, public URLs) may be acceptable in multiple environments but should still be reviewed\n- Regularly audit environment variable targets to prevent scope creep", - "Url": "https://hub.prowler.com/checks/vercel/project_environment_no_overly_broad_target" + "Url": "https://hub.prowler.com/check/project_environment_no_overly_broad_target" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/project/project_environment_no_secrets_in_plain_type/project_environment_no_secrets_in_plain_type.metadata.json b/prowler/providers/vercel/services/project/project_environment_no_secrets_in_plain_type/project_environment_no_secrets_in_plain_type.metadata.json index f8d5621914..90fea53eea 100644 --- a/prowler/providers/vercel/services/project/project_environment_no_secrets_in_plain_type/project_environment_no_secrets_in_plain_type.metadata.json +++ b/prowler/providers/vercel/services/project/project_environment_no_secrets_in_plain_type/project_environment_no_secrets_in_plain_type.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Use the **Sensitive** type for all environment variables that contain secrets, keys, tokens, or passwords.\n- Sensitive variables are never exposed in the dashboard or API responses after creation\n- Rotate all credentials that were previously stored as plain text\n- Implement naming conventions that make it easy to identify secret variables", - "Url": "https://hub.prowler.com/checks/vercel/project_environment_no_secrets_in_plain_type" + "Url": "https://hub.prowler.com/check/project_environment_no_secrets_in_plain_type" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/project/project_environment_production_vars_not_in_preview/project_environment_production_vars_not_in_preview.metadata.json b/prowler/providers/vercel/services/project/project_environment_production_vars_not_in_preview/project_environment_production_vars_not_in_preview.metadata.json index 55f468fa8a..6fc3e3af79 100644 --- a/prowler/providers/vercel/services/project/project_environment_production_vars_not_in_preview/project_environment_production_vars_not_in_preview.metadata.json +++ b/prowler/providers/vercel/services/project/project_environment_production_vars_not_in_preview/project_environment_production_vars_not_in_preview.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Maintain strict **environment separation** between production and preview deployments.\n- Use dedicated, limited-scope credentials for preview environments\n- Never share production database credentials, API keys, or signing keys with preview builds\n- Enable Vercel's deployment protection features to further restrict access to preview deployments\n- Regularly audit which environment variables target multiple environments", - "Url": "https://hub.prowler.com/checks/vercel/project_environment_production_vars_not_in_preview" + "Url": "https://hub.prowler.com/check/project_environment_production_vars_not_in_preview" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/project/project_git_fork_protection_enabled/project_git_fork_protection_enabled.metadata.json b/prowler/providers/vercel/services/project/project_git_fork_protection_enabled/project_git_fork_protection_enabled.metadata.json index 744a227d61..8e3db04fcd 100644 --- a/prowler/providers/vercel/services/project/project_git_fork_protection_enabled/project_git_fork_protection_enabled.metadata.json +++ b/prowler/providers/vercel/services/project/project_git_fork_protection_enabled/project_git_fork_protection_enabled.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Enable Git fork protection to require explicit authorization before pull requests from forked repositories can trigger deployments. This prevents untrusted contributors from accessing environment variables and secrets through the build process. For open-source projects, review fork PRs manually before allowing builds.", - "Url": "https://hub.prowler.com/checks/vercel/project_git_fork_protection_enabled" + "Url": "https://hub.prowler.com/check/project_git_fork_protection_enabled" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/project/project_password_protection_enabled/project_password_protection_enabled.metadata.json b/prowler/providers/vercel/services/project/project_password_protection_enabled/project_password_protection_enabled.metadata.json index 58e3e46e41..2db77410d9 100644 --- a/prowler/providers/vercel/services/project/project_password_protection_enabled/project_password_protection_enabled.metadata.json +++ b/prowler/providers/vercel/services/project/project_password_protection_enabled/project_password_protection_enabled.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Enable password protection to add a shared-password gate to your deployments. This is especially recommended for preview deployments shared with external clients or stakeholders who do not have Vercel accounts. Combine with Vercel Authentication for defense-in-depth.", - "Url": "https://hub.prowler.com/checks/vercel/project_password_protection_enabled" + "Url": "https://hub.prowler.com/check/project_password_protection_enabled" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/project/project_production_deployment_protection_enabled/project_production_deployment_protection_enabled.metadata.json b/prowler/providers/vercel/services/project/project_production_deployment_protection_enabled/project_production_deployment_protection_enabled.metadata.json index 213bc51d07..3760eefb57 100644 --- a/prowler/providers/vercel/services/project/project_production_deployment_protection_enabled/project_production_deployment_protection_enabled.metadata.json +++ b/prowler/providers/vercel/services/project/project_production_deployment_protection_enabled/project_production_deployment_protection_enabled.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Enable deployment protection on production deployments for applications that should not be publicly accessible. This is critical for internal tools, admin dashboards, and pre-launch applications where unauthorized access could lead to data exposure or system compromise.", - "Url": "https://hub.prowler.com/checks/vercel/project_production_deployment_protection_enabled" + "Url": "https://hub.prowler.com/check/project_production_deployment_protection_enabled" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/project/project_skew_protection_enabled/project_skew_protection_enabled.metadata.json b/prowler/providers/vercel/services/project/project_skew_protection_enabled/project_skew_protection_enabled.metadata.json index b73aaa6991..a0a4f70cee 100644 --- a/prowler/providers/vercel/services/project/project_skew_protection_enabled/project_skew_protection_enabled.metadata.json +++ b/prowler/providers/vercel/services/project/project_skew_protection_enabled/project_skew_protection_enabled.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Enable skew protection to ensure that all client requests during a deployment rollout are routed to the same deployment version that served the initial page. This prevents version mismatch errors and ensures a consistent user experience during deployments.", - "Url": "https://hub.prowler.com/checks/vercel/project_skew_protection_enabled" + "Url": "https://hub.prowler.com/check/project_skew_protection_enabled" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/security/security_custom_rules_configured/security_custom_rules_configured.metadata.json b/prowler/providers/vercel/services/security/security_custom_rules_configured/security_custom_rules_configured.metadata.json index c3f986b173..a4b53baf4f 100644 --- a/prowler/providers/vercel/services/security/security_custom_rules_configured/security_custom_rules_configured.metadata.json +++ b/prowler/providers/vercel/services/security/security_custom_rules_configured/security_custom_rules_configured.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Configure custom firewall rules to protect application-specific endpoints and enforce security policies. Focus on protecting admin panels, API routes, authentication endpoints, and any paths that handle sensitive data.", - "Url": "https://hub.prowler.com/checks/vercel/security_custom_rules_configured" + "Url": "https://hub.prowler.com/check/security_custom_rules_configured" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/security/security_ip_blocking_rules_configured/security_ip_blocking_rules_configured.metadata.json b/prowler/providers/vercel/services/security/security_ip_blocking_rules_configured/security_ip_blocking_rules_configured.metadata.json index cc7712d4ec..a02cd35324 100644 --- a/prowler/providers/vercel/services/security/security_ip_blocking_rules_configured/security_ip_blocking_rules_configured.metadata.json +++ b/prowler/providers/vercel/services/security/security_ip_blocking_rules_configured/security_ip_blocking_rules_configured.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Configure IP blocking rules to deny traffic from known malicious sources. Maintain a blocklist of IPs identified through security monitoring, threat intelligence feeds, or incident investigation. Regularly review and update the blocklist.", - "Url": "https://hub.prowler.com/checks/vercel/security_ip_blocking_rules_configured" + "Url": "https://hub.prowler.com/check/security_ip_blocking_rules_configured" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/security/security_managed_rulesets_enabled/security_managed_rulesets_enabled.metadata.json b/prowler/providers/vercel/services/security/security_managed_rulesets_enabled/security_managed_rulesets_enabled.metadata.json index 95d7db6d20..ee66a3be21 100644 --- a/prowler/providers/vercel/services/security/security_managed_rulesets_enabled/security_managed_rulesets_enabled.metadata.json +++ b/prowler/providers/vercel/services/security/security_managed_rulesets_enabled/security_managed_rulesets_enabled.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Enable managed WAF rulesets to benefit from Vercel-curated protection against common attack patterns. If you are on a plan that does not support managed rulesets, consider upgrading to the Enterprise plan for enhanced security features.", - "Url": "https://hub.prowler.com/checks/vercel/security_managed_rulesets_enabled" + "Url": "https://hub.prowler.com/check/security_managed_rulesets_enabled" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/security/security_rate_limiting_configured/security_rate_limiting_configured.metadata.json b/prowler/providers/vercel/services/security/security_rate_limiting_configured/security_rate_limiting_configured.metadata.json index 28a9c44d5f..8a804233a5 100644 --- a/prowler/providers/vercel/services/security/security_rate_limiting_configured/security_rate_limiting_configured.metadata.json +++ b/prowler/providers/vercel/services/security/security_rate_limiting_configured/security_rate_limiting_configured.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Configure rate limiting rules to protect critical endpoints such as authentication, API routes, and form submissions. Start with conservative thresholds and adjust based on traffic patterns to avoid blocking legitimate users.", - "Url": "https://hub.prowler.com/checks/vercel/security_rate_limiting_configured" + "Url": "https://hub.prowler.com/check/security_rate_limiting_configured" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/security/security_waf_enabled/security_waf_enabled.metadata.json b/prowler/providers/vercel/services/security/security_waf_enabled/security_waf_enabled.metadata.json index c758c82f18..7598e7cccd 100644 --- a/prowler/providers/vercel/services/security/security_waf_enabled/security_waf_enabled.metadata.json +++ b/prowler/providers/vercel/services/security/security_waf_enabled/security_waf_enabled.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Enable the Vercel Web Application Firewall to protect your application against common web attacks. Start with managed rulesets for baseline protection and add custom rules as needed based on your application's threat model.", - "Url": "https://hub.prowler.com/checks/vercel/security_waf_enabled" + "Url": "https://hub.prowler.com/check/security_waf_enabled" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/team/team_directory_sync_enabled/team_directory_sync_enabled.metadata.json b/prowler/providers/vercel/services/team/team_directory_sync_enabled/team_directory_sync_enabled.metadata.json index 37019b79da..fda5c7b94d 100644 --- a/prowler/providers/vercel/services/team/team_directory_sync_enabled/team_directory_sync_enabled.metadata.json +++ b/prowler/providers/vercel/services/team/team_directory_sync_enabled/team_directory_sync_enabled.metadata.json @@ -25,7 +25,7 @@ }, "Recommendation": { "Text": "Enable directory sync (SCIM) to automate user lifecycle management. This ensures that team membership stays synchronized with your identity provider, automatically provisioning new members and revoking access when employees leave or change roles.", - "Url": "https://hub.prowler.com/checks/vercel/team_directory_sync_enabled" + "Url": "https://hub.prowler.com/check/team_directory_sync_enabled" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/team/team_member_role_least_privilege/team_member_role_least_privilege.metadata.json b/prowler/providers/vercel/services/team/team_member_role_least_privilege/team_member_role_least_privilege.metadata.json index 0e4750d703..37abf769ef 100644 --- a/prowler/providers/vercel/services/team/team_member_role_least_privilege/team_member_role_least_privilege.metadata.json +++ b/prowler/providers/vercel/services/team/team_member_role_least_privilege/team_member_role_least_privilege.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Limit the number of team owners to the minimum required for administration. Assign the least privileged role necessary for each member's responsibilities. Use MEMBER, DEVELOPER, or VIEWER roles for non-administrative team members.", - "Url": "https://hub.prowler.com/checks/vercel/team_member_role_least_privilege" + "Url": "https://hub.prowler.com/check/team_member_role_least_privilege" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/team/team_no_stale_invitations/team_no_stale_invitations.metadata.json b/prowler/providers/vercel/services/team/team_no_stale_invitations/team_no_stale_invitations.metadata.json index d05461c5c1..16a1d942e1 100644 --- a/prowler/providers/vercel/services/team/team_no_stale_invitations/team_no_stale_invitations.metadata.json +++ b/prowler/providers/vercel/services/team/team_no_stale_invitations/team_no_stale_invitations.metadata.json @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Regularly review and revoke stale team invitations. Establish a process to follow up on pending invitations within a reasonable timeframe and revoke those that are no longer needed to reduce the risk of unauthorized access.", - "Url": "https://hub.prowler.com/checks/vercel/team_no_stale_invitations" + "Url": "https://hub.prowler.com/check/team_no_stale_invitations" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/team/team_saml_sso_enabled/team_saml_sso_enabled.metadata.json b/prowler/providers/vercel/services/team/team_saml_sso_enabled/team_saml_sso_enabled.metadata.json index ebbe85ebc9..21785bf482 100644 --- a/prowler/providers/vercel/services/team/team_saml_sso_enabled/team_saml_sso_enabled.metadata.json +++ b/prowler/providers/vercel/services/team/team_saml_sso_enabled/team_saml_sso_enabled.metadata.json @@ -25,7 +25,7 @@ }, "Recommendation": { "Text": "Enable SAML SSO for the Vercel team to centralize authentication through your organization's identity provider. This ensures consistent security policies, simplifies user lifecycle management, and enables enforcement of MFA and other access controls.", - "Url": "https://hub.prowler.com/checks/vercel/team_saml_sso_enabled" + "Url": "https://hub.prowler.com/check/team_saml_sso_enabled" } }, "Categories": [ diff --git a/prowler/providers/vercel/services/team/team_saml_sso_enforced/team_saml_sso_enforced.metadata.json b/prowler/providers/vercel/services/team/team_saml_sso_enforced/team_saml_sso_enforced.metadata.json index f4de8e7ebe..feb43bc179 100644 --- a/prowler/providers/vercel/services/team/team_saml_sso_enforced/team_saml_sso_enforced.metadata.json +++ b/prowler/providers/vercel/services/team/team_saml_sso_enforced/team_saml_sso_enforced.metadata.json @@ -25,7 +25,7 @@ }, "Recommendation": { "Text": "Enforce SAML SSO for all team members to ensure authentication is managed exclusively through your identity provider. This prevents credential bypass, enforces MFA policies, and provides centralized access control and audit capabilities.", - "Url": "https://hub.prowler.com/checks/vercel/team_saml_sso_enforced" + "Url": "https://hub.prowler.com/check/team_saml_sso_enforced" } }, "Categories": [ diff --git a/pyproject.toml b/pyproject.toml index a3b6f7dece..0db8391be7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,11 +49,11 @@ dependencies = [ "cryptography==46.0.6", "dash==3.1.1", "dash-bootstrap-components==2.0.3", - "defusedxml>=0.7.1", + "defusedxml==0.7.1", "detect-secrets==1.5.0", "dulwich==0.23.0", "google-api-python-client==2.163.0", - "google-auth-httplib2>=0.1,<0.3", + "google-auth-httplib2==0.2.0", "jsonschema==4.23.0", "kubernetes==32.0.1", "markdown==3.10.2", @@ -63,9 +63,9 @@ dependencies = [ "openstacksdk==4.2.0", "pandas==2.2.3", "py-ocsf-models==0.8.1", - "pydantic (>=2.0,<3.0)", + "pydantic==2.12.5", "pygithub==2.8.0", - "python-dateutil (>=2.9.0.post0,<3.0.0)", + "python-dateutil==2.9.0.post0", "pytz==2025.1", "schema==0.7.5", "shodan==1.31.0", diff --git a/skills/prowler-provider/SKILL.md b/skills/prowler-provider/SKILL.md index 994d134776..c9f2811e97 100644 --- a/skills/prowler-provider/SKILL.md +++ b/skills/prowler-provider/SKILL.md @@ -45,6 +45,34 @@ prowler/providers/{provider}/ └── {check_name}.metadata.json ``` +## Sensitive CLI Arguments + +Flags that accept secrets (tokens, passwords, API keys) MUST follow these rules: + +1. **Use `nargs="?"` with `default=None`** — the flag accepts an optional value for backward compatibility; the recommended path is environment variables. +2. **Set `metavar` to the environment variable name** users should use (e.g., `metavar="GITHUB_PERSONAL_ACCESS_TOKEN"`). +3. **Add the flag to the `SENSITIVE_ARGUMENTS` frozenset** at the top of the provider's `arguments.py`. This set is used to redact values in HTML output and warn users who pass secrets directly. +4. **Do not add new arguments that require passing secrets as CLI values** — secrets should come from environment variables. The flag accepts a value for backward compatibility, but CLI warns users to prefer env vars. + +### Pattern + +```python +# prowler/providers/{provider}/lib/arguments/arguments.py + +SENSITIVE_ARGUMENTS = frozenset({"--my-api-key", "--my-password"}) + + +def init_parser(self): + auth_subparser = parser.add_argument_group("Authentication Modes") + auth_subparser.add_argument( + "--my-api-key", + nargs="?", + default=None, + metavar="MY_API_KEY", + help="API key for authentication. Use MY_API_KEY env var instead of passing directly.", + ) +``` + ## Provider Class Template ```python diff --git a/tests/lib/check/check_test.py b/tests/lib/check/check_test.py index 84708b96b1..cda33e8fc6 100644 --- a/tests/lib/check/check_test.py +++ b/tests/lib/check/check_test.py @@ -1048,6 +1048,34 @@ class TestCheck: ) self.verify_metadata_check_id(base_directory) + def test_vercel_checks_metadata_is_valid(self): + base_directory = os.path.abspath( + os.path.join( + os.path.dirname(__file__), + "../../../", + "prowler/providers/vercel/services", + ) + ) + self.verify_metadata_check_id(base_directory) + + def test_vercel_checks_metadata_use_canonical_hub_urls(self): + base_directory = pathlib.Path(__file__).resolve().parents[3] / "prowler" + provider_path = base_directory / "providers" / "vercel" / "services" + + invalid_urls = [] + + for metadata_file_path in provider_path.rglob("*.metadata.json"): + with metadata_file_path.open("r") as metadata_file: + data = json.load(metadata_file) + + recommendation = data.get("Remediation", {}).get("Recommendation", {}) + url = recommendation.get("Url", "") + + if url.startswith("https://hub.prowler.com/checks/vercel/"): + invalid_urls.append(f"{metadata_file_path}: {url}") + + assert not invalid_urls, "\n".join(invalid_urls) + def verify_metadata_check_id(self, provider_path): errors = [] # Walk through the base directory to find all service directories diff --git a/tests/lib/cli/redact_test.py b/tests/lib/cli/redact_test.py index 1f33998356..5de4cc8fd7 100644 --- a/tests/lib/cli/redact_test.py +++ b/tests/lib/cli/redact_test.py @@ -1,8 +1,14 @@ +import logging from unittest.mock import patch import pytest -from prowler.lib.cli.redact import REDACTED_VALUE, get_sensitive_arguments, redact_argv +from prowler.lib.cli.redact import ( + REDACTED_VALUE, + get_sensitive_arguments, + redact_argv, + warn_sensitive_argument_values, +) @pytest.fixture @@ -87,6 +93,62 @@ class TestRedactArgv: assert redact_argv(argv) == "aws --region=us-east-1" +class TestWarnSensitiveArgumentValues: + def test_no_warning_without_sensitive_flags(self, caplog, mock_sensitive_args): + with caplog.at_level(logging.WARNING): + warn_sensitive_argument_values(["aws", "--region", "eu-west-1"]) + assert caplog.text == "" + + def test_no_warning_flag_without_value(self, caplog, mock_sensitive_args): + with caplog.at_level(logging.WARNING): + warn_sensitive_argument_values(["github", "--personal-access-token"]) + assert caplog.text == "" + + def test_no_warning_flag_followed_by_another_flag( + self, caplog, mock_sensitive_args + ): + with caplog.at_level(logging.WARNING): + warn_sensitive_argument_values( + ["github", "--personal-access-token", "--region", "eu-west-1"] + ) + assert caplog.text == "" + + def test_warning_flag_with_value(self, caplog, mock_sensitive_args): + with caplog.at_level(logging.WARNING): + warn_sensitive_argument_values( + ["github", "--personal-access-token", "ghp_secret"] + ) + assert "--personal-access-token" in caplog.text + assert "not recommended" in caplog.text + + def test_warning_flag_with_equals_syntax(self, caplog, mock_sensitive_args): + with caplog.at_level(logging.WARNING): + warn_sensitive_argument_values(["aws", "--shodan=key123"]) + assert "--shodan" in caplog.text + assert "not recommended" in caplog.text + + def test_warning_multiple_flags(self, caplog, mock_sensitive_args): + with caplog.at_level(logging.WARNING): + warn_sensitive_argument_values( + [ + "github", + "--personal-access-token", + "ghp_secret", + "--shodan", + "key", + ] + ) + assert "--personal-access-token" in caplog.text + assert "--shodan" in caplog.text + + def test_no_color_output(self, caplog, mock_sensitive_args): + with caplog.at_level(logging.WARNING): + warn_sensitive_argument_values(["--no-color", "aws", "--shodan", "key123"]) + assert "not recommended" in caplog.text + # Should not contain ANSI escape codes + assert "\033[" not in caplog.text + + class TestGetSensitiveArguments: def test_discovers_known_sensitive_arguments(self): """Integration test: verify the discovery mechanism finds flags from provider modules.""" diff --git a/tests/lib/outputs/compliance/ccc/__init__.py b/tests/lib/outputs/compliance/ccc/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/lib/outputs/compliance/ccc/ccc_aws_test.py b/tests/lib/outputs/compliance/ccc/ccc_aws_test.py new file mode 100644 index 0000000000..39460fd0ec --- /dev/null +++ b/tests/lib/outputs/compliance/ccc/ccc_aws_test.py @@ -0,0 +1,138 @@ +from io import StringIO +from unittest import mock + +from freezegun import freeze_time +from mock import patch + +from prowler.lib.outputs.compliance.ccc.ccc_aws import CCC_AWS +from prowler.lib.outputs.compliance.ccc.models import CCC_AWSModel +from tests.lib.outputs.compliance.fixtures import CCC_AWS_FIXTURE +from tests.lib.outputs.fixtures.fixtures import generate_finding_output +from tests.providers.aws.utils import AWS_ACCOUNT_NUMBER, AWS_REGION_EU_WEST_1 + + +class TestAWSCCC: + def test_output_transform_evaluated_requirement(self): + findings = [ + generate_finding_output(compliance={"CCC-v2025.10": "CCC.Core.CN01.AR01"}) + ] + + output = CCC_AWS(findings, CCC_AWS_FIXTURE) + output_data = output.data[0] + + assert isinstance(output_data, CCC_AWSModel) + assert output_data.Provider == "aws" + assert output_data.AccountId == AWS_ACCOUNT_NUMBER + assert output_data.Region == AWS_REGION_EU_WEST_1 + assert output_data.Description == CCC_AWS_FIXTURE.Description + assert output_data.Requirements_Id == CCC_AWS_FIXTURE.Requirements[0].Id + assert ( + output_data.Requirements_Description + == CCC_AWS_FIXTURE.Requirements[0].Description + ) + attribute = CCC_AWS_FIXTURE.Requirements[0].Attributes[0] + assert output_data.Requirements_Attributes_FamilyName == attribute.FamilyName + assert ( + output_data.Requirements_Attributes_FamilyDescription + == attribute.FamilyDescription + ) + assert output_data.Requirements_Attributes_Section == attribute.Section + assert output_data.Requirements_Attributes_SubSection == attribute.SubSection + assert ( + output_data.Requirements_Attributes_SubSectionObjective + == attribute.SubSectionObjective + ) + assert ( + output_data.Requirements_Attributes_Applicability == attribute.Applicability + ) + assert ( + output_data.Requirements_Attributes_Recommendation + == attribute.Recommendation + ) + assert ( + output_data.Requirements_Attributes_SectionThreatMappings + == attribute.SectionThreatMappings + ) + assert ( + output_data.Requirements_Attributes_SectionGuidelineMappings + == attribute.SectionGuidelineMappings + ) + assert output_data.Status == "PASS" + assert output_data.StatusExtended == "" + assert output_data.ResourceId == "" + assert output_data.ResourceName == "" + assert output_data.CheckId == "service_test_check_id" + assert output_data.Muted is False + + def test_output_transform_manual_requirement(self): + # Use a finding for the evaluated requirement so the manual one is appended + # by the manual-loop branch (Checks=[]). + findings = [ + generate_finding_output(compliance={"CCC-v2025.10": "CCC.Core.CN01.AR01"}) + ] + + output = CCC_AWS(findings, CCC_AWS_FIXTURE) + # data[0] is the evaluated PASS row, data[1] is the manual row + manual_row = output.data[1] + + assert isinstance(manual_row, CCC_AWSModel) + assert manual_row.Provider == "aws" + assert manual_row.AccountId == "" + assert manual_row.Region == "" + assert manual_row.Description == CCC_AWS_FIXTURE.Description + assert manual_row.Requirements_Id == CCC_AWS_FIXTURE.Requirements[1].Id + manual_attribute = CCC_AWS_FIXTURE.Requirements[1].Attributes[0] + assert ( + manual_row.Requirements_Attributes_FamilyName == manual_attribute.FamilyName + ) + assert manual_row.Requirements_Attributes_Section == manual_attribute.Section + assert manual_row.Status == "MANUAL" + assert manual_row.StatusExtended == "Manual check" + assert manual_row.ResourceId == "manual_check" + assert manual_row.ResourceName == "Manual check" + assert manual_row.CheckId == "manual" + assert manual_row.Muted is False + + @freeze_time("2025-01-01 00:00:00") + @mock.patch( + "prowler.lib.outputs.compliance.ccc.ccc_aws.timestamp", + "2025-01-01 00:00:00", + ) + def test_batch_write_data_to_file(self): + mock_file = StringIO() + findings = [ + generate_finding_output(compliance={"CCC-v2025.10": "CCC.Core.CN01.AR01"}) + ] + output = CCC_AWS(findings, CCC_AWS_FIXTURE) + output._file_descriptor = mock_file + + with patch.object(mock_file, "close", return_value=None): + output.batch_write_data_to_file() + + mock_file.seek(0) + content = mock_file.read() + + # Header check: AWS-specific columns must be present + header = content.split("\r\n", 1)[0] + assert "ACCOUNTID" in header + assert "REGION" in header + assert "REQUIREMENTS_ATTRIBUTES_FAMILYNAME" in header + assert "REQUIREMENTS_ATTRIBUTES_SECTION" in header + assert "REQUIREMENTS_ATTRIBUTES_APPLICABILITY" in header + assert "REQUIREMENTS_ATTRIBUTES_SECTIONTHREATMAPPINGS" in header + # Header should NOT contain Azure or GCP-only columns + assert "SUBSCRIPTIONID" not in header + assert "PROJECTID" not in header + + # Body checks: evaluated row + manual row + rows = [r for r in content.split("\r\n") if r] + assert len(rows) == 3 # header + evaluated + manual + assert "CCC.Core.CN01.AR01" in rows[1] + assert "PASS" in rows[1] + assert AWS_ACCOUNT_NUMBER in rows[1] + assert AWS_REGION_EU_WEST_1 in rows[1] + assert "CCC.IAM.CN01.AR01" in rows[2] + assert "MANUAL" in rows[2] + assert "manual_check" in rows[2] + # The frozen timestamp should appear + assert "2025-01-01 00:00:00" in rows[1] diff --git a/tests/lib/outputs/compliance/ccc/ccc_azure_test.py b/tests/lib/outputs/compliance/ccc/ccc_azure_test.py new file mode 100644 index 0000000000..a3a2f7d028 --- /dev/null +++ b/tests/lib/outputs/compliance/ccc/ccc_azure_test.py @@ -0,0 +1,99 @@ +from io import StringIO +from unittest import mock + +from freezegun import freeze_time +from mock import patch + +from prowler.lib.outputs.compliance.ccc.ccc_azure import CCC_Azure +from prowler.lib.outputs.compliance.ccc.models import CCC_AzureModel +from tests.lib.outputs.compliance.fixtures import CCC_AZURE_FIXTURE +from tests.lib.outputs.fixtures.fixtures import generate_finding_output +from tests.providers.azure.azure_fixtures import AZURE_SUBSCRIPTION_ID + +AZURE_LOCATION = "westeurope" + + +class TestAzureCCC: + def test_output_transform_evaluated_requirement(self): + findings = [ + generate_finding_output( + provider="azure", + compliance={"CCC-v2025.10": "CCC.Core.CN01.AR01"}, + account_uid=AZURE_SUBSCRIPTION_ID, + region=AZURE_LOCATION, + ) + ] + + output = CCC_Azure(findings, CCC_AZURE_FIXTURE) + output_data = output.data[0] + + assert isinstance(output_data, CCC_AzureModel) + assert output_data.Provider == "azure" + assert output_data.SubscriptionId == AZURE_SUBSCRIPTION_ID + assert output_data.Location == AZURE_LOCATION + assert output_data.Description == CCC_AZURE_FIXTURE.Description + assert output_data.Requirements_Id == CCC_AZURE_FIXTURE.Requirements[0].Id + attribute = CCC_AZURE_FIXTURE.Requirements[0].Attributes[0] + assert output_data.Requirements_Attributes_FamilyName == attribute.FamilyName + assert output_data.Requirements_Attributes_Section == attribute.Section + assert ( + output_data.Requirements_Attributes_Applicability == attribute.Applicability + ) + assert output_data.Status == "PASS" + assert output_data.CheckId == "service_test_check_id" + + def test_output_transform_manual_requirement(self): + findings = [ + generate_finding_output( + provider="azure", + compliance={"CCC-v2025.10": "CCC.Core.CN01.AR01"}, + account_uid=AZURE_SUBSCRIPTION_ID, + region=AZURE_LOCATION, + ) + ] + output = CCC_Azure(findings, CCC_AZURE_FIXTURE) + manual_row = output.data[1] + + assert isinstance(manual_row, CCC_AzureModel) + assert manual_row.Provider == "azure" + assert manual_row.SubscriptionId == "" + assert manual_row.Location == "" + assert manual_row.Requirements_Id == CCC_AZURE_FIXTURE.Requirements[1].Id + assert manual_row.Status == "MANUAL" + assert manual_row.CheckId == "manual" + + @freeze_time("2025-01-01 00:00:00") + @mock.patch( + "prowler.lib.outputs.compliance.ccc.ccc_azure.timestamp", + "2025-01-01 00:00:00", + ) + def test_batch_write_data_to_file(self): + mock_file = StringIO() + findings = [ + generate_finding_output( + provider="azure", + compliance={"CCC-v2025.10": "CCC.Core.CN01.AR01"}, + account_uid=AZURE_SUBSCRIPTION_ID, + region=AZURE_LOCATION, + ) + ] + output = CCC_Azure(findings, CCC_AZURE_FIXTURE) + output._file_descriptor = mock_file + + with patch.object(mock_file, "close", return_value=None): + output.batch_write_data_to_file() + + mock_file.seek(0) + content = mock_file.read() + header = content.split("\r\n", 1)[0] + assert "SUBSCRIPTIONID" in header + assert "LOCATION" in header + assert "ACCOUNTID" not in header + assert "PROJECTID" not in header + assert "REGION" not in header + rows = [r for r in content.split("\r\n") if r] + assert len(rows) == 3 + assert "CCC.Core.CN01.AR01" in rows[1] + assert AZURE_SUBSCRIPTION_ID in rows[1] + assert "CCC.IAM.CN01.AR01" in rows[2] + assert "MANUAL" in rows[2] diff --git a/tests/lib/outputs/compliance/ccc/ccc_gcp_test.py b/tests/lib/outputs/compliance/ccc/ccc_gcp_test.py new file mode 100644 index 0000000000..9ba2127235 --- /dev/null +++ b/tests/lib/outputs/compliance/ccc/ccc_gcp_test.py @@ -0,0 +1,99 @@ +from io import StringIO +from unittest import mock + +from freezegun import freeze_time +from mock import patch + +from prowler.lib.outputs.compliance.ccc.ccc_gcp import CCC_GCP +from prowler.lib.outputs.compliance.ccc.models import CCC_GCPModel +from tests.lib.outputs.compliance.fixtures import CCC_GCP_FIXTURE +from tests.lib.outputs.fixtures.fixtures import generate_finding_output + +GCP_PROJECT_ID = "test-project" +GCP_LOCATION = "europe-west1" + + +class TestGCPCCC: + def test_output_transform_evaluated_requirement(self): + findings = [ + generate_finding_output( + provider="gcp", + compliance={"CCC-v2025.10": "CCC.Core.CN01.AR01"}, + account_uid=GCP_PROJECT_ID, + region=GCP_LOCATION, + ) + ] + + output = CCC_GCP(findings, CCC_GCP_FIXTURE) + output_data = output.data[0] + + assert isinstance(output_data, CCC_GCPModel) + assert output_data.Provider == "gcp" + assert output_data.ProjectId == GCP_PROJECT_ID + assert output_data.Location == GCP_LOCATION + assert output_data.Description == CCC_GCP_FIXTURE.Description + assert output_data.Requirements_Id == CCC_GCP_FIXTURE.Requirements[0].Id + attribute = CCC_GCP_FIXTURE.Requirements[0].Attributes[0] + assert output_data.Requirements_Attributes_FamilyName == attribute.FamilyName + assert output_data.Requirements_Attributes_Section == attribute.Section + assert ( + output_data.Requirements_Attributes_Applicability == attribute.Applicability + ) + assert output_data.Status == "PASS" + assert output_data.CheckId == "service_test_check_id" + + def test_output_transform_manual_requirement(self): + findings = [ + generate_finding_output( + provider="gcp", + compliance={"CCC-v2025.10": "CCC.Core.CN01.AR01"}, + account_uid=GCP_PROJECT_ID, + region=GCP_LOCATION, + ) + ] + output = CCC_GCP(findings, CCC_GCP_FIXTURE) + manual_row = output.data[1] + + assert isinstance(manual_row, CCC_GCPModel) + assert manual_row.Provider == "gcp" + assert manual_row.ProjectId == "" + assert manual_row.Location == "" + assert manual_row.Requirements_Id == CCC_GCP_FIXTURE.Requirements[1].Id + assert manual_row.Status == "MANUAL" + assert manual_row.CheckId == "manual" + + @freeze_time("2025-01-01 00:00:00") + @mock.patch( + "prowler.lib.outputs.compliance.ccc.ccc_gcp.timestamp", + "2025-01-01 00:00:00", + ) + def test_batch_write_data_to_file(self): + mock_file = StringIO() + findings = [ + generate_finding_output( + provider="gcp", + compliance={"CCC-v2025.10": "CCC.Core.CN01.AR01"}, + account_uid=GCP_PROJECT_ID, + region=GCP_LOCATION, + ) + ] + output = CCC_GCP(findings, CCC_GCP_FIXTURE) + output._file_descriptor = mock_file + + with patch.object(mock_file, "close", return_value=None): + output.batch_write_data_to_file() + + mock_file.seek(0) + content = mock_file.read() + header = content.split("\r\n", 1)[0] + assert "PROJECTID" in header + assert "LOCATION" in header + assert "ACCOUNTID" not in header + assert "SUBSCRIPTIONID" not in header + assert "REGION" not in header + rows = [r for r in content.split("\r\n") if r] + assert len(rows) == 3 + assert "CCC.Core.CN01.AR01" in rows[1] + assert GCP_PROJECT_ID in rows[1] + assert "CCC.IAM.CN01.AR01" in rows[2] + assert "MANUAL" in rows[2] diff --git a/tests/lib/outputs/compliance/fixtures.py b/tests/lib/outputs/compliance/fixtures.py index 7b29411663..41c68d148f 100644 --- a/tests/lib/outputs/compliance/fixtures.py +++ b/tests/lib/outputs/compliance/fixtures.py @@ -1,5 +1,6 @@ from prowler.lib.check.compliance_models import ( AWS_Well_Architected_Requirement_Attribute, + CCC_Requirement_Attribute, CIS_Requirement_Attribute, Compliance, Compliance_Requirement, @@ -1022,3 +1023,169 @@ PROWLER_THREATSCORE_M365 = Compliance( ), ], ) + + +# CCC fixtures cover the three providers Prowler ships catalogs for. Each +# fixture has one auto-evaluated requirement (with Checks) and one manual +# requirement (Checks=[]) so test suites can exercise both paths. +CCC_AWS_FIXTURE = Compliance( + Framework="CCC", + Name="Common Cloud Controls Catalog (CCC)", + Provider="AWS", + Version="v2025.10", + Description="Common Cloud Controls Catalog (CCC) for AWS", + Requirements=[ + Compliance_Requirement( + Checks=["service_test_check_id"], + Id="CCC.Core.CN01.AR01", + Description="When a port is exposed for non-SSH network traffic, all traffic MUST include a TLS handshake AND be encrypted using TLS 1.3 or higher.", + Attributes=[ + CCC_Requirement_Attribute( + FamilyName="Data", + FamilyDescription="The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + Section="CCC.Core.CN01 Encrypt Data for Transmission", + SubSection="", + SubSectionObjective="Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", + Applicability=["tlp-green", "tlp-amber", "tlp-red"], + Recommendation="Most cloud services enable TLS 1.3 by default.", + SectionThreatMappings=[ + {"ReferenceId": "CCC", "Identifiers": ["CCC.Core.TH02"]} + ], + SectionGuidelineMappings=[ + {"ReferenceId": "CCM", "Identifiers": ["CEK-03", "CEK-04"]} + ], + ) + ], + ), + Compliance_Requirement( + Checks=[], + Id="CCC.IAM.CN01.AR01", + Description="When an identity policy for a non-administrative principal is evaluated, it MUST NOT grant permissions for creating credentials or generating temporary session tokens.", + Attributes=[ + CCC_Requirement_Attribute( + FamilyName="Identity and Access Management", + FamilyDescription="Controls that restrict who can access and modify IAM resources.", + Section="CCC.IAM.CN01 Restrict IAM User Credentials Creation", + SubSection="", + SubSectionObjective="Prevent non-administrative principals from creating new long-lived credentials.", + Applicability=["tlp-clear", "tlp-green", "tlp-amber", "tlp-red"], + Recommendation="", + SectionThreatMappings=[ + {"ReferenceId": "CCC", "Identifiers": ["CCC.IAM.TH03"]} + ], + SectionGuidelineMappings=[ + {"ReferenceId": "NIST-CSF", "Identifiers": ["PR.AA-05"]} + ], + ) + ], + ), + ], +) + +CCC_AZURE_FIXTURE = Compliance( + Framework="CCC", + Name="Common Cloud Controls Catalog (CCC)", + Provider="Azure", + Version="v2025.10", + Description="Common Cloud Controls Catalog (CCC) for Azure", + Requirements=[ + Compliance_Requirement( + Checks=["service_test_check_id"], + Id="CCC.Core.CN01.AR01", + Description="When a port is exposed for non-SSH network traffic, all traffic MUST include a TLS handshake AND be encrypted using TLS 1.3 or higher.", + Attributes=[ + CCC_Requirement_Attribute( + FamilyName="Data", + FamilyDescription="The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + Section="CCC.Core.CN01 Encrypt Data for Transmission", + SubSection="", + SubSectionObjective="Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", + Applicability=["tlp-green", "tlp-amber", "tlp-red"], + Recommendation="Most cloud services enable TLS 1.3 by default.", + SectionThreatMappings=[ + {"ReferenceId": "CCC", "Identifiers": ["CCC.Core.TH02"]} + ], + SectionGuidelineMappings=[ + {"ReferenceId": "CCM", "Identifiers": ["CEK-03", "CEK-04"]} + ], + ) + ], + ), + Compliance_Requirement( + Checks=[], + Id="CCC.IAM.CN01.AR01", + Description="When an identity policy for a non-administrative principal is evaluated, it MUST NOT grant permissions for creating credentials.", + Attributes=[ + CCC_Requirement_Attribute( + FamilyName="Identity and Access Management", + FamilyDescription="Controls that restrict who can access and modify IAM resources.", + Section="CCC.IAM.CN01 Restrict IAM User Credentials Creation", + SubSection="", + SubSectionObjective="Prevent non-administrative principals from creating new long-lived credentials.", + Applicability=["tlp-clear", "tlp-green", "tlp-amber", "tlp-red"], + Recommendation="", + SectionThreatMappings=[ + {"ReferenceId": "CCC", "Identifiers": ["CCC.IAM.TH03"]} + ], + SectionGuidelineMappings=[ + {"ReferenceId": "NIST-CSF", "Identifiers": ["PR.AA-05"]} + ], + ) + ], + ), + ], +) + +CCC_GCP_FIXTURE = Compliance( + Framework="CCC", + Name="Common Cloud Controls Catalog (CCC)", + Provider="GCP", + Version="v2025.10", + Description="Common Cloud Controls Catalog (CCC) for GCP", + Requirements=[ + Compliance_Requirement( + Checks=["service_test_check_id"], + Id="CCC.Core.CN01.AR01", + Description="When a port is exposed for non-SSH network traffic, all traffic MUST include a TLS handshake AND be encrypted using TLS 1.3 or higher.", + Attributes=[ + CCC_Requirement_Attribute( + FamilyName="Data", + FamilyDescription="The Data control family ensures the confidentiality, integrity, availability, and sovereignty of data across its lifecycle.", + Section="CCC.Core.CN01 Encrypt Data for Transmission", + SubSection="", + SubSectionObjective="Ensure that all communications are encrypted in transit to protect data integrity and confidentiality.", + Applicability=["tlp-green", "tlp-amber", "tlp-red"], + Recommendation="Most cloud services enable TLS 1.3 by default.", + SectionThreatMappings=[ + {"ReferenceId": "CCC", "Identifiers": ["CCC.Core.TH02"]} + ], + SectionGuidelineMappings=[ + {"ReferenceId": "CCM", "Identifiers": ["CEK-03", "CEK-04"]} + ], + ) + ], + ), + Compliance_Requirement( + Checks=[], + Id="CCC.IAM.CN01.AR01", + Description="When an identity policy for a non-administrative principal is evaluated, it MUST NOT grant permissions for creating credentials.", + Attributes=[ + CCC_Requirement_Attribute( + FamilyName="Identity and Access Management", + FamilyDescription="Controls that restrict who can access and modify IAM resources.", + Section="CCC.IAM.CN01 Restrict IAM User Credentials Creation", + SubSection="", + SubSectionObjective="Prevent non-administrative principals from creating new long-lived credentials.", + Applicability=["tlp-clear", "tlp-green", "tlp-amber", "tlp-red"], + Recommendation="", + SectionThreatMappings=[ + {"ReferenceId": "CCC", "Identifiers": ["CCC.IAM.TH03"]} + ], + SectionGuidelineMappings=[ + {"ReferenceId": "NIST-CSF", "Identifiers": ["PR.AA-05"]} + ], + ) + ], + ), + ], +) diff --git a/tests/providers/aws/services/iam/lib/policy_test.py b/tests/providers/aws/services/iam/lib/policy_test.py index afea8ca658..50cca4cbad 100644 --- a/tests/providers/aws/services/iam/lib/policy_test.py +++ b/tests/providers/aws/services/iam/lib/policy_test.py @@ -1413,6 +1413,115 @@ class Test_Policy: condition_statement, TRUSTED_AWS_ACCOUNT_NUMBER ) + def test_condition_parser_string_equals_aws_CalledVia_str(self): + condition_statement = { + "StringEquals": {"aws:CalledVia": "cloudformation.amazonaws.com"} + } + assert is_condition_block_restrictive( + condition_statement, + TRUSTED_AWS_ACCOUNT_NUMBER, + is_cross_account_allowed=True, + ) + + def test_condition_parser_string_equals_aws_CalledViaFirst_str(self): + condition_statement = { + "StringEquals": {"aws:CalledViaFirst": "cloudformation.amazonaws.com"} + } + assert is_condition_block_restrictive( + condition_statement, + TRUSTED_AWS_ACCOUNT_NUMBER, + is_cross_account_allowed=True, + ) + + def test_condition_parser_string_equals_aws_CalledViaLast_str(self): + condition_statement = { + "StringEquals": {"aws:CalledViaLast": "glue.amazonaws.com"} + } + assert is_condition_block_restrictive( + condition_statement, + TRUSTED_AWS_ACCOUNT_NUMBER, + is_cross_account_allowed=True, + ) + + def test_condition_parser_string_like_aws_CalledVia_str(self): + condition_statement = {"StringLike": {"aws:CalledVia": "*.amazonaws.com"}} + assert is_condition_block_restrictive( + condition_statement, + TRUSTED_AWS_ACCOUNT_NUMBER, + is_cross_account_allowed=True, + ) + + def test_condition_parser_string_equals_kms_CallerAccount_str(self): + condition_statement = { + "StringEquals": {"kms:CallerAccount": TRUSTED_AWS_ACCOUNT_NUMBER} + } + assert is_condition_block_restrictive( + condition_statement, TRUSTED_AWS_ACCOUNT_NUMBER + ) + + def test_condition_parser_string_equals_kms_CallerAccount_str_not_valid(self): + condition_statement = { + "StringEquals": {"kms:CallerAccount": NON_TRUSTED_AWS_ACCOUNT_NUMBER} + } + assert not is_condition_block_restrictive( + condition_statement, TRUSTED_AWS_ACCOUNT_NUMBER + ) + + def test_condition_parser_string_equals_kms_CallerAccount_list(self): + condition_statement = { + "StringEquals": {"kms:CallerAccount": [TRUSTED_AWS_ACCOUNT_NUMBER]} + } + assert is_condition_block_restrictive( + condition_statement, TRUSTED_AWS_ACCOUNT_NUMBER + ) + + def test_condition_parser_string_equals_kms_CallerAccount_list_not_valid(self): + condition_statement = { + "StringEquals": { + "kms:CallerAccount": [ + TRUSTED_AWS_ACCOUNT_NUMBER, + NON_TRUSTED_AWS_ACCOUNT_NUMBER, + ] + } + } + assert not is_condition_block_restrictive( + condition_statement, TRUSTED_AWS_ACCOUNT_NUMBER + ) + + def test_condition_parser_string_equals_kms_ViaService_str(self): + condition_statement = { + "StringEquals": {"kms:ViaService": "glue.eu-central-1.amazonaws.com"} + } + assert is_condition_block_restrictive( + condition_statement, + TRUSTED_AWS_ACCOUNT_NUMBER, + is_cross_account_allowed=True, + ) + + def test_condition_parser_string_like_kms_CallerAccount_str(self): + condition_statement = { + "StringLike": {"kms:CallerAccount": TRUSTED_AWS_ACCOUNT_NUMBER} + } + assert is_condition_block_restrictive( + condition_statement, TRUSTED_AWS_ACCOUNT_NUMBER + ) + + def test_condition_parser_string_like_kms_CallerAccount_str_not_valid(self): + condition_statement = { + "StringLike": {"kms:CallerAccount": NON_TRUSTED_AWS_ACCOUNT_NUMBER} + } + assert not is_condition_block_restrictive( + condition_statement, TRUSTED_AWS_ACCOUNT_NUMBER + ) + + def test_condition_parser_string_like_kms_ViaService_str(self): + condition_statement = {"StringLike": {"kms:ViaService": "glue.*.amazonaws.com"}} + assert is_condition_block_restrictive( + condition_statement, + TRUSTED_AWS_ACCOUNT_NUMBER, + is_cross_account_allowed=True, + ) + def test_condition_parser_two_lists_unrestrictive(self): condition_statement = { "StringLike": { @@ -2357,6 +2466,71 @@ class Test_Policy: trusted_ips=["1.2.3.4", "5.6.7.8"], ) + def test_is_policy_public_kms_caller_account_and_via_service(self): + policy = { + "Version": "2008-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": {"AWS": "*"}, + "Action": [ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:CreateGrant", + "kms:DescribeKey", + ], + "Resource": "*", + "Condition": { + "StringEquals": { + "kms:ViaService": "glue.eu-central-1.amazonaws.com", + "kms:CallerAccount": TRUSTED_AWS_ACCOUNT_NUMBER, + } + }, + }, + ], + } + assert not is_policy_public(policy, TRUSTED_AWS_ACCOUNT_NUMBER) + + def test_is_policy_public_kms_caller_account_only(self): + policy = { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": {"AWS": "*"}, + "Action": ["kms:Decrypt"], + "Resource": "*", + "Condition": { + "StringEquals": { + "kms:CallerAccount": TRUSTED_AWS_ACCOUNT_NUMBER, + } + }, + }, + ], + } + assert not is_policy_public(policy, TRUSTED_AWS_ACCOUNT_NUMBER) + + def test_is_policy_public_kms_via_service_without_account_restriction(self): + policy = { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": {"AWS": "*"}, + "Action": ["kms:Decrypt"], + "Resource": "*", + "Condition": { + "StringEquals": { + "kms:ViaService": "glue.eu-central-1.amazonaws.com", + } + }, + }, + ], + } + assert not is_policy_public(policy, TRUSTED_AWS_ACCOUNT_NUMBER) + def test_check_admin_access(self): policy = { "Version": "2012-10-17", diff --git a/tests/providers/aws/services/stepfunctions/stepfunctions_statemachine_no_secrets_in_definition/__init__.py b/tests/providers/aws/services/stepfunctions/stepfunctions_statemachine_no_secrets_in_definition/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/providers/aws/services/stepfunctions/stepfunctions_statemachine_no_secrets_in_definition/stepfunctions_statemachine_no_secrets_in_definition_test.py b/tests/providers/aws/services/stepfunctions/stepfunctions_statemachine_no_secrets_in_definition/stepfunctions_statemachine_no_secrets_in_definition_test.py new file mode 100644 index 0000000000..628525e542 --- /dev/null +++ b/tests/providers/aws/services/stepfunctions/stepfunctions_statemachine_no_secrets_in_definition/stepfunctions_statemachine_no_secrets_in_definition_test.py @@ -0,0 +1,180 @@ +from datetime import datetime +from unittest import mock + +from tests.providers.aws.utils import AWS_ACCOUNT_NUMBER, AWS_REGION_US_EAST_1 + + +class Test_stepfunctions_statemachine_no_secrets_in_definition: + def test_no_statemachines(self): + stepfunctions_client = mock.MagicMock() + stepfunctions_client.state_machines = {} + stepfunctions_client.audit_config = {"secrets_ignore_patterns": []} + + with ( + mock.patch( + "prowler.providers.aws.services.stepfunctions.stepfunctions_service.StepFunctions", + stepfunctions_client, + ), + mock.patch( + "prowler.providers.aws.services.stepfunctions.stepfunctions_statemachine_no_secrets_in_definition.stepfunctions_statemachine_no_secrets_in_definition.stepfunctions_client", + stepfunctions_client, + ), + ): + from prowler.providers.aws.services.stepfunctions.stepfunctions_statemachine_no_secrets_in_definition.stepfunctions_statemachine_no_secrets_in_definition import ( + stepfunctions_statemachine_no_secrets_in_definition, + ) + + check = stepfunctions_statemachine_no_secrets_in_definition() + result = check.execute() + + assert len(result) == 0 + + def test_statemachine_with_no_definition(self): + stepfunctions_client = mock.MagicMock() + + from prowler.providers.aws.services.stepfunctions.stepfunctions_service import ( + StateMachine, + StateMachineStatus, + StateMachineType, + ) + + statemachine_arn = f"arn:aws:states:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:stateMachine:TestStateMachine" + stepfunctions_client.state_machines = { + statemachine_arn: StateMachine( + id="TestStateMachine", + arn=statemachine_arn, + name="TestStateMachine", + status=StateMachineStatus.ACTIVE, + definition=None, + region=AWS_REGION_US_EAST_1, + type=StateMachineType.STANDARD, + creation_date=datetime.now(), + ) + } + stepfunctions_client.audit_config = {"secrets_ignore_patterns": []} + + with ( + mock.patch( + "prowler.providers.aws.services.stepfunctions.stepfunctions_service.StepFunctions", + stepfunctions_client, + ), + mock.patch( + "prowler.providers.aws.services.stepfunctions.stepfunctions_statemachine_no_secrets_in_definition.stepfunctions_statemachine_no_secrets_in_definition.stepfunctions_client", + stepfunctions_client, + ), + ): + from prowler.providers.aws.services.stepfunctions.stepfunctions_statemachine_no_secrets_in_definition.stepfunctions_statemachine_no_secrets_in_definition import ( + stepfunctions_statemachine_no_secrets_in_definition, + ) + + check = stepfunctions_statemachine_no_secrets_in_definition() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "No secrets found in Step Functions state machine TestStateMachine definition." + ) + assert result[0].region == AWS_REGION_US_EAST_1 + assert result[0].resource_id == "TestStateMachine" + assert result[0].resource_arn == statemachine_arn + + def test_statemachine_with_no_secrets_in_definition(self): + stepfunctions_client = mock.MagicMock() + + from prowler.providers.aws.services.stepfunctions.stepfunctions_service import ( + StateMachine, + StateMachineStatus, + StateMachineType, + ) + + statemachine_arn = f"arn:aws:states:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:stateMachine:TestStateMachine" + stepfunctions_client.state_machines = { + statemachine_arn: StateMachine( + id="TestStateMachine", + arn=statemachine_arn, + name="TestStateMachine", + status=StateMachineStatus.ACTIVE, + definition='{"Comment": "A simple example", "StartAt": "HelloWorld", "States": {"HelloWorld": {"Type": "Pass", "End": true}}}', + region=AWS_REGION_US_EAST_1, + type=StateMachineType.STANDARD, + creation_date=datetime.now(), + ) + } + stepfunctions_client.audit_config = {"secrets_ignore_patterns": []} + + with ( + mock.patch( + "prowler.providers.aws.services.stepfunctions.stepfunctions_service.StepFunctions", + stepfunctions_client, + ), + mock.patch( + "prowler.providers.aws.services.stepfunctions.stepfunctions_statemachine_no_secrets_in_definition.stepfunctions_statemachine_no_secrets_in_definition.stepfunctions_client", + stepfunctions_client, + ), + ): + from prowler.providers.aws.services.stepfunctions.stepfunctions_statemachine_no_secrets_in_definition.stepfunctions_statemachine_no_secrets_in_definition import ( + stepfunctions_statemachine_no_secrets_in_definition, + ) + + check = stepfunctions_statemachine_no_secrets_in_definition() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "No secrets found in Step Functions state machine TestStateMachine definition." + ) + assert result[0].region == AWS_REGION_US_EAST_1 + assert result[0].resource_id == "TestStateMachine" + assert result[0].resource_arn == statemachine_arn + + def test_statemachine_with_secrets_in_definition(self): + stepfunctions_client = mock.MagicMock() + + from prowler.providers.aws.services.stepfunctions.stepfunctions_service import ( + StateMachine, + StateMachineStatus, + StateMachineType, + ) + + statemachine_arn = f"arn:aws:states:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:stateMachine:TestStateMachine" + stepfunctions_client.state_machines = { + statemachine_arn: StateMachine( + id="TestStateMachine", + arn=statemachine_arn, + name="TestStateMachine", + status=StateMachineStatus.ACTIVE, + definition='{"Comment": "Example with secret", "StartAt": "MyTask", "States": {"MyTask": {"Type": "Task", "Parameters": {"api_key": "AKIAIOSFODNN7EXAMPLE"}, "End": true}}}', + region=AWS_REGION_US_EAST_1, + type=StateMachineType.STANDARD, + creation_date=datetime.now(), + ) + } + stepfunctions_client.audit_config = {"secrets_ignore_patterns": []} + + with ( + mock.patch( + "prowler.providers.aws.services.stepfunctions.stepfunctions_service.StepFunctions", + stepfunctions_client, + ), + mock.patch( + "prowler.providers.aws.services.stepfunctions.stepfunctions_statemachine_no_secrets_in_definition.stepfunctions_statemachine_no_secrets_in_definition.stepfunctions_client", + stepfunctions_client, + ), + ): + from prowler.providers.aws.services.stepfunctions.stepfunctions_statemachine_no_secrets_in_definition.stepfunctions_statemachine_no_secrets_in_definition import ( + stepfunctions_statemachine_no_secrets_in_definition, + ) + + check = stepfunctions_statemachine_no_secrets_in_definition() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert "TestStateMachine" in result[0].status_extended + assert result[0].region == AWS_REGION_US_EAST_1 + assert result[0].resource_id == "TestStateMachine" + assert result[0].resource_arn == statemachine_arn diff --git a/tests/providers/googleworkspace/googleworkspace_fixtures.py b/tests/providers/googleworkspace/googleworkspace_fixtures.py index c823c21339..792c4699c3 100644 --- a/tests/providers/googleworkspace/googleworkspace_fixtures.py +++ b/tests/providers/googleworkspace/googleworkspace_fixtures.py @@ -43,6 +43,39 @@ USER_3 = { } +# Role data for Directory API role tests +SUPER_ADMIN_ROLE_ID = "13801188331880449" +SEED_ADMIN_ROLE_ID = "13801188331880451" +GROUPS_ADMIN_ROLE_ID = "13801188331880450" + +ROLE_SUPER_ADMIN = { + "roleId": SUPER_ADMIN_ROLE_ID, + "roleName": "Super Admin", + "roleDescription": "Super Admin", + "isSystemRole": True, + "isSuperAdminRole": True, +} + +# Google automatically assigns _SEED_ADMIN_ROLE to the first account that +# created the domain. It is a super-admin-capable system role with a +# different name, so it must also be excluded when counting "extra" roles. +ROLE_SEED_ADMIN = { + "roleId": SEED_ADMIN_ROLE_ID, + "roleName": "_SEED_ADMIN_ROLE", + "roleDescription": "Super Admin", + "isSystemRole": True, + "isSuperAdminRole": True, +} + +ROLE_GROUPS_ADMIN = { + "roleId": GROUPS_ADMIN_ROLE_ID, + "roleName": "_GROUPS_ADMIN_ROLE", + "roleDescription": "Groups Administrator", + "isSystemRole": True, + "isSuperAdminRole": False, +} + + def set_mocked_googleworkspace_provider( identity: GoogleWorkspaceIdentityInfo = GoogleWorkspaceIdentityInfo( domain=DOMAIN, diff --git a/tests/providers/googleworkspace/services/calendar/calendar_external_invitations_warning/calendar_external_invitations_warning_test.py b/tests/providers/googleworkspace/services/calendar/calendar_external_invitations_warning/calendar_external_invitations_warning_test.py new file mode 100644 index 0000000000..f367d5938d --- /dev/null +++ b/tests/providers/googleworkspace/services/calendar/calendar_external_invitations_warning/calendar_external_invitations_warning_test.py @@ -0,0 +1,130 @@ +from unittest.mock import patch + +from prowler.providers.googleworkspace.services.calendar.calendar_service import ( + CalendarPolicies, +) +from tests.providers.googleworkspace.googleworkspace_fixtures import ( + CUSTOMER_ID, + DOMAIN, + set_mocked_googleworkspace_provider, +) + + +class TestCalendarExternalInvitationsWarning: + def test_pass_warnings_enabled(self): + """Test PASS when external invitation warnings are enabled""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.calendar.calendar_external_invitations_warning.calendar_external_invitations_warning.calendar_client" + ) as mock_calendar_client, + ): + from prowler.providers.googleworkspace.services.calendar.calendar_external_invitations_warning.calendar_external_invitations_warning import ( + calendar_external_invitations_warning, + ) + + mock_calendar_client.provider = mock_provider + mock_calendar_client.policies_fetched = True + mock_calendar_client.policies = CalendarPolicies( + external_invitations_warning=True + ) + + check = calendar_external_invitations_warning() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "PASS" + assert "enabled" in findings[0].status_extended + assert findings[0].resource_name == DOMAIN + assert findings[0].customer_id == CUSTOMER_ID + + def test_fail_warnings_disabled(self): + """Test FAIL when external invitation warnings are disabled""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.calendar.calendar_external_invitations_warning.calendar_external_invitations_warning.calendar_client" + ) as mock_calendar_client, + ): + from prowler.providers.googleworkspace.services.calendar.calendar_external_invitations_warning.calendar_external_invitations_warning import ( + calendar_external_invitations_warning, + ) + + mock_calendar_client.provider = mock_provider + mock_calendar_client.policies_fetched = True + mock_calendar_client.policies = CalendarPolicies( + external_invitations_warning=False + ) + + check = calendar_external_invitations_warning() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "disabled" in findings[0].status_extended + + def test_fail_no_policy_set(self): + """Test FAIL when no explicit policy is set (None) but fetch succeeded""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.calendar.calendar_external_invitations_warning.calendar_external_invitations_warning.calendar_client" + ) as mock_calendar_client, + ): + from prowler.providers.googleworkspace.services.calendar.calendar_external_invitations_warning.calendar_external_invitations_warning import ( + calendar_external_invitations_warning, + ) + + mock_calendar_client.provider = mock_provider + mock_calendar_client.policies_fetched = True + mock_calendar_client.policies = CalendarPolicies( + external_invitations_warning=None + ) + + check = calendar_external_invitations_warning() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "not explicitly configured" in findings[0].status_extended + + def test_no_findings_when_fetch_failed(self): + """Test no findings returned when the API fetch failed""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.calendar.calendar_external_invitations_warning.calendar_external_invitations_warning.calendar_client" + ) as mock_calendar_client, + ): + from prowler.providers.googleworkspace.services.calendar.calendar_external_invitations_warning.calendar_external_invitations_warning import ( + calendar_external_invitations_warning, + ) + + mock_calendar_client.provider = mock_provider + mock_calendar_client.policies_fetched = False + mock_calendar_client.policies = CalendarPolicies() + + check = calendar_external_invitations_warning() + findings = check.execute() + + assert len(findings) == 0 diff --git a/tests/providers/googleworkspace/services/calendar/calendar_external_sharing_primary_calendar/calendar_external_sharing_primary_calendar_test.py b/tests/providers/googleworkspace/services/calendar/calendar_external_sharing_primary_calendar/calendar_external_sharing_primary_calendar_test.py new file mode 100644 index 0000000000..32056e9fcb --- /dev/null +++ b/tests/providers/googleworkspace/services/calendar/calendar_external_sharing_primary_calendar/calendar_external_sharing_primary_calendar_test.py @@ -0,0 +1,161 @@ +from unittest.mock import patch + +from prowler.providers.googleworkspace.services.calendar.calendar_service import ( + CalendarPolicies, +) +from tests.providers.googleworkspace.googleworkspace_fixtures import ( + CUSTOMER_ID, + DOMAIN, + set_mocked_googleworkspace_provider, +) + + +class TestCalendarExternalSharingPrimaryCalendar: + def test_pass_free_busy_only(self): + """Test PASS when external sharing is restricted to free/busy only""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.calendar.calendar_external_sharing_primary_calendar.calendar_external_sharing_primary_calendar.calendar_client" + ) as mock_calendar_client, + ): + from prowler.providers.googleworkspace.services.calendar.calendar_external_sharing_primary_calendar.calendar_external_sharing_primary_calendar import ( + calendar_external_sharing_primary_calendar, + ) + + mock_calendar_client.provider = mock_provider + mock_calendar_client.policies_fetched = True + mock_calendar_client.policies = CalendarPolicies( + primary_calendar_external_sharing="EXTERNAL_FREE_BUSY_ONLY" + ) + + check = calendar_external_sharing_primary_calendar() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "PASS" + assert "free/busy information only" in findings[0].status_extended + assert findings[0].resource_name == DOMAIN + assert findings[0].customer_id == CUSTOMER_ID + + def test_fail_read_only(self): + """Test FAIL when external sharing allows read-only access""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.calendar.calendar_external_sharing_primary_calendar.calendar_external_sharing_primary_calendar.calendar_client" + ) as mock_calendar_client, + ): + from prowler.providers.googleworkspace.services.calendar.calendar_external_sharing_primary_calendar.calendar_external_sharing_primary_calendar import ( + calendar_external_sharing_primary_calendar, + ) + + mock_calendar_client.provider = mock_provider + mock_calendar_client.policies_fetched = True + mock_calendar_client.policies = CalendarPolicies( + primary_calendar_external_sharing="EXTERNAL_ALL_INFO_READ_ONLY" + ) + + check = calendar_external_sharing_primary_calendar() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "EXTERNAL_ALL_INFO_READ_ONLY" in findings[0].status_extended + assert "free/busy information only" in findings[0].status_extended + + def test_fail_read_write(self): + """Test FAIL when external sharing allows read-write access""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.calendar.calendar_external_sharing_primary_calendar.calendar_external_sharing_primary_calendar.calendar_client" + ) as mock_calendar_client, + ): + from prowler.providers.googleworkspace.services.calendar.calendar_external_sharing_primary_calendar.calendar_external_sharing_primary_calendar import ( + calendar_external_sharing_primary_calendar, + ) + + mock_calendar_client.provider = mock_provider + mock_calendar_client.policies_fetched = True + mock_calendar_client.policies = CalendarPolicies( + primary_calendar_external_sharing="EXTERNAL_ALL_INFO_READ_WRITE" + ) + + check = calendar_external_sharing_primary_calendar() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "EXTERNAL_ALL_INFO_READ_WRITE" in findings[0].status_extended + + def test_fail_no_policy_set(self): + """Test FAIL when no explicit policy is set (None) but fetch succeeded""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.calendar.calendar_external_sharing_primary_calendar.calendar_external_sharing_primary_calendar.calendar_client" + ) as mock_calendar_client, + ): + from prowler.providers.googleworkspace.services.calendar.calendar_external_sharing_primary_calendar.calendar_external_sharing_primary_calendar import ( + calendar_external_sharing_primary_calendar, + ) + + mock_calendar_client.provider = mock_provider + mock_calendar_client.policies_fetched = True + mock_calendar_client.policies = CalendarPolicies( + primary_calendar_external_sharing=None + ) + + check = calendar_external_sharing_primary_calendar() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "not explicitly configured" in findings[0].status_extended + + def test_no_findings_when_fetch_failed(self): + """Test no findings returned when the API fetch failed""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.calendar.calendar_external_sharing_primary_calendar.calendar_external_sharing_primary_calendar.calendar_client" + ) as mock_calendar_client, + ): + from prowler.providers.googleworkspace.services.calendar.calendar_external_sharing_primary_calendar.calendar_external_sharing_primary_calendar import ( + calendar_external_sharing_primary_calendar, + ) + + mock_calendar_client.provider = mock_provider + mock_calendar_client.policies_fetched = False + mock_calendar_client.policies = CalendarPolicies() + + check = calendar_external_sharing_primary_calendar() + findings = check.execute() + + assert len(findings) == 0 diff --git a/tests/providers/googleworkspace/services/calendar/calendar_external_sharing_secondary_calendar/calendar_external_sharing_secondary_calendar_test.py b/tests/providers/googleworkspace/services/calendar/calendar_external_sharing_secondary_calendar/calendar_external_sharing_secondary_calendar_test.py new file mode 100644 index 0000000000..800f9ab5f0 --- /dev/null +++ b/tests/providers/googleworkspace/services/calendar/calendar_external_sharing_secondary_calendar/calendar_external_sharing_secondary_calendar_test.py @@ -0,0 +1,161 @@ +from unittest.mock import patch + +from prowler.providers.googleworkspace.services.calendar.calendar_service import ( + CalendarPolicies, +) +from tests.providers.googleworkspace.googleworkspace_fixtures import ( + CUSTOMER_ID, + DOMAIN, + set_mocked_googleworkspace_provider, +) + + +class TestCalendarExternalSharingSecondaryCalendar: + def test_pass_free_busy_only(self): + """Test PASS when external sharing is restricted to free/busy only""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.calendar.calendar_external_sharing_secondary_calendar.calendar_external_sharing_secondary_calendar.calendar_client" + ) as mock_calendar_client, + ): + from prowler.providers.googleworkspace.services.calendar.calendar_external_sharing_secondary_calendar.calendar_external_sharing_secondary_calendar import ( + calendar_external_sharing_secondary_calendar, + ) + + mock_calendar_client.provider = mock_provider + mock_calendar_client.policies_fetched = True + mock_calendar_client.policies = CalendarPolicies( + secondary_calendar_external_sharing="EXTERNAL_FREE_BUSY_ONLY" + ) + + check = calendar_external_sharing_secondary_calendar() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "PASS" + assert "free/busy information only" in findings[0].status_extended + assert findings[0].resource_name == DOMAIN + assert findings[0].customer_id == CUSTOMER_ID + + def test_fail_read_only(self): + """Test FAIL when external sharing allows read-only access""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.calendar.calendar_external_sharing_secondary_calendar.calendar_external_sharing_secondary_calendar.calendar_client" + ) as mock_calendar_client, + ): + from prowler.providers.googleworkspace.services.calendar.calendar_external_sharing_secondary_calendar.calendar_external_sharing_secondary_calendar import ( + calendar_external_sharing_secondary_calendar, + ) + + mock_calendar_client.provider = mock_provider + mock_calendar_client.policies_fetched = True + mock_calendar_client.policies = CalendarPolicies( + secondary_calendar_external_sharing="EXTERNAL_ALL_INFO_READ_ONLY" + ) + + check = calendar_external_sharing_secondary_calendar() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "EXTERNAL_ALL_INFO_READ_ONLY" in findings[0].status_extended + assert "free/busy information only" in findings[0].status_extended + + def test_fail_read_write_manage(self): + """Test FAIL when external sharing allows read-write-manage access""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.calendar.calendar_external_sharing_secondary_calendar.calendar_external_sharing_secondary_calendar.calendar_client" + ) as mock_calendar_client, + ): + from prowler.providers.googleworkspace.services.calendar.calendar_external_sharing_secondary_calendar.calendar_external_sharing_secondary_calendar import ( + calendar_external_sharing_secondary_calendar, + ) + + mock_calendar_client.provider = mock_provider + mock_calendar_client.policies_fetched = True + mock_calendar_client.policies = CalendarPolicies( + secondary_calendar_external_sharing="EXTERNAL_ALL_INFO_READ_WRITE_MANAGE" + ) + + check = calendar_external_sharing_secondary_calendar() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "EXTERNAL_ALL_INFO_READ_WRITE_MANAGE" in findings[0].status_extended + + def test_fail_no_policy_set(self): + """Test FAIL when no explicit policy is set (None) but fetch succeeded""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.calendar.calendar_external_sharing_secondary_calendar.calendar_external_sharing_secondary_calendar.calendar_client" + ) as mock_calendar_client, + ): + from prowler.providers.googleworkspace.services.calendar.calendar_external_sharing_secondary_calendar.calendar_external_sharing_secondary_calendar import ( + calendar_external_sharing_secondary_calendar, + ) + + mock_calendar_client.provider = mock_provider + mock_calendar_client.policies_fetched = True + mock_calendar_client.policies = CalendarPolicies( + secondary_calendar_external_sharing=None + ) + + check = calendar_external_sharing_secondary_calendar() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "not explicitly configured" in findings[0].status_extended + + def test_no_findings_when_fetch_failed(self): + """Test no findings returned when the API fetch failed""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.calendar.calendar_external_sharing_secondary_calendar.calendar_external_sharing_secondary_calendar.calendar_client" + ) as mock_calendar_client, + ): + from prowler.providers.googleworkspace.services.calendar.calendar_external_sharing_secondary_calendar.calendar_external_sharing_secondary_calendar import ( + calendar_external_sharing_secondary_calendar, + ) + + mock_calendar_client.provider = mock_provider + mock_calendar_client.policies_fetched = False + mock_calendar_client.policies = CalendarPolicies() + + check = calendar_external_sharing_secondary_calendar() + findings = check.execute() + + assert len(findings) == 0 diff --git a/tests/providers/googleworkspace/services/calendar/calendar_service_test.py b/tests/providers/googleworkspace/services/calendar/calendar_service_test.py new file mode 100644 index 0000000000..1491f839fb --- /dev/null +++ b/tests/providers/googleworkspace/services/calendar/calendar_service_test.py @@ -0,0 +1,231 @@ +from unittest.mock import MagicMock, patch + +from tests.providers.googleworkspace.googleworkspace_fixtures import ( + set_mocked_googleworkspace_provider, +) + + +class TestCalendarService: + def test_calendar_fetch_policies_all_settings(self): + """Test fetching all 3 calendar policy settings from Cloud Identity API""" + mock_provider = set_mocked_googleworkspace_provider() + mock_provider.audit_config = {} + mock_provider.fixer_config = {} + mock_credentials = MagicMock() + mock_session = MagicMock() + mock_session.credentials = mock_credentials + mock_provider.session = mock_session + + mock_service = MagicMock() + mock_policies_list = MagicMock() + # Mock the actual Cloud Identity Policy API v1 response shape: + # - "type" (not "name"), prefixed with "settings/" + # - inner value field names are camelCase + mock_policies_list.execute.return_value = { + "policies": [ + { + "setting": { + "type": "settings/calendar.primary_calendar_max_allowed_external_sharing", + "value": { + "maxAllowedExternalSharing": "EXTERNAL_FREE_BUSY_ONLY" + }, + } + }, + { + "setting": { + "type": "settings/calendar.secondary_calendar_max_allowed_external_sharing", + "value": { + "maxAllowedExternalSharing": "EXTERNAL_ALL_INFO_READ_ONLY" + }, + } + }, + { + "setting": { + "type": "settings/calendar.external_invitations", + "value": {"warnOnInvite": True}, + } + }, + ] + } + mock_service.policies().list.return_value = mock_policies_list + mock_service.policies().list_next.return_value = None + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.calendar.calendar_service.GoogleWorkspaceService._build_service", + return_value=mock_service, + ), + ): + from prowler.providers.googleworkspace.services.calendar.calendar_service import ( + Calendar, + ) + + calendar = Calendar(mock_provider) + + assert calendar.policies_fetched is True + assert ( + calendar.policies.primary_calendar_external_sharing + == "EXTERNAL_FREE_BUSY_ONLY" + ) + assert ( + calendar.policies.secondary_calendar_external_sharing + == "EXTERNAL_ALL_INFO_READ_ONLY" + ) + assert calendar.policies.external_invitations_warning is True + + def test_calendar_fetch_policies_empty_response(self): + """Test handling empty policies response""" + mock_provider = set_mocked_googleworkspace_provider() + mock_provider.audit_config = {} + mock_provider.fixer_config = {} + mock_session = MagicMock() + mock_session.credentials = MagicMock() + mock_provider.session = mock_session + + mock_service = MagicMock() + mock_policies_list = MagicMock() + mock_policies_list.execute.return_value = {"policies": []} + mock_service.policies().list.return_value = mock_policies_list + mock_service.policies().list_next.return_value = None + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.calendar.calendar_service.GoogleWorkspaceService._build_service", + return_value=mock_service, + ), + ): + from prowler.providers.googleworkspace.services.calendar.calendar_service import ( + Calendar, + ) + + calendar = Calendar(mock_provider) + + assert calendar.policies_fetched is True + assert calendar.policies.primary_calendar_external_sharing is None + assert calendar.policies.secondary_calendar_external_sharing is None + assert calendar.policies.external_invitations_warning is None + + def test_calendar_fetch_policies_api_error(self): + """Test handling of API errors during policy fetch""" + mock_provider = set_mocked_googleworkspace_provider() + mock_provider.audit_config = {} + mock_provider.fixer_config = {} + mock_session = MagicMock() + mock_session.credentials = MagicMock() + mock_provider.session = mock_session + + mock_service = MagicMock() + mock_service.policies().list.side_effect = Exception("API Error") + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.calendar.calendar_service.GoogleWorkspaceService._build_service", + return_value=mock_service, + ), + ): + from prowler.providers.googleworkspace.services.calendar.calendar_service import ( + Calendar, + ) + + calendar = Calendar(mock_provider) + + assert calendar.policies_fetched is False + assert calendar.policies.primary_calendar_external_sharing is None + assert calendar.policies.secondary_calendar_external_sharing is None + assert calendar.policies.external_invitations_warning is None + + def test_calendar_fetch_policies_build_service_returns_none(self): + """Test early return when _build_service fails to construct the client""" + mock_provider = set_mocked_googleworkspace_provider() + mock_provider.audit_config = {} + mock_provider.fixer_config = {} + mock_session = MagicMock() + mock_session.credentials = MagicMock() + mock_provider.session = mock_session + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.calendar.calendar_service.GoogleWorkspaceService._build_service", + return_value=None, + ), + ): + from prowler.providers.googleworkspace.services.calendar.calendar_service import ( + Calendar, + ) + + calendar = Calendar(mock_provider) + + assert calendar.policies_fetched is False + assert calendar.policies.primary_calendar_external_sharing is None + assert calendar.policies.secondary_calendar_external_sharing is None + assert calendar.policies.external_invitations_warning is None + + def test_calendar_fetch_policies_execute_raises(self): + """Test inner except handler when request.execute() raises during pagination""" + mock_provider = set_mocked_googleworkspace_provider() + mock_provider.audit_config = {} + mock_provider.fixer_config = {} + mock_session = MagicMock() + mock_session.credentials = MagicMock() + mock_provider.session = mock_session + + mock_service = MagicMock() + mock_request = MagicMock() + mock_request.execute.side_effect = Exception("Execute failed") + mock_service.policies().list.return_value = mock_request + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.calendar.calendar_service.GoogleWorkspaceService._build_service", + return_value=mock_service, + ), + ): + from prowler.providers.googleworkspace.services.calendar.calendar_service import ( + Calendar, + ) + + calendar = Calendar(mock_provider) + + assert calendar.policies_fetched is False + assert calendar.policies.primary_calendar_external_sharing is None + assert calendar.policies.secondary_calendar_external_sharing is None + assert calendar.policies.external_invitations_warning is None + + def test_calendar_policies_model(self): + """Test CalendarPolicies Pydantic model""" + from prowler.providers.googleworkspace.services.calendar.calendar_service import ( + CalendarPolicies, + ) + + policies = CalendarPolicies( + primary_calendar_external_sharing="EXTERNAL_FREE_BUSY_ONLY", + secondary_calendar_external_sharing="EXTERNAL_ALL_INFO_READ_WRITE", + external_invitations_warning=True, + ) + + assert policies.primary_calendar_external_sharing == "EXTERNAL_FREE_BUSY_ONLY" + assert ( + policies.secondary_calendar_external_sharing + == "EXTERNAL_ALL_INFO_READ_WRITE" + ) + assert policies.external_invitations_warning is True diff --git a/tests/providers/googleworkspace/services/directory/directory_service_test.py b/tests/providers/googleworkspace/services/directory/directory_service_test.py index 83c7e594c0..50fc8e00bd 100644 --- a/tests/providers/googleworkspace/services/directory/directory_service_test.py +++ b/tests/providers/googleworkspace/services/directory/directory_service_test.py @@ -1,6 +1,12 @@ from unittest.mock import MagicMock, patch from tests.providers.googleworkspace.googleworkspace_fixtures import ( + GROUPS_ADMIN_ROLE_ID, + ROLE_GROUPS_ADMIN, + ROLE_SEED_ADMIN, + ROLE_SUPER_ADMIN, + SEED_ADMIN_ROLE_ID, + SUPER_ADMIN_ROLE_ID, USER_1, USER_2, USER_3, @@ -25,6 +31,24 @@ class TestDirectoryService: mock_service.users().list.return_value = mock_users_list mock_service.users().list_next.return_value = None + # Mock roles response + mock_roles_list = MagicMock() + mock_roles_list.execute.return_value = { + "items": [ROLE_SUPER_ADMIN, ROLE_GROUPS_ADMIN] + } + mock_service.roles().list.return_value = mock_roles_list + mock_service.roles().list_next.return_value = None + + mock_ra = MagicMock() + mock_ra.execute.return_value = { + "items": [ + {"assignedTo": "user1-id", "roleId": SUPER_ADMIN_ROLE_ID}, + {"assignedTo": "user2-id", "roleId": SUPER_ADMIN_ROLE_ID}, + ] + } + mock_service.roleAssignments().list.return_value = mock_ra + mock_service.roleAssignments().list_next.return_value = None + with ( patch( "prowler.providers.common.provider.Provider.get_global_provider", @@ -67,6 +91,17 @@ class TestDirectoryService: mock_service.users().list.return_value = mock_users_list mock_service.users().list_next.return_value = None + # Mock roles response + mock_roles_list = MagicMock() + mock_roles_list.execute.return_value = {"items": []} + mock_service.roles().list.return_value = mock_roles_list + mock_service.roles().list_next.return_value = None + + mock_ra = MagicMock() + mock_ra.execute.return_value = {"items": []} + mock_service.roleAssignments().list.return_value = mock_ra + mock_service.roleAssignments().list_next.return_value = None + with ( patch( "prowler.providers.common.provider.Provider.get_global_provider", @@ -97,6 +132,16 @@ class TestDirectoryService: mock_service = MagicMock() mock_service.users().list.side_effect = Exception("API Error") + mock_roles_list = MagicMock() + mock_roles_list.execute.return_value = {"items": []} + mock_service.roles().list.return_value = mock_roles_list + mock_service.roles().list_next.return_value = None + + mock_ra = MagicMock() + mock_ra.execute.return_value = {"items": []} + mock_service.roleAssignments().list.return_value = mock_ra + mock_service.roleAssignments().list_next.return_value = None + with ( patch( "prowler.providers.common.provider.Provider.get_global_provider", @@ -130,3 +175,193 @@ class TestDirectoryService: assert user.id == "test-id" assert user.email == "test@test-company.com" assert user.is_admin is True + assert user.role_assignments == [] + + def test_directory_list_roles(self): + """Test that _list_roles correctly builds a roleId-to-roleName mapping""" + mock_provider = set_mocked_googleworkspace_provider() + mock_provider.audit_config = {} + mock_provider.fixer_config = {} + mock_session = MagicMock() + mock_session.credentials = MagicMock() + mock_provider.session = mock_session + + mock_service = MagicMock() + + # Mock empty users + mock_users_list = MagicMock() + mock_users_list.execute.return_value = {"users": []} + mock_service.users().list.return_value = mock_users_list + mock_service.users().list_next.return_value = None + + # Mock roles response + mock_roles_list = MagicMock() + mock_roles_list.execute.return_value = { + "items": [ROLE_SUPER_ADMIN, ROLE_GROUPS_ADMIN] + } + mock_service.roles().list.return_value = mock_roles_list + mock_service.roles().list_next.return_value = None + + mock_ra = MagicMock() + mock_ra.execute.return_value = {"items": []} + mock_service.roleAssignments().list.return_value = mock_ra + mock_service.roleAssignments().list_next.return_value = None + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.directory.directory_service.GoogleWorkspaceService._build_service", + return_value=mock_service, + ), + ): + from prowler.providers.googleworkspace.services.directory.directory_service import ( + Directory, + ) + + directory = Directory(mock_provider) + + super_admin_role = directory._roles[SUPER_ADMIN_ROLE_ID] + assert super_admin_role.name == "Super Admin" + assert super_admin_role.description == "Super Admin" + assert super_admin_role.is_super_admin_role is True + + groups_admin_role = directory._roles[GROUPS_ADMIN_ROLE_ID] + assert groups_admin_role.name == "_GROUPS_ADMIN_ROLE" + assert groups_admin_role.description == "Groups Administrator" + assert groups_admin_role.is_super_admin_role is False + + def test_directory_role_assignments_populated(self): + """Test that role assignments are fetched and resolved for super admins""" + mock_provider = set_mocked_googleworkspace_provider() + mock_provider.audit_config = {} + mock_provider.fixer_config = {} + mock_session = MagicMock() + mock_session.credentials = MagicMock() + mock_provider.session = mock_session + + mock_service = MagicMock() + + # Mock users - one super admin + mock_users_list = MagicMock() + mock_users_list.execute.return_value = {"users": [USER_1]} + mock_service.users().list.return_value = mock_users_list + mock_service.users().list_next.return_value = None + + # Mock roles + mock_roles_list = MagicMock() + mock_roles_list.execute.return_value = { + "items": [ROLE_SUPER_ADMIN, ROLE_GROUPS_ADMIN] + } + mock_service.roles().list.return_value = mock_roles_list + mock_service.roles().list_next.return_value = None + + mock_ra = MagicMock() + mock_ra.execute.return_value = { + "items": [ + {"assignedTo": "user1-id", "roleId": SUPER_ADMIN_ROLE_ID}, + {"assignedTo": "user1-id", "roleId": GROUPS_ADMIN_ROLE_ID}, + ] + } + mock_service.roleAssignments().list.return_value = mock_ra + mock_service.roleAssignments().list_next.return_value = None + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.directory.directory_service.GoogleWorkspaceService._build_service", + return_value=mock_service, + ), + ): + from prowler.providers.googleworkspace.services.directory.directory_service import ( + Directory, + ) + + directory = Directory(mock_provider) + + user = directory.users["user1-id"] + role_names = [r.name for r in user.role_assignments] + role_descriptions = [r.description for r in user.role_assignments] + assert "Super Admin" in role_names + assert "_GROUPS_ADMIN_ROLE" in role_names + assert "Groups Administrator" in role_descriptions + assert len(user.role_assignments) == 2 + assert user.is_admin is True + + def test_directory_second_super_admin_detected_via_role_assignments(self): + """Regression: a second super admin whose users.list().isAdmin still + reads False (e.g. API propagation lag, or only holding + _SEED_ADMIN_ROLE) must still be recognised as a super admin through + the Role Assignments API, AND any extra non-super-admin roles they + hold must be surfaced on their User object.""" + mock_provider = set_mocked_googleworkspace_provider() + mock_provider.audit_config = {} + mock_provider.fixer_config = {} + mock_session = MagicMock() + mock_session.credentials = MagicMock() + mock_provider.session = mock_session + + mock_service = MagicMock() + + stale_user_1 = { + "id": "user1-id", + "primaryEmail": "admin1@test-company.com", + "isAdmin": False, + } + stale_user_2 = { + "id": "user2-id", + "primaryEmail": "admin2@test-company.com", + "isAdmin": False, + } + mock_users_list = MagicMock() + mock_users_list.execute.return_value = {"users": [stale_user_1, stale_user_2]} + mock_service.users().list.return_value = mock_users_list + mock_service.users().list_next.return_value = None + + mock_roles_list = MagicMock() + mock_roles_list.execute.return_value = { + "items": [ROLE_SUPER_ADMIN, ROLE_SEED_ADMIN, ROLE_GROUPS_ADMIN] + } + mock_service.roles().list.return_value = mock_roles_list + mock_service.roles().list_next.return_value = None + + mock_ra = MagicMock() + mock_ra.execute.return_value = { + "items": [ + {"assignedTo": "user1-id", "roleId": SEED_ADMIN_ROLE_ID}, + {"assignedTo": "user2-id", "roleId": SUPER_ADMIN_ROLE_ID}, + {"assignedTo": "user2-id", "roleId": GROUPS_ADMIN_ROLE_ID}, + ] + } + mock_service.roleAssignments().list.return_value = mock_ra + mock_service.roleAssignments().list_next.return_value = None + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.directory.directory_service.GoogleWorkspaceService._build_service", + return_value=mock_service, + ), + ): + from prowler.providers.googleworkspace.services.directory.directory_service import ( + Directory, + ) + + directory = Directory(mock_provider) + + user1 = directory.users["user1-id"] + user2 = directory.users["user2-id"] + assert user1.is_admin is True + assert user2.is_admin is True + + assert [r.name for r in user1.role_assignments] == ["_SEED_ADMIN_ROLE"] + user2_role_names = {r.name for r in user2.role_assignments} + assert user2_role_names == {"Super Admin", "_GROUPS_ADMIN_ROLE"} diff --git a/tests/providers/googleworkspace/services/directory/directory_super_admin_only_admin_roles/directory_super_admin_only_admin_roles_test.py b/tests/providers/googleworkspace/services/directory/directory_super_admin_only_admin_roles/directory_super_admin_only_admin_roles_test.py new file mode 100644 index 0000000000..4025717bf7 --- /dev/null +++ b/tests/providers/googleworkspace/services/directory/directory_super_admin_only_admin_roles/directory_super_admin_only_admin_roles_test.py @@ -0,0 +1,446 @@ +from unittest.mock import patch + +from prowler.providers.googleworkspace.services.directory.directory_service import ( + Role, + User, +) +from tests.providers.googleworkspace.googleworkspace_fixtures import ( + CUSTOMER_ID, + DOMAIN, + set_mocked_googleworkspace_provider, +) + +SUPER_ADMIN_ROLE = Role( + id="13801188331880449", + name="Super Admin", + description="Super Admin", + is_super_admin_role=True, +) +SEED_ADMIN_ROLE = Role( + id="13801188331880451", + name="_SEED_ADMIN_ROLE", + description="Super Admin", + is_super_admin_role=True, +) +GROUPS_ADMIN_ROLE = Role( + id="13801188331880450", + name="_GROUPS_ADMIN_ROLE", + description="Groups Administrator", + is_super_admin_role=False, +) +USER_MANAGEMENT_ADMIN_ROLE = Role( + id="13801188331880452", + name="_USER_MANAGEMENT_ADMIN_ROLE", + description="User Management Administrator", + is_super_admin_role=False, +) +CUSTOM_ROLE_NO_DESCRIPTION = Role( + id="13801188331880453", + name="custom-helpdesk-role", + description="", + is_super_admin_role=False, +) + + +class TestDirectorySuperAdminOnlyAdminRoles: + def test_pass_super_admins_only_super_admin_role(self): + """Test PASS when super admins have only the Super Admin role""" + users = { + "admin1-id": User( + id="admin1-id", + email="admin1@test-company.com", + is_admin=True, + role_assignments=[SUPER_ADMIN_ROLE], + ), + "admin2-id": User( + id="admin2-id", + email="admin2@test-company.com", + is_admin=True, + role_assignments=[SUPER_ADMIN_ROLE], + ), + "user1-id": User( + id="user1-id", + email="user@test-company.com", + is_admin=False, + role_assignments=[], + ), + } + + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.directory.directory_super_admin_only_admin_roles.directory_super_admin_only_admin_roles.directory_client" + ) as mock_directory_client, + ): + from prowler.providers.googleworkspace.services.directory.directory_super_admin_only_admin_roles.directory_super_admin_only_admin_roles import ( + directory_super_admin_only_admin_roles, + ) + + mock_directory_client.users = users + mock_directory_client.provider = mock_provider + + check = directory_super_admin_only_admin_roles() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "PASS" + assert "used only for super admin activities" in findings[0].status_extended + assert findings[0].resource_name == DOMAIN + assert findings[0].customer_id == CUSTOMER_ID + + def test_pass_super_admin_with_seed_admin_role(self): + """Test PASS when a super admin only holds _SEED_ADMIN_ROLE. + + _SEED_ADMIN_ROLE is auto-assigned by Google to the original domain + creator and has isSuperAdminRole=True, so it must not count as an + "extra" role. + """ + users = { + "admin1-id": User( + id="admin1-id", + email="playground@prowler.cloud", + is_admin=True, + role_assignments=[SEED_ADMIN_ROLE], + ), + } + + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.directory.directory_super_admin_only_admin_roles.directory_super_admin_only_admin_roles.directory_client" + ) as mock_directory_client, + ): + from prowler.providers.googleworkspace.services.directory.directory_super_admin_only_admin_roles.directory_super_admin_only_admin_roles import ( + directory_super_admin_only_admin_roles, + ) + + mock_directory_client.users = users + mock_directory_client.provider = mock_provider + + check = directory_super_admin_only_admin_roles() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "PASS" + assert "_SEED_ADMIN_ROLE" not in findings[0].status_extended + + def test_pass_super_admin_with_both_super_admin_and_seed_admin(self): + """Test PASS when admin holds both Super Admin and _SEED_ADMIN_ROLE""" + users = { + "admin1-id": User( + id="admin1-id", + email="playground@prowler.cloud", + is_admin=True, + role_assignments=[SUPER_ADMIN_ROLE, SEED_ADMIN_ROLE], + ), + } + + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.directory.directory_super_admin_only_admin_roles.directory_super_admin_only_admin_roles.directory_client" + ) as mock_directory_client, + ): + from prowler.providers.googleworkspace.services.directory.directory_super_admin_only_admin_roles.directory_super_admin_only_admin_roles import ( + directory_super_admin_only_admin_roles, + ) + + mock_directory_client.users = users + mock_directory_client.provider = mock_provider + + check = directory_super_admin_only_admin_roles() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "PASS" + + def test_fail_super_admin_with_additional_roles(self): + """Test FAIL when a super admin also has additional admin roles""" + users = { + "admin1-id": User( + id="admin1-id", + email="admin1@test-company.com", + is_admin=True, + role_assignments=[SUPER_ADMIN_ROLE, GROUPS_ADMIN_ROLE], + ), + "user1-id": User( + id="user1-id", + email="user@test-company.com", + is_admin=False, + role_assignments=[], + ), + } + + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.directory.directory_super_admin_only_admin_roles.directory_super_admin_only_admin_roles.directory_client" + ) as mock_directory_client, + ): + from prowler.providers.googleworkspace.services.directory.directory_super_admin_only_admin_roles.directory_super_admin_only_admin_roles import ( + directory_super_admin_only_admin_roles, + ) + + mock_directory_client.users = users + mock_directory_client.provider = mock_provider + + check = directory_super_admin_only_admin_roles() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "admin1@test-company.com" in findings[0].status_extended + assert "Groups Administrator" in findings[0].status_extended + assert "_GROUPS_ADMIN_ROLE" not in findings[0].status_extended + assert "used only for super admin activities" in findings[0].status_extended + assert findings[0].resource_name == DOMAIN + assert findings[0].customer_id == CUSTOMER_ID + + def test_fail_seed_admin_with_additional_roles(self): + """Test FAIL when a _SEED_ADMIN_ROLE holder also has extra roles""" + users = { + "admin1-id": User( + id="admin1-id", + email="playground@prowler.cloud", + is_admin=True, + role_assignments=[SEED_ADMIN_ROLE, GROUPS_ADMIN_ROLE], + ), + } + + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.directory.directory_super_admin_only_admin_roles.directory_super_admin_only_admin_roles.directory_client" + ) as mock_directory_client, + ): + from prowler.providers.googleworkspace.services.directory.directory_super_admin_only_admin_roles.directory_super_admin_only_admin_roles import ( + directory_super_admin_only_admin_roles, + ) + + mock_directory_client.users = users + mock_directory_client.provider = mock_provider + + check = directory_super_admin_only_admin_roles() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "playground@prowler.cloud" in findings[0].status_extended + assert "Groups Administrator" in findings[0].status_extended + assert "_GROUPS_ADMIN_ROLE" not in findings[0].status_extended + assert "_SEED_ADMIN_ROLE" not in findings[0].status_extended + + def test_fail_multiple_super_admins_with_extra_roles(self): + """Test FAIL lists all super admins that have additional roles""" + users = { + "admin1-id": User( + id="admin1-id", + email="admin1@test-company.com", + is_admin=True, + role_assignments=[SUPER_ADMIN_ROLE, GROUPS_ADMIN_ROLE], + ), + "admin2-id": User( + id="admin2-id", + email="admin2@test-company.com", + is_admin=True, + role_assignments=[SUPER_ADMIN_ROLE, USER_MANAGEMENT_ADMIN_ROLE], + ), + "admin3-id": User( + id="admin3-id", + email="admin3@test-company.com", + is_admin=True, + role_assignments=[SUPER_ADMIN_ROLE], + ), + } + + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.directory.directory_super_admin_only_admin_roles.directory_super_admin_only_admin_roles.directory_client" + ) as mock_directory_client, + ): + from prowler.providers.googleworkspace.services.directory.directory_super_admin_only_admin_roles.directory_super_admin_only_admin_roles import ( + directory_super_admin_only_admin_roles, + ) + + mock_directory_client.users = users + mock_directory_client.provider = mock_provider + + check = directory_super_admin_only_admin_roles() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "admin1@test-company.com" in findings[0].status_extended + assert "admin2@test-company.com" in findings[0].status_extended + assert "admin3@test-company.com" not in findings[0].status_extended + + def test_no_findings_when_no_users(self): + """Test no findings when there are no users""" + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.directory.directory_super_admin_only_admin_roles.directory_super_admin_only_admin_roles.directory_client" + ) as mock_directory_client, + ): + from prowler.providers.googleworkspace.services.directory.directory_super_admin_only_admin_roles.directory_super_admin_only_admin_roles import ( + directory_super_admin_only_admin_roles, + ) + + mock_directory_client.users = {} + mock_directory_client.provider = mock_provider + + check = directory_super_admin_only_admin_roles() + findings = check.execute() + + assert len(findings) == 0 + + def test_non_super_admin_with_roles_not_flagged(self): + """Test that users who are not super admins are ignored even if they have roles""" + users = { + "admin1-id": User( + id="admin1-id", + email="admin1@test-company.com", + is_admin=True, + role_assignments=[SUPER_ADMIN_ROLE], + ), + "delegated1-id": User( + id="delegated1-id", + email="delegated@test-company.com", + is_admin=False, + role_assignments=[GROUPS_ADMIN_ROLE], + ), + } + + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.directory.directory_super_admin_only_admin_roles.directory_super_admin_only_admin_roles.directory_client" + ) as mock_directory_client, + ): + from prowler.providers.googleworkspace.services.directory.directory_super_admin_only_admin_roles.directory_super_admin_only_admin_roles import ( + directory_super_admin_only_admin_roles, + ) + + mock_directory_client.users = users + mock_directory_client.provider = mock_provider + + check = directory_super_admin_only_admin_roles() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "PASS" + assert "delegated@test-company.com" not in findings[0].status_extended + + def test_pass_super_admin_with_empty_role_assignments(self): + """Test PASS when super admin has no role assignments (edge case)""" + users = { + "admin1-id": User( + id="admin1-id", + email="admin1@test-company.com", + is_admin=True, + role_assignments=[], + ), + } + + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.directory.directory_super_admin_only_admin_roles.directory_super_admin_only_admin_roles.directory_client" + ) as mock_directory_client, + ): + from prowler.providers.googleworkspace.services.directory.directory_super_admin_only_admin_roles.directory_super_admin_only_admin_roles import ( + directory_super_admin_only_admin_roles, + ) + + mock_directory_client.users = users + mock_directory_client.provider = mock_provider + + check = directory_super_admin_only_admin_roles() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "PASS" + + def test_fail_custom_role_without_description_falls_back_to_name(self): + """A custom role with an empty description should be displayed + using its name as a fall-back, so the FAIL message is never blank + for users that genuinely hold extra roles.""" + users = { + "admin1-id": User( + id="admin1-id", + email="admin1@test-company.com", + is_admin=True, + role_assignments=[SUPER_ADMIN_ROLE, CUSTOM_ROLE_NO_DESCRIPTION], + ), + } + + mock_provider = set_mocked_googleworkspace_provider() + + with ( + patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=mock_provider, + ), + patch( + "prowler.providers.googleworkspace.services.directory.directory_super_admin_only_admin_roles.directory_super_admin_only_admin_roles.directory_client" + ) as mock_directory_client, + ): + from prowler.providers.googleworkspace.services.directory.directory_super_admin_only_admin_roles.directory_super_admin_only_admin_roles import ( + directory_super_admin_only_admin_roles, + ) + + mock_directory_client.users = users + mock_directory_client.provider = mock_provider + + check = directory_super_admin_only_admin_roles() + findings = check.execute() + + assert len(findings) == 1 + assert findings[0].status == "FAIL" + assert "custom-helpdesk-role" in findings[0].status_extended diff --git a/ui/.husky/pre-commit b/ui/.husky/pre-commit index 98be29c453..0755cd1872 100755 --- a/ui/.husky/pre-commit +++ b/ui/.husky/pre-commit @@ -6,6 +6,12 @@ set -e +# The Python pre-commit framework (see .pre-commit-config.yaml, hook "ui-checks") +# exports GIT_WORK_TREE, GIT_DIR, and GIT_INDEX_FILE pointing to its temp staging +# area. Unset them so git commands below resolve against the real repo and index. +# See: https://github.com/prowler-cloud/prowler/pull/10574 +unset GIT_WORK_TREE GIT_DIR GIT_INDEX_FILE GIT_PREFIX GIT_COMMON_DIR GIT_OBJECT_DIRECTORY + # Colors RED='\033[0;31m' GREEN='\033[0;32m' diff --git a/ui/CHANGELOG.md b/ui/CHANGELOG.md index 941e7c5ff4..c115911475 100644 --- a/ui/CHANGELOG.md +++ b/ui/CHANGELOG.md @@ -6,6 +6,9 @@ All notable changes to the **Prowler UI** are documented in this file. ### 🚀 Added +- Invitation accept smart router for handling invitation flow routing [(#10573)](https://github.com/prowler-cloud/prowler/pull/10573) +- Invitation link backward compatibility [(#10583)](https://github.com/prowler-cloud/prowler/pull/10583) +- Updated invitation link to use smart router [(#10575)](https://github.com/prowler-cloud/prowler/pull/10575) - Multi-tenant organization management: create, switch, edit, and delete organizations from the profile page [(#10491)](https://github.com/prowler-cloud/prowler/pull/10491) - Findings grouped view with drill-down table showing resources per check, resource detail drawer, infinite scroll pagination, and bulk mute support [(#10425)](https://github.com/prowler-cloud/prowler/pull/10425) - Resource events tool to Lighthouse AI [(#10412)](https://github.com/prowler-cloud/prowler/pull/10412) @@ -18,6 +21,7 @@ All notable changes to the **Prowler UI** are documented in this file. ### 🐞 Fixed +- Preserve query parameters in callbackUrl during invitation flow [(#10571)](https://github.com/prowler-cloud/prowler/pull/10571) - Deleting the active organization now switches to the target org before deleting, preventing JWT rejection from the backend [(#10491)](https://github.com/prowler-cloud/prowler/pull/10491) - Clear Filters now resets all filters including muted findings and auto-applies, Clear all in pills only removes pill-visible sub-filters, and the discard icon is now an Undo text button [(#10446)](https://github.com/prowler-cloud/prowler/pull/10446) - Send to Jira modal now dynamically fetches and displays available issue types per project instead of hardcoding `"Task"`, fixing failures on non-English Jira instances [(#10534)](https://github.com/prowler-cloud/prowler/pull/10534) diff --git a/ui/actions/finding-groups/finding-groups.adapter.test.ts b/ui/actions/finding-groups/finding-groups.adapter.test.ts index 5874ddf7e5..1d5d04e62d 100644 --- a/ui/actions/finding-groups/finding-groups.adapter.test.ts +++ b/ui/actions/finding-groups/finding-groups.adapter.test.ts @@ -163,6 +163,7 @@ describe("adaptFindingGroupResourcesResponse — malformed input", () => { alias: "production", }, status: "FAIL", + delta: "new", severity: "critical", first_seen_at: null, last_seen_at: "2024-01-01T00:00:00Z", @@ -178,5 +179,6 @@ describe("adaptFindingGroupResourcesResponse — malformed input", () => { expect(result).toHaveLength(1); expect(result[0].checkId).toBe("s3_check"); expect(result[0].resourceName).toBe("my-bucket"); + expect(result[0].delta).toBe("new"); }); }); diff --git a/ui/actions/finding-groups/finding-groups.adapter.ts b/ui/actions/finding-groups/finding-groups.adapter.ts index 593171078d..2e3964522e 100644 --- a/ui/actions/finding-groups/finding-groups.adapter.ts +++ b/ui/actions/finding-groups/finding-groups.adapter.ts @@ -98,6 +98,7 @@ interface FindingGroupResourceAttributes { resource: ResourceInfo; provider: ProviderInfo; status: string; + delta?: string | null; severity: string; first_seen_at: string | null; last_seen_at: string | null; @@ -137,14 +138,15 @@ export function adaptFindingGroupResourcesResponse( providerAlias: item.attributes.provider?.alias || "", providerUid: item.attributes.provider?.uid || "", resourceName: item.attributes.resource?.name || "-", + resourceType: item.attributes.resource?.type || "-", resourceGroup: item.attributes.resource?.resource_group || "-", resourceUid: item.attributes.resource?.uid || "-", service: item.attributes.resource?.service || "-", region: item.attributes.resource?.region || "-", severity: (item.attributes.severity || "informational") as Severity, status: item.attributes.status, + delta: item.attributes.delta || null, isMuted: item.attributes.status === "MUTED", - // TODO: remove fallback once the API returns muted_reason in finding-group-resources mutedReason: item.attributes.muted_reason || undefined, firstSeenAt: item.attributes.first_seen_at, lastSeenAt: item.attributes.last_seen_at, diff --git a/ui/actions/finding-groups/finding-groups.test.ts b/ui/actions/finding-groups/finding-groups.test.ts index 0d8c2df53a..9f4bdc5830 100644 --- a/ui/actions/finding-groups/finding-groups.test.ts +++ b/ui/actions/finding-groups/finding-groups.test.ts @@ -47,10 +47,6 @@ import { getLatestFindingGroupResources, } from "./finding-groups"; -// --------------------------------------------------------------------------- -// Blocker 1 + 2: FAIL-first sort and FAIL-only filter for drill-down resources -// --------------------------------------------------------------------------- - // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- @@ -169,7 +165,7 @@ describe("getLatestFindingGroupResources — SSRF path traversal protection", () }); // --------------------------------------------------------------------------- -// Blocker 1: Resources list must show FAIL first (sort=-status) +// Resources list keeps FAIL-first sort but no longer forces FAIL-only filtering // --------------------------------------------------------------------------- describe("getFindingGroupResources — Blocker 1: FAIL-first sort", () => { @@ -181,30 +177,30 @@ describe("getFindingGroupResources — Blocker 1: FAIL-first sort", () => { fetchMock.mockResolvedValue(new Response("", { status: 200 })); }); - it("should include sort=-status in the API call so FAIL resources appear first", async () => { + it("should include the composite sort so FAIL resources appear first, then severity", async () => { // Given const checkId = "s3_bucket_public_access"; // When await getFindingGroupResources({ checkId }); - // Then — the URL must contain sort=-status + // Then — the URL must contain the composite sort const calledUrl = fetchMock.mock.calls[0][0] as string; const url = new URL(calledUrl); - expect(url.searchParams.get("sort")).toBe("-status"); + expect(url.searchParams.get("sort")).toBe("-severity,-delta,-last_seen_at"); }); - it("should include filter[status]=FAIL in the API call so only impacted resources are shown", async () => { + it("should not force filter[status]=FAIL so PASS resources can also be shown", async () => { // Given const checkId = "s3_bucket_public_access"; // When await getFindingGroupResources({ checkId }); - // Then — the URL must contain filter[status]=FAIL + // Then — the URL should not add a hardcoded status filter const calledUrl = fetchMock.mock.calls[0][0] as string; const url = new URL(calledUrl); - expect(url.searchParams.get("filter[status]")).toBe("FAIL"); + expect(url.searchParams.get("filter[status]")).toBeNull(); }); }); @@ -217,7 +213,7 @@ describe("getLatestFindingGroupResources — Blocker 1: FAIL-first sort", () => fetchMock.mockResolvedValue(new Response("", { status: 200 })); }); - it("should include sort=-status in the API call so FAIL resources appear first", async () => { + it("should include the composite sort so FAIL resources appear first, then severity", async () => { // Given const checkId = "iam_user_mfa_enabled"; @@ -227,10 +223,10 @@ describe("getLatestFindingGroupResources — Blocker 1: FAIL-first sort", () => // Then const calledUrl = fetchMock.mock.calls[0][0] as string; const url = new URL(calledUrl); - expect(url.searchParams.get("sort")).toBe("-status"); + expect(url.searchParams.get("sort")).toBe("-severity,-delta,-last_seen_at"); }); - it("should include filter[status]=FAIL in the API call so only impacted resources are shown", async () => { + it("should not force filter[status]=FAIL so PASS resources can also be shown", async () => { // Given const checkId = "iam_user_mfa_enabled"; @@ -240,7 +236,7 @@ describe("getLatestFindingGroupResources — Blocker 1: FAIL-first sort", () => // Then const calledUrl = fetchMock.mock.calls[0][0] as string; const url = new URL(calledUrl); - expect(url.searchParams.get("filter[status]")).toBe("FAIL"); + expect(url.searchParams.get("filter[status]")).toBeNull(); }); }); @@ -257,7 +253,7 @@ describe("getFindingGroupResources — triangulation: params coexist", () => { fetchMock.mockResolvedValue(new Response("", { status: 200 })); }); - it("should send sort=-status AND filter[status]=FAIL alongside pagination params", async () => { + it("should send the composite sort alongside pagination params without forcing filter[status]", async () => { // Given const checkId = "s3_bucket_versioning"; @@ -269,8 +265,8 @@ describe("getFindingGroupResources — triangulation: params coexist", () => { const url = new URL(calledUrl); expect(url.searchParams.get("page[number]")).toBe("2"); expect(url.searchParams.get("page[size]")).toBe("50"); - expect(url.searchParams.get("sort")).toBe("-status"); - expect(url.searchParams.get("filter[status]")).toBe("FAIL"); + expect(url.searchParams.get("sort")).toBe("-severity,-delta,-last_seen_at"); + expect(url.searchParams.get("filter[status]")).toBeNull(); }); }); @@ -283,7 +279,7 @@ describe("getLatestFindingGroupResources — triangulation: params coexist", () fetchMock.mockResolvedValue(new Response("", { status: 200 })); }); - it("should send sort=-status AND filter[status]=FAIL alongside pagination params", async () => { + it("should send the composite sort alongside pagination params without forcing filter[status]", async () => { // Given const checkId = "iam_root_mfa_enabled"; @@ -295,16 +291,16 @@ describe("getLatestFindingGroupResources — triangulation: params coexist", () const url = new URL(calledUrl); expect(url.searchParams.get("page[number]")).toBe("3"); expect(url.searchParams.get("page[size]")).toBe("20"); - expect(url.searchParams.get("sort")).toBe("-status"); - expect(url.searchParams.get("filter[status]")).toBe("FAIL"); + expect(url.searchParams.get("sort")).toBe("-severity,-delta,-last_seen_at"); + expect(url.searchParams.get("filter[status]")).toBeNull(); }); }); // --------------------------------------------------------------------------- -// Blocker: Duplicate filter[status] — caller-supplied status must be stripped +// Caller filters should propagate unchanged to the drill-down resources endpoint // --------------------------------------------------------------------------- -describe("getFindingGroupResources — Blocker: caller filter[status] is always overridden to FAIL", () => { +describe("getFindingGroupResources — caller filters are preserved", () => { beforeEach(() => { vi.clearAllMocks(); vi.stubGlobal("fetch", fetchMock); @@ -313,23 +309,7 @@ describe("getFindingGroupResources — Blocker: caller filter[status] is always fetchMock.mockResolvedValue(new Response("", { status: 200 })); }); - it("should use filter[status]=FAIL even when caller passes filter[status]=PASS", async () => { - // Given — caller explicitly passes PASS, which must be ignored - const checkId = "s3_bucket_public_access"; - const filters = { "filter[status]": "PASS" }; - - // When - await getFindingGroupResources({ checkId, filters }); - - // Then — the final URL must have exactly one filter[status]=FAIL, not PASS - const calledUrl = fetchMock.mock.calls[0][0] as string; - const url = new URL(calledUrl); - const allStatusValues = url.searchParams.getAll("filter[status]"); - expect(allStatusValues).toHaveLength(1); - expect(allStatusValues[0]).toBe("FAIL"); - }); - - it("should not have duplicate filter[status] params when caller passes filter[status]", async () => { + it("should preserve caller filter[status] when explicitly provided", async () => { // Given const checkId = "s3_bucket_public_access"; const filters = { "filter[status]": "PASS" }; @@ -337,14 +317,56 @@ describe("getFindingGroupResources — Blocker: caller filter[status] is always // When await getFindingGroupResources({ checkId, filters }); - // Then — no duplicates + // Then const calledUrl = fetchMock.mock.calls[0][0] as string; const url = new URL(calledUrl); - expect(url.searchParams.getAll("filter[status]")).toHaveLength(1); + const allStatusValues = url.searchParams.getAll("filter[status]"); + expect(allStatusValues).toHaveLength(1); + expect(allStatusValues[0]).toBe("PASS"); + }); + + it("should translate a single group status__in filter into filter[status] for resources", async () => { + // Given + const checkId = "s3_bucket_public_access"; + const filters = { + "filter[status__in]": "PASS", + "filter[severity__in]": "medium", + "filter[provider_type__in]": "aws", + }; + + // When + await getFindingGroupResources({ checkId, filters }); + + // Then + const calledUrl = fetchMock.mock.calls[0][0] as string; + const url = new URL(calledUrl); + expect(url.searchParams.get("filter[status]")).toBe("PASS"); + expect(url.searchParams.get("filter[status__in]")).toBeNull(); + expect(url.searchParams.get("filter[severity__in]")).toBe("medium"); + expect(url.searchParams.get("filter[provider_type__in]")).toBe("aws"); + }); + + it("should keep the composite sort when the resource search filter is applied", async () => { + // Given + const checkId = "s3_bucket_public_access"; + const filters = { + "filter[name__icontains]": "bucket-prod", + "filter[severity__in]": "high", + }; + + // When + await getFindingGroupResources({ checkId, filters }); + + // Then + const calledUrl = fetchMock.mock.calls[0][0] as string; + const url = new URL(calledUrl); + expect(url.searchParams.get("sort")).toBe("-severity,-delta,-last_seen_at"); + expect(url.searchParams.get("filter[name__icontains]")).toBe("bucket-prod"); + expect(url.searchParams.get("filter[severity__in]")).toBe("high"); }); }); -describe("getLatestFindingGroupResources — Blocker: caller filter[status] is always overridden to FAIL", () => { +describe("getLatestFindingGroupResources — caller filters are preserved", () => { beforeEach(() => { vi.clearAllMocks(); vi.stubGlobal("fetch", fetchMock); @@ -353,23 +375,7 @@ describe("getLatestFindingGroupResources — Blocker: caller filter[status] is a fetchMock.mockResolvedValue(new Response("", { status: 200 })); }); - it("should use filter[status]=FAIL even when caller passes filter[status]=PASS", async () => { - // Given — caller explicitly passes PASS, which must be ignored - const checkId = "iam_user_mfa_enabled"; - const filters = { "filter[status]": "PASS" }; - - // When - await getLatestFindingGroupResources({ checkId, filters }); - - // Then — the final URL must have exactly one filter[status]=FAIL, not PASS - const calledUrl = fetchMock.mock.calls[0][0] as string; - const url = new URL(calledUrl); - const allStatusValues = url.searchParams.getAll("filter[status]"); - expect(allStatusValues).toHaveLength(1); - expect(allStatusValues[0]).toBe("FAIL"); - }); - - it("should not have duplicate filter[status] params when caller passes filter[status]", async () => { + it("should preserve caller filter[status] when explicitly provided", async () => { // Given const checkId = "iam_user_mfa_enabled"; const filters = { "filter[status]": "PASS" }; @@ -377,9 +383,53 @@ describe("getLatestFindingGroupResources — Blocker: caller filter[status] is a // When await getLatestFindingGroupResources({ checkId, filters }); - // Then — no duplicates + // Then const calledUrl = fetchMock.mock.calls[0][0] as string; const url = new URL(calledUrl); - expect(url.searchParams.getAll("filter[status]")).toHaveLength(1); + const allStatusValues = url.searchParams.getAll("filter[status]"); + expect(allStatusValues).toHaveLength(1); + expect(allStatusValues[0]).toBe("PASS"); + }); + + it("should translate a single group status__in filter into filter[status] for latest resources", async () => { + // Given + const checkId = "iam_user_mfa_enabled"; + const filters = { + "filter[status__in]": "PASS", + "filter[severity__in]": "low", + "filter[provider_type__in]": "aws", + }; + + // When + await getLatestFindingGroupResources({ checkId, filters }); + + // Then + const calledUrl = fetchMock.mock.calls[0][0] as string; + const url = new URL(calledUrl); + expect(url.searchParams.get("filter[status]")).toBe("PASS"); + expect(url.searchParams.get("filter[status__in]")).toBeNull(); + expect(url.searchParams.get("filter[severity__in]")).toBe("low"); + expect(url.searchParams.get("filter[provider_type__in]")).toBe("aws"); + }); + + it("should keep the composite sort when the resource search filter is applied", async () => { + // Given + const checkId = "iam_user_mfa_enabled"; + const filters = { + "filter[name__icontains]": "instance-prod", + "filter[status__in]": "PASS,FAIL", + }; + + // When + await getLatestFindingGroupResources({ checkId, filters }); + + // Then + const calledUrl = fetchMock.mock.calls[0][0] as string; + const url = new URL(calledUrl); + expect(url.searchParams.get("sort")).toBe("-severity,-delta,-last_seen_at"); + expect(url.searchParams.get("filter[name__icontains]")).toBe( + "instance-prod", + ); + expect(url.searchParams.get("filter[status__in]")).toBe("PASS,FAIL"); }); }); diff --git a/ui/actions/finding-groups/finding-groups.ts b/ui/actions/finding-groups/finding-groups.ts index b31d7a5bfc..b08293c882 100644 --- a/ui/actions/finding-groups/finding-groups.ts +++ b/ui/actions/finding-groups/finding-groups.ts @@ -23,17 +23,68 @@ function mapSearchFilter( return mapped; } -export const getFindingGroups = async ({ - page = 1, - pageSize = 10, - sort = "", - filters = {}, -}) => { +function splitCsvFilterValues(value: string | string[] | undefined): string[] { + if (Array.isArray(value)) { + return value + .flatMap((item) => item.split(",")) + .map((item) => item.trim()) + .filter(Boolean); + } + + if (typeof value === "string") { + return value + .split(",") + .map((item) => item.trim()) + .filter(Boolean); + } + + return []; +} + +function normalizeFindingGroupResourceFilters( + filters: Record, +): Record { + const normalized = { ...filters }; + const exactStatusFilter = normalized["filter[status]"]; + + if (exactStatusFilter !== undefined) { + delete normalized["filter[status__in]"]; + return normalized; + } + + const statusValues = splitCsvFilterValues(normalized["filter[status__in]"]); + if (statusValues.length === 1) { + normalized["filter[status]"] = statusValues[0]; + delete normalized["filter[status__in]"]; + } + + return normalized; +} + +const DEFAULT_FINDING_GROUPS_SORT = + "-severity,-delta,-fail_count,-last_seen_at"; + +interface FetchFindingGroupsParams { + page?: number; + pageSize?: number; + sort?: string; + filters?: Record; +} + +async function fetchFindingGroupsEndpoint( + endpoint: string, + { + page = 1, + pageSize = 10, + sort = DEFAULT_FINDING_GROUPS_SORT, + filters = {}, + }: FetchFindingGroupsParams, +) { const headers = await getAuthHeaders({ contentType: false }); if (isNaN(Number(page)) || page < 1) redirect("/findings"); - const url = new URL(`${apiBaseUrl}/finding-groups`); + const url = new URL(`${apiBaseUrl}/${endpoint}`); if (page) url.searchParams.append("page[number]", page.toString()); if (pageSize) url.searchParams.append("page[size]", pageSize.toString()); @@ -45,120 +96,60 @@ export const getFindingGroups = async ({ const response = await fetch(url.toString(), { headers }); return handleApiResponse(response); } catch (error) { - console.error("Error fetching finding groups:", error); + console.error(`Error fetching ${endpoint}:`, error); return undefined; } -}; +} -export const getLatestFindingGroups = async ({ - page = 1, - pageSize = 10, - sort = "", - filters = {}, -}) => { +export const getFindingGroups = async (params: FetchFindingGroupsParams = {}) => + fetchFindingGroupsEndpoint("finding-groups", params); + +export const getLatestFindingGroups = async ( + params: FetchFindingGroupsParams = {}, +) => fetchFindingGroupsEndpoint("finding-groups/latest", params); + +interface FetchFindingGroupResourcesParams { + checkId: string; + page?: number; + pageSize?: number; + filters?: Record; +} + +async function fetchFindingGroupResourcesEndpoint( + endpointPrefix: string, + { + checkId, + page = 1, + pageSize = 20, + filters = {}, + }: FetchFindingGroupResourcesParams, +) { const headers = await getAuthHeaders({ contentType: false }); + const normalizedFilters = normalizeFindingGroupResourceFilters(filters); - if (isNaN(Number(page)) || page < 1) redirect("/findings"); - - const url = new URL(`${apiBaseUrl}/finding-groups/latest`); + const url = new URL( + `${apiBaseUrl}/${endpointPrefix}/${encodeURIComponent(checkId)}/resources`, + ); if (page) url.searchParams.append("page[number]", page.toString()); if (pageSize) url.searchParams.append("page[size]", pageSize.toString()); - if (sort) url.searchParams.append("sort", sort); + url.searchParams.append("sort", "-severity,-delta,-last_seen_at"); - appendSanitizedProviderFilters(url, mapSearchFilter(filters)); + appendSanitizedProviderFilters(url, normalizedFilters); try { const response = await fetch(url.toString(), { headers }); return handleApiResponse(response); } catch (error) { - console.error("Error fetching latest finding groups:", error); + console.error(`Error fetching ${endpointPrefix} resources:`, error); return undefined; } -}; +} -export const getFindingGroupResources = async ({ - checkId, - page = 1, - pageSize = 20, - filters = {}, -}: { - checkId: string; - page?: number; - pageSize?: number; - filters?: Record; -}) => { - const headers = await getAuthHeaders({ contentType: false }); +export const getFindingGroupResources = async ( + params: FetchFindingGroupResourcesParams, +) => fetchFindingGroupResourcesEndpoint("finding-groups", params); - const url = new URL( - `${apiBaseUrl}/finding-groups/${encodeURIComponent(checkId)}/resources`, - ); - - if (page) url.searchParams.append("page[number]", page.toString()); - if (pageSize) url.searchParams.append("page[size]", pageSize.toString()); - // sort=-status is kept for future-proofing: if the filter[status]=FAIL - // constraint is ever relaxed to allow multiple statuses, the sort ensures - // FAIL resources still appear first in the result set. - url.searchParams.append("sort", "-status"); - - appendSanitizedProviderFilters(url, filters); - - // Use .set() AFTER appendSanitizedProviderFilters so our hardcoded FAIL - // always wins, even if the caller passed a different filter[status] value. - // Using .set() instead of .append() prevents duplicate filter[status] params. - url.searchParams.set("filter[status]", "FAIL"); - - try { - const response = await fetch(url.toString(), { - headers, - }); - - return handleApiResponse(response); - } catch (error) { - console.error("Error fetching finding group resources:", error); - return undefined; - } -}; - -export const getLatestFindingGroupResources = async ({ - checkId, - page = 1, - pageSize = 20, - filters = {}, -}: { - checkId: string; - page?: number; - pageSize?: number; - filters?: Record; -}) => { - const headers = await getAuthHeaders({ contentType: false }); - - const url = new URL( - `${apiBaseUrl}/finding-groups/latest/${encodeURIComponent(checkId)}/resources`, - ); - - if (page) url.searchParams.append("page[number]", page.toString()); - if (pageSize) url.searchParams.append("page[size]", pageSize.toString()); - // sort=-status is kept for future-proofing: if the filter[status]=FAIL - // constraint is ever relaxed to allow multiple statuses, the sort ensures - // FAIL resources still appear first in the result set. - url.searchParams.append("sort", "-status"); - - appendSanitizedProviderFilters(url, filters); - - // Use .set() AFTER appendSanitizedProviderFilters so our hardcoded FAIL - // always wins, even if the caller passed a different filter[status] value. - // Using .set() instead of .append() prevents duplicate filter[status] params. - url.searchParams.set("filter[status]", "FAIL"); - - try { - const response = await fetch(url.toString(), { - headers, - }); - - return handleApiResponse(response); - } catch (error) { - console.error("Error fetching latest finding group resources:", error); - return undefined; - } -}; +export const getLatestFindingGroupResources = async ( + params: FetchFindingGroupResourcesParams, +) => fetchFindingGroupResourcesEndpoint("finding-groups/latest", params); diff --git a/ui/actions/findings/findings-by-resource.ts b/ui/actions/findings/findings-by-resource.ts index 12900ffdca..4c69d2e5ef 100644 --- a/ui/actions/findings/findings-by-resource.ts +++ b/ui/actions/findings/findings-by-resource.ts @@ -379,6 +379,9 @@ export const getLatestFindingsByResourceUid = async ({ ); url.searchParams.append("filter[resource_uid]", resourceUid); + url.searchParams.append("filter[status]", "FAIL"); + url.searchParams.append("filter[muted]", "include"); + url.searchParams.append("sort", "-severity,status,-updated_at"); if (page) url.searchParams.append("page[number]", page.toString()); if (pageSize) url.searchParams.append("page[size]", pageSize.toString()); diff --git a/ui/actions/invitations/invitation.ts b/ui/actions/invitations/invitation.ts index 8ad037cbbe..72f591705a 100644 --- a/ui/actions/invitations/invitation.ts +++ b/ui/actions/invitations/invitation.ts @@ -2,10 +2,13 @@ import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; +import { z } from "zod"; import { apiBaseUrl, getAuthHeaders } from "@/lib"; import { handleApiError, handleApiResponse } from "@/lib/server-actions-helper"; +const invitationTokenSchema = z.string().min(1).max(500); + export const getInvitations = async ({ page = 1, query = "", @@ -195,3 +198,35 @@ export const revokeInvite = async (formData: FormData) => { handleApiError(error); } }; + +export const acceptInvitation = async (token: string) => { + const parsed = invitationTokenSchema.safeParse(token); + if (!parsed.success) { + return { error: "Invalid invitation token" }; + } + + const headers = await getAuthHeaders({ contentType: true }); + + const url = new URL(`${apiBaseUrl}/invitations/accept`); + + const body = JSON.stringify({ + data: { + type: "invitations", + attributes: { + invitation_token: parsed.data, + }, + }, + }); + + try { + const response = await fetch(url.toString(), { + method: "POST", + headers, + body, + }); + + return handleApiResponse(response); + } catch (error) { + return handleApiError(error); + } +}; diff --git a/ui/app/(auth)/(guest-only)/layout.tsx b/ui/app/(auth)/(guest-only)/layout.tsx new file mode 100644 index 0000000000..3ff93d836b --- /dev/null +++ b/ui/app/(auth)/(guest-only)/layout.tsx @@ -0,0 +1,18 @@ +import { redirect } from "next/navigation"; +import { ReactNode } from "react"; + +import { auth } from "@/auth.config"; + +export default async function GuestOnlyLayout({ + children, +}: { + children: ReactNode; +}) { + const session = await auth(); + + if (session?.user) { + redirect("/"); + } + + return <>{children}; +} diff --git a/ui/app/(auth)/sign-in/page.tsx b/ui/app/(auth)/(guest-only)/sign-in/page.tsx similarity index 100% rename from ui/app/(auth)/sign-in/page.tsx rename to ui/app/(auth)/(guest-only)/sign-in/page.tsx diff --git a/ui/app/(auth)/sign-up/page.tsx b/ui/app/(auth)/(guest-only)/sign-up/page.tsx similarity index 100% rename from ui/app/(auth)/sign-up/page.tsx rename to ui/app/(auth)/(guest-only)/sign-up/page.tsx diff --git a/ui/app/(auth)/invitation/accept/accept-invitation-client.tsx b/ui/app/(auth)/invitation/accept/accept-invitation-client.tsx new file mode 100644 index 0000000000..73e6dbe8fd --- /dev/null +++ b/ui/app/(auth)/invitation/accept/accept-invitation-client.tsx @@ -0,0 +1,219 @@ +"use client"; + +import { Icon } from "@iconify/react"; +import Link from "next/link"; +import { useRouter } from "next/navigation"; +import { signOut } from "next-auth/react"; +import { useEffect, useRef, useState } from "react"; + +import { acceptInvitation } from "@/actions/invitations"; +import { Button } from "@/components/shadcn"; +import { + INVITATION_ACTION_PARAM, + INVITATION_SIGNUP_ACTION, +} from "@/lib/invitation-routing"; + +type AcceptState = + | { kind: "no-token" } + | { kind: "accepting" } + | { kind: "error"; message: string; canRetry: boolean; needsSignOut: boolean } + | { kind: "choose" }; + +function mapApiError(status: number | undefined): { + message: string; + canRetry: boolean; + needsSignOut: boolean; +} { + switch (status) { + case 410: + return { + message: + "This invitation has expired. Please contact your administrator for a new one.", + canRetry: false, + needsSignOut: false, + }; + case 400: + return { + message: "This invitation has already been used.", + canRetry: false, + needsSignOut: false, + }; + case 404: + return { + message: + "This invitation was sent to a different email address. Please sign in with the correct account.", + canRetry: false, + needsSignOut: true, + }; + default: + return { + message: "Something went wrong while accepting the invitation.", + canRetry: true, + needsSignOut: false, + }; + } +} + +export function AcceptInvitationClient({ + isAuthenticated, + token, +}: { + isAuthenticated: boolean; + token: string | null; +}) { + const router = useRouter(); + const [state, setState] = useState(() => { + if (!token) return { kind: "no-token" }; + if (!isAuthenticated) return { kind: "choose" }; + return { kind: "accepting" }; + }); + const hasStartedRef = useRef(false); + + async function doAccept() { + if (!token) return; + setState({ kind: "accepting" }); + + const result = await acceptInvitation(token); + + if (result?.error) { + const { message, canRetry, needsSignOut } = mapApiError(result.status); + setState({ kind: "error", message, canRetry, needsSignOut }); + } else { + router.push("/"); + } + } + + async function handleSignOutAndRedirect() { + if (!token) return; + const callbackPath = `/invitation/accept?invitation_token=${encodeURIComponent(token)}`; + await signOut({ redirect: false }); + router.push(`/sign-in?callbackUrl=${encodeURIComponent(callbackPath)}`); + } + + useEffect(() => { + if (hasStartedRef.current) return; + hasStartedRef.current = true; + + if (!token) { + setState({ kind: "no-token" }); + return; + } + + if (isAuthenticated) { + doAccept(); + } else { + setState({ kind: "choose" }); + } + }, [token, isAuthenticated]); // eslint-disable-line react-hooks/exhaustive-deps + + return ( +
+
+ {/* No token */} + {state.kind === "no-token" && ( +
+ +

Invalid Invitation Link

+

+ No invitation token was provided. Please check the link you + received. +

+ +
+ )} + + {/* Accepting */} + {state.kind === "accepting" && ( +
+ +

Accepting Invitation...

+

+ Please wait while we process your invitation. +

+
+ )} + + {/* Error */} + {state.kind === "error" && ( +
+ +

+ Could Not Accept Invitation +

+

{state.message}

+
+ {state.canRetry && } + {state.needsSignOut ? ( + + ) : ( + + )} +
+
+ )} + + {/* Choice page for unauthenticated users */} + {state.kind === "choose" && ( +
+ +
+

+ You've Been Invited +

+

+ You've been invited to join a tenant on Prowler. How would + you like to continue? +

+
+
+ + +
+
+ )} +
+
+ ); +} diff --git a/ui/app/(auth)/invitation/accept/page.tsx b/ui/app/(auth)/invitation/accept/page.tsx new file mode 100644 index 0000000000..9bfc5e0110 --- /dev/null +++ b/ui/app/(auth)/invitation/accept/page.tsx @@ -0,0 +1,22 @@ +import { auth } from "@/auth.config"; +import { SearchParamsProps } from "@/types"; + +import { AcceptInvitationClient } from "./accept-invitation-client"; + +export default async function AcceptInvitationPage({ + searchParams, +}: { + searchParams: Promise; +}) { + const session = await auth(); + const resolvedSearchParams = await searchParams; + + const token = + typeof resolvedSearchParams?.invitation_token === "string" + ? resolvedSearchParams.invitation_token + : null; + + return ( + + ); +} diff --git a/ui/app/(auth)/layout.tsx b/ui/app/(auth)/layout.tsx index ab6b1e9bfa..07fe3a60c3 100644 --- a/ui/app/(auth)/layout.tsx +++ b/ui/app/(auth)/layout.tsx @@ -2,10 +2,8 @@ import "@/styles/globals.css"; import { GoogleTagManager } from "@next/third-parties/google"; import { Metadata, Viewport } from "next"; -import { redirect } from "next/navigation"; -import { ReactNode } from "react"; +import { ReactNode, Suspense } from "react"; -import { auth } from "@/auth.config"; import { NavigationProgress, Toaster } from "@/components/ui"; import { fontSans } from "@/config/fonts"; import { siteConfig } from "@/config/site"; @@ -31,17 +29,7 @@ export const viewport: Viewport = { ], }; -export default async function RootLayout({ - children, -}: { - children: ReactNode; -}) { - const session = await auth(); - - if (session?.user) { - redirect("/"); - } - +export default function AuthLayout({ children }: { children: ReactNode }) { return ( @@ -53,7 +41,9 @@ export default async function RootLayout({ )} > - + + + {children} { + const currentDir = path.dirname(fileURLToPath(import.meta.url)); + const pagePath = path.join(currentDir, "page.tsx"); + const source = readFileSync(pagePath, "utf8"); + + it("only passes sort to fetchFindingGroups when the user has an explicit sort param", () => { + expect(source).toContain("...(encodedSort && { sort: encodedSort })"); + }); + + it("normalizes scan filters with the required inserted_at params before fetching historical finding groups", () => { + expect(source).toContain("resolveFindingScanDateFilters"); + }); + + it("uses getLatestFindingGroups for non-date/scan queries and getFindingGroups for historical", () => { + expect(source).toContain("hasDateOrScan"); + expect(source).toContain("getFindingGroups"); + expect(source).toContain("getLatestFindingGroups"); + }); + + it("guards errors array access with a length check", () => { + expect(source).toContain("errors?.length > 0"); + }); +}); diff --git a/ui/app/(prowler)/findings/page.tsx b/ui/app/(prowler)/findings/page.tsx index 576d34ca1e..46749b38c2 100644 --- a/ui/app/(prowler)/findings/page.tsx +++ b/ui/app/(prowler)/findings/page.tsx @@ -7,7 +7,7 @@ import { } from "@/actions/finding-groups"; import { getLatestMetadataInfo, getMetadataInfo } from "@/actions/findings"; import { getProviders } from "@/actions/providers"; -import { getScans } from "@/actions/scans"; +import { getScan, getScans } from "@/actions/scans"; import { FindingsFilters } from "@/components/findings/findings-filters"; import { FindingsGroupTable, @@ -21,6 +21,7 @@ import { extractSortAndKey, hasDateOrScanFilter, } from "@/lib"; +import { resolveFindingScanDateFilters } from "@/lib/findings-scan-filters"; import { ScanEntity, ScanProps } from "@/types"; import { SearchParamsProps } from "@/types/components"; @@ -39,16 +40,28 @@ export default async function Findings({ // TODO: Re-implement deep link support (/findings?id=) using the grouped view's resource detail drawer // once the legacy FindingDetailsSheet is fully deprecated (still used by /resources and overview dashboard). - const [metadataInfoData, providersData, scansData] = await Promise.all([ - (hasDateOrScan ? getMetadataInfo : getLatestMetadataInfo)({ - query, - sort: encodedSort, - filters, - }), + const [providersData, scansData] = await Promise.all([ getProviders({ pageSize: 50 }), getScans({ pageSize: 50 }), ]); + const filtersWithScanDates = await resolveFindingScanDateFilters({ + filters, + scans: scansData?.data || [], + loadScan: async (scanId: string) => { + const response = await getScan(scanId); + return response?.data; + }, + }); + + const metadataInfoData = await ( + hasDateOrScan ? getMetadataInfo : getLatestMetadataInfo + )({ + query, + sort: encodedSort, + filters: filtersWithScanDates, + }); + // Extract unique regions, services, categories, groups from the new endpoint const uniqueRegions = metadataInfoData?.data?.attributes?.regions || []; const uniqueServices = metadataInfoData?.data?.attributes?.services || []; @@ -88,7 +101,10 @@ export default async function Findings({ /> }> - + @@ -97,19 +113,15 @@ export default async function Findings({ const SSRDataTable = async ({ searchParams, + filters, }: { searchParams: SearchParamsProps; + filters: Record; }) => { const page = parseInt(searchParams.page?.toString() || "1", 10); const pageSize = parseInt(searchParams.pageSize?.toString() || "10", 10); - const defaultSort = "-severity,-fail_count,-last_seen_at"; - const { encodedSort } = extractSortAndKey({ - ...searchParams, - sort: searchParams.sort ?? defaultSort, - }); - - const { filters } = extractFiltersAndQuery(searchParams); + const { encodedSort } = extractSortAndKey(searchParams); // Check if the searchParams contain any date or scan filter const hasDateOrScan = hasDateOrScanFilter(searchParams); @@ -119,7 +131,7 @@ const SSRDataTable = async ({ const findingGroupsData = await fetchFindingGroups({ page, - sort: encodedSort, + ...(encodedSort && { sort: encodedSort }), filters, pageSize, }); @@ -131,7 +143,7 @@ const SSRDataTable = async ({ return ( <> - {findingGroupsData?.errors && ( + {findingGroupsData?.errors?.length > 0 && (

Error:

{findingGroupsData.errors[0].detail}

diff --git a/ui/app/(prowler)/profile/page.tsx b/ui/app/(prowler)/profile/page.tsx index 77cf9d50fc..c992734283 100644 --- a/ui/app/(prowler)/profile/page.tsx +++ b/ui/app/(prowler)/profile/page.tsx @@ -77,11 +77,7 @@ const SSRDataUser = async ({ {}, ); - const firstUserMembership = membershipsIncluded.find( - (m) => m.relationships?.user?.data?.id === userData.id, - ); - - const userTenantId = firstUserMembership?.relationships?.tenant?.data?.id; + const userTenantId = session?.tenantId; const userRoleIds = userData.relationships?.roles?.data?.map((r) => r.id) || []; diff --git a/ui/app/(prowler)/scans/page.tsx b/ui/app/(prowler)/scans/page.tsx index 2d0416f37c..55dbf7eb8a 100644 --- a/ui/app/(prowler)/scans/page.tsx +++ b/ui/app/(prowler)/scans/page.tsx @@ -18,7 +18,12 @@ import { createProviderDetailsMapping, extractProviderUIDs, } from "@/lib/provider-helpers"; -import { ProviderProps, ScanProps, SearchParamsProps } from "@/types"; +import { + ExpandedScanData, + ProviderProps, + ScanProps, + SearchParamsProps, +} from "@/types"; export default async function Scans({ searchParams, @@ -30,7 +35,34 @@ export default async function Scans({ const filteredParams = { ...resolvedSearchParams }; delete filteredParams.scanId; - const providersData = await getAllProviders(); + const [providersData, completedScansData] = await Promise.all([ + getAllProviders(), + getScans({ + filters: { "filter[state]": "completed" }, + pageSize: 50, + fields: { scans: "name,completed_at,provider" }, + include: "provider", + }), + ]); + + const completedScans: ExpandedScanData[] = (completedScansData?.data ?? []) + .map((scan: ScanProps) => { + const providerId = scan.relationships?.provider?.data?.id; + const providerData = completedScansData?.included?.find( + (item: { type: string; id: string }) => + item.type === "providers" && item.id === providerId, + ); + if (!providerData) return null; + return { + ...scan, + providerInfo: { + provider: providerData.attributes.provider, + uid: providerData.attributes.uid, + alias: providerData.attributes.alias, + }, + }; + }) + .filter(Boolean) as ExpandedScanData[]; const providerInfo = providersData?.data @@ -90,6 +122,7 @@ export default async function Scans({
diff --git a/ui/auth.config.ts b/ui/auth.config.ts index 4bf7a660cb..c43ff55cc6 100644 --- a/ui/auth.config.ts +++ b/ui/auth.config.ts @@ -281,15 +281,20 @@ export const authConfig = { const sessionError = auth?.error; const isSignUpPage = nextUrl.pathname === "/sign-up"; const isSignInPage = nextUrl.pathname === "/sign-in"; + const isInvitationPage = + nextUrl.pathname.startsWith("/invitation/accept"); - // Allow access to sign-up and sign-in pages - if (isSignUpPage || isSignInPage) return true; + // Allow access to sign-up, sign-in, and invitation pages + if (isSignUpPage || isSignInPage || isInvitationPage) return true; // For all other routes, require authentication // Return NextResponse.redirect to preserve callbackUrl for post-login redirect if (!isLoggedIn) { const signInUrl = new URL("/sign-in", nextUrl.origin); - signInUrl.searchParams.set("callbackUrl", nextUrl.pathname); + signInUrl.searchParams.set( + "callbackUrl", + nextUrl.pathname + nextUrl.search, + ); // Include session error if present (e.g., RefreshAccessTokenError) if (sessionError) { signInUrl.searchParams.set("error", sessionError); diff --git a/ui/components/findings/findings-filters.tsx b/ui/components/findings/findings-filters.tsx index af169bfcc1..526b6240f3 100644 --- a/ui/components/findings/findings-filters.tsx +++ b/ui/components/findings/findings-filters.tsx @@ -18,11 +18,12 @@ import { Button } from "@/components/shadcn"; import { ExpandableSection } from "@/components/ui/expandable-section"; import { DataTableFilterCustom } from "@/components/ui/table"; import { useFilterBatch } from "@/hooks/use-filter-batch"; -import { formatLabel, getCategoryLabel, getGroupLabel } from "@/lib/categories"; -import { FilterType, FINDING_STATUS_DISPLAY_NAMES, ScanEntity } from "@/types"; +import { getCategoryLabel, getGroupLabel } from "@/lib/categories"; +import { FilterType, ScanEntity } from "@/types"; import { DATA_TABLE_FILTER_MODE, FilterParam } from "@/types/filters"; -import { getProviderDisplayName, ProviderProps } from "@/types/providers"; -import { SEVERITY_DISPLAY_NAMES } from "@/types/severities"; +import { ProviderProps } from "@/types/providers"; + +import { getFindingsFilterDisplayValue } from "./findings-filters.utils"; interface FindingsFiltersProps { /** Provider data for ProviderTypeSelector and AccountsSelector */ @@ -58,49 +59,6 @@ const FILTER_KEY_LABELS: Record = { "filter[muted]": "Muted", }; -/** - * Formats a raw filter value into a human-readable display string. - * - Provider types: uses shared getProviderDisplayName utility - * - Severities: uses shared SEVERITY_DISPLAY_NAMES (e.g. "critical" → "Critical") - * - Status: uses shared FINDING_STATUS_DISPLAY_NAMES (e.g. "FAIL" → "Fail") - * - Categories: uses getCategoryLabel (handles IAM, EC2, IMDSv1, etc.) - * - Resource groups: uses getGroupLabel (underscore-delimited) - * - Date (filter[inserted_at]): returns the ISO date string as-is (YYYY-MM-DD) - * - Other values: uses formatLabel as a generic fallback (avoids naive capitalisation) - */ -const formatFilterValue = (filterKey: string, value: string): string => { - if (!value) return value; - if (filterKey === "filter[provider_type__in]") { - return getProviderDisplayName(value); - } - if (filterKey === "filter[severity__in]") { - return ( - SEVERITY_DISPLAY_NAMES[ - value.toLowerCase() as keyof typeof SEVERITY_DISPLAY_NAMES - ] ?? formatLabel(value) - ); - } - if (filterKey === "filter[status__in]") { - return ( - FINDING_STATUS_DISPLAY_NAMES[ - value as keyof typeof FINDING_STATUS_DISPLAY_NAMES - ] ?? formatLabel(value) - ); - } - if (filterKey === "filter[category__in]") { - return getCategoryLabel(value); - } - if (filterKey === "filter[resource_groups__in]") { - return getGroupLabel(value); - } - // Date filter: preserve ISO date string (YYYY-MM-DD) — do not run through formatLabel - if (filterKey === "filter[inserted_at]") { - return value; - } - // Generic fallback: handles hyphen/underscore-delimited IDs with smart capitalisation - return formatLabel(value); -}; - export const FindingsFilters = ({ providers, completedScanIds, @@ -185,7 +143,10 @@ export const FindingsFilters = ({ key, label, value, - displayValue: formatFilterValue(key, value), + displayValue: getFindingsFilterDisplayValue(key, value, { + providers, + scans: scanDetails, + }), }); }); }); diff --git a/ui/components/findings/findings-filters.utils.test.ts b/ui/components/findings/findings-filters.utils.test.ts new file mode 100644 index 0000000000..86a3124b0a --- /dev/null +++ b/ui/components/findings/findings-filters.utils.test.ts @@ -0,0 +1,148 @@ +import { describe, expect, it } from "vitest"; + +import { ProviderProps } from "@/types/providers"; +import { ScanEntity } from "@/types/scans"; + +import { getFindingsFilterDisplayValue } from "./findings-filters.utils"; + +function makeProvider( + overrides: Partial & { id: string }, +): ProviderProps { + return { + type: "providers", + attributes: { + provider: "aws", + uid: "123456789012", + alias: "Production Account", + status: "completed", + resources: 10, + connection: { connected: true, last_checked_at: "2026-04-07T10:00:00Z" }, + scanner_args: { + only_logs: false, + excluded_checks: [], + aws_retries_max_attempts: 3, + }, + inserted_at: "2026-04-07T10:00:00Z", + updated_at: "2026-04-07T10:00:00Z", + created_by: { object: "user", id: "user-1" }, + }, + relationships: { + secret: { data: null }, + provider_groups: { meta: { count: 0 }, data: [] }, + }, + ...overrides, + } as ProviderProps; +} + +function makeScanMap( + scanId: string, + overrides?: Partial, +): { [scanId: string]: ScanEntity } { + return { + [scanId]: { + id: scanId, + providerInfo: { + provider: "aws", + alias: "Scan Account", + uid: "123456789012", + }, + attributes: { + name: "Nightly scan", + completed_at: "2026-04-07T10:00:00Z", + }, + ...overrides, + }, + }; +} + +const providers = [makeProvider({ id: "provider-1" })]; +const scans = [makeScanMap("scan-1")]; + +describe("getFindingsFilterDisplayValue", () => { + it("shows the account alias for provider_id filters instead of the raw provider id", () => { + expect( + getFindingsFilterDisplayValue("filter[provider_id__in]", "provider-1", { + providers, + }), + ).toBe("Production Account"); + }); + + it("falls back to the provider uid when the alias is empty", () => { + expect( + getFindingsFilterDisplayValue("filter[provider_id__in]", "provider-2", { + providers: [ + ...providers, + makeProvider({ + id: "provider-2", + attributes: { + ...providers[0].attributes, + alias: "", + uid: "210987654321", + }, + }), + ], + }), + ).toBe("210987654321"); + }); + + it("keeps the raw value when the provider cannot be resolved", () => { + expect( + getFindingsFilterDisplayValue( + "filter[provider_id__in]", + "missing-provider", + { providers }, + ), + ).toBe("missing-provider"); + }); + + it("shows the resolved scan badge label for scan filters instead of formatting the raw scan id", () => { + expect( + getFindingsFilterDisplayValue("filter[scan__in]", "scan-1", { scans }), + ).toBe("Scan Account"); + }); + + it("falls back to the scan provider uid when the alias is missing", () => { + expect( + getFindingsFilterDisplayValue("filter[scan__in]", "scan-2", { + scans: [ + ...scans, + makeScanMap("scan-2", { + providerInfo: { provider: "aws", uid: "210987654321" }, + attributes: { + name: "Weekly scan", + completed_at: "2026-04-08T10:00:00Z", + }, + }), + ], + }), + ).toBe("210987654321"); + }); + + it("keeps the raw scan value when the scan cannot be resolved", () => { + expect( + getFindingsFilterDisplayValue("filter[scan__in]", "missing-scan", { + scans, + }), + ).toBe("missing-scan"); + }); + + it("passes through date values for inserted_at__gte filters", () => { + expect( + getFindingsFilterDisplayValue( + "filter[inserted_at__gte]", + "2026-04-03", + {}, + ), + ).toBe("2026-04-03"); + }); + + it("passes through date values for inserted_at__lte filters", () => { + expect( + getFindingsFilterDisplayValue( + "filter[inserted_at__lte]", + "2026-04-07", + {}, + ), + ).toBe("2026-04-07"); + }); +}); diff --git a/ui/components/findings/findings-filters.utils.ts b/ui/components/findings/findings-filters.utils.ts new file mode 100644 index 0000000000..6cb9c19b28 --- /dev/null +++ b/ui/components/findings/findings-filters.utils.ts @@ -0,0 +1,80 @@ +import { formatLabel, getCategoryLabel, getGroupLabel } from "@/lib/categories"; +import { FINDING_STATUS_DISPLAY_NAMES } from "@/types"; +import { getProviderDisplayName, ProviderProps } from "@/types/providers"; +import { ScanEntity } from "@/types/scans"; +import { SEVERITY_DISPLAY_NAMES } from "@/types/severities"; + +interface GetFindingsFilterDisplayValueOptions { + providers?: ProviderProps[]; + scans?: Array<{ [scanId: string]: ScanEntity }>; +} + +function getProviderAccountDisplayValue( + providerId: string, + providers: ProviderProps[], +): string { + const provider = providers.find((item) => item.id === providerId); + if (!provider) { + return providerId; + } + + return provider.attributes.alias || provider.attributes.uid || providerId; +} + +function getScanDisplayValue( + scanId: string, + scans: Array<{ [scanId: string]: ScanEntity }>, +): string { + const scan = scans.find((item) => item[scanId])?.[scanId]; + if (!scan) { + return scanId; + } + + return scan.providerInfo.alias || scan.providerInfo.uid || scanId; +} + +export function getFindingsFilterDisplayValue( + filterKey: string, + value: string, + options: GetFindingsFilterDisplayValueOptions = {}, +): string { + if (!value) return value; + if (filterKey === "filter[provider_type__in]") { + return getProviderDisplayName(value); + } + if (filterKey === "filter[provider_id__in]") { + return getProviderAccountDisplayValue(value, options.providers || []); + } + if (filterKey === "filter[scan__in]") { + return getScanDisplayValue(value, options.scans || []); + } + if (filterKey === "filter[severity__in]") { + return ( + SEVERITY_DISPLAY_NAMES[ + value.toLowerCase() as keyof typeof SEVERITY_DISPLAY_NAMES + ] ?? formatLabel(value) + ); + } + if (filterKey === "filter[status__in]") { + return ( + FINDING_STATUS_DISPLAY_NAMES[ + value as keyof typeof FINDING_STATUS_DISPLAY_NAMES + ] ?? formatLabel(value) + ); + } + if (filterKey === "filter[category__in]") { + return getCategoryLabel(value); + } + if (filterKey === "filter[resource_groups__in]") { + return getGroupLabel(value); + } + if ( + filterKey === "filter[inserted_at]" || + filterKey === "filter[inserted_at__gte]" || + filterKey === "filter[inserted_at__lte]" + ) { + return value; + } + + return formatLabel(value); +} diff --git a/ui/components/findings/table/column-finding-groups.test.tsx b/ui/components/findings/table/column-finding-groups.test.tsx index ab5d26bc62..e723e51862 100644 --- a/ui/components/findings/table/column-finding-groups.test.tsx +++ b/ui/components/findings/table/column-finding-groups.test.tsx @@ -17,11 +17,20 @@ vi.mock("next/navigation", () => ({ vi.mock("@/components/shadcn", () => ({ Checkbox: ({ "aria-label": ariaLabel, + onCheckedChange, ...props }: InputHTMLAttributes & { "aria-label"?: string; size?: string; - }) => , + onCheckedChange?: (checked: boolean) => void; + }) => ( + onCheckedChange?.(event.target.checked)} + {...props} + /> + ), })); vi.mock("@/components/ui/table", () => ({ @@ -52,7 +61,13 @@ vi.mock("./impacted-providers-cell", () => ({ })); vi.mock("./impacted-resources-cell", () => ({ - ImpactedResourcesCell: () => null, + ImpactedResourcesCell: ({ + impacted, + total, + }: { + impacted: number; + total: number; + }) => {`${impacted}/${total}`}, })); vi.mock("./notification-indicator", () => ({ @@ -94,6 +109,7 @@ function makeGroup(overrides?: Partial): FindingGroupRow { function renderFindingCell( checkTitle: string, onDrillDown: (checkId: string, group: FindingGroupRow) => void, + overrides?: Partial, ) { const columns = getColumnFindingGroups({ rowSelection: {}, @@ -107,7 +123,7 @@ function renderFindingCell( ); if (!findingColumn?.cell) throw new Error("finding column not found"); - const group = makeGroup({ checkTitle }); + const group = makeGroup({ checkTitle, ...overrides }); // Render the cell directly with a minimal row mock const CellComponent = findingColumn.cell as (props: { row: { original: FindingGroupRow }; @@ -116,6 +132,67 @@ function renderFindingCell( render(
{CellComponent({ row: { original: group } })}
); } +function renderImpactedResourcesCell(overrides?: Partial) { + const columns = getColumnFindingGroups({ + rowSelection: {}, + selectableRowCount: 1, + onDrillDown: vi.fn(), + }); + + const impactedResourcesColumn = columns.find( + (col) => (col as { id?: string }).id === "impactedResources", + ); + if (!impactedResourcesColumn?.cell) { + throw new Error("impactedResources column not found"); + } + + const group = makeGroup(overrides); + const CellComponent = impactedResourcesColumn.cell as (props: { + row: { original: FindingGroupRow }; + }) => ReactNode; + + render(
{CellComponent({ row: { original: group } })}
); +} + +function renderSelectCell(overrides?: Partial) { + const toggleSelected = vi.fn(); + const columns = getColumnFindingGroups({ + rowSelection: {}, + selectableRowCount: 1, + onDrillDown: vi.fn(), + }); + + const selectColumn = columns.find( + (col) => (col as { id?: string }).id === "select", + ); + if (!selectColumn?.cell) { + throw new Error("select column not found"); + } + + const group = makeGroup(overrides); + const CellComponent = selectColumn.cell as (props: { + row: { + id: string; + original: FindingGroupRow; + toggleSelected: (selected: boolean) => void; + }; + }) => ReactNode; + + render( +
+ {CellComponent({ + row: { + id: "0", + original: group, + toggleSelected, + }, + })} +
, + ); + + return { toggleSelected }; +} + // --------------------------------------------------------------------------- // Fix 5: Accessibility —

→ + ), +})); + +vi.mock("@/components/shadcn/info-field/info-field", () => ({ + InfoField: () => null, +})); + +vi.mock("@/components/shadcn/spinner/spinner", () => ({ + Spinner: () => null, +})); + +vi.mock("@/components/ui/entities", () => ({ + DateWithTime: () => null, +})); + +vi.mock("@/components/ui/entities/entity-info", () => ({ + EntityInfo: ({ + entityAlias, + entityId, + }: { + entityAlias?: string; + entityId?: string; + }) => ( +

+ {entityAlias} + {entityId} +
+ ), +})); + +vi.mock("@/components/ui/table", () => ({ + SeverityBadge: ({ severity }: { severity: string }) => ( + {severity} + ), +})); + +vi.mock("@/components/ui/table/data-table-column-header", () => ({ + DataTableColumnHeader: ({ title }: { title: string }) => {title}, +})); + +vi.mock("@/components/ui/table/status-finding-badge", () => ({ + StatusFindingBadge: ({ status }: { status: string }) => {status}, +})); + +vi.mock("@/lib/date-utils", () => ({ + getFailingForLabel: () => "2d", +})); + +const notificationIndicatorMock = vi.fn((_props: unknown) => null); + +vi.mock("./notification-indicator", () => ({ + NotificationIndicator: (props: unknown) => { + notificationIndicatorMock(props); + return null; + }, +})); + +import type { FindingResourceRow } from "@/types"; + +import { getColumnFindingResources } from "./column-finding-resources"; + +function makeResource( + overrides?: Partial, +): FindingResourceRow { + return { + id: "resource-row-1", + rowType: "resource", + findingId: "finding-1", + checkId: "s3_check", + providerType: "aws", + providerAlias: "production", + providerUid: "123456789", + resourceName: "my-bucket", + resourceType: "bucket", + resourceGroup: "default", + resourceUid: "arn:aws:s3:::my-bucket", + service: "s3", + region: "us-east-1", + severity: "critical", + status: "FAIL", + delta: "new", + isMuted: false, + firstSeenAt: null, + lastSeenAt: "2024-01-01T00:00:00Z", + ...overrides, + }; +} + +describe("column-finding-resources", () => { + it("should pass delta to NotificationIndicator for resource rows", () => { + const columns = getColumnFindingResources({ + rowSelection: {}, + selectableRowCount: 1, + }); + + const selectColumn = columns.find( + (col) => (col as { id?: string }).id === "select", + ); + if (!selectColumn?.cell) { + throw new Error("select column not found"); + } + + const CellComponent = selectColumn.cell as (props: { + row: { + id: string; + original: FindingResourceRow; + toggleSelected: (selected: boolean) => void; + }; + }) => ReactNode; + + render( +
+ {CellComponent({ + row: { + id: "0", + original: makeResource(), + toggleSelected: vi.fn(), + }, + })} +
, + ); + + expect(screen.getByLabelText("Select resource")).toBeInTheDocument(); + expect(notificationIndicatorMock).toHaveBeenCalledWith( + expect.objectContaining({ + delta: "new", + isMuted: false, + }), + ); + }); + + it("should render the resource EntityInfo with resourceName as alias", () => { + const columns = getColumnFindingResources({ + rowSelection: {}, + selectableRowCount: 1, + }); + + const resourceColumn = columns.find( + (col) => (col as { id?: string }).id === "resource", + ); + if (!resourceColumn?.cell) { + throw new Error("resource column not found"); + } + + const CellComponent = resourceColumn.cell as (props: { + row: { original: FindingResourceRow }; + }) => ReactNode; + + render( +
+ {CellComponent({ + row: { + original: makeResource(), + }, + })} +
, + ); + + expect(screen.getByText("my-bucket")).toBeInTheDocument(); + expect(screen.getByText("arn:aws:s3:::my-bucket")).toBeInTheDocument(); + }); +}); diff --git a/ui/components/findings/table/column-finding-resources.tsx b/ui/components/findings/table/column-finding-resources.tsx index 98541a8316..50fbb52f20 100644 --- a/ui/components/findings/table/column-finding-resources.tsx +++ b/ui/components/findings/table/column-finding-resources.tsx @@ -25,11 +25,16 @@ import { import { getFailingForLabel } from "@/lib/date-utils"; import { FindingResourceRow } from "@/types"; +import { canMuteFindingResource } from "./finding-resource-selection"; import { FindingsSelectionContext } from "./findings-selection-context"; -import { NotificationIndicator } from "./notification-indicator"; +import { + type DeltaType, + NotificationIndicator, +} from "./notification-indicator"; const ResourceRowActions = ({ row }: { row: Row }) => { const resource = row.original; + const canMute = canMuteFindingResource(resource); const [isMuteModalOpen, setIsMuteModalOpen] = useState(false); const [isJiraModalOpen, setIsJiraModalOpen] = useState(false); const [resolvedIds, setResolvedIds] = useState([]); @@ -81,7 +86,7 @@ const ResourceRowActions = ({ row }: { row: Row }) => { return ( <> - {!resource.isMuted && ( + {canMute && ( }) => { ) } label={isResolving ? "Resolving..." : getMuteLabel()} - disabled={resource.isMuted || isResolving} + disabled={!canMute || isResolving} onSelect={handleMuteClick} /> (
@@ -178,7 +184,7 @@ export function getColumnFindingResources({ row.toggleSelected(checked === true)} onClick={(e) => e.stopPropagation()} aria-label="Select resource" @@ -198,7 +204,7 @@ export function getColumnFindingResources({
} - entityAlias={row.original.resourceGroup} + entityAlias={row.original.resourceName} entityId={row.original.resourceUid} />
@@ -213,8 +219,12 @@ export function getColumnFindingResources({ ), cell: ({ row }) => { const rawStatus = row.original.status; - const status = - rawStatus === "MUTED" ? "FAIL" : (rawStatus as FindingStatus); + const status: FindingStatus = + rawStatus === "MUTED" || rawStatus === "FAIL" + ? "FAIL" + : rawStatus === "PASS" + ? "PASS" + : "FAIL"; return ; }, enableSorting: false, diff --git a/ui/components/findings/table/finding-group-selection.test.ts b/ui/components/findings/table/finding-group-selection.test.ts new file mode 100644 index 0000000000..f00d17a73e --- /dev/null +++ b/ui/components/findings/table/finding-group-selection.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, it } from "vitest"; + +import { canMuteFindingGroup } from "./finding-group-selection"; + +describe("canMuteFindingGroup", () => { + it("returns false when impacted resources is zero", () => { + expect( + canMuteFindingGroup({ + resourcesFail: 0, + resourcesTotal: 2, + mutedCount: 0, + }), + ).toBe(false); + }); + + it("returns false when all resources are already muted", () => { + expect( + canMuteFindingGroup({ + resourcesFail: 3, + resourcesTotal: 3, + mutedCount: 3, + }), + ).toBe(false); + }); + + it("returns false when all failing resources are muted even if PASS resources exist", () => { + expect( + canMuteFindingGroup({ + resourcesFail: 2, + resourcesTotal: 5, + mutedCount: 2, + }), + ).toBe(false); + }); + + it("returns true when the group still has failing resources to mute", () => { + expect( + canMuteFindingGroup({ + resourcesFail: 2, + resourcesTotal: 5, + mutedCount: 1, + }), + ).toBe(true); + }); +}); diff --git a/ui/components/findings/table/finding-group-selection.ts b/ui/components/findings/table/finding-group-selection.ts new file mode 100644 index 0000000000..f9db1cf11e --- /dev/null +++ b/ui/components/findings/table/finding-group-selection.ts @@ -0,0 +1,13 @@ +interface FindingGroupSelectionState { + resourcesFail: number; + resourcesTotal: number; + mutedCount: number; +} + +export function canMuteFindingGroup({ + resourcesFail, + mutedCount, +}: FindingGroupSelectionState): boolean { + const allMuted = mutedCount > 0 && mutedCount === resourcesFail; + return resourcesFail > 0 && !allMuted; +} diff --git a/ui/components/findings/table/finding-resource-selection.test.ts b/ui/components/findings/table/finding-resource-selection.test.ts new file mode 100644 index 0000000000..8abb3f7a8a --- /dev/null +++ b/ui/components/findings/table/finding-resource-selection.test.ts @@ -0,0 +1,47 @@ +import { describe, expect, it } from "vitest"; + +import type { FindingResourceRow } from "@/types"; + +import { canMuteFindingResource } from "./finding-resource-selection"; + +function makeResource( + overrides?: Partial, +): FindingResourceRow { + return { + id: "finding-1", + rowType: "resource", + findingId: "finding-1", + checkId: "check-1", + providerType: "aws", + providerAlias: "prod", + providerUid: "123456789012", + resourceName: "bucket-a", + resourceType: "Bucket", + resourceGroup: "bucket-a", + resourceUid: "arn:aws:s3:::bucket-a", + service: "s3", + region: "us-east-1", + severity: "high", + status: "FAIL", + isMuted: false, + firstSeenAt: null, + lastSeenAt: null, + ...overrides, + }; +} + +describe("canMuteFindingResource", () => { + it("should allow muting FAIL resources that are not muted", () => { + expect(canMuteFindingResource(makeResource())).toBe(true); + }); + + it("should disable muting for PASS resources", () => { + expect(canMuteFindingResource(makeResource({ status: "PASS" }))).toBe( + false, + ); + }); + + it("should disable muting for already muted resources", () => { + expect(canMuteFindingResource(makeResource({ isMuted: true }))).toBe(false); + }); +}); diff --git a/ui/components/findings/table/finding-resource-selection.ts b/ui/components/findings/table/finding-resource-selection.ts new file mode 100644 index 0000000000..f6bfb3312a --- /dev/null +++ b/ui/components/findings/table/finding-resource-selection.ts @@ -0,0 +1,5 @@ +import { FindingResourceRow } from "@/types"; + +export function canMuteFindingResource(resource: FindingResourceRow): boolean { + return resource.status === "FAIL" && !resource.isMuted; +} diff --git a/ui/components/findings/table/findings-group-drill-down.tsx b/ui/components/findings/table/findings-group-drill-down.tsx index 3046c09a4a..53a568e759 100644 --- a/ui/components/findings/table/findings-group-drill-down.tsx +++ b/ui/components/findings/table/findings-group-drill-down.tsx @@ -28,6 +28,7 @@ import { FindingGroupRow, FindingResourceRow } from "@/types"; import { FloatingMuteButton } from "../floating-mute-button"; import { getColumnFindingResources } from "./column-finding-resources"; +import { canMuteFindingResource } from "./finding-resource-selection"; import { FindingsSelectionContext } from "./findings-selection-context"; import { ImpactedResourcesCell } from "./impacted-resources-cell"; import { DeltaValues, NotificationIndicator } from "./notification-indicator"; @@ -82,7 +83,7 @@ export function FindingsGroupDrillDown({ setIsLoading(loading); }; - const { sentinelRef, refresh, loadMore } = useInfiniteResources({ + const { sentinelRef, refresh, loadMore, totalCount } = useInfiniteResources({ checkId: group.checkId, hasDateOrScanFilter: hasDateOrScan, filters, @@ -95,7 +96,7 @@ export function FindingsGroupDrillDown({ const drawer = useResourceDetailDrawer({ resources, checkId: group.checkId, - totalResourceCount: group.resourcesTotal, + totalResourceCount: totalCount ?? group.resourcesTotal, onRequestMoreResources: loadMore, }); @@ -108,7 +109,7 @@ export function FindingsGroupDrillDown({ const selectedFindingIds = Object.keys(rowSelection) .filter((key) => rowSelection[key]) .map((idx) => resources[parseInt(idx)]?.findingId) - .filter(Boolean); + .filter((id): id is string => id !== null && id !== undefined && id !== ""); /** Converts resource_ids (display) → resourceUids → finding UUIDs via API. */ const resolveResourceIds = async (ids: string[]) => { @@ -124,10 +125,10 @@ export function FindingsGroupDrillDown({ }); }; - const selectableRowCount = resources.filter((r) => !r.isMuted).length; + const selectableRowCount = resources.filter(canMuteFindingResource).length; const getRowCanSelect = (row: Row): boolean => { - return !row.original.isMuted; + return canMuteFindingResource(row.original); }; const clearSelection = () => { diff --git a/ui/components/findings/table/findings-group-table.tsx b/ui/components/findings/table/findings-group-table.tsx index bcbc8934b0..3d1fd24882 100644 --- a/ui/components/findings/table/findings-group-table.tsx +++ b/ui/components/findings/table/findings-group-table.tsx @@ -14,6 +14,7 @@ import { FindingGroupRow, MetaDataProps } from "@/types"; import { FloatingMuteButton } from "../floating-mute-button"; import { getColumnFindingGroups } from "./column-finding-groups"; +import { canMuteFindingGroup } from "./finding-group-selection"; import { FindingsSelectionContext } from "./findings-selection-context"; import { InlineResourceContainer, @@ -88,13 +89,21 @@ export function FindingsGroupTable({ .filter(Boolean); // Count of selectable rows (groups where not ALL findings are muted) - const selectableRowCount = safeData.filter( - (g) => !(g.mutedCount > 0 && g.mutedCount === g.resourcesTotal), + const selectableRowCount = safeData.filter((g) => + canMuteFindingGroup({ + resourcesFail: g.resourcesFail, + resourcesTotal: g.resourcesTotal, + mutedCount: g.mutedCount, + }), ).length; const getRowCanSelect = (row: Row): boolean => { const group = row.original; - return !(group.mutedCount > 0 && group.mutedCount === group.resourcesTotal); + return canMuteFindingGroup({ + resourcesFail: group.resourcesFail, + resourcesTotal: group.resourcesTotal, + mutedCount: group.mutedCount, + }); }; const clearSelection = () => { @@ -136,8 +145,8 @@ export function FindingsGroupTable({ }; const handleDrillDown = (checkId: string, group: FindingGroupRow) => { - // No impacted resources → nothing to show, skip drill-down - if (group.resourcesFail === 0) return; + // No resources in the group → nothing to show, skip drill-down + if (group.resourcesTotal === 0) return; // Toggle: same group = collapse, different = switch if (expandedCheckId === checkId) { diff --git a/ui/components/findings/table/inline-resource-container.tsx b/ui/components/findings/table/inline-resource-container.tsx index e44c3db2de..9b85b58406 100644 --- a/ui/components/findings/table/inline-resource-container.tsx +++ b/ui/components/findings/table/inline-resource-container.tsx @@ -22,6 +22,7 @@ import { hasDateOrScanFilter } from "@/lib"; import { FindingGroupRow, FindingResourceRow } from "@/types"; import { getColumnFindingResources } from "./column-finding-resources"; +import { canMuteFindingResource } from "./finding-resource-selection"; import { FindingsSelectionContext } from "./findings-selection-context"; import { ResourceDetailDrawer, @@ -180,7 +181,7 @@ export function InlineResourceContainer({ setIsLoading(loading); }; - const { sentinelRef, refresh, loadMore } = useInfiniteResources({ + const { sentinelRef, refresh, loadMore, totalCount } = useInfiniteResources({ checkId: group.checkId, hasDateOrScanFilter: hasDateOrScan, filters, @@ -194,7 +195,7 @@ export function InlineResourceContainer({ const drawer = useResourceDetailDrawer({ resources, checkId: group.checkId, - totalResourceCount: group.resourcesTotal, + totalResourceCount: totalCount ?? group.resourcesTotal, onRequestMoreResources: loadMore, }); @@ -222,10 +223,10 @@ export function InlineResourceContainer({ }); }; - const selectableRowCount = resources.filter((r) => !r.isMuted).length; + const selectableRowCount = resources.filter(canMuteFindingResource).length; const getRowCanSelect = (row: Row): boolean => { - return !row.original.isMuted; + return canMuteFindingResource(row.original); }; const clearSelection = () => { diff --git a/ui/components/findings/table/resource-detail-drawer/resource-detail-drawer-content.test.tsx b/ui/components/findings/table/resource-detail-drawer/resource-detail-drawer-content.test.tsx index d3e588dd9e..3835242e19 100644 --- a/ui/components/findings/table/resource-detail-drawer/resource-detail-drawer-content.test.tsx +++ b/ui/components/findings/table/resource-detail-drawer/resource-detail-drawer-content.test.tsx @@ -1,6 +1,7 @@ -import { render, screen } from "@testing-library/react"; +import { render, screen, within } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import type { ButtonHTMLAttributes, HTMLAttributes, ReactNode } from "react"; +import { createPortal } from "react-dom"; import { afterEach, describe, expect, it, vi } from "vitest"; // --------------------------------------------------------------------------- @@ -10,17 +11,17 @@ import { afterEach, describe, expect, it, vi } from "vitest"; const { mockGetComplianceIcon, mockGetCompliancesOverview, - mockRouterPush, + mockWindowOpen, mockSearchParamsState, } = vi.hoisted(() => ({ mockGetComplianceIcon: vi.fn((_: string) => null as string | null), mockGetCompliancesOverview: vi.fn(), - mockRouterPush: vi.fn(), + mockWindowOpen: vi.fn(), mockSearchParamsState: { value: "" }, })); vi.mock("next/navigation", () => ({ - useRouter: () => ({ push: mockRouterPush, refresh: vi.fn() }), + useRouter: () => ({ refresh: vi.fn() }), usePathname: () => "/findings", useSearchParams: () => new URLSearchParams(mockSearchParamsState.value), redirect: vi.fn(), @@ -104,10 +105,30 @@ vi.mock("@/components/shadcn/card/card", () => ({ })); vi.mock("@/components/shadcn/dropdown", () => ({ - ActionDropdown: ({ children }: { children: ReactNode }) => ( -
{children}
+ ActionDropdown: ({ + children, + ariaLabel, + }: { + children: ReactNode; + ariaLabel?: string; + }) => ( +
+ {children} +
+ ), + ActionDropdownItem: ({ + label, + disabled, + onSelect, + }: { + label: string; + disabled?: boolean; + onSelect?: () => void; + }) => ( + ), - ActionDropdownItem: () => null, })); vi.mock("@/components/shadcn/skeleton/skeleton", () => ({ @@ -125,7 +146,25 @@ vi.mock("@/components/shadcn/tooltip", () => ({ })); vi.mock("@/components/findings/mute-findings-modal", () => ({ - MuteFindingsModal: () => null, + MuteFindingsModal: ({ + isOpen, + findingIds, + onComplete, + }: { + isOpen: boolean; + findingIds: string[]; + onComplete?: () => void; + }) => + isOpen + ? globalThis.document?.body && + // Render into body to mirror the real modal portal behavior. + createPortal( + , + globalThis.document.body, + ) + : null, })); vi.mock("@/components/findings/send-to-jira-modal", () => ({ @@ -547,9 +586,14 @@ describe("ResourceDetailDrawerContent — compliance icon styling", () => { }); describe("ResourceDetailDrawerContent — compliance navigation", () => { + afterEach(() => { + vi.unstubAllGlobals(); + }); + it("should resolve the clicked framework against the selected scan and navigate to compliance detail", async () => { // Given const user = userEvent.setup(); + vi.stubGlobal("open", mockWindowOpen); mockSearchParamsState.value = "filter[scan__in]=scan-selected&filter[region__in]=eu-west-1"; mockGetCompliancesOverview.mockResolvedValue({ @@ -595,14 +639,17 @@ describe("ResourceDetailDrawerContent — compliance navigation", () => { expect(mockGetCompliancesOverview).toHaveBeenCalledWith({ scanId: "scan-selected", }); - expect(mockRouterPush).toHaveBeenCalledWith( + expect(mockWindowOpen).toHaveBeenCalledWith( "/compliance/PCI-DSS?complianceId=compliance-1&version=4.0&scanId=scan-selected&filter%5Bregion__in%5D=eu-west-1", + "_blank", + "noopener,noreferrer", ); }); it("should use the current finding scan when no scan filter is active", async () => { // Given const user = userEvent.setup(); + vi.stubGlobal("open", mockWindowOpen); mockGetCompliancesOverview.mockResolvedValue({ data: [ { @@ -662,8 +709,134 @@ describe("ResourceDetailDrawerContent — compliance navigation", () => { expect(mockGetCompliancesOverview).toHaveBeenCalledWith({ scanId: "scan-from-finding", }); - expect(mockRouterPush).toHaveBeenCalledWith( + expect(mockWindowOpen).toHaveBeenCalledWith( "/compliance/PCI-DSS?complianceId=compliance-2&version=4.0&scanId=scan-from-finding&scanData=%7B%22id%22%3A%22scan-from-finding%22%2C%22providerInfo%22%3A%7B%22provider%22%3A%22aws%22%2C%22alias%22%3A%22prod%22%2C%22uid%22%3A%22123456789%22%7D%2C%22attributes%22%3A%7B%22name%22%3A%22Nightly+scan%22%2C%22completed_at%22%3A%222026-03-30T10%3A05%3A00Z%22%7D%7D", + "_blank", + "noopener,noreferrer", + ); + }); + + it("should navigate when the finding framework is a short alias of the compliance overview framework", async () => { + // Given + const user = userEvent.setup(); + vi.stubGlobal("open", mockWindowOpen); + mockGetComplianceIcon.mockImplementation((framework: string) => + framework.toLowerCase().includes("kisa") ? "/kisa.svg" : null, + ); + mockGetCompliancesOverview.mockResolvedValue({ + data: [ + { + id: "compliance-kisa", + type: "compliance-overviews", + attributes: { + framework: "KISA-ISMS-P", + version: "1.0", + requirements_passed: 5, + requirements_failed: 1, + requirements_manual: 0, + total_requirements: 6, + }, + }, + ], + }); + const findingWithScan = { + ...mockFinding, + scan: { + id: "scan-from-finding", + name: "Nightly scan", + trigger: "manual", + state: "completed", + uniqueResourceCount: 25, + progress: 100, + duration: 300, + startedAt: "2026-03-30T10:00:00Z", + completedAt: "2026-03-30T10:05:00Z", + insertedAt: "2026-03-30T09:59:00Z", + scheduledAt: null, + }, + }; + + render( + , + ); + + // When + await user.click( + screen.getByRole("button", { + name: "Open KISA compliance details", + }), + ); + + // Then + expect(mockGetCompliancesOverview).toHaveBeenCalledWith({ + scanId: "scan-from-finding", + }); + expect(mockWindowOpen).toHaveBeenCalledWith( + "/compliance/KISA-ISMS-P?complianceId=compliance-kisa&version=1.0&scanId=scan-from-finding&scanData=%7B%22id%22%3A%22scan-from-finding%22%2C%22providerInfo%22%3A%7B%22provider%22%3A%22aws%22%2C%22alias%22%3A%22prod%22%2C%22uid%22%3A%22123456789%22%7D%2C%22attributes%22%3A%7B%22name%22%3A%22Nightly+scan%22%2C%22completed_at%22%3A%222026-03-30T10%3A05%3A00Z%22%7D%7D", + "_blank", + "noopener,noreferrer", ); }); }); + +describe("ResourceDetailDrawerContent — other findings mute refresh", () => { + it("should update only the muted other-finding row without refreshing the current finding group", async () => { + // Given + const user = userEvent.setup(); + const onMuteComplete = vi.fn(); + const otherFinding: ResourceDrawerFinding = { + ...mockFinding, + id: "finding-2", + uid: "uid-2", + checkId: "ec2_check", + checkTitle: "EC2 Check", + updatedAt: "2026-03-30T10:05:00Z", + }; + + render( + , + ); + + // When + const row = screen.getByText("EC2 Check").closest("tr"); + expect(row).not.toBeNull(); + + await user.click( + within(row as HTMLElement).getByRole("button", { name: "Mute" }), + ); + await user.click( + screen.getByRole("button", { name: "Confirm mute finding-2" }), + ); + + // Then + expect( + within(row as HTMLElement).getByRole("button", { name: "Muted" }), + ).toBeDisabled(); + expect(onMuteComplete).not.toHaveBeenCalled(); + }); +}); diff --git a/ui/components/findings/table/resource-detail-drawer/resource-detail-drawer-content.tsx b/ui/components/findings/table/resource-detail-drawer/resource-detail-drawer-content.tsx index 509f128b97..09bf786f07 100644 --- a/ui/components/findings/table/resource-detail-drawer/resource-detail-drawer-content.tsx +++ b/ui/components/findings/table/resource-detail-drawer/resource-detail-drawer-content.tsx @@ -12,7 +12,7 @@ import { } from "lucide-react"; import Image from "next/image"; import Link from "next/link"; -import { useRouter, useSearchParams } from "next/navigation"; +import { useSearchParams } from "next/navigation"; import { useState } from "react"; import { getCompliancesOverview } from "@/actions/compliances"; @@ -84,7 +84,90 @@ function normalizeComplianceFrameworkName(framework: string): string { return framework .trim() .toLowerCase() - .replace(/[\s_]+/g, "-"); + .replace(/[\s_]+/g, "-") + .replace(/-+/g, "-"); +} + +function stripComplianceVersionSuffix(framework: string): string { + return framework.replace(/-\d+(?:\.\d+)*$/g, ""); +} + +function canonicalComplianceKey(framework: string): string { + return stripComplianceVersionSuffix( + normalizeComplianceFrameworkName(framework), + ) + .replace(/[^a-z0-9]+/g, "") + .trim(); +} + +function complianceTokens(framework: string): string[] { + return stripComplianceVersionSuffix( + normalizeComplianceFrameworkName(framework), + ) + .split("-") + .map((token) => token.trim()) + .filter(Boolean) + .filter((token) => !/^\d+(?:\.\d+)*$/.test(token)); +} + +function complianceMatchScore( + sourceFramework: string, + targetFramework: string, +): number { + const normalizedSource = normalizeComplianceFrameworkName(sourceFramework); + const normalizedTarget = normalizeComplianceFrameworkName(targetFramework); + + if (normalizedSource === normalizedTarget) { + return 5; + } + + const canonicalSource = canonicalComplianceKey(sourceFramework); + const canonicalTarget = canonicalComplianceKey(targetFramework); + + if (canonicalSource === canonicalTarget) { + return 4; + } + + if (canonicalSource && canonicalTarget) { + const sourceTokens = canonicalSource.split("-"); + const targetTokens = canonicalTarget.split("-"); + if ( + sourceTokens.length !== targetTokens.length && + (sourceTokens.every((t) => targetTokens.includes(t)) || + targetTokens.every((t) => sourceTokens.includes(t))) + ) { + return 3; + } + } + + const sourceTokens = complianceTokens(sourceFramework); + const targetTokens = complianceTokens(targetFramework); + if (!sourceTokens.length || !targetTokens.length) { + return 0; + } + + const sourceMatchesTarget = sourceTokens.every((token) => + targetTokens.includes(token), + ); + const targetMatchesSource = targetTokens.every((token) => + sourceTokens.includes(token), + ); + + if (sourceMatchesTarget || targetMatchesSource) { + return 2; + } + + if ( + sourceTokens.some((token) => targetTokens.includes(token)) && + canonicalSource && + canonicalTarget && + (canonicalTarget.includes(canonicalSource) || + canonicalSource.includes(canonicalTarget)) + ) { + return 1; + } + + return 0; } function parseSelectedScanIds(scanFilterValue: string | null): string[] { @@ -110,12 +193,13 @@ function resolveComplianceMatch( return null; } - const normalizedFramework = normalizeComplianceFrameworkName(framework); - const match = compliances.find( - (compliance) => - normalizeComplianceFrameworkName(compliance.attributes.framework) === - normalizedFramework, - ); + const match = compliances + .map((compliance) => ({ + compliance, + score: complianceMatchScore(framework, compliance.attributes.framework), + })) + .filter(({ score }) => score > 0) + .sort((a, b) => b.score - a.score)[0]?.compliance; if (!match) { return null; @@ -202,13 +286,15 @@ export function ResourceDetailDrawerContent({ onNavigateNext, onMuteComplete, }: ResourceDetailDrawerContentProps) { - const router = useRouter(); const searchParams = useSearchParams(); const [isMuteModalOpen, setIsMuteModalOpen] = useState(false); const [isJiraModalOpen, setIsJiraModalOpen] = useState(false); const [resolvingFramework, setResolvingFramework] = useState( null, ); + const [optimisticallyMutedIds, setOptimisticallyMutedIds] = useState< + Set + >(new Set()); // Initial load — no check metadata yet if (!checkMeta && isLoading) { @@ -284,7 +370,7 @@ export function ResourceDetailDrawerContent({ return; } - router.push( + window.open( buildComplianceDetailHref({ complianceId: complianceMatch.complianceId, framework: complianceMatch.framework, @@ -294,6 +380,8 @@ export function ResourceDetailDrawerContent({ currentFinding: f, includeScanData: f?.scan?.id === complianceScanId, }), + "_blank", + "noopener,noreferrer", ); } catch (error) { console.error("Error resolving compliance detail:", error); @@ -428,10 +516,10 @@ export function ResourceDetailDrawerContent({ )}
- {/* Navigation: "Impacted Resource (X of N)" */} + {/* Navigation: "Resource (X of N)" */}
- Impacted Resource + Resource {currentIndex + 1} of {totalResources} @@ -477,7 +565,7 @@ export function ResourceDetailDrawerContent({ /> } - entityAlias={f.resourceGroup} + entityAlias={f.resourceName} entityId={f.resourceUid} idLabel="UID" /> @@ -505,7 +593,9 @@ export function ResourceDetailDrawerContent({ {getFailingForLabel(f.firstSeenAt) || "-"} -
+ + {f.resourceGroup || "-"} + {/* Row 3: IDs */} @@ -529,6 +619,11 @@ export function ResourceDetailDrawerContent({ className="max-w-full text-sm" /> + + {/* Row 4: Resource metadata */} + + {f.resourceType || "-"} +
{/* Actions button — fixed size, aligned with row 1 */} @@ -757,10 +852,7 @@ export function ResourceDetailDrawerContent({
) : ( <> -
-

- Failed Findings For This Resource -

+
{otherFindings.length} Total Entries @@ -796,7 +888,18 @@ export function ResourceDetailDrawerContent({ {otherFindings.length > 0 ? ( otherFindings.map((finding) => ( - + + setOptimisticallyMutedIds((prev) => + new Set(prev).add(finding.id), + ) + } + /> )) ) : ( @@ -908,19 +1011,32 @@ export function ResourceDetailDrawerContent({ ); } -function OtherFindingRow({ finding }: { finding: ResourceDrawerFinding }) { +function OtherFindingRow({ + finding, + isOptimisticallyMuted, + onMuted, +}: { + finding: ResourceDrawerFinding; + isOptimisticallyMuted: boolean; + onMuted: () => void; +}) { const [isMuteModalOpen, setIsMuteModalOpen] = useState(false); const [isJiraModalOpen, setIsJiraModalOpen] = useState(false); + const isMuted = finding.isMuted || isOptimisticallyMuted; const findingUrl = `/findings?filter%5Bcheck_id__in%5D=${encodeURIComponent(finding.checkId)}&filter%5Bmuted%5D=include`; return ( <> - {!finding.isMuted && ( + {!isMuted && ( { + setIsMuteModalOpen(false); + onMuted(); + }} /> )} window.open(findingUrl, "_blank", "noopener,noreferrer")} > - + @@ -955,14 +1071,14 @@ function OtherFindingRow({ finding }: { finding: ResourceDrawerFinding }) { ) : ( ) } - label={finding.isMuted ? "Muted" : "Mute"} - disabled={finding.isMuted} + label={isMuted ? "Muted" : "Mute"} + disabled={isMuted} onSelect={() => setIsMuteModalOpen(true)} /> ({ + Skeleton: ({ className }: { className?: string }) => ( +
+ ), +})); + +import { ResourceDetailSkeleton } from "./resource-detail-skeleton"; + +describe("ResourceDetailSkeleton", () => { + it("should include placeholders for group and resource type fields", () => { + render(); + + const blocks = screen.getAllByTestId("skeleton-block"); + const classes = blocks.map( + (block) => block.getAttribute("data-class") ?? "", + ); + + expect(classes).toContain("h-3.5 w-10 rounded"); + expect(classes).toContain("h-5 w-18 rounded"); + expect(classes).toContain("h-3.5 w-20 rounded"); + expect(classes).toContain("h-5 w-28 rounded"); + }); +}); diff --git a/ui/components/findings/table/resource-detail-drawer/resource-detail-skeleton.tsx b/ui/components/findings/table/resource-detail-drawer/resource-detail-skeleton.tsx index ed123983f5..9ef08ee14e 100644 --- a/ui/components/findings/table/resource-detail-drawer/resource-detail-skeleton.tsx +++ b/ui/components/findings/table/resource-detail-drawer/resource-detail-skeleton.tsx @@ -2,8 +2,8 @@ import { Skeleton } from "@/components/shadcn/skeleton/skeleton"; /** * Skeleton placeholder for the resource info grid in the detail drawer. - * Mirrors the 4-column layout: EntityInfo × 2, InfoField × 2 per row, - * plus the actions button. + * Mirrors the drawer layout so added metadata fields don't leave visual gaps + * while the next resource is loading. */ export function ResourceDetailSkeleton() { return ( @@ -15,16 +15,19 @@ export function ResourceDetailSkeleton() { - {/* Row 2: Last detected, First seen, Failing for */} + {/* Row 2: Last detected, First seen, Failing for, Group */} -
+ {/* Row 3: Check ID, Finding ID, Finding UID */} + + {/* Row 4: Resource type */} +
{/* Actions button */} diff --git a/ui/components/findings/table/resource-detail-drawer/use-resource-detail-drawer.test.ts b/ui/components/findings/table/resource-detail-drawer/use-resource-detail-drawer.test.ts index 404be9b165..4ce921a4e8 100644 --- a/ui/components/findings/table/resource-detail-drawer/use-resource-detail-drawer.test.ts +++ b/ui/components/findings/table/resource-detail-drawer/use-resource-detail-drawer.test.ts @@ -26,6 +26,7 @@ vi.mock("next/navigation", () => ({ // Import after mocks // --------------------------------------------------------------------------- +import type { ResourceDrawerFinding } from "@/actions/findings"; import type { FindingResourceRow } from "@/types"; import { useResourceDetailDrawer } from "./use-resource-detail-drawer"; @@ -60,6 +61,46 @@ function makeResource( } as FindingResourceRow; } +function makeDrawerFinding( + overrides?: Partial, +): ResourceDrawerFinding { + return { + id: "finding-1", + uid: "uid-1", + checkId: "s3_check", + checkTitle: "S3 Check", + status: "FAIL", + severity: "high", + delta: null, + isMuted: false, + mutedReason: null, + firstSeenAt: null, + updatedAt: null, + resourceId: "resource-1", + resourceUid: "arn:aws:s3:::my-bucket", + resourceName: "my-bucket", + resourceService: "s3", + resourceRegion: "us-east-1", + resourceType: "bucket", + resourceGroup: "default", + providerType: "aws", + providerAlias: "prod", + providerUid: "123", + risk: "high", + description: "desc", + statusExtended: "status", + complianceFrameworks: [], + categories: [], + remediation: { + recommendation: { text: "", url: "" }, + code: { cli: "", other: "", nativeiac: "", terraform: "" }, + }, + additionalUrls: [], + scan: null, + ...overrides, + }; +} + // --------------------------------------------------------------------------- // Fix 2: AbortController cleanup on unmount // --------------------------------------------------------------------------- @@ -128,3 +169,212 @@ describe("useResourceDetailDrawer — unmount cleanup", () => { expect(abortSpy).not.toHaveBeenCalled(); }); }); + +describe("useResourceDetailDrawer — other findings filtering", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("should exclude the current finding from otherFindings and preserve API order", async () => { + const resources = [makeResource()]; + + getLatestFindingsByResourceUidMock.mockResolvedValue({ data: [] }); + adaptFindingsByResourceResponseMock.mockReturnValue([ + makeDrawerFinding({ + id: "current", + checkId: "s3_check", + checkTitle: "Current", + status: "FAIL", + severity: "critical", + }), + makeDrawerFinding({ + id: "other-1", + checkId: "check-other-1", + checkTitle: "Other 1", + status: "PASS", + severity: "critical", + }), + makeDrawerFinding({ + id: "other-2", + checkId: "check-other-2", + checkTitle: "Other 2", + status: "FAIL", + severity: "medium", + }), + ]); + + const { result } = renderHook(() => + useResourceDetailDrawer({ + resources, + checkId: "s3_check", + }), + ); + + await act(async () => { + result.current.openDrawer(0); + await Promise.resolve(); + }); + + expect(result.current.otherFindings.map((finding) => finding.id)).toEqual([ + "other-1", + "other-2", + ]); + }); + + it("should keep isNavigating true for a cached resource long enough to render skeletons", async () => { + vi.useFakeTimers(); + + const resources = [ + makeResource({ + id: "row-1", + findingId: "finding-1", + resourceUid: "arn:aws:s3:::first-bucket", + resourceName: "first-bucket", + }), + makeResource({ + id: "row-2", + findingId: "finding-2", + resourceUid: "arn:aws:s3:::second-bucket", + resourceName: "second-bucket", + }), + ]; + + getLatestFindingsByResourceUidMock.mockImplementation( + async ({ resourceUid }: { resourceUid: string }) => ({ + data: [resourceUid], + }), + ); + adaptFindingsByResourceResponseMock.mockImplementation( + (response: { data: string[] }) => [ + makeDrawerFinding({ + id: response.data[0].includes("first") ? "finding-1" : "finding-2", + resourceUid: response.data[0], + resourceName: response.data[0].includes("first") + ? "first-bucket" + : "second-bucket", + }), + ], + ); + + const { result } = renderHook(() => + useResourceDetailDrawer({ + resources, + checkId: "s3_check", + }), + ); + + await act(async () => { + result.current.openDrawer(0); + await Promise.resolve(); + }); + + await act(async () => { + result.current.navigateNext(); + await Promise.resolve(); + }); + + expect(result.current.currentIndex).toBe(1); + expect(result.current.currentFinding?.id).toBe("finding-2"); + + act(() => { + result.current.navigatePrev(); + }); + + expect(result.current.currentIndex).toBe(0); + expect(result.current.isNavigating).toBe(true); + + await act(async () => { + vi.runAllTimers(); + await Promise.resolve(); + }); + + expect(result.current.isNavigating).toBe(false); + expect(result.current.currentFinding?.id).toBe("finding-1"); + + vi.useRealTimers(); + }); + + it("should keep isNavigating true for a fast uncached navigation long enough to avoid flicker", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date("2026-04-08T15:00:00.000Z")); + + const resources = [ + makeResource({ + id: "row-1", + findingId: "finding-1", + resourceUid: "arn:aws:s3:::first-bucket", + resourceName: "first-bucket", + }), + makeResource({ + id: "row-2", + findingId: "finding-2", + resourceUid: "arn:aws:s3:::second-bucket", + resourceName: "second-bucket", + }), + ]; + + getLatestFindingsByResourceUidMock.mockImplementation( + async ({ resourceUid }: { resourceUid: string }) => ({ + data: [resourceUid], + }), + ); + adaptFindingsByResourceResponseMock.mockImplementation( + (response: { data: string[] }) => [ + makeDrawerFinding({ + id: response.data[0].includes("first") ? "finding-1" : "finding-2", + resourceUid: response.data[0], + resourceName: response.data[0].includes("first") + ? "first-bucket" + : "second-bucket", + }), + ], + ); + + const { result } = renderHook(() => + useResourceDetailDrawer({ + resources, + checkId: "s3_check", + }), + ); + + await act(async () => { + result.current.openDrawer(0); + await Promise.resolve(); + }); + + act(() => { + result.current.navigateNext(); + }); + + expect(result.current.currentIndex).toBe(1); + expect(result.current.isNavigating).toBe(true); + + await act(async () => { + await Promise.resolve(); + }); + + expect(result.current.currentFinding?.id).toBe("finding-2"); + expect(result.current.isNavigating).toBe(true); + + await act(async () => { + vi.advanceTimersByTime(119); + await Promise.resolve(); + }); + + expect(result.current.isNavigating).toBe(true); + + await act(async () => { + vi.advanceTimersByTime(1); + await Promise.resolve(); + }); + + await act(async () => { + vi.runOnlyPendingTimers(); + await Promise.resolve(); + }); + + expect(result.current.isNavigating).toBe(false); + + vi.useRealTimers(); + }); +}); diff --git a/ui/components/findings/table/resource-detail-drawer/use-resource-detail-drawer.ts b/ui/components/findings/table/resource-detail-drawer/use-resource-detail-drawer.ts index d1d9eb96d3..c9bf263dc0 100644 --- a/ui/components/findings/table/resource-detail-drawer/use-resource-detail-drawer.ts +++ b/ui/components/findings/table/resource-detail-drawer/use-resource-detail-drawer.ts @@ -9,6 +9,10 @@ import { } from "@/actions/findings"; import { FindingResourceRow } from "@/types"; +// Keep fast carousel navigations in a loading state for one short beat so +// React doesn't batch away the skeleton frame when switching resources. +const MIN_NAVIGATION_SKELETON_MS = 300; + /** * Check-level metadata that is identical across all resources for a given check. * Extracted once on first successful fetch and kept stable during navigation. @@ -83,18 +87,65 @@ export function useResourceDetailDrawer({ const cacheRef = useRef>(new Map()); const checkMetaRef = useRef(null); const fetchControllerRef = useRef(null); + const navigationTimeoutRef = useRef | null>( + null, + ); + const navigationStartedAtRef = useRef(null); + + const clearNavigationTimeout = () => { + if (navigationTimeoutRef.current !== null) { + clearTimeout(navigationTimeoutRef.current); + navigationTimeoutRef.current = null; + } + }; + + const finishNavigation = () => { + clearNavigationTimeout(); + setIsLoading(false); + + const navigationStartedAt = navigationStartedAtRef.current; + if (navigationStartedAt === null) { + navigationStartedAtRef.current = null; + setIsNavigating(false); + return; + } + + const elapsed = Date.now() - navigationStartedAt; + const remaining = Math.max(0, MIN_NAVIGATION_SKELETON_MS - elapsed); + + if (remaining === 0) { + navigationStartedAtRef.current = null; + setIsNavigating(false); + return; + } + + navigationTimeoutRef.current = setTimeout(() => { + setIsNavigating(false); + navigationStartedAtRef.current = null; + navigationTimeoutRef.current = null; + }, remaining); + }; + + const startNavigation = () => { + clearNavigationTimeout(); + navigationStartedAtRef.current = Date.now(); + setIsNavigating(true); + }; // Abort any in-flight request on unmount to prevent state updates // on an already-unmounted component. useEffect(() => { return () => { fetchControllerRef.current?.abort(); + clearNavigationTimeout(); + navigationStartedAtRef.current = null; }; }, []); const fetchFindings = async (resourceUid: string) => { // Abort any in-flight request to prevent stale data from out-of-order responses fetchControllerRef.current?.abort(); + clearNavigationTimeout(); const controller = new AbortController(); fetchControllerRef.current = controller; @@ -106,8 +157,7 @@ export function useResourceDetailDrawer({ if (main) checkMetaRef.current = extractCheckMeta(main); } setFindings(cached); - setIsLoading(false); - setIsNavigating(false); + finishNavigation(); return; } @@ -135,8 +185,7 @@ export function useResourceDetailDrawer({ } } finally { if (!controller.signal.aborted) { - setIsLoading(false); - setIsNavigating(false); + finishNavigation(); } } }; @@ -145,8 +194,11 @@ export function useResourceDetailDrawer({ const resource = resources[index]; if (!resource) return; + clearNavigationTimeout(); + navigationStartedAtRef.current = null; setCurrentIndex(index); setIsOpen(true); + setIsNavigating(false); setFindings([]); fetchFindings(resource.resourceUid); }; @@ -159,7 +211,7 @@ export function useResourceDetailDrawer({ const resource = resources[currentIndex]; if (!resource) return; cacheRef.current.delete(resource.resourceUid); - setIsNavigating(true); + startNavigation(); fetchFindings(resource.resourceUid); }; @@ -168,7 +220,7 @@ export function useResourceDetailDrawer({ if (!resource) return; setCurrentIndex(index); - setIsNavigating(true); + startNavigation(); fetchFindings(resource.resourceUid); }; diff --git a/ui/components/invitations/invitation-details.tsx b/ui/components/invitations/invitation-details.tsx index 0df447907f..0cc8e2b919 100644 --- a/ui/components/invitations/invitation-details.tsx +++ b/ui/components/invitations/invitation-details.tsx @@ -53,7 +53,7 @@ export const InvitationDetails = ({ attributes }: InvitationDetailsProps) => { ? window.location.origin : "http://localhost:3000"; - const invitationLink = `${baseUrl}/sign-up?invitation_token=${attributes.token}`; + const invitationLink = `${baseUrl}/invitation/accept?invitation_token=${attributes.token}`; return (
diff --git a/ui/components/scans/scans-filters.tsx b/ui/components/scans/scans-filters.tsx index 6273acac77..60dbca0bfc 100644 --- a/ui/components/scans/scans-filters.tsx +++ b/ui/components/scans/scans-filters.tsx @@ -3,20 +3,23 @@ import { X } from "lucide-react"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; +import { ScanSelector } from "@/components/compliance/compliance-header"; import { filterScans } from "@/components/filters/data-filters"; import { FilterControls } from "@/components/filters/filter-controls"; import { Badge } from "@/components/shadcn/badge/badge"; import { useRelatedFilters } from "@/hooks"; -import { FilterEntity, FilterType } from "@/types"; +import { ExpandedScanData, FilterEntity, FilterType } from "@/types"; interface ScansFiltersProps { providerUIDs: string[]; providerDetails: { [uid: string]: FilterEntity }[]; + completedScans?: ExpandedScanData[]; } export const ScansFilters = ({ providerUIDs, providerDetails, + completedScans = [], }: ScansFiltersProps) => { const router = useRouter(); const pathname = usePathname(); @@ -36,24 +39,50 @@ export const ScansFilters = ({ router.push(`${pathname}?${params.toString()}`); }; - const scanIdChip = idFilter ? ( -
- - Scan: - {idFilter} + const handleScanChange = (selectedScanId: string) => { + const params = new URLSearchParams(searchParams.toString()); + params.set("filter[id__in]", selectedScanId); + router.push(`${pathname}?${params.toString()}`); + }; + + const scanIdElement = idFilter ? ( + completedScans.length > 0 ? ( +
+ - -
+
+ ) : ( +
+ + + Scan: + + {idFilter} + + +
+ ) ) : null; return ( @@ -68,7 +97,7 @@ export const ScansFilters = ({ index: 1, }, ]} - prependElement={scanIdChip} + prependElement={scanIdElement} /> ); }; diff --git a/ui/components/shadcn/card/card.tsx b/ui/components/shadcn/card/card.tsx index 8226ae698f..7c60a51f5f 100644 --- a/ui/components/shadcn/card/card.tsx +++ b/ui/components/shadcn/card/card.tsx @@ -20,7 +20,7 @@ const cardVariants = cva("flex flex-col gap-6 rounded-xl border", { inner: "rounded-[12px] backdrop-blur-[46px] border-border-neutral-tertiary bg-bg-neutral-tertiary", danger: - "gap-1 rounded-[12px] border-border-error-primary bg-bg-fail-secondary", + "gap-1 rounded-[12px] border-[rgba(67,34,50,0.5)] bg-[rgba(67,34,50,0.2)] dark:border-[rgba(67,34,50,0.7)] dark:bg-[rgba(67,34,50,0.3)]", }, padding: { default: "", diff --git a/ui/components/ui/entities/date-with-time.tsx b/ui/components/ui/entities/date-with-time.tsx index fd43fdbd5a..90a801e9ed 100644 --- a/ui/components/ui/entities/date-with-time.tsx +++ b/ui/components/ui/entities/date-with-time.tsx @@ -1,5 +1,10 @@ import { format, parseISO } from "date-fns"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/shadcn/tooltip"; import { cn } from "@/lib/utils"; interface DateWithTimeProps { @@ -33,25 +38,52 @@ export const DateWithTime = ({ ?.substring(0, 3) .toUpperCase() || ""; - return ( + const fullText = showTime + ? `${formattedDate} ${formattedTime} ${timezone}` + : formattedDate; + + const content = (
- + {formattedDate} {showTime && ( - + {formattedTime} {timezone} )}
); + + if (inline) { + return ( + + +
{content}
+
+ {fullText} +
+ ); + } + + return content; } catch { return -; } diff --git a/ui/hooks/use-infinite-resources.test.ts b/ui/hooks/use-infinite-resources.test.ts index 49ab0f9105..618de56fba 100644 --- a/ui/hooks/use-infinite-resources.test.ts +++ b/ui/hooks/use-infinite-resources.test.ts @@ -163,6 +163,38 @@ describe("useInfiniteResources", () => { findingGroupActionsMock.getLatestFindingGroupResources, ).not.toHaveBeenCalled(); }); + + it("should forward the active finding-group filters to the resources endpoint", async () => { + // Given + const apiResponse = makeApiResponse([], { pages: 1 }); + const filters = { + "filter[status__in]": "PASS", + "filter[severity__in]": "medium", + "filter[provider_type__in]": "aws", + }; + findingGroupActionsMock.getLatestFindingGroupResources.mockResolvedValue( + apiResponse, + ); + findingGroupActionsMock.adaptFindingGroupResourcesResponse.mockReturnValue( + [], + ); + + // When + renderHook(() => useInfiniteResources(defaultOptions({ filters }))); + await flushAsync(); + + // Then + expect( + findingGroupActionsMock.getLatestFindingGroupResources, + ).toHaveBeenCalledWith( + expect.objectContaining({ + checkId: "check_1", + page: 1, + pageSize: 10, + filters, + }), + ); + }); }); describe("when all resources fit in one page", () => { diff --git a/ui/hooks/use-infinite-resources.ts b/ui/hooks/use-infinite-resources.ts index fc720dd9f7..df710ebf10 100644 --- a/ui/hooks/use-infinite-resources.ts +++ b/ui/hooks/use-infinite-resources.ts @@ -32,6 +32,8 @@ interface UseInfiniteResourcesReturn { refresh: () => void; /** Imperatively load the next page (e.g. from drawer navigation). */ loadMore: () => void; + /** Total number of resources matching current filters (from API pagination). */ + totalCount: number | null; } /** @@ -60,6 +62,7 @@ export function useInfiniteResources({ const currentCheckIdRef = useRef(checkId); const controllerRef = useRef(null); const observerRef = useRef(null); + const totalCountRef = useRef(null); // Store latest values in refs so the fetch function always reads current values // without being recreated on every render @@ -70,6 +73,7 @@ export function useInfiniteResources({ const onSetLoadingRef = useRef(onSetLoading); // Keep refs in sync with latest props + currentCheckIdRef.current = checkId; hasDateOrScanRef.current = hasDateOrScanFilter; filtersRef.current = filters; onSetResourcesRef.current = onSetResources; @@ -110,6 +114,7 @@ export function useInfiniteResources({ ); const totalPages = response?.meta?.pagination?.pages ?? 1; const hasMore = page < totalPages; + totalCountRef.current = response?.meta?.pagination?.count ?? null; // Commit the page number only after a successful (non-aborted) fetch. // This prevents a premature pageRef increment from loadNextPage being @@ -209,5 +214,10 @@ export function useInfiniteResources({ fetchPage(1, false, currentCheckIdRef.current, controller.signal); } - return { sentinelRef, refresh, loadMore: loadNextPage }; + return { + sentinelRef, + refresh, + loadMore: loadNextPage, + totalCount: totalCountRef.current, + }; } diff --git a/ui/lib/findings-scan-filters.test.ts b/ui/lib/findings-scan-filters.test.ts new file mode 100644 index 0000000000..fcf32b507d --- /dev/null +++ b/ui/lib/findings-scan-filters.test.ts @@ -0,0 +1,112 @@ +import { describe, expect, it, vi } from "vitest"; + +import { + buildFindingScanDateFilters, + resolveFindingScanDateFilters, +} from "./findings-scan-filters"; + +describe("buildFindingScanDateFilters", () => { + it("uses an exact inserted_at filter when all selected scans belong to the same day", () => { + expect( + buildFindingScanDateFilters([ + "2026-04-07T10:00:00Z", + "2026-04-07T18:30:00Z", + ]), + ).toEqual({ + "filter[inserted_at]": "2026-04-07", + }); + }); + + it("ignores whitespace-only date strings", () => { + expect(buildFindingScanDateFilters([" ", "2026-04-07T10:00:00Z"])).toEqual( + { + "filter[inserted_at]": "2026-04-07", + }, + ); + }); + + it("uses a date range when selected scans span multiple days", () => { + expect( + buildFindingScanDateFilters([ + "2026-04-03T10:00:00Z", + "2026-04-07T18:30:00Z", + "2026-04-05T12:00:00Z", + ]), + ).toEqual({ + "filter[inserted_at__gte]": "2026-04-03", + "filter[inserted_at__lte]": "2026-04-07", + }); + }); +}); + +describe("resolveFindingScanDateFilters", () => { + it("adds the required inserted_at filter for a selected scan when the URL only contains scan__in", async () => { + const result = await resolveFindingScanDateFilters({ + filters: { + "filter[muted]": "false", + "filter[scan__in]": "scan-1", + }, + scans: [ + { + id: "scan-1", + attributes: { + inserted_at: "2026-04-07T10:00:00Z", + }, + }, + ], + loadScan: vi.fn(), + }); + + expect(result).toEqual({ + "filter[muted]": "false", + "filter[scan__in]": "scan-1", + "filter[inserted_at]": "2026-04-07", + }); + }); + + it("fetches missing scan details when the selected scan is not present in the prefetched scans list", async () => { + const loadScan = vi.fn().mockResolvedValue({ + id: "scan-2", + attributes: { + inserted_at: "2026-04-05T08:00:00Z", + }, + }); + + const result = await resolveFindingScanDateFilters({ + filters: { + "filter[scan__in]": "scan-2", + }, + scans: [], + loadScan, + }); + + expect(loadScan).toHaveBeenCalledWith("scan-2"); + expect(result).toEqual({ + "filter[scan__in]": "scan-2", + "filter[inserted_at]": "2026-04-05", + }); + }); + + it("does not override an explicit inserted_at filter already chosen in the frontend", async () => { + const result = await resolveFindingScanDateFilters({ + filters: { + "filter[scan__in]": "scan-1", + "filter[inserted_at__gte]": "2026-04-01", + }, + scans: [ + { + id: "scan-1", + attributes: { + inserted_at: "2026-04-07T10:00:00Z", + }, + }, + ], + loadScan: vi.fn(), + }); + + expect(result).toEqual({ + "filter[scan__in]": "scan-1", + "filter[inserted_at__gte]": "2026-04-01", + }); + }); +}); diff --git a/ui/lib/findings-scan-filters.ts b/ui/lib/findings-scan-filters.ts new file mode 100644 index 0000000000..dfbb1bc44c --- /dev/null +++ b/ui/lib/findings-scan-filters.ts @@ -0,0 +1,99 @@ +interface ScanDateSource { + id: string; + attributes?: { + inserted_at?: string; + }; +} + +interface ResolveFindingScanDateFiltersOptions { + filters: Record; + scans: ScanDateSource[]; + loadScan: (scanId: string) => Promise; +} + +const INSERTED_AT_FILTER_KEYS = [ + "filter[inserted_at]", + "filter[inserted_at__date]", + "filter[inserted_at__gte]", + "filter[inserted_at__lte]", +] as const; + +function getScanFilterIds(filters: Record): string[] { + const scanIds = filters["filter[scan__in]"] || filters["filter[scan]"] || ""; + return Array.from(new Set(scanIds.split(",").filter(Boolean))); +} + +function formatScanDate(dateTime?: string): string | null { + if (!dateTime) return null; + const [date] = dateTime.split("T"); + return date?.trim() || null; +} + +function hasInsertedAtFilter(filters: Record): boolean { + return INSERTED_AT_FILTER_KEYS.some((key) => Boolean(filters[key])); +} + +export function buildFindingScanDateFilters( + scanInsertedAtValues: string[], +): Record { + const dates = Array.from( + new Set(scanInsertedAtValues.map(formatScanDate).filter(Boolean)), + ).sort() as string[]; + + if (dates.length === 0) { + return {}; + } + + if (dates.length === 1) { + return { + "filter[inserted_at]": dates[0], + }; + } + + return { + "filter[inserted_at__gte]": dates[0], + "filter[inserted_at__lte]": dates[dates.length - 1], + }; +} + +export async function resolveFindingScanDateFilters({ + filters, + scans, + loadScan, +}: ResolveFindingScanDateFiltersOptions): Promise> { + const scanIds = getScanFilterIds(filters); + + if (scanIds.length === 0 || hasInsertedAtFilter(filters)) { + return filters; + } + + const scansById = new Map(scans.map((scan) => [scan.id, scan])); + const missingScanIds = scanIds.filter((scanId) => !scansById.has(scanId)); + + if (missingScanIds.length > 0) { + const missingScans = await Promise.all( + missingScanIds.map((scanId) => loadScan(scanId)), + ); + + missingScans.forEach((scan) => { + if (scan) { + scansById.set(scan.id, scan); + } + }); + } + + const scanInsertedAtValues = scanIds + .map((scanId) => scansById.get(scanId)?.attributes?.inserted_at) + .filter((insertedAt): insertedAt is string => Boolean(insertedAt)); + + const dateFilters = buildFindingScanDateFilters(scanInsertedAtValues); + + if (Object.keys(dateFilters).length === 0) { + return filters; + } + + return { + ...filters, + ...dateFilters, + }; +} diff --git a/ui/lib/invitation-routing.ts b/ui/lib/invitation-routing.ts new file mode 100644 index 0000000000..85f132d922 --- /dev/null +++ b/ui/lib/invitation-routing.ts @@ -0,0 +1,10 @@ +/** + * Query param name + value used to bypass the backward-compat redirect + * in proxy.ts when the user explicitly chose "Create an account" + * from the invitation smart router. + * + * Client sends: /sign-up?invitation_token=…&action=signup + * Proxy skips redirect when "action" param is present. + */ +export const INVITATION_ACTION_PARAM = "action"; +export const INVITATION_SIGNUP_ACTION = "signup"; diff --git a/ui/proxy.ts b/ui/proxy.ts index 98b0725dea..553de6e9ba 100644 --- a/ui/proxy.ts +++ b/ui/proxy.ts @@ -1,10 +1,12 @@ import { NextRequest, NextResponse } from "next/server"; import { auth } from "@/auth.config"; +import { INVITATION_ACTION_PARAM } from "@/lib/invitation-routing"; const publicRoutes = [ "/sign-in", "/sign-up", + "/invitation/accept", // In Cloud uncomment the following lines: // "/reset-password", // "/email-verification", @@ -18,6 +20,22 @@ const isPublicRoute = (pathname: string): boolean => { // NextAuth's auth() wrapper - renamed from middleware to proxy export default auth((req: NextRequest & { auth: any }) => { const { pathname } = req.nextUrl; + + // Backward compatibility: redirect old invitation links to new smart router + // Skip redirect when the user explicitly chose "Create an account" from the smart router + if ( + pathname === "/sign-up" && + req.nextUrl.searchParams.has("invitation_token") && + !req.nextUrl.searchParams.has(INVITATION_ACTION_PARAM) + ) { + const acceptUrl = new URL("/invitation/accept", req.url); + acceptUrl.searchParams.set( + "invitation_token", + req.nextUrl.searchParams.get("invitation_token")!, + ); + return NextResponse.redirect(acceptUrl); + } + const user = req.auth?.user; const sessionError = req.auth?.error; @@ -25,13 +43,13 @@ export default auth((req: NextRequest & { auth: any }) => { if (sessionError && !isPublicRoute(pathname)) { const signInUrl = new URL("/sign-in", req.url); signInUrl.searchParams.set("error", sessionError); - signInUrl.searchParams.set("callbackUrl", pathname); + signInUrl.searchParams.set("callbackUrl", pathname + req.nextUrl.search); return NextResponse.redirect(signInUrl); } if (!user && !isPublicRoute(pathname)) { const signInUrl = new URL("/sign-in", req.url); - signInUrl.searchParams.set("callbackUrl", pathname); + signInUrl.searchParams.set("callbackUrl", pathname + req.nextUrl.search); return NextResponse.redirect(signInUrl); } diff --git a/ui/tests/auth/auth-middleware.spec.ts b/ui/tests/auth/auth-middleware.spec.ts index 959f08250d..d7f1d23619 100644 --- a/ui/tests/auth/auth-middleware.spec.ts +++ b/ui/tests/auth/auth-middleware.spec.ts @@ -65,7 +65,9 @@ test.describe("Middleware Error Handling", () => { await freshPage.goto(`/scans?e2e_mw=${cacheBuster}`, { waitUntil: "commit", }); - await freshSignInPage.verifyRedirectWithCallback("/scans"); + await freshSignInPage.verifyRedirectWithCallback( + `/scans?e2e_mw=${cacheBuster}`, + ); } finally { await invalidSessionContext.close(); } diff --git a/ui/tests/auth/auth-session-errors.spec.ts b/ui/tests/auth/auth-session-errors.spec.ts index 000ade831d..ac640d53a3 100644 --- a/ui/tests/auth/auth-session-errors.spec.ts +++ b/ui/tests/auth/auth-session-errors.spec.ts @@ -69,4 +69,19 @@ test.describe("Session Error Messages", () => { await signInPage.verifyRedirectWithCallback("/providers"); }, ); + + test( + "should preserve query parameters in callbackUrl", + { tag: ["@e2e", "@auth", "@session", "@AUTH-SESSION-E2E-005"] }, + async ({ page, context }) => { + const signInPage = new SignInPage(page); + await context.clearCookies(); + + // Navigate to a protected route with query params and assert they are preserved. + await page.goto("/providers?ref=test", { + waitUntil: "commit", + }); + await signInPage.verifyRedirectWithCallback("/providers?ref=test"); + }, + ); }); diff --git a/ui/types/findings-table.ts b/ui/types/findings-table.ts index 30198404f5..f8837884f0 100644 --- a/ui/types/findings-table.ts +++ b/ui/types/findings-table.ts @@ -34,12 +34,14 @@ export interface FindingResourceRow { providerAlias: string; providerUid: string; resourceName: string; + resourceType: string; resourceGroup: string; resourceUid: string; service: string; region: string; severity: Severity; status: string; + delta?: string | null; isMuted: boolean; mutedReason?: string; firstSeenAt: string | null;