mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 13:01:56 +00:00
Merge branch 'master' into
PROWLER-465-add-alibaba-cloud-provider-support-to-the-api
This commit is contained in:
+210
-113
@@ -1,4 +1,4 @@
|
||||
name: UI - E2E Tests
|
||||
name: UI - E2E Cloud Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -6,125 +6,185 @@ on:
|
||||
- master
|
||||
- "v5.*"
|
||||
paths:
|
||||
- '.github/workflows/ui-e2e-tests.yml'
|
||||
- 'ui/**'
|
||||
- ".github/workflows/ui-e2e-tests.yml"
|
||||
- "ui/**"
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- "v5.*"
|
||||
paths:
|
||||
- ".github/workflows/ui-e2e-cloud-tests.yml"
|
||||
- "ui/**"
|
||||
workflow_run:
|
||||
workflows:
|
||||
- "API - Build, Push and Deploy"
|
||||
- "UI - Build, Push and Deploy"
|
||||
types: [completed]
|
||||
branches: [master, v5.*]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
environment:
|
||||
description: "Environment to test"
|
||||
required: true
|
||||
default: "dev"
|
||||
type: choice
|
||||
options:
|
||||
- dev
|
||||
- stg
|
||||
- pro
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
|
||||
e2e-tests:
|
||||
if: github.repository == 'prowler-cloud/prowler'
|
||||
if: github.repository == 'prowler-cloud/prowler-cloud'
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
AUTH_SECRET: 'fallback-ci-secret-for-testing'
|
||||
AUTH_TRUST_HOST: true
|
||||
NEXTAUTH_URL: 'http://localhost:3000'
|
||||
NEXT_PUBLIC_API_BASE_URL: 'http://localhost:8080/api/v1'
|
||||
E2E_ADMIN_USER: ${{ secrets.E2E_ADMIN_USER }}
|
||||
E2E_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD }}
|
||||
E2E_AWS_PROVIDER_ACCOUNT_ID: ${{ secrets.E2E_AWS_PROVIDER_ACCOUNT_ID }}
|
||||
E2E_AWS_PROVIDER_ACCESS_KEY: ${{ secrets.E2E_AWS_PROVIDER_ACCESS_KEY }}
|
||||
E2E_AWS_PROVIDER_SECRET_KEY: ${{ secrets.E2E_AWS_PROVIDER_SECRET_KEY }}
|
||||
E2E_AWS_PROVIDER_ROLE_ARN: ${{ secrets.E2E_AWS_PROVIDER_ROLE_ARN }}
|
||||
E2E_AZURE_SUBSCRIPTION_ID: ${{ secrets.E2E_AZURE_SUBSCRIPTION_ID }}
|
||||
E2E_AZURE_CLIENT_ID: ${{ secrets.E2E_AZURE_CLIENT_ID }}
|
||||
E2E_AZURE_SECRET_ID: ${{ secrets.E2E_AZURE_SECRET_ID }}
|
||||
E2E_AZURE_TENANT_ID: ${{ secrets.E2E_AZURE_TENANT_ID }}
|
||||
E2E_M365_DOMAIN_ID: ${{ secrets.E2E_M365_DOMAIN_ID }}
|
||||
E2E_M365_CLIENT_ID: ${{ secrets.E2E_M365_CLIENT_ID }}
|
||||
E2E_M365_SECRET_ID: ${{ secrets.E2E_M365_SECRET_ID }}
|
||||
E2E_M365_TENANT_ID: ${{ secrets.E2E_M365_TENANT_ID }}
|
||||
E2E_M365_CERTIFICATE_CONTENT: ${{ secrets.E2E_M365_CERTIFICATE_CONTENT }}
|
||||
E2E_KUBERNETES_CONTEXT: 'kind-kind'
|
||||
E2E_KUBERNETES_KUBECONFIG_PATH: /home/runner/.kube/config
|
||||
E2E_GCP_BASE64_SERVICE_ACCOUNT_KEY: ${{ secrets.E2E_GCP_BASE64_SERVICE_ACCOUNT_KEY }}
|
||||
E2E_GCP_PROJECT_ID: ${{ secrets.E2E_GCP_PROJECT_ID }}
|
||||
E2E_GITHUB_APP_ID: ${{ secrets.E2E_GITHUB_APP_ID }}
|
||||
E2E_GITHUB_BASE64_APP_PRIVATE_KEY: ${{ secrets.E2E_GITHUB_BASE64_APP_PRIVATE_KEY }}
|
||||
E2E_GITHUB_USERNAME: ${{ secrets.E2E_GITHUB_USERNAME }}
|
||||
E2E_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.E2E_GITHUB_PERSONAL_ACCESS_TOKEN }}
|
||||
E2E_GITHUB_ORGANIZATION: ${{ secrets.E2E_GITHUB_ORGANIZATION }}
|
||||
E2E_GITHUB_ORGANIZATION_ACCESS_TOKEN: ${{ secrets.E2E_GITHUB_ORGANIZATION_ACCESS_TOKEN }}
|
||||
E2E_ORGANIZATION_ID: ${{ secrets.E2E_ORGANIZATION_ID }}
|
||||
E2E_OCI_TENANCY_ID: ${{ secrets.E2E_OCI_TENANCY_ID }}
|
||||
E2E_OCI_USER_ID: ${{ secrets.E2E_OCI_USER_ID }}
|
||||
E2E_OCI_FINGERPRINT: ${{ secrets.E2E_OCI_FINGERPRINT }}
|
||||
E2E_OCI_KEY_CONTENT: ${{ secrets.E2E_OCI_KEY_CONTENT }}
|
||||
E2E_OCI_REGION: ${{ secrets.E2E_OCI_REGION }}
|
||||
E2E_NEW_USER_PASSWORD: ${{ secrets.E2E_NEW_USER_PASSWORD }}
|
||||
|
||||
NEXTAUTH_URL: "http://localhost:3000"
|
||||
AUTH_SECRET: "fallback-ci-secret-for-testing"
|
||||
AUTH_TRUST_HOST: "true"
|
||||
steps:
|
||||
- name: Determine environment
|
||||
id: env
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "pull_request" || "${{ github.event_name }}" == "push" ]]; then
|
||||
echo "environment=dev" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ github.event_name }}" == "workflow_run" && "${{ github.event.workflow_run.conclusion }}" == "success" && "${{ github.event.workflow_run.event }}" == "release" ]]; then
|
||||
echo "environment=stg" >> $GITHUB_OUTPUT
|
||||
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
echo "environment=${{ github.event.inputs.environment }}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "Unknown trigger, skipping..."
|
||||
exit 1
|
||||
fi
|
||||
- name: Set environment variables
|
||||
id: vars
|
||||
run: |
|
||||
case "${{ steps.env.outputs.environment }}" in
|
||||
"dev")
|
||||
echo "api_url=https://api.dev.prowler.com/api/v1" >> $GITHUB_OUTPUT
|
||||
echo "e2e_user_secret=DEV_E2E_USER" >> $GITHUB_OUTPUT
|
||||
echo "e2e_password_secret=DEV_E2E_PASSWORD" >> $GITHUB_OUTPUT
|
||||
echo "environment_name=DEV" >> $GITHUB_OUTPUT
|
||||
;;
|
||||
"stg")
|
||||
echo "api_url=https://api.stg.prowler.com/api/v1" >> $GITHUB_OUTPUT
|
||||
echo "e2e_user_secret=STG_E2E_USER" >> $GITHUB_OUTPUT
|
||||
echo "e2e_password_secret=STG_E2E_PASSWORD" >> $GITHUB_OUTPUT
|
||||
echo "environment_name=STG" >> $GITHUB_OUTPUT
|
||||
;;
|
||||
"pro")
|
||||
echo "api_url=https://api.prowler.com/api/v1" >> $GITHUB_OUTPUT
|
||||
echo "e2e_user_secret=PRO_E2E_USER" >> $GITHUB_OUTPUT
|
||||
echo "e2e_password_secret=PRO_E2E_PASSWORD" >> $GITHUB_OUTPUT
|
||||
echo "environment_name=PRO" >> $GITHUB_OUTPUT
|
||||
;;
|
||||
esac
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Create k8s Kind Cluster
|
||||
uses: helm/kind-action@v1
|
||||
- name: Environment info
|
||||
env:
|
||||
ENV_NAME: ${{ steps.vars.outputs.environment_name }}
|
||||
API_URL: ${{ steps.vars.outputs.api_url }}
|
||||
run: |
|
||||
echo "Environment: $ENV_NAME"
|
||||
echo "API URL: $API_URL"
|
||||
echo "Workflow: ${{ github.workflow }}"
|
||||
echo "Event: ${{ github.event_name }}"
|
||||
echo "Started at: $(date)"
|
||||
- name: Verify both STG deployments completed
|
||||
if: steps.env.outputs.environment == 'stg'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
echo "Verifying that both API and UI deployments completed successfully..."
|
||||
|
||||
# Get the latest runs for both workflows triggered by the same release
|
||||
API_RUN=$(gh run list --workflow="API - Build, Push and Deploy" --event=release --limit=1 --json status,conclusion,createdAt --jq '.[0]')
|
||||
API_STATUS=$(echo "$API_RUN" | jq -r '.status')
|
||||
API_CONCLUSION=$(echo "$API_RUN" | jq -r '.conclusion')
|
||||
|
||||
UI_RUN=$(gh run list --workflow="UI - Build, Push and Deploy" --event=release --limit=1 --json status,conclusion,createdAt --jq '.[0]')
|
||||
UI_STATUS=$(echo "$UI_RUN" | jq -r '.status')
|
||||
UI_CONCLUSION=$(echo "$UI_RUN" | jq -r '.conclusion')
|
||||
|
||||
echo "API workflow - Status: $API_STATUS, Conclusion: $API_CONCLUSION"
|
||||
echo "UI workflow - Status: $UI_STATUS, Conclusion: $UI_CONCLUSION"
|
||||
|
||||
# Verify both workflows completed successfully
|
||||
if [[ "$API_STATUS" != "completed" || "$API_CONCLUSION" != "success" ]]; then
|
||||
echo "API deployment not ready (Status: $API_STATUS, Conclusion: $API_CONCLUSION)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$UI_STATUS" != "completed" || "$UI_CONCLUSION" != "success" ]]; then
|
||||
echo "UI deployment not ready (Status: $UI_STATUS, Conclusion: $UI_CONCLUSION)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Both API and UI deployments completed successfully for STG"
|
||||
- name: Verify both PRO deployments completed
|
||||
if: steps.env.outputs.environment == 'pro'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
echo "Verifying that both API and UI deployments completed successfully..."
|
||||
|
||||
# Get the latest manual runs for both workflows
|
||||
API_RUN=$(gh run list --workflow="API - Build, Push and Deploy" --event=workflow_dispatch --limit=1 --json status,conclusion,createdAt --jq '.[0]')
|
||||
API_STATUS=$(echo "$API_RUN" | jq -r '.status')
|
||||
API_CONCLUSION=$(echo "$API_RUN" | jq -r '.conclusion')
|
||||
|
||||
UI_RUN=$(gh run list --workflow="UI - Build, Push and Deploy" --event=workflow_dispatch --limit=1 --json status,conclusion,createdAt --jq '.[0]')
|
||||
UI_STATUS=$(echo "$UI_RUN" | jq -r '.status')
|
||||
UI_CONCLUSION=$(echo "$UI_RUN" | jq -r '.conclusion')
|
||||
|
||||
echo "API workflow - Status: $API_STATUS, Conclusion: $API_CONCLUSION"
|
||||
echo "UI workflow - Status: $UI_STATUS, Conclusion: $UI_CONCLUSION"
|
||||
|
||||
# Verify both workflows completed successfully
|
||||
if [[ "$API_STATUS" != "completed" || "$API_CONCLUSION" != "success" ]]; then
|
||||
echo "API deployment not ready (Status: $API_STATUS, Conclusion: $API_CONCLUSION)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$UI_STATUS" != "completed" || "$UI_CONCLUSION" != "success" ]]; then
|
||||
echo "UI deployment not ready (Status: $UI_STATUS, Conclusion: $UI_CONCLUSION)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Both API and UI deployments completed successfully for PRO"
|
||||
- name: Setup Tailscale
|
||||
if: steps.env.outputs.environment != 'pro'
|
||||
uses: tailscale/github-action@84a3f23bb4d843bcf4da6cf824ec1be473daf4de # v3.2.3
|
||||
with:
|
||||
cluster_name: kind
|
||||
- name: Modify kubeconfig
|
||||
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
||||
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
|
||||
tags: tag:github-actions
|
||||
- name: Verify API is accessible
|
||||
env:
|
||||
API_URL: ${{ steps.vars.outputs.api_url }}
|
||||
ENV_NAME: ${{ steps.vars.outputs.environment_name }}
|
||||
run: |
|
||||
# Modify the kubeconfig to use the kind cluster server to https://kind-control-plane:6443
|
||||
# from worker service into docker-compose.yml
|
||||
kubectl config set-cluster kind-kind --server=https://kind-control-plane:6443
|
||||
kubectl config view
|
||||
- name: Add network kind to docker compose
|
||||
run: |
|
||||
# Add the network kind to the docker compose to interconnect to kind cluster
|
||||
yq -i '.networks.kind.external = true' docker-compose.yml
|
||||
# Add network kind to worker service and default network too
|
||||
yq -i '.services.worker.networks = ["kind","default"]' docker-compose.yml
|
||||
- name: Fix API data directory permissions
|
||||
run: docker run --rm -v $(pwd)/_data/api:/data alpine chown -R 1000:1000 /data
|
||||
- name: Add AWS credentials for testing AWS SDK Default Adding Provider
|
||||
run: |
|
||||
echo "Adding AWS credentials for testing AWS SDK Default Adding Provider..."
|
||||
echo "AWS_ACCESS_KEY_ID=${{ secrets.E2E_AWS_PROVIDER_ACCESS_KEY }}" >> .env
|
||||
echo "AWS_SECRET_ACCESS_KEY=${{ secrets.E2E_AWS_PROVIDER_SECRET_KEY }}" >> .env
|
||||
- name: Start API services
|
||||
run: |
|
||||
# Override docker-compose image tag to use latest instead of stable
|
||||
# This overrides any PROWLER_API_VERSION set in .env file
|
||||
export PROWLER_API_VERSION=latest
|
||||
echo "Using PROWLER_API_VERSION=${PROWLER_API_VERSION}"
|
||||
docker compose up -d api worker worker-beat
|
||||
- name: Wait for API to be ready
|
||||
run: |
|
||||
echo "Waiting for prowler-api..."
|
||||
timeout=150 # 5 minutes max
|
||||
elapsed=0
|
||||
while [ $elapsed -lt $timeout ]; do
|
||||
if curl -s ${NEXT_PUBLIC_API_BASE_URL}/docs >/dev/null 2>&1; then
|
||||
echo "Prowler API is ready!"
|
||||
exit 0
|
||||
fi
|
||||
echo "Waiting for prowler-api... (${elapsed}s elapsed)"
|
||||
sleep 5
|
||||
elapsed=$((elapsed + 5))
|
||||
done
|
||||
echo "Timeout waiting for prowler-api to start"
|
||||
exit 1
|
||||
- name: Load database fixtures for E2E tests
|
||||
run: |
|
||||
docker compose exec -T api sh -c '
|
||||
echo "Loading all fixtures from api/fixtures/dev/..."
|
||||
for fixture in api/fixtures/dev/*.json; do
|
||||
if [ -f "$fixture" ]; then
|
||||
echo "Loading $fixture"
|
||||
poetry run python manage.py loaddata "$fixture" --database admin
|
||||
fi
|
||||
done
|
||||
echo "All database fixtures loaded successfully!"
|
||||
'
|
||||
echo "Checking $ENV_NAME API at $API_URL/docs..."
|
||||
curl -f --connect-timeout 30 --max-time 60 ${API_URL}/docs
|
||||
echo "$ENV_NAME API is accessible"
|
||||
- name: Setup Node.js environment
|
||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
||||
with:
|
||||
node-version: '20.x'
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
node-version: "20.x"
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
with:
|
||||
version: 10
|
||||
version: 9
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
- name: Setup pnpm cache
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
@@ -137,6 +197,10 @@ jobs:
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build UI application
|
||||
working-directory: ./ui
|
||||
env:
|
||||
NEXT_PUBLIC_API_BASE_URL: ${{ steps.vars.outputs.api_url }}
|
||||
NEXT_PUBLIC_IS_CLOUD_ENV: "true"
|
||||
CLOUD_API_BASE_URL: ${{ steps.vars.outputs.api_url }}
|
||||
run: pnpm run build
|
||||
- name: Cache Playwright browsers
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
@@ -152,17 +216,50 @@ jobs:
|
||||
run: pnpm run test:e2e:install
|
||||
- name: Run E2E tests
|
||||
working-directory: ./ui
|
||||
run: pnpm run test:e2e
|
||||
env:
|
||||
NEXT_PUBLIC_API_BASE_URL: ${{ steps.vars.outputs.api_url }}
|
||||
NEXT_PUBLIC_IS_CLOUD_ENV: "true"
|
||||
CLOUD_API_BASE_URL: ${{ steps.vars.outputs.api_url }}
|
||||
E2E_USER: ${{ secrets[steps.vars.outputs.e2e_user_secret] }}
|
||||
E2E_PASSWORD: ${{ secrets[steps.vars.outputs.e2e_password_secret] }}
|
||||
E2E_ADMIN_USER: ${{ secrets.E2E_ADMIN_USER }}
|
||||
E2E_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD }}
|
||||
E2E_AWS_PROVIDER_ACCOUNT_ID: ${{ secrets.E2E_AWS_PROVIDER_ACCOUNT_ID }}
|
||||
E2E_AWS_PROVIDER_ACCESS_KEY: ${{ secrets.E2E_AWS_PROVIDER_ACCESS_KEY }}
|
||||
E2E_AWS_PROVIDER_SECRET_KEY: ${{ secrets.E2E_AWS_PROVIDER_SECRET_KEY }}
|
||||
E2E_AWS_PROVIDER_ROLE_ARN: ${{ secrets.E2E_AWS_PROVIDER_ROLE_ARN }}
|
||||
E2E_AZURE_SUBSCRIPTION_ID: ${{ secrets.E2E_AZURE_SUBSCRIPTION_ID }}
|
||||
E2E_AZURE_CLIENT_ID: ${{ secrets.E2E_AZURE_CLIENT_ID }}
|
||||
E2E_AZURE_SECRET_ID: ${{ secrets.E2E_AZURE_SECRET_ID }}
|
||||
E2E_AZURE_TENANT_ID: ${{ secrets.E2E_AZURE_TENANT_ID }}
|
||||
E2E_M365_DOMAIN_ID: ${{ secrets.E2E_M365_DOMAIN_ID }}
|
||||
E2E_M365_CLIENT_ID: ${{ secrets.E2E_M365_CLIENT_ID }}
|
||||
E2E_M365_SECRET_ID: ${{ secrets.E2E_M365_SECRET_ID }}
|
||||
E2E_M365_TENANT_ID: ${{ secrets.E2E_M365_TENANT_ID }}
|
||||
E2E_M365_CERTIFICATE_CONTENT: ${{ secrets.E2E_M365_CERTIFICATE_CONTENT }}
|
||||
E2E_KUBERNETES_CONTEXT: "kind-kind"
|
||||
E2E_KUBERNETES_KUBECONFIG_PATH: /home/runner/.kube/config
|
||||
E2E_GCP_BASE64_SERVICE_ACCOUNT_KEY: ${{ secrets.E2E_GCP_BASE64_SERVICE_ACCOUNT_KEY }}
|
||||
E2E_GCP_PROJECT_ID: ${{ secrets.E2E_GCP_PROJECT_ID }}
|
||||
E2E_GITHUB_APP_ID: ${{ secrets.E2E_GITHUB_APP_ID }}
|
||||
E2E_GITHUB_BASE64_APP_PRIVATE_KEY: ${{ secrets.E2E_GITHUB_BASE64_APP_PRIVATE_KEY }}
|
||||
E2E_GITHUB_USERNAME: ${{ secrets.E2E_GITHUB_USERNAME }}
|
||||
E2E_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.E2E_GITHUB_PERSONAL_ACCESS_TOKEN }}
|
||||
E2E_GITHUB_ORGANIZATION: ${{ secrets.E2E_GITHUB_ORGANIZATION }}
|
||||
E2E_GITHUB_ORGANIZATION_ACCESS_TOKEN: ${{ secrets.E2E_GITHUB_ORGANIZATION_ACCESS_TOKEN }}
|
||||
E2E_ORGANIZATION_ID: ${{ secrets.E2E_ORGANIZATION_ID }}
|
||||
E2E_OCI_TENANCY_ID: ${{ secrets.E2E_OCI_TENANCY_ID }}
|
||||
E2E_OCI_USER_ID: ${{ secrets.E2E_OCI_USER_ID }}
|
||||
E2E_OCI_FINGERPRINT: ${{ secrets.E2E_OCI_FINGERPRINT }}
|
||||
E2E_OCI_KEY_CONTENT: ${{ secrets.E2E_OCI_KEY_CONTENT }}
|
||||
E2E_OCI_REGION: ${{ secrets.E2E_OCI_REGION }}
|
||||
E2E_NEW_USER_PASSWORD: ${{ secrets.E2E_NEW_USER_PASSWORD }}
|
||||
|
||||
run: pnpm run test:e2e-cloud
|
||||
- name: Upload test reports
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
if: failure()
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
name: playwright-report-${{ steps.env.outputs.environment }}-${{ github.run_number }}
|
||||
path: ui/playwright-report/
|
||||
retention-days: 30
|
||||
- name: Cleanup services
|
||||
if: always()
|
||||
run: |
|
||||
echo "Shutting down services..."
|
||||
docker compose down -v || true
|
||||
echo "Cleanup completed"
|
||||
|
||||
@@ -58,7 +58,7 @@ Before you begin, ensure you have:
|
||||
|
||||
### Authentication
|
||||
|
||||
Prowler supports multiple authentication methods for OCI. For detailed authentication setup, see the [OCI Authentication Guide](./authentication.mdx).
|
||||
Prowler supports multiple authentication methods for OCI. For detailed authentication setup, see the [OCI Authentication Guide](./authentication).
|
||||
|
||||
**Note:** OCI Session Authentication and Config File Authentication both use the same `~/.oci/config` file. The difference is how the config file is generated - automatically via browser (session auth) or manually with API keys.
|
||||
|
||||
@@ -107,7 +107,7 @@ The easiest and most secure method is using OCI session authentication, which au
|
||||
|
||||
#### Alternative: Manual API Key Setup
|
||||
|
||||
If you prefer to manually generate API keys instead of using browser-based session authentication, see the detailed instructions in the [Authentication Guide](./authentication.mdx#config-file-authentication-manual-api-key-setup).
|
||||
If you prefer to manually generate API keys instead of using browser-based session authentication, see the detailed instructions in the [Authentication Guide](./authentication#config-file-authentication-manual-api-key-setup).
|
||||
|
||||
**Note:** Both methods use the same `~/.oci/config` file - the difference is that manual setup uses static API keys while session authentication uses temporary session tokens.
|
||||
|
||||
|
||||
@@ -12,6 +12,10 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- `compute_instance_automatic_restart_enabled` check for GCP provider [(#9271)](https://github.com/prowler-cloud/prowler/pull/9271)
|
||||
- `compute_instance_deletion_protection_enabled` check for GCP provider [(#9358)](https://github.com/prowler-cloud/prowler/pull/9358)
|
||||
- Add needed changes to AlibabaCloud provider from the API [(#9485)](https://github.com/prowler-cloud/prowler/pull/9485)
|
||||
- Update SOC2 - Azure with Processing Integrity requirements [(#9463)](https://github.com/prowler-cloud/prowler/pull/9463)
|
||||
- Update SOC2 - GCP with Processing Integrity requirements [(#9464)](https://github.com/prowler-cloud/prowler/pull/9464)
|
||||
- Update SOC2 - AWS with Processing Integrity requirements [(#9462)](https://github.com/prowler-cloud/prowler/pull/9462)
|
||||
- RBI Cyber Security Framework compliance for Azure provider [(#8822)](https://github.com/prowler-cloud/prowler/pull/8822)
|
||||
|
||||
### Changed
|
||||
- Update AWS Macie service metadata to new format [(#9265)](https://github.com/prowler-cloud/prowler/pull/9265)
|
||||
@@ -22,11 +26,15 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- Update AWS Macie service metadata to new format [(#9265)](https://github.com/prowler-cloud/prowler/pull/9265)
|
||||
- Update AWS Lightsail service metadata to new format [(#9264)](https://github.com/prowler-cloud/prowler/pull/9264)
|
||||
|
||||
### Fixed
|
||||
- GCP `cloudstorage_uses_vpc_service_controls` check to handle VPC Service Controls blocked API access [(#9478)](https://github.com/prowler-cloud/prowler/pull/9478)
|
||||
|
||||
---
|
||||
|
||||
## [v5.14.3] (Prowler UNRELEASED)
|
||||
|
||||
### Fixed
|
||||
- Fix duplicate requirement IDs in ISO 27001:2013 AWS compliance framework by adding unique letter suffixes
|
||||
- Removed incorrect threat-detection category from checks metadata [(#9489)](https://github.com/prowler-cloud/prowler/pull/9489)
|
||||
|
||||
---
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -547,6 +547,106 @@
|
||||
"cloudwatch_log_group_retention_policy_specific_days_enabled",
|
||||
"kinesis_stream_data_retention_period"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "pi_1_2",
|
||||
"Name": "PI1.2 System inputs are measured and recorded completely, accurately, and timely to meet the entity's processing integrity commitments and system requirements",
|
||||
"Description": "The entity implements policies and procedures over system inputs, including controls over completeness and accuracy, to result in products, services, and reporting to meet the entity's objectives. This includes defining accuracy targets, monitoring input quality, and creating detailed records of each input event.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "pi_1_2",
|
||||
"Section": "PI1.0 - Processing Integrity",
|
||||
"Service": "aws",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"apigateway_restapi_logging_enabled",
|
||||
"apigatewayv2_api_access_logging_enabled",
|
||||
"elbv2_logging_enabled",
|
||||
"elb_logging_enabled",
|
||||
"wafv2_webacl_logging_enabled",
|
||||
"waf_global_webacl_logging_enabled",
|
||||
"cloudtrail_s3_dataevents_write_enabled",
|
||||
"cloudfront_distributions_logging_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "pi_1_3",
|
||||
"Name": "PI1.3 Data is processed completely, accurately, and timely as authorized to meet the entity's processing integrity commitments and system requirements",
|
||||
"Description": "The entity implements controls to ensure data is processed completely, accurately, and timely. This includes defining processing specifications, identifying processing activities, detecting and correcting errors throughout processing, recording processing activities with accurate logs, and ensuring completeness and timeliness of processing.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "pi_1_3",
|
||||
"Section": "PI1.0 - Processing Integrity",
|
||||
"Service": "aws",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"cloudtrail_multi_region_enabled",
|
||||
"cloudtrail_log_file_validation_enabled",
|
||||
"cloudtrail_cloudwatch_logging_enabled",
|
||||
"cloudwatch_log_metric_filter_unauthorized_api_calls",
|
||||
"cloudwatch_log_metric_filter_authentication_failures",
|
||||
"cloudwatch_log_metric_filter_policy_changes",
|
||||
"cloudwatch_log_metric_filter_root_usage",
|
||||
"config_recorder_all_regions_enabled",
|
||||
"rds_instance_integration_cloudwatch_logs",
|
||||
"rds_cluster_integration_cloudwatch_logs",
|
||||
"glue_etl_jobs_logging_enabled",
|
||||
"stepfunctions_statemachine_logging_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "pi_1_4",
|
||||
"Name": "PI1.4 System outputs are complete, accurate, distributed only to intended parties, and retained to meet the entity's processing integrity commitments and system requirements",
|
||||
"Description": "The entity implements controls to ensure system outputs are delivered to authorized recipients in the correct format and protected against unauthorized access, modification, theft, destruction, or corruption. This includes output encryption, access controls, and audit trails for output delivery.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "pi_1_4",
|
||||
"Section": "PI1.0 - Processing Integrity",
|
||||
"Service": "aws",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"s3_bucket_default_encryption",
|
||||
"s3_bucket_kms_encryption",
|
||||
"cloudwatch_log_group_kms_encryption_enabled",
|
||||
"sns_topics_kms_encryption_at_rest_enabled",
|
||||
"kinesis_stream_encrypted_at_rest",
|
||||
"cloudfront_distributions_field_level_encryption_enabled",
|
||||
"cloudwatch_log_group_not_publicly_accessible",
|
||||
"cloudwatch_cross_account_sharing_disabled",
|
||||
"glue_etl_jobs_cloudwatch_logs_encryption_enabled",
|
||||
"glue_etl_jobs_amazon_s3_encryption_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "pi_1_5",
|
||||
"Name": "PI1.5 Stored data is maintained complete, accurate, and protected from unauthorized modification to meet the entity's processing integrity commitments and system requirements",
|
||||
"Description": "The entity implements controls to protect stored inputs, items in processing, and outputs from theft, destruction, corruption, or deterioration. This includes data encryption at rest, key management, backup and recovery procedures, access controls, and data integrity validation.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "pi_1_5",
|
||||
"Section": "PI1.0 - Processing Integrity",
|
||||
"Service": "aws",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"s3_bucket_object_versioning",
|
||||
"s3_bucket_object_lock",
|
||||
"rds_instance_storage_encrypted",
|
||||
"rds_cluster_storage_encrypted",
|
||||
"dynamodb_tables_kms_cmk_encryption_enabled",
|
||||
"ec2_ebs_volume_encryption",
|
||||
"backup_plans_exist",
|
||||
"backup_recovery_point_encrypted",
|
||||
"backup_vaults_encrypted",
|
||||
"kms_cmk_rotation_enabled"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
{
|
||||
"Framework": "RBI-Cyber-Security-Framework",
|
||||
"Name": "Reserve Bank of India (RBI) Cyber Security Framework",
|
||||
"Version": "",
|
||||
"Provider": "Azure",
|
||||
"Description": "The Reserve Bank had prescribed a set of baseline cyber security controls for primary (Urban) cooperative banks (UCBs) in October 2018. On further examination, it has been decided to prescribe a comprehensive cyber security framework for the UCBs, as a graded approach, based on their digital depth and interconnectedness with the payment systems landscape, digital products offered by them and assessment of cyber security risk. The framework would mandate implementation of progressively stronger security measures based on the nature, variety and scale of digital product offerings of banks.",
|
||||
"Requirements": [
|
||||
{
|
||||
"Id": "annex_i_1_1",
|
||||
"Name": "Annex I (1.1)",
|
||||
"Description": "UCBs should maintain an up-to-date business IT Asset Inventory Register containing the following fields, as a minimum: a) Details of the IT Asset (viz., hardware/software/network devices, key personnel, services, etc.), b. Details of systems where customer data are stored, c. Associated business applications, if any, d. Criticality of the IT asset (For example, High/Medium/Low).",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "annex_i_1_1",
|
||||
"Service": "vm"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"vm_ensure_using_approved_images",
|
||||
"vm_ensure_using_managed_disks",
|
||||
"vm_trusted_launch_enabled",
|
||||
"aks_cluster_rbac_enabled",
|
||||
"aks_clusters_created_with_private_nodes",
|
||||
"appinsights_ensure_is_configured",
|
||||
"containerregistry_admin_user_disabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "annex_i_1_3",
|
||||
"Name": "Annex I (1.3)",
|
||||
"Description": "Appropriately manage and provide protection within and outside UCB/network, keeping in mind how the data/information is stored, transmitted, processed, accessed and put to use within/outside the UCB's network, and level of risk they are exposed to depending on the sensitivity of the data/information.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "annex_i_1_3",
|
||||
"Service": "azure"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"keyvault_key_rotation_enabled",
|
||||
"keyvault_access_only_through_private_endpoints",
|
||||
"keyvault_private_endpoints",
|
||||
"keyvault_rbac_enabled",
|
||||
"app_function_not_publicly_accessible",
|
||||
"app_ensure_http_is_redirected_to_https",
|
||||
"app_minimum_tls_version_12",
|
||||
"storage_blob_public_access_level_is_disabled",
|
||||
"storage_secure_transfer_required_is_enabled",
|
||||
"storage_ensure_encryption_with_customer_managed_keys",
|
||||
"storage_ensure_minimum_tls_version_12",
|
||||
"storage_default_network_access_rule_is_denied",
|
||||
"storage_ensure_private_endpoints_in_storage_accounts",
|
||||
"network_ssh_internet_access_restricted",
|
||||
"sqlserver_unrestricted_inbound_access",
|
||||
"sqlserver_tde_encryption_enabled",
|
||||
"sqlserver_tde_encrypted_with_cmk",
|
||||
"cosmosdb_account_use_private_endpoints",
|
||||
"cosmosdb_account_firewall_use_selected_networks",
|
||||
"mysql_flexible_server_ssl_connection_enabled",
|
||||
"mysql_flexible_server_minimum_tls_version_12",
|
||||
"postgresql_flexible_server_enforce_ssl_enabled",
|
||||
"aks_clusters_public_access_disabled",
|
||||
"containerregistry_not_publicly_accessible",
|
||||
"containerregistry_uses_private_link",
|
||||
"aisearch_service_not_publicly_accessible"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "annex_i_5_1",
|
||||
"Name": "Annex I (5.1)",
|
||||
"Description": "The firewall configurations should be set to the highest security level and evaluation of critical device (such as firewall, network switches, security devices, etc.) configurations should be done periodically.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "annex_i_5_1",
|
||||
"Service": "network"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"network_rdp_internet_access_restricted",
|
||||
"network_http_internet_access_restricted",
|
||||
"network_udp_internet_access_restricted",
|
||||
"network_ssh_internet_access_restricted",
|
||||
"network_flow_log_captured_sent",
|
||||
"network_flow_log_more_than_90_days",
|
||||
"network_watcher_enabled",
|
||||
"network_bastion_host_exists",
|
||||
"aks_network_policy_enabled",
|
||||
"storage_default_network_access_rule_is_denied"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "annex_i_6",
|
||||
"Name": "Annex I (6)",
|
||||
"Description": "Put in place systems and processes to identify, track, manage and monitor the status of patches to servers, operating system and application software running at the systems used by the UCB officials (end-users). Implement and update antivirus protection for all servers and applicable end points preferably through a centralised system.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "annex_i_6",
|
||||
"Service": "defender"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"defender_ensure_system_updates_are_applied",
|
||||
"defender_assessments_vm_endpoint_protection_installed",
|
||||
"defender_ensure_defender_for_server_is_on",
|
||||
"defender_ensure_defender_for_app_services_is_on",
|
||||
"defender_ensure_defender_for_sql_servers_is_on",
|
||||
"defender_ensure_defender_for_azure_sql_databases_is_on",
|
||||
"defender_ensure_defender_for_storage_is_on",
|
||||
"defender_ensure_defender_for_containers_is_on",
|
||||
"defender_ensure_defender_for_keyvault_is_on",
|
||||
"defender_ensure_defender_for_arm_is_on",
|
||||
"defender_ensure_defender_for_dns_is_on",
|
||||
"defender_ensure_defender_for_databases_is_on",
|
||||
"defender_ensure_defender_for_cosmosdb_is_on",
|
||||
"defender_container_images_scan_enabled",
|
||||
"defender_container_images_resolved_vulnerabilities",
|
||||
"defender_auto_provisioning_vulnerabilty_assessments_machines_on",
|
||||
"vm_backup_enabled",
|
||||
"app_ensure_java_version_is_latest",
|
||||
"app_ensure_php_version_is_latest",
|
||||
"app_ensure_python_version_is_latest"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "annex_i_7_1",
|
||||
"Name": "Annex I (7.1)",
|
||||
"Description": "Disallow administrative rights on end-user workstations/PCs/laptops and provide access rights on a 'need to know' and 'need to do' basis.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "annex_i_7_1",
|
||||
"Service": "iam"
|
||||
}
|
||||
],
|
||||
"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_policy_ensure_default_user_cannot_create_apps",
|
||||
"entra_policy_ensure_default_user_cannot_create_tenants",
|
||||
"entra_policy_default_users_cannot_create_security_groups",
|
||||
"entra_policy_guest_invite_only_for_admin_roles",
|
||||
"entra_policy_guest_users_access_restrictions",
|
||||
"app_function_identity_without_admin_privileges"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "annex_i_7_2",
|
||||
"Name": "Annex I (7.2)",
|
||||
"Description": "Passwords should be set as complex and lengthy and users should not use same passwords for all the applications/systems/devices.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "annex_i_7_2",
|
||||
"Service": "entra"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"entra_non_privileged_user_has_mfa",
|
||||
"entra_privileged_user_has_mfa",
|
||||
"entra_policy_user_consent_for_verified_apps",
|
||||
"entra_policy_restricts_user_consent_for_apps",
|
||||
"entra_user_with_vm_access_has_mfa",
|
||||
"entra_security_defaults_enabled",
|
||||
"entra_conditional_access_policy_require_mfa_for_management_api",
|
||||
"entra_trusted_named_locations_exists",
|
||||
"sqlserver_azuread_administrator_enabled",
|
||||
"postgresql_flexible_server_entra_id_authentication_enabled",
|
||||
"cosmosdb_account_use_aad_and_rbac"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "annex_i_7_3",
|
||||
"Name": "Annex I (7.3)",
|
||||
"Description": "Remote Desktop Protocol (RDP) which allows others to access the computer remotely over a network or over the internet should be always disabled and should be enabled only with the approval of the authorised officer of the UCB. Logs for such remote access shall be enabled and monitored for suspicious activities.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "annex_i_7_3",
|
||||
"Service": "network"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"network_rdp_internet_access_restricted",
|
||||
"vm_jit_access_enabled",
|
||||
"network_bastion_host_exists",
|
||||
"vm_linux_enforce_ssh_authentication"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "annex_i_7_4",
|
||||
"Name": "Annex I (7.4)",
|
||||
"Description": "Implement appropriate (e.g. centralised) systems and controls to allow, manage, log and monitor privileged/super user/administrative access to critical systems (servers/databases, applications, network devices etc.)",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "annex_i_7_4",
|
||||
"Service": "monitor"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"monitor_alert_create_update_nsg",
|
||||
"monitor_alert_delete_nsg",
|
||||
"monitor_diagnostic_setting_with_appropriate_categories",
|
||||
"monitor_diagnostic_settings_exists",
|
||||
"monitor_alert_create_policy_assignment",
|
||||
"monitor_alert_delete_policy_assignment",
|
||||
"monitor_alert_create_update_security_solution",
|
||||
"monitor_alert_delete_security_solution",
|
||||
"monitor_alert_create_update_sqlserver_fr",
|
||||
"monitor_alert_delete_sqlserver_fr",
|
||||
"monitor_alert_create_update_public_ip_address_rule",
|
||||
"monitor_alert_delete_public_ip_address_rule",
|
||||
"monitor_alert_service_health_exists",
|
||||
"monitor_storage_account_with_activity_logs_cmk_encrypted",
|
||||
"monitor_storage_account_with_activity_logs_is_private",
|
||||
"keyvault_logging_enabled",
|
||||
"sqlserver_auditing_enabled",
|
||||
"sqlserver_auditing_retention_90_days",
|
||||
"app_http_logs_enabled",
|
||||
"app_function_application_insights_enabled",
|
||||
"defender_additional_email_configured_with_a_security_contact",
|
||||
"defender_ensure_notify_alerts_severity_is_high",
|
||||
"defender_ensure_notify_emails_to_owners",
|
||||
"defender_ensure_mcas_is_enabled",
|
||||
"defender_ensure_wdatp_is_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "annex_i_12",
|
||||
"Name": "Annex I (12)",
|
||||
"Description": "Take periodic back up of the important data and store this data 'off line' (i.e., transferring important files to a storage device that can be detached from a computer/system after copying all the files).",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "annex_i_12",
|
||||
"Service": "azure"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"vm_backup_enabled",
|
||||
"vm_sufficient_daily_backup_retention_period",
|
||||
"storage_ensure_file_shares_soft_delete_is_enabled",
|
||||
"storage_blob_versioning_is_enabled",
|
||||
"storage_ensure_soft_delete_is_enabled",
|
||||
"storage_geo_redundant_enabled",
|
||||
"keyvault_recoverable",
|
||||
"sqlserver_vulnerability_assessment_enabled",
|
||||
"sqlserver_va_periodic_recurring_scans_enabled"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -619,6 +619,92 @@
|
||||
"sqlserver_auditing_retention_90_days",
|
||||
"storage_ensure_soft_delete_is_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "pi_1_2",
|
||||
"Name": "PI1.2 System inputs are measured and recorded completely, accurately, and timely to meet the entity's processing integrity commitments and system requirements",
|
||||
"Description": "The entity implements policies and procedures over system inputs, including controls over completeness and accuracy, to result in products, services, and reporting to meet the entity's objectives. This includes defining accuracy targets, monitoring input quality, and creating detailed records of each input event.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "pi_1_2",
|
||||
"Section": "PI1.0 - Processing Integrity",
|
||||
"Service": "azure",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"app_http_logs_enabled",
|
||||
"network_flow_log_captured_sent",
|
||||
"keyvault_logging_enabled",
|
||||
"monitor_diagnostic_settings_exists",
|
||||
"sqlserver_auditing_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "pi_1_3",
|
||||
"Name": "PI1.3 Data is processed completely, accurately, and timely as authorized to meet the entity's processing integrity commitments and system requirements",
|
||||
"Description": "The entity implements controls to ensure data is processed completely, accurately, and timely. This includes defining processing specifications, identifying processing activities, detecting and correcting errors throughout processing, recording processing activities with accurate logs, and ensuring completeness and timeliness of processing.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "pi_1_3",
|
||||
"Section": "PI1.0 - Processing Integrity",
|
||||
"Service": "azure",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"monitor_diagnostic_setting_with_appropriate_categories",
|
||||
"monitor_diagnostic_settings_exists",
|
||||
"defender_auto_provisioning_log_analytics_agent_vms_on",
|
||||
"mysql_flexible_server_audit_log_enabled",
|
||||
"postgresql_flexible_server_log_checkpoints_on",
|
||||
"postgresql_flexible_server_log_connections_on",
|
||||
"postgresql_flexible_server_log_disconnections_on",
|
||||
"network_flow_log_more_than_90_days"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "pi_1_4",
|
||||
"Name": "PI1.4 System outputs are complete, accurate, distributed only to intended parties, and retained to meet the entity's processing integrity commitments and system requirements",
|
||||
"Description": "The entity implements controls to ensure system outputs are delivered to authorized recipients in the correct format and protected against unauthorized access, modification, theft, destruction, or corruption. This includes output encryption, access controls, and audit trails for output delivery.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "pi_1_4",
|
||||
"Section": "PI1.0 - Processing Integrity",
|
||||
"Service": "azure",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"storage_ensure_encryption_with_customer_managed_keys",
|
||||
"storage_infrastructure_encryption_is_enabled",
|
||||
"monitor_storage_account_with_activity_logs_cmk_encrypted",
|
||||
"monitor_storage_account_with_activity_logs_is_private",
|
||||
"sqlserver_tde_encryption_enabled",
|
||||
"sqlserver_tde_encrypted_with_cmk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "pi_1_5",
|
||||
"Name": "PI1.5 Stored data is maintained complete, accurate, and protected from unauthorized modification to meet the entity's processing integrity commitments and system requirements",
|
||||
"Description": "The entity implements controls to protect stored inputs, items in processing, and outputs from theft, destruction, corruption, or deterioration. This includes data encryption at rest, key management, backup and recovery procedures, access controls, and data integrity validation.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "pi_1_5",
|
||||
"Section": "PI1.0 - Processing Integrity",
|
||||
"Service": "azure",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"storage_ensure_encryption_with_customer_managed_keys",
|
||||
"storage_infrastructure_encryption_is_enabled",
|
||||
"storage_ensure_soft_delete_is_enabled",
|
||||
"vm_ensure_attached_disks_encrypted_with_cmk",
|
||||
"vm_ensure_unattached_disks_encrypted_with_cmk",
|
||||
"keyvault_key_rotation_enabled",
|
||||
"keyvault_recoverable"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -492,6 +492,87 @@
|
||||
"Checks": [
|
||||
"cloudstorage_bucket_log_retention_policy_lock"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "pi_1_2",
|
||||
"Name": "PI1.2 System inputs are measured and recorded completely, accurately, and timely to meet the entity's processing integrity commitments and system requirements",
|
||||
"Description": "The entity implements policies and procedures over system inputs, including controls over completeness and accuracy, to result in products, services, and reporting to meet the entity's objectives. This includes defining accuracy targets, monitoring input quality, and creating detailed records of each input event.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "pi_1_2",
|
||||
"Section": "PI1.0 - Processing Integrity",
|
||||
"Service": "gcp",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"compute_loadbalancer_logging_enabled",
|
||||
"compute_subnet_flow_logs_enabled",
|
||||
"logging_sink_created",
|
||||
"iam_audit_logs_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "pi_1_3",
|
||||
"Name": "PI1.3 Data is processed completely, accurately, and timely as authorized to meet the entity's processing integrity commitments and system requirements",
|
||||
"Description": "The entity implements controls to ensure data is processed completely, accurately, and timely. This includes defining processing specifications, identifying processing activities, detecting and correcting errors throughout processing, recording processing activities with accurate logs, and ensuring completeness and timeliness of processing.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "pi_1_3",
|
||||
"Section": "PI1.0 - Processing Integrity",
|
||||
"Service": "gcp",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"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_sql_instance_configuration_changes_enabled",
|
||||
"cloudsql_instance_postgres_log_connections_flag",
|
||||
"cloudsql_instance_postgres_log_disconnections_flag",
|
||||
"cloudsql_instance_postgres_log_statement_flag",
|
||||
"iam_audit_logs_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "pi_1_4",
|
||||
"Name": "PI1.4 System outputs are complete, accurate, distributed only to intended parties, and retained to meet the entity's processing integrity commitments and system requirements",
|
||||
"Description": "The entity implements controls to ensure system outputs are delivered to authorized recipients in the correct format and protected against unauthorized access, modification, theft, destruction, or corruption. This includes output encryption, access controls, and audit trails for output delivery.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "pi_1_4",
|
||||
"Section": "PI1.0 - Processing Integrity",
|
||||
"Service": "gcp",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"cloudstorage_bucket_uniform_bucket_level_access",
|
||||
"bigquery_dataset_cmk_encryption",
|
||||
"bigquery_table_cmk_encryption",
|
||||
"compute_instance_confidential_computing_enabled",
|
||||
"pubsub_topic_encryption_with_cmk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "pi_1_5",
|
||||
"Name": "PI1.5 Stored data is maintained complete, accurate, and protected from unauthorized modification to meet the entity's processing integrity commitments and system requirements",
|
||||
"Description": "The entity implements controls to protect stored inputs, items in processing, and outputs from theft, destruction, corruption, or deterioration. This includes data encryption at rest, key management, backup and recovery procedures, access controls, and data integrity validation.",
|
||||
"Attributes": [
|
||||
{
|
||||
"ItemId": "pi_1_5",
|
||||
"Section": "PI1.0 - Processing Integrity",
|
||||
"Service": "gcp",
|
||||
"Type": "automated"
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"cloudstorage_bucket_log_retention_policy_lock",
|
||||
"cloudsql_instance_automated_backups",
|
||||
"compute_instance_encryption_with_csek_enabled",
|
||||
"kms_key_rotation_enabled",
|
||||
"dataproc_encrypted_with_cmks_disabled"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
from typing import Optional
|
||||
|
||||
from googleapiclient.errors import HttpError
|
||||
from pydantic.v1 import BaseModel
|
||||
|
||||
from prowler.lib.logger import logger
|
||||
@@ -12,6 +13,7 @@ class CloudStorage(GCPService):
|
||||
def __init__(self, provider: GcpProvider):
|
||||
super().__init__("storage", provider)
|
||||
self.buckets = []
|
||||
self.vpc_service_controls_protected_projects = set()
|
||||
self._get_buckets()
|
||||
|
||||
def _get_buckets(self):
|
||||
@@ -93,6 +95,17 @@ class CloudStorage(GCPService):
|
||||
request = self.client.buckets().list_next(
|
||||
previous_request=request, previous_response=response
|
||||
)
|
||||
except HttpError as http_error:
|
||||
# Check if the error is due to VPC Service Controls blocking the API
|
||||
if "vpcServiceControlsUniqueIdentifier" in str(http_error):
|
||||
self.vpc_service_controls_protected_projects.add(project_id)
|
||||
logger.warning(
|
||||
f"Project {project_id} is protected by VPC Service Controls for Cloud Storage API."
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
f"{http_error.__class__.__name__}[{http_error.__traceback__.tb_lineno}]: {http_error}"
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
|
||||
+16
-2
@@ -5,14 +5,22 @@ from prowler.providers.gcp.services.accesscontextmanager.accesscontextmanager_cl
|
||||
from prowler.providers.gcp.services.cloudresourcemanager.cloudresourcemanager_client import (
|
||||
cloudresourcemanager_client,
|
||||
)
|
||||
from prowler.providers.gcp.services.cloudstorage.cloudstorage_client import (
|
||||
cloudstorage_client,
|
||||
)
|
||||
|
||||
|
||||
class cloudstorage_uses_vpc_service_controls(Check):
|
||||
"""
|
||||
Ensure Cloud Storage is protected by VPC Service Controls at project level.
|
||||
|
||||
Reports PASS if a project is in a VPC Service Controls perimeter
|
||||
with storage.googleapis.com as a restricted service, otherwise FAIL.
|
||||
Reports PASS if:
|
||||
- A project is in a VPC Service Controls perimeter with storage.googleapis.com
|
||||
as a restricted service, OR
|
||||
- The Cloud Storage API access is blocked by VPC Service Controls
|
||||
(verified by vpcServiceControlsUniqueIdentifier in the error response)
|
||||
|
||||
Otherwise reports FAIL.
|
||||
"""
|
||||
|
||||
def execute(self) -> list[Check_Report_GCP]:
|
||||
@@ -47,6 +55,12 @@ class cloudstorage_uses_vpc_service_controls(Check):
|
||||
if project_resource_id in protected_projects:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Project {project.id} has VPC Service Controls enabled for Cloud Storage in perimeter {protected_projects[project_resource_id]}."
|
||||
elif (
|
||||
project.id
|
||||
in cloudstorage_client.vpc_service_controls_protected_projects
|
||||
):
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Project {project.id} has VPC Service Controls enabled for Cloud Storage in undetermined perimeter (verified by API access restriction)."
|
||||
|
||||
findings.append(report)
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from googleapiclient.errors import HttpError
|
||||
|
||||
from prowler.providers.gcp.services.cloudstorage.cloudstorage_service import (
|
||||
CloudStorage,
|
||||
@@ -56,3 +58,36 @@ class TestCloudStorageService:
|
||||
assert not cloudstorage_client.buckets[1].public
|
||||
assert cloudstorage_client.buckets[1].retention_policy is None
|
||||
assert cloudstorage_client.buckets[1].project_id == GCP_PROJECT_ID
|
||||
|
||||
def test_vpc_service_controls_blocked(self):
|
||||
with (
|
||||
patch(
|
||||
"prowler.providers.gcp.lib.service.service.GCPService.__is_api_active__",
|
||||
new=mock_is_api_active,
|
||||
),
|
||||
patch(
|
||||
"prowler.providers.gcp.lib.service.service.GCPService.__generate_client__",
|
||||
) as mock_client,
|
||||
):
|
||||
mock_resp = MagicMock()
|
||||
mock_resp.status = 403
|
||||
mock_resp.reason = "Forbidden"
|
||||
|
||||
vpc_error = HttpError(
|
||||
resp=mock_resp,
|
||||
content=b'{"error": {"message": "Request is prohibited by organization\'s policy. vpcServiceControlsUniqueIdentifier: 12345"}}',
|
||||
)
|
||||
|
||||
mock_buckets = MagicMock()
|
||||
mock_buckets.list.return_value.execute.side_effect = vpc_error
|
||||
mock_client.return_value.buckets.return_value = mock_buckets
|
||||
|
||||
cloudstorage_client = CloudStorage(
|
||||
set_mocked_gcp_provider(project_ids=[GCP_PROJECT_ID])
|
||||
)
|
||||
|
||||
assert (
|
||||
GCP_PROJECT_ID
|
||||
in cloudstorage_client.vpc_service_controls_protected_projects
|
||||
)
|
||||
assert len(cloudstorage_client.buckets) == 0
|
||||
|
||||
+67
@@ -315,3 +315,70 @@ class TestCloudStorageUsesVPCServiceControls:
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
def test_project_protected_by_vpc_sc_api_blocked(self):
|
||||
cloudresourcemanager_client = mock.MagicMock()
|
||||
accesscontextmanager_client = mock.MagicMock()
|
||||
cloudstorage_client = mock.MagicMock()
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_gcp_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.gcp.services.cloudstorage.cloudstorage_uses_vpc_service_controls.cloudstorage_uses_vpc_service_controls.cloudresourcemanager_client",
|
||||
new=cloudresourcemanager_client,
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.gcp.services.cloudstorage.cloudstorage_uses_vpc_service_controls.cloudstorage_uses_vpc_service_controls.accesscontextmanager_client",
|
||||
new=accesscontextmanager_client,
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.gcp.services.cloudstorage.cloudstorage_uses_vpc_service_controls.cloudstorage_uses_vpc_service_controls.cloudstorage_client",
|
||||
new=cloudstorage_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.gcp.services.cloudresourcemanager.cloudresourcemanager_service import (
|
||||
Project,
|
||||
)
|
||||
from prowler.providers.gcp.services.cloudstorage.cloudstorage_uses_vpc_service_controls.cloudstorage_uses_vpc_service_controls import (
|
||||
cloudstorage_uses_vpc_service_controls,
|
||||
)
|
||||
|
||||
project1 = Project(
|
||||
id=GCP_PROJECT_ID, number="123456789012", audit_logging=True
|
||||
)
|
||||
|
||||
cloudresourcemanager_client.project_ids = [GCP_PROJECT_ID]
|
||||
cloudresourcemanager_client.cloud_resource_manager_projects = [project1]
|
||||
cloudresourcemanager_client.projects = {
|
||||
GCP_PROJECT_ID: GCPProject(
|
||||
id=GCP_PROJECT_ID,
|
||||
number="123456789012",
|
||||
name="test-project",
|
||||
labels={},
|
||||
lifecycle_state="ACTIVE",
|
||||
)
|
||||
}
|
||||
cloudresourcemanager_client.region = GCP_US_CENTER1_LOCATION
|
||||
|
||||
# No service perimeters configured, but API access is blocked by VPC SC
|
||||
accesscontextmanager_client.service_perimeters = []
|
||||
cloudstorage_client.vpc_service_controls_protected_projects = {
|
||||
GCP_PROJECT_ID
|
||||
}
|
||||
|
||||
check = cloudstorage_uses_vpc_service_controls()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Project {GCP_PROJECT_ID} has VPC Service Controls enabled for Cloud Storage in undetermined perimeter (verified by API access restriction)."
|
||||
)
|
||||
assert result[0].resource_id == GCP_PROJECT_ID
|
||||
assert result[0].resource_name == "test-project"
|
||||
assert result[0].location == GCP_US_CENTER1_LOCATION
|
||||
assert result[0].project_id == GCP_PROJECT_ID
|
||||
|
||||
Reference in New Issue
Block a user