#!/bin/sh set -eu ROOT=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd) cd "$ROOT" SHELLCHECK_IMAGE="koalaman/shellcheck-alpine:v0.11.0@sha256:9955be09ea7f0dbf7ae942ac1f2094355bb30d96fffba0ec09f5432207544002" HADOLINT_IMAGE="hadolint/hadolint:v2.14.0-debian@sha256:158cd0184dcaa18bd8ec20b61f4c1cabdf8b32a592d062f57bdcb8e4c1d312e2" ACTIONLINT_IMAGE="rhysd/actionlint:1.7.12@sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667" TRIVY_IMAGE="aquasec/trivy:0.72.0@sha256:cffe3f5161a47a6823fbd23d985795b3ed72a4c806da4c4df16266c02accdd6f" PROMETHEUS_IMAGE="quay.io/prometheus/prometheus:v3.13.1@sha256:3c42b892cf723fa54d2f262c37a0e1f80aa8c8ddb1da7b9b0df9455a35a7f893" ALERTMANAGER_IMAGE="quay.io/prometheus/alertmanager:v0.33.1@sha256:9e082985f56f4c8c9f724e18f2288c6708f472e56a5286b8863d080434ea065d" PYTHON_IMAGE="python:3.14.6-alpine3.23@sha256:b165067c5afc37fa5608a3c05609cc3d51aafd808a30fbfd822ee594fef55ad4" run_id="$(date -u +%Y%m%d%H%M%S)-$$" project="wnh_quality_$(printf '%s' "$run_id" | tr -d '-')" quality_image="who-need-help:quality-$run_id" assets_image="who-need-help:assets-audit-$run_id" e2e_image="who-need-help:e2e-audit-$run_id" release_image="who-need-help:security-$run_id" backup_image="who-need-help:backup-audit-$run_id" minio_image="who-need-help:minio-audit-$run_id" mc_image="who-need-help:mc-audit-$run_id" boundary_mock_image="who-need-help:boundary-mock-audit-$run_id" socket_proxy_image="who-need-help:socket-proxy-audit-$run_id" postgis_image="who-need-help:postgis-audit-$run_id" caddy_image="who-need-help:caddy-audit-$run_id" traefik_image="who-need-help:traefik-audit-$run_id" socket_proxy_container="wnh-socket-proxy-audit-$run_id" scan_dir=$(mktemp -d "${TMPDIR:-/tmp}/wnh-quality-scan.XXXXXX") scan_list="${scan_dir}.files" scan_tar="${scan_dir}.tar" android_fingerprint_probe="$ROOT/android/.quality-source-fingerprint-$run_id" umask 077 QUALITY_POSTGRES_USER="wnh_quality_$(openssl rand -hex 6)" QUALITY_POSTGRES_PASSWORD=$(openssl rand -base64 48 | tr -d '\n') export QUALITY_POSTGRES_USER QUALITY_POSTGRES_PASSWORD QUALITY_POSTGIS_IMAGE=$postgis_image export QUALITY_POSTGIS_IMAGE compose="docker compose -p $project -f $ROOT/compose.quality.yaml" cleanup() { trap - EXIT HUP INT TERM $compose down --volumes --remove-orphans >/dev/null 2>&1 || true docker rm --force "$socket_proxy_container" >/dev/null 2>&1 || true docker image rm "$quality_image" "$assets_image" "$e2e_image" "$release_image" \ "$backup_image" "$minio_image" "$mc_image" \ "$boundary_mock_image" "$socket_proxy_image" "$postgis_image" \ "$caddy_image" "$traefik_image" \ >/dev/null 2>&1 || true rm -f "$android_fingerprint_probe" rm -rf "$scan_dir" "$scan_list" "$scan_tar" } trap cleanup EXIT HUP INT TERM scan_image() { image=$1 scan_image_sequence=$((scan_image_sequence + 1)) image_archive="$scan_dir/trivy-image-$scan_image_sequence.tar" if ! docker image inspect "$image" >/dev/null 2>&1; then docker pull "$image" >/dev/null fi docker image save --output "$image_archive" "$image" docker run --rm \ --volume "$scan_dir:/scan:ro" \ --volume "$ROOT/.tools/trivy-cache:/root/.cache/trivy" \ "$TRIVY_IMAGE" image \ --input "/scan/$(basename "$image_archive")" \ --scanners vuln \ --severity HIGH,CRITICAL \ --ignore-unfixed \ --exit-code 1 rm -f "$image_archive" } scan_image_sequence=0 echo "Checking shell scripts with ShellCheck 0.11.0" # Word splitting is intentional: find emits repository-controlled paths and # ShellCheck expects each file as a separate argument. # shellcheck disable=SC2046 docker run --rm \ --volume "$ROOT:/mnt:ro" \ --workdir /mnt \ --entrypoint shellcheck \ "$SHELLCHECK_IMAGE" \ $(find scripts -type f -name '*.sh' -print | sort) echo "Checking the production read-only load safety boundary" ./scripts/production-readonly-load-drill.sh echo "Checking isolated production application rollback plan/apply" ./scripts/production-rollback-drill.sh echo "Checking release migration compatibility policy" ./scripts/release-migration-policy-drill.sh echo "Checking isolated production release orchestration" ./scripts/production-release-drill.sh echo "Checking Dockerfiles with Hadolint 2.14.0" for dockerfile in Dockerfile Dockerfile.backup Dockerfile.minio \ Dockerfile.postgis Dockerfile.socket-proxy Dockerfile.traefik \ Dockerfile.caddy \ android/Dockerfile e2e/Dockerfile ops/external-boundaries/Dockerfile; do docker run --rm --interactive "$HADOLINT_IMAGE" \ hadolint --failure-threshold warning - <"$dockerfile" done echo "Checking the GitHub and Gitea Actions workflows with actionlint 1.7.12" docker run --rm \ --volume "$ROOT:/repo:ro" \ --workdir /repo \ "$ACTIONLINT_IMAGE" \ -config-file .github/actionlint.yaml \ .github/workflows/quality.yml \ .gitea/workflows/quality.yml echo "Checking crash dumps are excluded from the Docker build context" grep -Fx 'core' .dockerignore >/dev/null grep -Fx 'core.*' .dockerignore >/dev/null echo "Checking local Gitea runner state is excluded from Git and Docker contexts" grep -Fx '/.runner' .gitignore >/dev/null grep -Fx '/act_runner' .gitignore >/dev/null grep -Fx '/act_runner-data/' .gitignore >/dev/null grep -Fx '/.runner' .dockerignore >/dev/null grep -Fx '/act_runner' .dockerignore >/dev/null grep -Fx '/act_runner-data/' .dockerignore >/dev/null echo "Checking Android artifacts are bound to their exact source tree" android_fingerprint_before=$(./scripts/android-source-fingerprint.sh) android_artifact_probe="$scan_dir/android-artifact" mkdir "$android_artifact_probe" printf '%s\n' "$android_fingerprint_before" \ >"$android_artifact_probe/source-fingerprint.sha256" ./scripts/verify-android-artifact-source.sh \ "$android_artifact_probe" \ scripts/android-development-build.sh >/dev/null printf '%s\n' 'quality source mutation' >"$android_fingerprint_probe" android_fingerprint_after=$(./scripts/android-source-fingerprint.sh) if [ "$android_fingerprint_before" = "$android_fingerprint_after" ]; then echo "Android source fingerprint did not change for a source mutation." >&2 exit 1 fi if ./scripts/verify-android-artifact-source.sh \ "$android_artifact_probe" \ scripts/android-development-build.sh >/dev/null 2>&1; then echo "Android artifact verifier accepted stale source inputs." >&2 exit 1 fi rm -f "$android_fingerprint_probe" test "$(./scripts/android-source-fingerprint.sh)" = "$android_fingerprint_before" printf '%s\n' malformed >"$android_artifact_probe/source-fingerprint.sha256" if ./scripts/verify-android-artifact-source.sh \ "$android_artifact_probe" \ scripts/android-development-build.sh >/dev/null 2>&1; then echo "Android artifact verifier accepted a malformed fingerprint." >&2 exit 1 fi rm -f "$android_artifact_probe/source-fingerprint.sha256" if ./scripts/verify-android-artifact-source.sh \ "$android_artifact_probe" \ scripts/android-development-build.sh >/dev/null 2>&1; then echo "Android artifact verifier accepted a missing fingerprint." >&2 exit 1 fi unset android_fingerprint_before android_fingerprint_after echo "Checking atomic environment credential imports" credential_env="$scan_dir/credentials.env" cp .env.example "$credential_env" chmod 600 "$credential_env" credential_values="$scan_dir/credential-values" printf '%s\n' \ 'GOOGLE_OAUTH_CLIENT_ID=quality-imported-client' \ 'GOOGLE_OAUTH_CLIENT_SECRET=quality-imported-secret' \ "SMTP_PASSWORD=quality\$literal" >"$credential_values" chmod 600 "$credential_values" credential_output=$( ./scripts/set-env-values.sh "$credential_env" "$credential_values" ) if printf '%s' "$credential_output" | grep -F 'quality-imported-secret' >/dev/null; then echo "Environment updater printed a secret value." >&2 exit 1 fi test "$(stat -c '%a' "$credential_env")" = 600 grep -Fx 'GOOGLE_OAUTH_CLIENT_ID=quality-imported-client' "$credential_env" >/dev/null grep -Fx 'GOOGLE_OAUTH_CLIENT_SECRET=quality-imported-secret' "$credential_env" >/dev/null grep -Fx "SMTP_PASSWORD=quality\$\$literal" "$credential_env" >/dev/null compose_env_probe="$scan_dir/compose-env-probe.yaml" printf '%s\n' \ 'services:' \ ' probe:' \ " image: $SHELLCHECK_IMAGE" \ ' network_mode: none' \ ' entrypoint: ["/usr/bin/env"]' \ ' environment:' \ " SMTP_PASSWORD: \${SMTP_PASSWORD}" \ >"$compose_env_probe" resolved_smtp_password=$( docker compose \ --project-name "$project" \ --env-file "$credential_env" \ --file "$compose_env_probe" \ run --rm --no-deps probe | awk -F= ' $1 == "SMTP_PASSWORD" { print substr($0, index($0, "=") + 1) exit } ' ) test "$resolved_smtp_password" = "quality\$literal" duplicate_env="$scan_dir/credentials-duplicate.env" cp "$credential_env" "$duplicate_env" printf '%s\n' 'GOOGLE_OAUTH_CLIENT_ID=duplicate' >>"$duplicate_env" if ./scripts/set-env-values.sh \ "$duplicate_env" "$credential_values" >/dev/null 2>&1; then echo "Environment updater accepted a duplicate target key." >&2 exit 1 fi echo "Checking single-file environment template synchronization" sync_template="$scan_dir/sync-template.env.example" sync_env="$scan_dir/sync.env" printf '%s\n' \ '# Template comment' \ 'FIRST_VALUE=template-default' \ 'SECOND_VALUE=' \ >"$sync_template" printf '%s\n' \ 'SECOND_VALUE=preserve-this-value' \ 'LOCAL_ONLY_VALUE=preserve-local-key' \ 'FIRST_VALUE=preserve-first-value' \ >"$sync_env" chmod 600 "$sync_env" sync_output=$( ./scripts/sync-env-template.sh "$sync_env" "$sync_template" ) if printf '%s' "$sync_output" | grep -F 'preserve-this-value' >/dev/null; then echo "Environment synchronizer printed an environment value." >&2 exit 1 fi test "$(stat -c '%a' "$sync_env")" = 600 grep -Fx '# Template comment' "$sync_env" >/dev/null grep -Fx 'FIRST_VALUE=preserve-first-value' "$sync_env" >/dev/null grep -Fx 'SECOND_VALUE=preserve-this-value' "$sync_env" >/dev/null grep -Fx 'LOCAL_ONLY_VALUE=preserve-local-key' "$sync_env" >/dev/null test "$(grep -Fc 'FIRST_VALUE=' "$sync_env")" = 1 test "$(grep -Fc 'SECOND_VALUE=' "$sync_env")" = 1 test "$(grep -Fc 'LOCAL_ONLY_VALUE=' "$sync_env")" = 1 sync_hash=$(sha256sum "$sync_env" | awk '{print $1}') ./scripts/sync-env-template.sh "$sync_env" "$sync_template" >/dev/null test "$(sha256sum "$sync_env" | awk '{print $1}')" = "$sync_hash" sync_duplicate="$scan_dir/sync-duplicate.env" printf '%s\n' \ 'FIRST_VALUE=one' \ 'FIRST_VALUE=two' \ >"$sync_duplicate" chmod 600 "$sync_duplicate" if ./scripts/sync-env-template.sh \ "$sync_duplicate" "$sync_template" >/dev/null 2>&1; then echo "Environment synchronizer accepted duplicate source keys." >&2 exit 1 fi google_client="$scan_dir/google-oauth-client.json" printf '%s\n' \ '{"web":{"client_id":"quality-google-client","project_id":"quality-development","client_secret":"quality-google-secret","redirect_uris":["https://dev.help.test/auth/google/callback"]}}' \ >"$google_client" chmod 600 "$google_client" sed -i 's|^WNH_BASE_URL=.*|WNH_BASE_URL=https://dev.help.test|' "$credential_env" oauth_output=$( ./scripts/import-google-oauth-client.sh "$credential_env" "$google_client" ) if printf '%s' "$oauth_output" | grep -F 'quality-google-secret' >/dev/null; then echo "Google OAuth importer printed a client secret." >&2 exit 1 fi grep -Fx 'GOOGLE_OAUTH_CLIENT_ID=quality-google-client' "$credential_env" >/dev/null grep -Fx 'GOOGLE_OAUTH_CLIENT_SECRET=quality-google-secret' "$credential_env" >/dev/null firebase_client="$scan_dir/google-services.json" printf '%s\n' \ '{"project_info":{"project_number":"123456789","project_id":"quality-development"},"client":[{"client_info":{"mobilesdk_app_id":"1:123456789:android:quality","android_client_info":{"package_name":"org.whoneedhelp.mobile.staging"}},"api_key":[{"current_key":"quality-firebase-api-key"}]}]}' \ >"$firebase_client" sed -i \ 's|^ANDROID_APP_LINKS_PACKAGE_NAME=.*|ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile.staging|' \ "$credential_env" ./scripts/import-firebase-android-config.sh \ "$credential_env" "$firebase_client" >/dev/null grep -Fx 'WNH_FIREBASE_APPLICATION_ID=1:123456789:android:quality' "$credential_env" >/dev/null grep -Fx 'WNH_FIREBASE_API_KEY=quality-firebase-api-key' "$credential_env" >/dev/null grep -Fx 'WNH_FIREBASE_PROJECT_ID=quality-development' "$credential_env" >/dev/null grep -Fx 'WNH_FIREBASE_GCM_SENDER_ID=123456789' "$credential_env" >/dev/null firebase_mismatched_client="$scan_dir/google-services-mismatched.json" printf '%s\n' \ '{"project_info":{"project_number":"123456789","project_id":"quality-development"},"client":[{"client_info":{"mobilesdk_app_id":"1:987654321:android:quality","android_client_info":{"package_name":"org.whoneedhelp.mobile.staging"}},"api_key":[{"current_key":"quality-firebase-api-key"}]}]}' \ >"$firebase_mismatched_client" if ./scripts/import-firebase-android-config.sh \ "$credential_env" "$firebase_mismatched_client" >/dev/null 2>&1; then echo "Firebase importer accepted an application ID from another project number." >&2 exit 1 fi firebase_injected_client="$scan_dir/google-services-injected.json" injected_firebase_project=$( printf 'quality-development\nGOOGLE_OAUTH_CLIENT_SECRET=injected' ) jq --null-input \ --arg project_id "$injected_firebase_project" \ '{ project_info: { project_number: "123456789", project_id: $project_id }, client: [ { client_info: { mobilesdk_app_id: "1:123456789:android:quality", android_client_info: { package_name: "org.whoneedhelp.mobile.staging" } }, api_key: [ { current_key: "quality-firebase-api-key" } ] } ] }' >"$firebase_injected_client" credential_hash=$(sha256sum "$credential_env" | awk '{print $1}') if ./scripts/import-firebase-android-config.sh \ "$credential_env" "$firebase_injected_client" >/dev/null 2>&1; then echo "Firebase importer accepted a line-breaking project ID." >&2 exit 1 fi test "$(sha256sum "$credential_env" | awk '{print $1}')" = "$credential_hash" echo "Checking Android environment isolation" android_fingerprint=D7:C4:F1:12:4D:F4:68:E5:B3:54:DE:D8:96:E8:01:51:29:41:F1:8A:71:0C:18:B0:E7:98:AA:2B:81:DA:11:DF android_env="$scan_dir/android-development.env" printf '%s\n' \ 'DEPLOYMENT_ENV=development' \ 'PHX_HOST=dev.help.test' \ 'PHX_SCHEME=https' \ 'PHX_URL_PORT=443' \ 'WNH_BASE_URL=https://dev.help.test' \ 'GOOGLE_OAUTH_CLIENT_ID=quality-web-client' \ 'GOOGLE_OAUTH_AUTHORIZED_PARTY_IDS=quality-android-client' \ 'ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile.development' \ "ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=$android_fingerprint" \ 'ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=' \ >"$android_env" chmod 600 "$android_env" ./scripts/validate-android-environment.sh \ "$android_env" development >/dev/null android_missing_authorized_party_env="$scan_dir/android-missing-authorized-party.env" grep -v '^GOOGLE_OAUTH_AUTHORIZED_PARTY_IDS=' \ "$android_env" >"$android_missing_authorized_party_env" chmod 600 "$android_missing_authorized_party_env" if ./scripts/validate-android-environment.sh \ "$android_missing_authorized_party_env" development >/dev/null 2>&1; then echo "Android validation accepted an empty Google authorized-party allowlist." >&2 exit 1 fi sed -i \ 's|^ANDROID_APP_LINKS_PACKAGE_NAME=.*|ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile.staging|' \ "$android_env" if ./scripts/validate-android-environment.sh \ "$android_env" development >/dev/null 2>&1; then echo "Development Android validation accepted the test package." >&2 exit 1 fi sed -i \ 's|^DEPLOYMENT_ENV=.*|DEPLOYMENT_ENV=test|; s|^ANDROID_APP_LINKS_PACKAGE_NAME=.*|ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile.staging|' \ "$android_env" ./scripts/validate-android-environment.sh "$android_env" test >/dev/null sed -i \ "s|^DEPLOYMENT_ENV=.*|DEPLOYMENT_ENV=production|; s|^ANDROID_APP_LINKS_PACKAGE_NAME=.*|ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile|; s|^ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=.*|ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=$android_fingerprint|" \ "$android_env" ./scripts/validate-android-environment.sh \ "$android_env" production >/dev/null fcm_service_account="$scan_dir/fcm-service-account.json" printf '%s\n' \ '{"type":"service_account","project_id":"quality-development","client_email":"quality-fcm@quality-development.iam.gserviceaccount.com","private_key":"quality-private-key"}' \ >"$fcm_service_account" chmod 600 "$fcm_service_account" fcm_output=$( ./scripts/import-fcm-service-account.sh \ "$credential_env" "$fcm_service_account" ) if printf '%s' "$fcm_output" | grep -F 'quality-private-key' >/dev/null; then echo "FCM importer printed a private key." >&2 exit 1 fi grep -Fx 'FCM_PROJECT_ID=quality-development' "$credential_env" >/dev/null grep -Fx 'FCM_SERVICE_ACCOUNT_FILE=' "$credential_env" >/dev/null credential_fcm_base64=$( awk -F= ' $1 == "FCM_SERVICE_ACCOUNT_JSON_BASE64" { print substr($0, index($0, "=") + 1) exit } ' "$credential_env" ) printf '%s' "$credential_fcm_base64" | base64 -d | jq --exit-status \ '.project_id == "quality-development" and .private_key == "quality-private-key"' \ >/dev/null fcm_injected_service_account="$scan_dir/fcm-service-account-injected.json" injected_fcm_project=$( printf 'quality-development\nGOOGLE_OAUTH_CLIENT_SECRET=injected' ) jq --null-input \ --arg project_id "$injected_fcm_project" \ '{ type: "service_account", project_id: $project_id, client_email: "quality-fcm@example.invalid", private_key: "quality-private-key" }' >"$fcm_injected_service_account" chmod 600 "$fcm_injected_service_account" credential_hash=$(sha256sum "$credential_env" | awk '{print $1}') if ./scripts/import-fcm-service-account.sh \ "$credential_env" "$fcm_injected_service_account" >/dev/null 2>&1; then echo "FCM importer accepted a line-breaking project ID." >&2 exit 1 fi test "$(sha256sum "$credential_env" | awk '{print $1}')" = "$credential_hash" rm -f "$fcm_injected_service_account" echo "Checking the existing load environment upgrade path" legacy_load_env="$scan_dir/legacy-load.env" printf '%s\n' \ 'LOAD_PROJECT=who_need_help_load' \ 'LOAD_WEB_REPLICAS=3' \ 'SECRET_KEY_BASE=preserve-existing-secret' >"$legacy_load_env" chmod 600 "$legacy_load_env" WNH_LOAD_ENV_FILE="$legacy_load_env" \ ./scripts/ensure-local-load-env.sh >/dev/null test "$(stat -c '%a' "$legacy_load_env")" = 600 grep -Fx 'APP_IMAGE=who-need-help:load' "$legacy_load_env" >/dev/null grep -Fx 'POSTGIS_IMAGE=who-need-help:postgis-load' "$legacy_load_env" >/dev/null grep -Fx 'SOCKET_PROXY_IMAGE=who-need-help:socket-proxy-load' "$legacy_load_env" >/dev/null grep -Fx 'APP_TOPOLOGY=split' "$legacy_load_env" >/dev/null grep -Fx 'LOAD_WEB_REPLICAS=3' "$legacy_load_env" >/dev/null test "$(grep -Fc 'SECRET_KEY_BASE=preserve-existing-secret' "$legacy_load_env")" = 1 legacy_load_hash=$(sha256sum "$legacy_load_env" | awk '{print $1}') WNH_LOAD_ENV_FILE="$legacy_load_env" \ ./scripts/ensure-local-load-env.sh >/dev/null test "$(sha256sum "$legacy_load_env" | awk '{print $1}')" = "$legacy_load_hash" echo "Checking independent test and production environment initialization" quality_fcm_base64=$( printf '%s' \ '{"type":"service_account","project_id":"quality-production","client_email":"quality-fcm@quality-production.iam.gserviceaccount.com","private_key":"quality-private-key"}' | base64 -w 0 ) quality_test_fcm_base64=$( printf '%s' \ '{"type":"service_account","project_id":"quality-test","client_email":"quality-fcm@quality-test.iam.gserviceaccount.com","private_key":"quality-private-key"}' | base64 -w 0 ) quality_other_fcm_base64=$( printf '%s' \ '{"type":"service_account","project_id":"quality-other","client_email":"quality-fcm@quality-other.iam.gserviceaccount.com","private_key":"quality-private-key"}' | base64 -w 0 ) test_env="$scan_dir/test.env" if ./scripts/init-test-env.sh test.help.test \ "$scan_dir/test.missing-codex.env" >/dev/null 2>&1; then echo "Test environment initializer accepted a missing Codex session ID." >&2 exit 1 fi TEST_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ TEST_GOOGLE_OAUTH_CLIENT_ID=quality-test-client \ TEST_GOOGLE_OAUTH_CLIENT_SECRET="quality-test\$secret" \ TEST_GOOGLE_OAUTH_AUTHORIZED_PARTY_IDS=quality-test-android-client \ TEST_WEB_PUSH_VAPID_PUBLIC_KEY=quality-test-vapid-public \ TEST_WEB_PUSH_VAPID_PRIVATE_KEY=quality-test-vapid-private \ TEST_WEB_PUSH_VAPID_SUBJECT=mailto:test@help.test \ TEST_WNH_FIREBASE_APPLICATION_ID=1:123456789:android:quality-test \ TEST_WNH_FIREBASE_API_KEY=quality-test-public-client-key \ TEST_WNH_FIREBASE_PROJECT_ID=quality-test \ TEST_WNH_FIREBASE_GCM_SENDER_ID=123456789 \ TEST_FCM_PROJECT_ID=quality-test \ TEST_FCM_SERVICE_ACCOUNT_JSON_BASE64="$quality_test_fcm_base64" \ TEST_ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile.staging \ TEST_ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=D7:C4:F1:12:4D:F4:68:E5:B3:54:DE:D8:96:E8:01:51:29:41:F1:8A:71:0C:18:B0:E7:98:AA:2B:81:DA:11:DF \ ./scripts/init-test-env.sh test.help.test "$test_env" >/dev/null test "$(stat -c '%a' "$test_env")" = 600 grep -Fx 'DEPLOYMENT_ENV=test' "$test_env" >/dev/null grep -Fx 'COMPOSE_PROJECT_NAME=who_need_help_test' "$test_env" >/dev/null grep -E '^APP_IMAGE=who-need-help:test-[0-9a-f]{12}$' "$test_env" >/dev/null grep -Fx 'APP_TOPOLOGY=compact' "$test_env" >/dev/null grep -Fx 'DATABASE_MODE=container' "$test_env" >/dev/null grep -Fx 'POSTGRES_DB=who_need_help_test' "$test_env" >/dev/null grep -Fx 'PUBLIC_EDGE_ENABLED=true' "$test_env" >/dev/null grep -Fx 'PUBLIC_UPSTREAM_NAME=who-need-help-test' "$test_env" >/dev/null grep -Fx 'PHX_HOST=test.help.test' "$test_env" >/dev/null grep -Fx 'PHX_SCHEME=https' "$test_env" >/dev/null grep -Fx 'PHX_URL_PORT=443' "$test_env" >/dev/null grep -Fx 'EMAIL_DELIVERY_PROVIDER=smtp' "$test_env" >/dev/null grep -Fx 'SMTP_RELAY=mailpit' "$test_env" >/dev/null grep -Fx 'EMAIL_FROM_NAME="Who Need Help Test"' "$test_env" >/dev/null grep -Fx 'GOOGLE_OAUTH_CLIENT_ID=quality-test-client' "$test_env" >/dev/null grep -Fx "GOOGLE_OAUTH_CLIENT_SECRET=quality-test\$\$secret" "$test_env" >/dev/null grep -Fx 'GOOGLE_OAUTH_AUTHORIZED_PARTY_IDS=quality-test-android-client' \ "$test_env" >/dev/null grep -Fx 'WEB_PUSH_VAPID_SUBJECT=mailto:test@help.test' "$test_env" >/dev/null grep -Fx 'WNH_FIREBASE_APPLICATION_ID=1:123456789:android:quality-test' "$test_env" >/dev/null grep -Fx 'WNH_FIREBASE_PROJECT_ID=quality-test' "$test_env" >/dev/null grep -Fx 'WNH_FIREBASE_GCM_SENDER_ID=123456789' "$test_env" >/dev/null grep -Fx 'FCM_PROJECT_ID=quality-test' "$test_env" >/dev/null grep -Fx 'ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile.staging' "$test_env" >/dev/null grep -Fx 'ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=D7:C4:F1:12:4D:F4:68:E5:B3:54:DE:D8:96:E8:01:51:29:41:F1:8A:71:0C:18:B0:E7:98:AA:2B:81:DA:11:DF' "$test_env" >/dev/null ./scripts/validate-test-env.sh "$test_env" test.help.test >/dev/null if TEST_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ TEST_GOOGLE_OAUTH_CLIENT_ID=quality-test-client \ ./scripts/init-test-env.sh test.help.test \ "$scan_dir/test.partial-google.env" >/dev/null 2>&1; then echo "Test environment initializer accepted partial Google OAuth credentials." >&2 exit 1 fi if TEST_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ ./scripts/init-test-env.sh test.help.test "$test_env" >/dev/null 2>&1; then echo "Test environment initializer overwrote an existing file." >&2 exit 1 fi if TEST_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ TEST_WNH_FIREBASE_APPLICATION_ID=1:987654321:android:quality-test \ TEST_WNH_FIREBASE_API_KEY=quality-test-public-client-key \ TEST_WNH_FIREBASE_PROJECT_ID=quality-test \ TEST_WNH_FIREBASE_GCM_SENDER_ID=123456789 \ ./scripts/init-test-env.sh test.help.test \ "$scan_dir/test.mismatched-firebase.env" >/dev/null 2>&1; then echo "Test environment initializer accepted a Firebase application from another sender." >&2 exit 1 fi if TEST_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ TEST_FCM_PROJECT_ID=quality-test \ TEST_FCM_SERVICE_ACCOUNT_JSON_BASE64="$quality_other_fcm_base64" \ ./scripts/init-test-env.sh test.help.test \ "$scan_dir/test.mismatched-fcm-credential.env" >/dev/null 2>&1; then echo "Test environment initializer accepted an FCM service account from another project." >&2 exit 1 fi if TEST_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ TEST_WNH_FIREBASE_APPLICATION_ID=1:123456789:android:quality-test \ TEST_WNH_FIREBASE_API_KEY=quality-test-public-client-key \ TEST_WNH_FIREBASE_PROJECT_ID=quality-test \ TEST_WNH_FIREBASE_GCM_SENDER_ID=123456789 \ TEST_FCM_PROJECT_ID=quality-other \ TEST_FCM_SERVICE_ACCOUNT_JSON_BASE64="$quality_other_fcm_base64" \ ./scripts/init-test-env.sh test.help.test \ "$scan_dir/test.mismatched-firebase-fcm.env" >/dev/null 2>&1; then echo "Test environment initializer accepted different Firebase and FCM projects." >&2 exit 1 fi if TEST_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ TEST_ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile \ TEST_ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=D7:C4:F1:12:4D:F4:68:E5:B3:54:DE:D8:96:E8:01:51:29:41:F1:8A:71:0C:18:B0:E7:98:AA:2B:81:DA:11:DF \ ./scripts/init-test-env.sh test.help.test \ "$scan_dir/test.production-package.env" >/dev/null 2>&1; then echo "Test environment initializer accepted the production Android package." >&2 exit 1 fi if TEST_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ TEST_WEB_PUSH_VAPID_PUBLIC_KEY=quality-test-vapid-public \ TEST_WEB_PUSH_VAPID_PRIVATE_KEY=quality-test-vapid-private \ TEST_WEB_PUSH_VAPID_SUBJECT=ftp://help.test \ ./scripts/init-test-env.sh test.help.test \ "$scan_dir/test.invalid-vapid-subject.env" >/dev/null 2>&1; then echo "Test environment initializer accepted an invalid VAPID subject." >&2 exit 1 fi injected_test_secret=$( printf 'quality-test-secret\nSMTP_PASSWORD=injected' ) if TEST_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ TEST_GOOGLE_OAUTH_CLIENT_ID=quality-test-client \ TEST_GOOGLE_OAUTH_CLIENT_SECRET="$injected_test_secret" \ ./scripts/init-test-env.sh test.help.test \ "$scan_dir/test.injected-line.env" >/dev/null 2>&1; then echo "Test environment initializer accepted a line-breaking credential." >&2 exit 1 fi test ! -e "$scan_dir/test.injected-line.env" production_env="$scan_dir/production.env" missing_codex_env="$scan_dir/production.missing-codex.env" if PRODUCTION_TRAEFIK_TRUSTED_IPS=172.20.0.1/32 \ PRODUCTION_SMTP_RELAY=smtp.help.test \ PRODUCTION_SMTP_PORT=587 \ PRODUCTION_SMTP_USERNAME=quality-user \ PRODUCTION_SMTP_PASSWORD=quality-password \ PRODUCTION_SMTP_AUTH=always \ PRODUCTION_SMTP_TLS=always \ PRODUCTION_SMTP_SSL=false \ PRODUCTION_EMAIL_FROM_ADDRESS=contact@help.test \ ./scripts/init-production-env.sh help.test "$missing_codex_env" >/dev/null 2>&1; then echo "Production environment initializer accepted a missing Codex session ID." >&2 exit 1 fi PRODUCTION_TRAEFIK_TRUSTED_IPS=172.20.0.1/32 \ PRODUCTION_SMTP_RELAY=smtp.help.test \ PRODUCTION_SMTP_PORT=587 \ PRODUCTION_SMTP_USERNAME=quality-user \ PRODUCTION_SMTP_PASSWORD="quality\$password" \ PRODUCTION_SMTP_AUTH=always \ PRODUCTION_SMTP_TLS=always \ PRODUCTION_SMTP_SSL=false \ PRODUCTION_EMAIL_FROM_ADDRESS=contact@help.test \ PRODUCTION_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ PRODUCTION_GOOGLE_OAUTH_CLIENT_ID=quality-production-client \ PRODUCTION_GOOGLE_OAUTH_CLIENT_SECRET=quality-production-secret \ PRODUCTION_GOOGLE_OAUTH_AUTHORIZED_PARTY_IDS=quality-production-android-client \ PRODUCTION_WEB_PUSH_VAPID_PUBLIC_KEY=quality-vapid-public \ PRODUCTION_WEB_PUSH_VAPID_PRIVATE_KEY=quality-vapid-private \ PRODUCTION_WEB_PUSH_VAPID_SUBJECT=mailto:contact@help.test \ PRODUCTION_WNH_FIREBASE_APPLICATION_ID=1:123456789:android:quality \ PRODUCTION_WNH_FIREBASE_API_KEY=quality-public-client-key \ PRODUCTION_WNH_FIREBASE_PROJECT_ID=quality-production \ PRODUCTION_WNH_FIREBASE_GCM_SENDER_ID=123456789 \ PRODUCTION_FCM_PROJECT_ID=quality-production \ PRODUCTION_FCM_SERVICE_ACCOUNT_JSON_BASE64="$quality_fcm_base64" \ PRODUCTION_ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile \ PRODUCTION_ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=A5:74:2B:AE:70:C6:D0:34:E3:75:44:B6:2E:37:A3:75:C0:E0:05:64:74:50:F4:0F:29:B2:A9:84:F9:FD:B8:FB \ PRODUCTION_ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=A5:74:2B:AE:70:C6:D0:34:E3:75:44:B6:2E:37:A3:75:C0:E0:05:64:74:50:F4:0F:29:B2:A9:84:F9:FD:B8:FB \ PRODUCTION_SUPPORT_INBOX_ADDRESS=support@help.test \ ./scripts/init-production-env.sh help.test "$production_env" >/dev/null test "$(stat -c '%a' "$production_env")" = 600 grep -Fx "SMTP_PASSWORD=quality\$\$password" "$production_env" >/dev/null grep -Fx 'GOOGLE_OAUTH_AUTHORIZED_PARTY_IDS=quality-production-android-client' \ "$production_env" >/dev/null ./scripts/validate-production-env.sh "$production_env" help.test >/dev/null ./scripts/check-environment-readiness.sh "$production_env" --require-release >/dev/null if PRODUCTION_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ PRODUCTION_WNH_FIREBASE_APPLICATION_ID=1:987654321:android:quality \ PRODUCTION_WNH_FIREBASE_API_KEY=quality-public-client-key \ PRODUCTION_WNH_FIREBASE_PROJECT_ID=quality-production \ PRODUCTION_WNH_FIREBASE_GCM_SENDER_ID=123456789 \ ./scripts/init-production-env.sh help.test \ "$scan_dir/production.mismatched-firebase-init.env" >/dev/null 2>&1; then echo "Production environment initializer accepted a Firebase application from another sender." >&2 exit 1 fi if PRODUCTION_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ PRODUCTION_FCM_PROJECT_ID=quality-production \ PRODUCTION_FCM_SERVICE_ACCOUNT_JSON_BASE64="$quality_other_fcm_base64" \ ./scripts/init-production-env.sh help.test \ "$scan_dir/production.mismatched-fcm-credential.env" >/dev/null 2>&1; then echo "Production environment initializer accepted an FCM service account from another project." >&2 exit 1 fi if PRODUCTION_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ PRODUCTION_WNH_FIREBASE_APPLICATION_ID=1:123456789:android:quality \ PRODUCTION_WNH_FIREBASE_API_KEY=quality-public-client-key \ PRODUCTION_WNH_FIREBASE_PROJECT_ID=quality-production \ PRODUCTION_WNH_FIREBASE_GCM_SENDER_ID=123456789 \ PRODUCTION_FCM_PROJECT_ID=quality-other \ PRODUCTION_FCM_SERVICE_ACCOUNT_JSON_BASE64="$quality_other_fcm_base64" \ ./scripts/init-production-env.sh help.test \ "$scan_dir/production.mismatched-firebase-fcm.env" >/dev/null 2>&1; then echo "Production environment initializer accepted different Firebase and FCM projects." >&2 exit 1 fi if PRODUCTION_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ PRODUCTION_ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile.staging \ PRODUCTION_ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=A5:74:2B:AE:70:C6:D0:34:E3:75:44:B6:2E:37:A3:75:C0:E0:05:64:74:50:F4:0F:29:B2:A9:84:F9:FD:B8:FB \ PRODUCTION_ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=A5:74:2B:AE:70:C6:D0:34:E3:75:44:B6:2E:37:A3:75:C0:E0:05:64:74:50:F4:0F:29:B2:A9:84:F9:FD:B8:FB \ ./scripts/init-production-env.sh help.test \ "$scan_dir/production.staging-package.env" >/dev/null 2>&1; then echo "Production environment initializer accepted the staging Android package." >&2 exit 1 fi if PRODUCTION_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ PRODUCTION_ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile \ PRODUCTION_ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=A5:74:2B:AE:70:C6:D0:34:E3:75:44:B6:2E:37:A3:75:C0:E0:05:64:74:50:F4:0F:29:B2:A9:84:F9:FD:B8:FB \ PRODUCTION_ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=D7:C4:F1:12:4D:F4:68:E5:B3:54:DE:D8:96:E8:01:51:29:41:F1:8A:71:0C:18:B0:E7:98:AA:2B:81:DA:11:DF \ ./scripts/init-production-env.sh help.test \ "$scan_dir/production.unpublished-play-signing.env" >/dev/null 2>&1; then echo "Production environment initializer accepted a Play fingerprint absent from assetlinks." >&2 exit 1 fi if PRODUCTION_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ PRODUCTION_WEB_PUSH_VAPID_PUBLIC_KEY=quality-vapid-public \ PRODUCTION_WEB_PUSH_VAPID_PRIVATE_KEY=quality-vapid-private \ PRODUCTION_WEB_PUSH_VAPID_SUBJECT=ftp://help.test \ ./scripts/init-production-env.sh help.test \ "$scan_dir/production.invalid-vapid-subject.env" >/dev/null 2>&1; then echo "Production environment initializer accepted an invalid VAPID subject." >&2 exit 1 fi injected_smtp_password=$( printf 'quality-password\nGOOGLE_OAUTH_CLIENT_SECRET=injected' ) if PRODUCTION_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ PRODUCTION_SMTP_PASSWORD="$injected_smtp_password" \ ./scripts/init-production-env.sh help.test \ "$scan_dir/production.injected-line.env" >/dev/null 2>&1; then echo "Production environment initializer accepted a line-breaking credential." >&2 exit 1 fi test ! -e "$scan_dir/production.injected-line.env" invalid_fcm_env="$scan_dir/production.invalid-fcm.env" invalid_fcm_base64=$( printf '%s' '{"type":"service_account","project_id":"quality-production"}' | base64 -w 0 ) cp "$production_env" "$invalid_fcm_env" sed -i \ "s|^FCM_SERVICE_ACCOUNT_JSON_BASE64=.*|FCM_SERVICE_ACCOUNT_JSON_BASE64=$invalid_fcm_base64|" \ "$invalid_fcm_env" if ./scripts/validate-production-env.sh \ "$invalid_fcm_env" help.test >/dev/null 2>&1; then echo "Production validation accepted an incomplete FCM service account." >&2 exit 1 fi if ./scripts/check-environment-readiness.sh \ "$invalid_fcm_env" --require-release >/dev/null 2>&1; then echo "Environment readiness accepted an incomplete FCM service account." >&2 exit 1 fi mismatched_firebase_env="$scan_dir/production.mismatched-firebase.env" cp "$production_env" "$mismatched_firebase_env" sed -i \ 's|^WNH_FIREBASE_APPLICATION_ID=.*|WNH_FIREBASE_APPLICATION_ID=1:987654321:android:quality|' \ "$mismatched_firebase_env" if ./scripts/validate-production-env.sh \ "$mismatched_firebase_env" help.test >/dev/null 2>&1; then echo "Production validation accepted a Firebase application from another sender." >&2 exit 1 fi if ./scripts/check-environment-readiness.sh \ "$mismatched_firebase_env" --require-release >/dev/null 2>&1; then echo "Environment readiness accepted a Firebase application from another sender." >&2 exit 1 fi mismatched_fcm_env="$scan_dir/production.mismatched-fcm.env" mismatched_fcm_base64=$( printf '%s' \ '{"type":"service_account","project_id":"another-project","client_email":"quality-fcm@another-project.iam.gserviceaccount.com","private_key":"quality-private-key"}' | base64 -w 0 ) cp "$production_env" "$mismatched_fcm_env" sed -i \ "s|^FCM_SERVICE_ACCOUNT_JSON_BASE64=.*|FCM_SERVICE_ACCOUNT_JSON_BASE64=$mismatched_fcm_base64|" \ "$mismatched_fcm_env" if ./scripts/validate-production-env.sh \ "$mismatched_fcm_env" help.test >/dev/null 2>&1; then echo "Production validation accepted an FCM service account from another project." >&2 exit 1 fi if ./scripts/check-environment-readiness.sh \ "$mismatched_fcm_env" --require-release >/dev/null 2>&1; then echo "Environment readiness accepted an FCM service account from another project." >&2 exit 1 fi mismatched_app_links_env="$scan_dir/production.mismatched-app-links.env" cp "$production_env" "$mismatched_app_links_env" sed -i \ 's|^ANDROID_APP_LINKS_PACKAGE_NAME=.*|ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile.staging|' \ "$mismatched_app_links_env" if ./scripts/validate-production-env.sh \ "$mismatched_app_links_env" help.test >/dev/null 2>&1; then echo "Production validation accepted the staging Android package." >&2 exit 1 fi if ./scripts/check-environment-readiness.sh \ "$mismatched_app_links_env" --require-release >/dev/null 2>&1; then echo "Environment readiness accepted the staging Android package for production." >&2 exit 1 fi upload_only_app_links_env="$scan_dir/production.upload-only-app-links.env" cp "$production_env" "$upload_only_app_links_env" sed -i \ 's|^ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=.*|ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=|' \ "$upload_only_app_links_env" if ./scripts/validate-production-env.sh \ "$upload_only_app_links_env" help.test >/dev/null 2>&1; then echo "Production validation accepted upload-only Android App Links." >&2 exit 1 fi if ./scripts/check-environment-readiness.sh \ "$upload_only_app_links_env" --require-release >/dev/null 2>&1; then echo "Environment readiness accepted upload-only Android App Links." >&2 exit 1 fi ./scripts/validate-production-env.sh \ "$upload_only_app_links_env" help.test --allow-pre-play >/dev/null ./scripts/check-environment-readiness.sh \ "$upload_only_app_links_env" --require-server-release >/dev/null unpublished_play_app_links_env="$scan_dir/production.unpublished-play-app-links.env" cp "$production_env" "$unpublished_play_app_links_env" sed -i \ 's|^ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=.*|ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=D7:C4:F1:12:4D:F4:68:E5:B3:54:DE:D8:96:E8:01:51:29:41:F1:8A:71:0C:18:B0:E7:98:AA:2B:81:DA:11:DF|' \ "$unpublished_play_app_links_env" if ./scripts/validate-production-env.sh \ "$unpublished_play_app_links_env" help.test >/dev/null 2>&1; then echo "Production validation accepted an unpublished Play App Signing fingerprint." >&2 exit 1 fi if ./scripts/check-environment-readiness.sh \ "$unpublished_play_app_links_env" --require-release >/dev/null 2>&1; then echo "Environment readiness accepted an unpublished Play App Signing fingerprint." >&2 exit 1 fi if ./scripts/validate-production-env.sh \ "$unpublished_play_app_links_env" help.test \ --allow-pre-play >/dev/null 2>&1; then echo "Pre-Play validation accepted an unpublished Play App Signing fingerprint." >&2 exit 1 fi if ./scripts/check-environment-readiness.sh \ "$unpublished_play_app_links_env" \ --require-server-release >/dev/null 2>&1; then echo "Server-release readiness accepted an unpublished Play App Signing fingerprint." >&2 exit 1 fi grep -Fx 'COMPOSE_PROJECT_NAME=who_need_help_production' "$production_env" >/dev/null grep -E '^APP_IMAGE=who-need-help:production-[0-9a-f]{12}$' "$production_env" >/dev/null grep -Fx 'EDGE_COMPOSE_PROJECT_NAME=who_need_help_edge' "$production_env" >/dev/null grep -Fx 'PRIMARY_DOMAIN=help.test' "$production_env" >/dev/null grep -Fx 'TEST_DOMAIN=test.help.test' "$production_env" >/dev/null grep -Fx 'ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile' "$production_env" >/dev/null grep -Fx 'ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=A5:74:2B:AE:70:C6:D0:34:E3:75:44:B6:2E:37:A3:75:C0:E0:05:64:74:50:F4:0F:29:B2:A9:84:F9:FD:B8:FB' "$production_env" >/dev/null grep -Fx 'ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=A5:74:2B:AE:70:C6:D0:34:E3:75:44:B6:2E:37:A3:75:C0:E0:05:64:74:50:F4:0F:29:B2:A9:84:F9:FD:B8:FB' "$production_env" >/dev/null ./scripts/validate-edge-env.sh "$production_env" >/dev/null test_checkout="$scan_dir/test-checkout" production_checkout="$scan_dir/production-checkout" mkdir "$test_checkout" "$production_checkout" git -C "$test_checkout" init --quiet git -C "$production_checkout" init --quiet cp "$test_env" "$test_checkout/.env" cp "$production_env" "$production_checkout/.env" chmod 600 "$test_checkout/.env" "$production_checkout/.env" ./scripts/validate-deployment-isolation.sh \ "$test_checkout" "$production_checkout" >/dev/null # Account-level SMTP logins may be shared by a relay, but the independently # revocable SMTP passwords must remain isolated between deployments. production_smtp_relay=$(awk -F= '$1 == "SMTP_RELAY" { print substr($0, index($0, "=") + 1); exit }' "$production_checkout/.env") production_smtp_username=$(awk -F= '$1 == "SMTP_USERNAME" { print substr($0, index($0, "=") + 1); exit }' "$production_checkout/.env") production_smtp_password=$(awk -F= '$1 == "SMTP_PASSWORD" { print substr($0, index($0, "=") + 1); exit }' "$production_checkout/.env") sed -i \ -e 's/^EMAIL_DELIVERY_PROVIDER=.*/EMAIL_DELIVERY_PROVIDER=smtp/' \ -e "s|^SMTP_RELAY=.*|SMTP_RELAY=$production_smtp_relay|" \ -e "s|^SMTP_USERNAME=.*|SMTP_USERNAME=$production_smtp_username|" \ -e 's/^SMTP_PASSWORD=.*/SMTP_PASSWORD=quality-test-isolated-smtp-password/' \ "$test_checkout/.env" ./scripts/validate-deployment-isolation.sh \ "$test_checkout" "$production_checkout" >/dev/null sed -i \ "s|^SMTP_PASSWORD=.*|SMTP_PASSWORD=$production_smtp_password|" \ "$test_checkout/.env" if ./scripts/validate-deployment-isolation.sh \ "$test_checkout" "$production_checkout" >/dev/null 2>&1; then echo "Deployment isolation accepted a shared SMTP password." >&2 exit 1 fi cp "$test_env" "$test_checkout/.env" chmod 600 "$test_checkout/.env" placeholder_codex_env="$scan_dir/.env.production.placeholder-codex" cp "$production_env" "$placeholder_codex_env" chmod 600 "$placeholder_codex_env" sed -i \ 's/^CODEX_SESSION_ID=.*/CODEX_SESSION_ID=copy-the-main-local-codex-session-id/' \ "$placeholder_codex_env" if ./scripts/validate-production-env.sh \ "$placeholder_codex_env" help.test >/dev/null 2>&1; then echo "Production environment validator accepted the template Codex session ID." >&2 exit 1 fi external_production_env="$scan_dir/.env.production.external-db" PRODUCTION_DATABASE_MODE=external \ PRODUCTION_DATABASE_URL=ecto://quality:external-password@database.internal/who_need_help \ PRODUCTION_SMTP_RELAY=smtp.help.test \ PRODUCTION_SMTP_PORT=587 \ PRODUCTION_SMTP_USERNAME=quality-user \ PRODUCTION_SMTP_PASSWORD=quality-password \ PRODUCTION_SMTP_AUTH=always \ PRODUCTION_SMTP_TLS=always \ PRODUCTION_SMTP_SSL=false \ PRODUCTION_EMAIL_FROM_ADDRESS=contact@help.test \ PRODUCTION_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ ./scripts/init-production-env.sh help.test "$external_production_env" >/dev/null ./scripts/validate-production-env.sh "$external_production_env" help.test >/dev/null external_socket_production_env="$scan_dir/.env.production.external-db-socket" PRODUCTION_DATABASE_MODE=external \ PRODUCTION_DATABASE_URL=ecto://quality:external-password@localhost/who_need_help \ PRODUCTION_DATABASE_SOCKET_DIR=/var/run/postgresql \ PRODUCTION_SMTP_RELAY=smtp.help.test \ PRODUCTION_SMTP_PORT=587 \ PRODUCTION_SMTP_USERNAME=quality-user \ PRODUCTION_SMTP_PASSWORD=quality-password \ PRODUCTION_SMTP_AUTH=always \ PRODUCTION_SMTP_TLS=always \ PRODUCTION_SMTP_SSL=false \ PRODUCTION_EMAIL_FROM_ADDRESS=contact@help.test \ PRODUCTION_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ ./scripts/init-production-env.sh help.test "$external_socket_production_env" >/dev/null ./scripts/validate-production-env.sh \ "$external_socket_production_env" help.test >/dev/null if PRODUCTION_DATABASE_MODE=external \ PRODUCTION_DATABASE_URL=ecto://quality:external-password@localhost/who_need_help \ PRODUCTION_DATABASE_SOCKET_DIR=relative/socket \ PRODUCTION_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ ./scripts/init-production-env.sh \ help.test "$scan_dir/.env.production.invalid-socket" >/dev/null 2>&1; then echo "Production initializer accepted a relative database socket path." >&2 exit 1 fi if PRODUCTION_DATABASE_SOCKET_DIR=/var/run/postgresql \ PRODUCTION_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ ./scripts/init-production-env.sh \ help.test "$scan_dir/.env.production.container-socket" >/dev/null 2>&1; then echo "Production initializer accepted a host socket in container database mode." >&2 exit 1 fi external_split_production_env="$scan_dir/.env.production.external-db-split" PRODUCTION_APP_TOPOLOGY=split \ PRODUCTION_DATABASE_MODE=external \ PRODUCTION_DATABASE_URL=ecto://quality:external-password@database.internal/who_need_help \ PRODUCTION_TRAEFIK_TRUSTED_IPS=172.20.0.1/32 \ PRODUCTION_SMTP_RELAY=smtp.help.test \ PRODUCTION_SMTP_PORT=587 \ PRODUCTION_SMTP_USERNAME=quality-user \ PRODUCTION_SMTP_PASSWORD=quality-password \ PRODUCTION_SMTP_AUTH=always \ PRODUCTION_SMTP_TLS=always \ PRODUCTION_SMTP_SSL=false \ PRODUCTION_EMAIL_FROM_ADDRESS=contact@help.test \ PRODUCTION_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ ./scripts/init-production-env.sh help.test "$external_split_production_env" >/dev/null ./scripts/validate-production-env.sh "$external_split_production_env" help.test >/dev/null invalid_external_env="$scan_dir/.env.production.invalid-external-db" cp "$external_production_env" "$invalid_external_env" chmod 600 "$invalid_external_env" sed -i 's#^DATABASE_URL=.*#DATABASE_URL=ecto://quality:external-password@db/who_need_help#' \ "$invalid_external_env" if ./scripts/validate-production-env.sh \ "$invalid_external_env" help.test >/dev/null 2>&1; then echo "Production environment validator accepted the Compose db host in external mode." >&2 exit 1 fi partial_google_env="$scan_dir/.env.production.partial-google" cp "$production_env" "$partial_google_env" chmod 600 "$partial_google_env" sed -i 's/^GOOGLE_OAUTH_CLIENT_ID=.*/GOOGLE_OAUTH_CLIENT_ID=quality-client/' \ "$partial_google_env" sed -i 's/^GOOGLE_OAUTH_CLIENT_SECRET=.*/GOOGLE_OAUTH_CLIENT_SECRET=/' \ "$partial_google_env" if ./scripts/validate-production-env.sh \ "$partial_google_env" help.test >/dev/null 2>&1; then echo "Production environment validator accepted partial Google OAuth credentials." >&2 exit 1 fi if ./scripts/init-production-env.sh help.test "$production_env" >/dev/null 2>&1; then echo "Production environment initializer overwrote an existing file." >&2 exit 1 fi if PRODUCTION_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ PRODUCTION_WNH_FIREBASE_PROJECT_ID=partial-firebase \ ./scripts/init-production-env.sh \ help.test "$scan_dir/.env.production.partial-firebase" >/dev/null 2>&1; then echo "Production initializer accepted partial Firebase Android configuration." >&2 exit 1 fi incomplete_production_env="$scan_dir/.env.production.incomplete" PRODUCTION_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \ ./scripts/init-production-env.sh help.test "$incomplete_production_env" >/dev/null if ./scripts/validate-production-env.sh \ "$incomplete_production_env" help.test >/dev/null 2>&1; then echo "Production environment validator accepted unresolved deployment inputs." >&2 exit 1 fi echo "Rendering every Docker Compose profile" docker compose --project-name who_need_help_edge \ --project-directory "$ROOT" --env-file "$production_env" \ --file compose.edge.yaml config --format json | jq --exit-status ' .name == "who_need_help_edge" and (.services.edge.image | startswith("who-need-help:caddy-production-")) and .services.edge.user == "1000:1000" and .services.edge.read_only == true and .services.edge.cap_drop == ["ALL"] and .services.edge.cap_add == ["NET_BIND_SERVICE"] and .services.edge.security_opt == ["no-new-privileges:true"] and (.services.edge.tmpfs | index("/tmp") != null) and (.services.edge.ports | map(select(.target == 80 and .published == "80" and .protocol == "tcp")) | length) == 1 and (.services.edge.ports | map(select(.target == 443 and .published == "443" and .protocol == "tcp")) | length) == 1 and (.services.edge.ports | map(select(.target == 443 and .published == "443" and .protocol == "udp")) | length) == 1 and .networks.public_edge.name == "who_need_help_public_edge" ' >/dev/null ./scripts/compose.sh .env.example config --quiet ./scripts/compose.sh "$production_env" config --quiet ./scripts/compose.sh "$external_production_env" config --quiet ./scripts/compose.sh "$external_socket_production_env" config --quiet ./scripts/compose.sh "$external_split_production_env" config --quiet ./scripts/compose.sh "$test_env" config --format json | jq --exit-status ' .services.app.networks.internal.interface_name == "eth0" and .services.app.networks.egress.interface_name == "eth1" and .services.app.networks.public_edge.interface_name == "eth2" and .services.app.networks.public_edge.aliases == ["who-need-help-test"] ' >/dev/null ./scripts/compose.sh .env.example config --format json | jq --exit-status ' . as $root | [$root.services.migrate, $root.services.web, $root.services.worker] | all( .environment.ERL_ZFLAGS == "+Q 65536" and .environment.CLUSTER_INTERFACE == "eth0" and .read_only == true and .cap_drop == ["ALL"] and .security_opt == ["no-new-privileges:true"] and (.tmpfs | index("/tmp") != null) ) and $root.services.web.environment.POOL_SIZE == "4" and $root.services.worker.environment.POOL_SIZE == "2" and $root.services.migrate.environment.POOL_SIZE == "2" and $root.services.worker.environment.OBAN_MAINTENANCE_CONCURRENCY == "2" and $root.services.worker.environment.OBAN_PUSH_CONCURRENCY == "1" and $root.services.web.deploy.replicas == 2 and $root.services.worker.deploy.replicas == 2 and ($root.services.proxy.networks | keys | sort) == ["docker-api", "edge", "ingress"] and ($root.services.web.networks | keys | sort) == ["egress", "ingress", "internal"] and ($root.services.worker.networks | keys | sort) == ["egress", "internal"] and ($root.services.migrate.networks | keys | sort) == ["egress", "internal"] and $root.services.web.networks.internal.interface_name == "eth0" and $root.services.web.networks.internal.aliases == ["cluster-web"] and $root.services.web.networks.ingress.interface_name == "eth1" and $root.services.web.networks.egress.interface_name == "eth2" and $root.services.web.networks.egress.gw_priority == 1 and $root.services.worker.networks.internal.interface_name == "eth0" and $root.services.worker.networks.egress.interface_name == "eth1" and $root.services.worker.networks.egress.gw_priority == 1 and ($root.services.db.networks | keys) == ["internal"] and $root.networks.ingress.internal == true and $root.networks.internal.internal == true and ($root.networks.egress.internal // false) == false and $root.services.db.security_opt == ["no-new-privileges:true"] and $root.services.proxy.ports[0].host_ip == "0.0.0.0" and $root.services.mailpit.ports[0].host_ip == "127.0.0.1" ' >/dev/null ./scripts/compose.sh "$production_env" config --format json | jq --exit-status ' (.services | has("app")) and (.services | has("db")) and (.services | has("web") | not) and (.services | has("worker") | not) and (.services | has("proxy") | not) and (.services | has("docker-api-proxy") | not) and (.services | has("mailpit") | not) and .services.app.environment.APP_ROLE == "combined" and .services.app.environment.DNS_CLUSTER_QUERY == "ignore" and .services.app.environment.POOL_SIZE == "4" and .services.app.networks.internal.interface_name == "eth0" and .services.app.networks.egress.interface_name == "eth1" and .services.app.networks.public_edge.interface_name == "eth2" and .services.app.networks.public_edge.aliases == ["who-need-help-production"] and .networks.public_edge.external == true and .services.app.ports[0].host_ip == "127.0.0.1" ' >/dev/null ./scripts/compose.sh "$external_production_env" config --format json | jq --exit-status ' (.services | has("app")) and (.services | has("db") | not) and (.services | has("web") | not) and (.services | has("worker") | not) and (.services | has("proxy") | not) and .services.app.networks.public_edge.interface_name == "eth2" and .services.app.networks.public_edge.aliases == ["who-need-help-production"] ' >/dev/null ./scripts/compose.sh "$external_socket_production_env" config --format json | jq --exit-status ' (.services | has("app")) and (.services | has("db") | not) and .services.app.environment.DATABASE_SOCKET_DIR == "/var/run/postgresql" and .services.migrate.environment.DATABASE_SOCKET_DIR == "/var/run/postgresql" and (.services.app.volumes | any( .type == "bind" and .source == "/var/run/postgresql" and .target == "/var/run/postgresql" and .read_only == true )) and (.services.migrate.volumes | any( .type == "bind" and .source == "/var/run/postgresql" and .target == "/var/run/postgresql" and .read_only == true )) ' >/dev/null ./scripts/compose.sh "$external_socket_production_env" config --profiles | grep -Fx container-database >/dev/null ./scripts/compose.sh "$external_split_production_env" config --format json | jq --exit-status ' (.services | has("db") | not) and (.services | has("app") | not) and (.services | has("web")) and (.services | has("worker")) and (.services | has("proxy")) and .services.web.networks.internal.interface_name == "eth0" and .services.web.networks.ingress.interface_name == "eth1" and .services.web.networks.egress.interface_name == "eth2" and .services.web.networks.public_edge.interface_name == "eth3" and .services.web.networks.public_edge.aliases == ["who-need-help-production"] and .services.web.deploy.replicas == 2 and .services.worker.deploy.replicas == 2 ' >/dev/null HTTP_BIND_ADDRESS=127.0.0.1 \ ./scripts/compose.sh .env.example config --format json | jq --exit-status ' .services.proxy.ports[0].host_ip == "127.0.0.1" ' >/dev/null WEB_REPLICAS=1 WORKER_REPLICAS=1 \ ./scripts/compose.sh .env.example config --format json | jq --exit-status ' .services.web.deploy.replicas == 1 and .services.worker.deploy.replicas == 1 ' >/dev/null CPU_REPLAY_CPUSET=0 \ CPU_REPLAY_WEB_CPUS=1 \ CPU_REPLAY_WORKER_CPUS=1 \ CPU_REPLAY_WEB_SCHEDULERS=1 \ CPU_REPLAY_WORKER_SCHEDULERS=1 \ docker compose --env-file .env.example \ -f compose.yaml -f compose.cpu-replay.yaml config --quiet APP_IMAGE=who-need-help:portability-render \ SOCKET_PROXY_IMAGE=who-need-help:socket-proxy-portability-render \ POSTGIS_IMAGE=who-need-help:postgis-portability-render \ PORTABILITY_IMAGE=who-need-help:portability-render \ PORTABILITY_SOCKET_PROXY_IMAGE=who-need-help:socket-proxy-portability-render \ PORTABILITY_POSTGIS_IMAGE=who-need-help:postgis-portability-render \ PORTABILITY_TRAEFIK_IMAGE=who-need-help:traefik-portability-render \ docker compose --env-file .env.example \ -f compose.yaml -f compose.portability.yaml config --quiet mkdir -p "$scan_dir/e2e-output" E2E_OUTPUT_DIR="$scan_dir/e2e-output" docker compose --env-file .env.e2e.example \ -f compose.yaml -f compose.e2e.yaml config --quiet REHEARSAL_IMAGE=who-need-help:rehearsal-render \ docker compose --env-file .env.e2e.example \ -f compose.yaml -f compose.upgrade-rehearsal.yaml config --quiet docker compose --env-file .env.load.example \ -f compose.yaml -f compose.load.yaml config --quiet test "$( docker compose --env-file .env.load.example \ -f compose.yaml -f compose.load.yaml config --format json | jq -r '.services.db.command | join(" ")' )" = "postgres -c shared_preload_libraries=pg_stat_statements -c compute_query_id=on" mkdir -p "$scan_dir/observability-runtime/prometheus" \ "$scan_dir/observability-runtime/grafana" OBSERVABILITY_RUNTIME_DIR="$scan_dir/observability-runtime" \ docker compose --env-file .env.load.example \ -f compose.yaml -f compose.load.yaml -f compose.observability.yaml \ --profile observability config --quiet mkdir -p "$scan_dir/backup-runtime" BACKUP_RUNTIME_DIR="$scan_dir/backup-runtime" \ BACKUP_HOST_UID="$(id -u)" \ BACKUP_HOST_GID="$(id -g)" \ docker compose --env-file .env.load.example \ -f compose.yaml -f compose.load.yaml -f compose.backup.yaml \ --profile backup config --quiet docker compose -p "$project" -f compose.quality.yaml config --quiet mkdir -p "$scan_dir/external-boundary-output" EXTERNAL_BOUNDARY_APP_IMAGE=who-need-help:boundary-render \ EXTERNAL_BOUNDARY_MOCK_IMAGE=who-need-help:boundary-mock-render \ EXTERNAL_BOUNDARY_OUTPUT_DIR="$scan_dir/external-boundary-output" \ EXTERNAL_BOUNDARY_HOST_UID="$(id -u)" \ EXTERNAL_BOUNDARY_HOST_GID="$(id -g)" \ EXTERNAL_OAUTH_CLIENT_ID=render-client \ EXTERNAL_OAUTH_CLIENT_SECRET=render-secret \ EXTERNAL_PUSH_BEARER_TOKEN=render-push-token \ EXTERNAL_METRICS_TOKEN=render-metrics-token \ EXTERNAL_POSTGRES_PASSWORD=render-database-secret \ EXTERNAL_DATABASE_URL=ecto://boundary:render-database-secret@boundary-db/boundary \ EXTERNAL_SECRET_KEY_BASE=render-secret-key-base \ EXTERNAL_HANDOVER_SECRET=render-handover-secret \ docker compose -f compose.external-boundaries.yaml config --quiet echo "Validating local observability configuration" sed \ -e 's/__SCRAPE_INTERVAL__/1s/g' \ -e 's/__EVALUATION_INTERVAL__/1s/g' \ ops/observability/prometheus.template.yml \ >"$scan_dir/observability-runtime/prometheus/prometheus.yml" printf '%s' 'isolated-quality-metrics-token' \ >"$scan_dir/observability-runtime/prometheus/metrics-token" printf '%s\n' '[]' \ >"$scan_dir/observability-runtime/prometheus/web-targets.json" printf '%s\n' '[]' \ >"$scan_dir/observability-runtime/prometheus/worker-targets.json" docker run --rm \ --user 0:0 \ --volume "$scan_dir/observability-runtime/prometheus:/runtime:ro" \ --volume "$ROOT/ops/observability/rules.yml:/etc/prometheus/rules.yml:ro" \ --entrypoint /bin/promtool \ "$PROMETHEUS_IMAGE" check config /runtime/prometheus.yml docker run --rm \ --user 0:0 \ --volume "$ROOT/ops/observability/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro" \ --entrypoint /bin/amtool \ "$ALERTMANAGER_IMAGE" check-config /etc/alertmanager/alertmanager.yml docker run --rm \ --volume "$ROOT/scripts/alert-receiver.py:/src/alert-receiver.py:ro" \ "$PYTHON_IMAGE" python -c \ 'import py_compile; py_compile.compile("/src/alert-receiver.py", cfile="/tmp/alert-receiver.pyc", doraise=True)' docker run --rm \ --volume "$ROOT/ops/external-boundaries/mock_server.py:/src/mock_server.py:ro" \ "$PYTHON_IMAGE" python -c \ 'import py_compile; py_compile.compile("/src/mock_server.py", cfile="/tmp/mock_server.pyc", doraise=True)' jq --exit-status \ 'type == "object" and .uid == "wnh-overview" and (.panels | length) == 10' \ ops/observability/grafana/dashboards/who-need-help-overview.json \ >/dev/null echo "Linting the Helm chart" "$ROOT/scripts/bootstrap-kubernetes-tools.sh" >/dev/null "$ROOT/.tools/bin/helm" lint \ --values "$ROOT/deploy/helm/who-need-help/values-kind.yaml" \ "$ROOT/deploy/helm/who-need-help" echo "Scanning only tracked and non-ignored source files" # The single-quoted program must expand $path inside the child shell. # shellcheck disable=SC2016 git ls-files --cached --others --exclude-standard -z | xargs -0 -r sh -c ' for path do if [ -f "$path" ]; then printf "%s\0" "$path" fi done ' sh >"$scan_list" tar --null --no-recursion --files-from="$scan_list" --create --file="$scan_tar" tar --extract --file="$scan_tar" --directory "$scan_dir" "$ROOT/.tools/bin/helm" template who-need-help \ --values "$ROOT/deploy/helm/who-need-help/values-kind.yaml" \ "$ROOT/deploy/helm/who-need-help" \ >"$scan_dir/rendered-helm.yaml" test "$( grep -c 'name: ERL_ZFLAGS' "$scan_dir/rendered-helm.yaml" )" -eq 5 test "$( grep -c 'value: "+Q 65536"' "$scan_dir/rendered-helm.yaml" )" -eq 5 test "$( grep -c '^kind: NetworkPolicy$' "$scan_dir/rendered-helm.yaml" )" -eq 1 mkdir -p "$ROOT/.tools/trivy-cache" docker run --rm \ --volume "$scan_dir:/scan:ro" \ --volume "$ROOT/.tools/trivy-cache:/root/.cache/trivy" \ "$TRIVY_IMAGE" fs \ --scanners misconfig,secret \ --severity HIGH,CRITICAL \ --exit-code 1 \ /scan echo "Building, smoke-testing, and scanning pinned runtime infrastructure images" docker build --tag "$socket_proxy_image" --file Dockerfile.socket-proxy . docker build --tag "$postgis_image" --file Dockerfile.postgis . docker build --tag "$caddy_image" --file Dockerfile.caddy . docker build --tag "$traefik_image" --file Dockerfile.traefik . test "$(docker image inspect --format '{{.Config.User}}' "$socket_proxy_image")" = "haproxy" test "$(docker image inspect --format '{{.Config.User}}' "$postgis_image")" = "postgres" test "$(docker image inspect --format '{{.Config.User}}' "$caddy_image")" = "1000:1000" docker run --rm "$caddy_image" version | grep -F 'v2.11.4-wnh-grpc1.82.1-xtext0.40.0' >/dev/null docker run --rm "$traefik_image" version | grep -F 'v3.7.10-wnh-grpc1.82.1' >/dev/null docker run --rm --entrypoint sh "$postgis_image" -euc ' test ! -e /usr/local/bin/gosu test "$(id -u)" = 70 ' docker run --detach \ --name "$socket_proxy_container" \ --read-only \ --tmpfs /run:uid=99,gid=99,mode=0755 \ --tmpfs /tmp \ --cap-drop ALL \ --group-add "$(stat -c '%g' /var/run/docker.sock)" \ --security-opt no-new-privileges \ --env CONTAINERS=1 \ --env EVENTS=1 \ --env INFO=1 \ --env NETWORKS=1 \ --env PING=1 \ --env POST=0 \ --env VERSION=1 \ --volume /var/run/docker.sock:/var/run/docker.sock:ro \ --publish 127.0.0.1::2375 \ "$socket_proxy_image" >/dev/null socket_proxy_port=$( docker port "$socket_proxy_container" 2375/tcp | sed -n 's/.*://p' | head -n 1 ) test -n "$socket_proxy_port" socket_proxy_ready=false for _attempt in $(seq 1 30); do if curl --fail --silent --show-error \ "http://127.0.0.1:$socket_proxy_port/_ping" >/dev/null; then socket_proxy_ready=true break fi sleep 1 done test "$socket_proxy_ready" = true test "$( curl --silent --output /dev/null --write-out '%{http_code}' \ "http://127.0.0.1:$socket_proxy_port/containers/json" )" = "200" test "$( curl --silent --output /dev/null --write-out '%{http_code}' \ --request POST \ "http://127.0.0.1:$socket_proxy_port/containers/create" )" = "403" docker rm --force "$socket_proxy_container" >/dev/null for image in \ "$socket_proxy_image" \ "$postgis_image" \ "$traefik_image" \ "$caddy_image" \ "axllent/mailpit:v1.30.4@sha256:5a49a77c5bdbe7c5474450b4f46348d09949df3695257729c93a30369382d4f6"; do scan_image "$image" done echo "Building the pinned quality image and cached Dialyzer PLTs" docker build --target quality --tag "$quality_image" . echo "Checking isolated VAPID generation and atomic single-file import" generated_vapid_env="$scan_dir/generated-vapid.env" cp .env.example "$generated_vapid_env" chmod 600 "$generated_vapid_env" vapid_output=$( WNH_VAPID_GENERATOR_IMAGE="$quality_image" \ ./scripts/generate-vapid-env.sh \ "$generated_vapid_env" mailto:contact@help.test ) generated_vapid_public=$( awk -F= ' $1 == "WEB_PUSH_VAPID_PUBLIC_KEY" { print substr($0, index($0, "=") + 1) exit } ' "$generated_vapid_env" ) generated_vapid_private=$( awk -F= ' $1 == "WEB_PUSH_VAPID_PRIVATE_KEY" { print substr($0, index($0, "=") + 1) exit } ' "$generated_vapid_env" ) test -n "$generated_vapid_public" test -n "$generated_vapid_private" test "$generated_vapid_public" != "$generated_vapid_private" grep -Fx 'WEB_PUSH_VAPID_SUBJECT=mailto:contact@help.test' \ "$generated_vapid_env" >/dev/null if printf '%s' "$vapid_output" | grep -F "$generated_vapid_public" >/dev/null || printf '%s' "$vapid_output" | grep -F "$generated_vapid_private" >/dev/null; then echo "VAPID generator printed generated key material." >&2 exit 1 fi docker run --rm \ --network none \ --read-only \ --volume "$generated_vapid_env:/tmp/generated-vapid.env:ro" \ --entrypoint elixir \ "$quality_image" \ -e ' values = "/tmp/generated-vapid.env" |> File.read!() |> String.split("\n", trim: true) |> Enum.reject(&(String.starts_with?(&1, "#") or not String.contains?(&1, "="))) |> Map.new(fn line -> [key, value] = String.split(line, "=", parts: 2) {key, value} end) {:ok, public_key} = Base.url_decode64(values["WEB_PUSH_VAPID_PUBLIC_KEY"], padding: false) {:ok, private_key} = Base.url_decode64(values["WEB_PUSH_VAPID_PRIVATE_KEY"], padding: false) unless byte_size(public_key) == 65 and :binary.first(public_key) == 4 and byte_size(private_key) == 32 do raise "unexpected VAPID key shape" end ' if WNH_VAPID_GENERATOR_IMAGE="$quality_image" \ ./scripts/generate-vapid-env.sh \ "$generated_vapid_env" mailto:contact@help.test >/dev/null 2>&1; then echo "VAPID generator rotated an existing environment identity." >&2 exit 1 fi fresh_vapid_env="$scan_dir/fresh-vapid.env" cp .env.example "$fresh_vapid_env" chmod 600 "$fresh_vapid_env" if WNH_VAPID_GENERATOR_IMAGE="$quality_image" \ ./scripts/generate-vapid-env.sh \ "$fresh_vapid_env" ftp://help.test >/dev/null 2>&1; then echo "VAPID generator accepted an invalid subject." >&2 exit 1 fi fresh_vapid_hash=$(sha256sum "$fresh_vapid_env" | awk '{print $1}') injected_vapid_subject=$( printf 'mailto:contact@help.test\nGOOGLE_OAUTH_CLIENT_SECRET=injected' ) if WNH_VAPID_GENERATOR_IMAGE="$quality_image" \ ./scripts/generate-vapid-env.sh \ "$fresh_vapid_env" "$injected_vapid_subject" >/dev/null 2>&1; then echo "VAPID generator accepted a line-breaking subject." >&2 exit 1 fi test "$(sha256sum "$fresh_vapid_env" | awk '{print $1}')" = "$fresh_vapid_hash" if find "$scan_dir" -maxdepth 1 -name '.vapid-generation.*' -print | grep -q .; then echo "VAPID generator retained a temporary credential directory." >&2 exit 1 fi unset generated_vapid_public generated_vapid_private echo "Running Elixir format, compiler, xref, Credo, Sobelow, Dialyzer, and Hex audit" docker run --rm "$quality_image" sh -euc ' mix format --check-formatted mix compile --force --warnings-as-errors mix xref graph --label compile-connected --fail-above 0 mix credo --strict --min-priority high mix sobelow --exit --strict --private --skip mix dialyzer mix hex.audit ' echo "Starting an isolated PostgreSQL/PostGIS volume for the Phoenix suite" $compose up --detach --wait db docker run --rm \ --network "${project}_internal" \ --env MIX_ENV=test \ --env DB_HOST=db \ --env "DB_USER=$QUALITY_POSTGRES_USER" \ --env "DB_PASSWORD=$QUALITY_POSTGRES_PASSWORD" \ --env TEST_POOL_SIZE=10 \ "$quality_image" \ mix test echo "Auditing locked browser dependencies" docker build --target node_deps --tag "$assets_image" . docker run --rm \ --volume "$ROOT/assets/js:/assets/js:ro" \ "$assets_image" \ npm test docker run --rm "$assets_image" npm audit --audit-level=high docker build --tag "$e2e_image" e2e docker run --rm "$e2e_image" npm audit --audit-level=high echo "Building and scanning the pinned non-root backup tool image" docker build --tag "$backup_image" --file Dockerfile.backup . test "$(docker image inspect --format '{{.Config.User}}' "$backup_image")" = \ "10001:10001" backup_versions=$(docker run --rm \ --user 10001:10001 \ --read-only \ --tmpfs /tmp \ "$backup_image" \ sh -euc 'restic version; pg_dump --version; test "$(id -u)" = 10001') printf '%s\n' "$backup_versions" printf '%s\n' "$backup_versions" | grep -F 'restic 0.19.1 compiled with go1.26.5' >/dev/null printf '%s\n' "$backup_versions" | grep -F 'pg_dump (PostgreSQL) 18.4' >/dev/null scan_image "$backup_image" echo "Building and scanning the pinned non-root MinIO server and client images" docker build --target server --tag "$minio_image" --file Dockerfile.minio . docker build --target client --tag "$mc_image" --file Dockerfile.minio . test "$(docker image inspect --format '{{.Config.User}}' "$minio_image")" = \ "10001:10001" test "$(docker image inspect --format '{{.Config.User}}' "$mc_image")" = \ "10001:10001" minio_version=$(docker run --rm \ --user 10001:10001 \ --read-only \ --tmpfs /tmp \ "$minio_image" \ --version) mc_version=$(docker run --rm \ --user 10001:10001 \ --read-only \ --tmpfs /tmp \ "$mc_image" \ --version) printf '%s\n' "$minio_version" printf '%s\n' "$mc_version" printf '%s\n' "$minio_version" | grep -F 'RELEASE.2025-10-15T17-29-55Z' >/dev/null printf '%s\n' "$minio_version" | grep -F 'commit-id=9e49d5e7a648f00e26f2246f4dc28e6b07f8c84a' >/dev/null printf '%s\n' "$minio_version" | grep -F 'Runtime: go1.26.5 linux/' >/dev/null printf '%s\n' "$mc_version" | grep -F 'RELEASE.2025-08-13T08-35-41Z' >/dev/null printf '%s\n' "$mc_version" | grep -F 'commit-id=7394ce0dd2a80935aded936b09fa12cbb3cb8096' >/dev/null printf '%s\n' "$mc_version" | grep -F 'Runtime: go1.26.5 linux/' >/dev/null for image in "$minio_image" "$mc_image"; do scan_image "$image" done echo "Building and scanning the pinned non-root external-boundary mock image" docker build \ --tag "$boundary_mock_image" \ --file ops/external-boundaries/Dockerfile \ ops/external-boundaries test "$(docker image inspect --format '{{.Config.User}}' "$boundary_mock_image")" = \ "10001:10001" scan_image "$boundary_mock_image" echo "Building and scanning the production release image" docker build --target release --tag "$release_image" . scan_image "$release_image" echo "All isolated quality and security gates passed."