#!/usr/bin/env bash set -euo pipefail # Contract tests for production handoff controls. Network calls are replaced by # a narrow aws stub so we test fail-closed command construction without cloud credentials. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../scripts/bash/casan-paths.sh" SHIP="$CASAN_HARNESS_ROOT/scripts/bash/audit-ship-s3.sh" PREFLIGHT="$CASAN_HARNESS_ROOT/scripts/bash/production-preflight.sh" WORK="$(mktemp -d)"; trap 'rm -rf "$WORK"' EXIT PASS=0; FAIL=0 pass() { echo "PASS: $1"; PASS=$((PASS+1)); } fail() { echo "FAIL: $1"; FAIL=$((FAIL+1)); } rc() { set +e; "$@" >/dev/null 2>&1; local code=$?; set -e; echo "$code"; } mkdir -p "$WORK/bin" cat > "$WORK/bin/aws" <<'AWS' #!/usr/bin/env bash set -euo pipefail printf '%s\n' "$*" >> "${AWS_LOG:?}" if [[ "$1 $2" == "s3api get-object-lock-configuration" ]]; then printf '%s\n' '{"ObjectLockConfiguration":{"ObjectLockEnabled":"Enabled","Rule":{"DefaultRetention":{"Mode":"COMPLIANCE","Days":365}}}}' fi if [[ "$1 $2" == "s3api head-object" ]]; then exit 1 fi AWS chmod +x "$WORK/bin/aws" printf '%064d\n' 0 > "$WORK/head.txt" AWS_LOG="$WORK/aws.log" PATH="$WORK/bin:$PATH" CASAN_PROFILE=prod \ CASAN_S3_BUCKET=casan-production-audit CASAN_S3_REGION=ap-northeast-1 \ CASAN_S3_RETENTION_DAYS=365 CASAN_S3_KMS_KEY_ID=alias/casan-audit bash "$SHIP" "$WORK/head.txt" >/dev/null 2>&1 \ && grep -q -- '--object-lock-mode COMPLIANCE' "$WORK/aws.log" \ && grep -q -- '--server-side-encryption aws:kms' "$WORK/aws.log" \ && grep -q -- '--ssekms-key-id alias/casan-audit' "$WORK/aws.log" \ && pass "S3 anchor uses COMPLIANCE Object Lock and SSE-KMS" || fail "S3 anchor missing immutable controls" [[ "$(rc env PATH="$WORK/bin:$PATH" CASAN_PROFILE=dev CASAN_S3_BUCKET=casan-production-audit CASAN_S3_REGION=ap-northeast-1 CASAN_S3_KMS_KEY_ID=alias/casan-audit bash "$SHIP" "$WORK/head.txt")" -ne 0 ]] \ && pass "non-production profile is refused" || fail "non-production profile accepted" cat > "$WORK/bin/aws" <<'AWS' #!/usr/bin/env bash if [[ "$1 $2" == "s3api get-object-lock-configuration" ]]; then printf '%s\n' '{"ObjectLockConfiguration":{"ObjectLockEnabled":"Disabled","Rule":{"DefaultRetention":{"Mode":"COMPLIANCE","Days":365}}}}' fi AWS chmod +x "$WORK/bin/aws" [[ "$(rc env PATH="$WORK/bin:$PATH" CASAN_PROFILE=prod CASAN_S3_BUCKET=casan-production-audit CASAN_S3_REGION=ap-northeast-1 CASAN_S3_KMS_KEY_ID=alias/casan-audit bash "$SHIP" "$WORK/head.txt")" -ne 0 ]] \ && pass "bucket without Object Lock is refused" || fail "Object-Lock-free bucket accepted" printf 'not-a-digest\n' > "$WORK/bad-head.txt" [[ "$(rc env PATH="$WORK/bin:$PATH" CASAN_PROFILE=prod CASAN_S3_BUCKET=casan-production-audit CASAN_S3_REGION=ap-northeast-1 CASAN_S3_KMS_KEY_ID=alias/casan-audit bash "$SHIP" "$WORK/bad-head.txt")" -ne 0 ]] \ && pass "malformed audit head is refused" || fail "malformed audit head accepted" echo "===== Production configuration boundary =====" mkdir -p "$WORK/tls" "$WORK/state/logs/audit" "$WORK/output" openssl req -x509 -newkey rsa:2048 -nodes -days 60 \ -subj '/CN=control.casan.internal' -addext 'subjectAltName=DNS:control.casan.internal' \ -keyout "$WORK/tls/tls.key" -out "$WORK/tls/tls.crt" >/dev/null 2>&1 openssl genrsa -out "$WORK/idp-private.pem" 2048 >/dev/null 2>&1 openssl rsa -in "$WORK/idp-private.pem" -pubout -out "$WORK/idp-public.pem" >/dev/null 2>&1 printf 'test-ca\n' > "$WORK/vault-ca.pem" printf '%064d\n' 0 > "$WORK/state/logs/audit/audit-head.txt" cat > "$WORK/oauth.env" <<'EOF' OAUTH2_PROXY_PROVIDER=oidc OAUTH2_PROXY_OIDC_ISSUER_URL=https://id.casan.internal/realms/casan OAUTH2_PROXY_CLIENT_ID=casan-control-plane OAUTH2_PROXY_CLIENT_SECRET=secret-manager-injected OAUTH2_PROXY_COOKIE_SECRET=base64-cookie-secret-value OAUTH2_PROXY_COOKIE_SECURE=true OAUTH2_PROXY_REDIRECT_URL=https://control.casan.internal/oauth2/callback OAUTH2_PROXY_OIDC_GROUPS_CLAIM=groups OAUTH2_PROXY_SET_XAUTHREQUEST=true OAUTH2_PROXY_PASS_ACCESS_TOKEN=true OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER=true EOF cat > "$WORK/vault.env" < "$WORK/runtime.env" < "$WORK/casan-prod.env" < "$WORK/bin/curl" <<'EOF' #!/usr/bin/env bash printf '%s\n' '{"data":{"ttl":300,"policies":["casan-audit-sign"]}}' EOF cat > "$WORK/bin/docker" <<'EOF' #!/usr/bin/env bash exit 0 EOF cat > "$WORK/bin/aws" <<'EOF' #!/usr/bin/env bash if [[ "$1 $2" == "s3api get-object-lock-configuration" ]]; then printf '%s\n' '{"ObjectLockConfiguration":{"ObjectLockEnabled":"Enabled","Rule":{"DefaultRetention":{"Mode":"COMPLIANCE","Days":365}}}}' elif [[ "$1 $2" == "s3api head-object" ]]; then exit 1 fi exit 0 EOF chmod +x "$WORK/bin/curl" "$WORK/bin/docker" "$WORK/bin/aws" write_runtime casan-control-plane vault_kms s3_object_lock write_prod_env "$WORK/idp-public.pem" [[ "$(rc env PATH="$WORK/bin:$PATH" bash "$PREFLIGHT" "$WORK/casan-prod.env")" -eq 0 ]] \ && pass "complete JWT/Vault/Object-Lock production contract passes with local service stubs" \ || fail "valid production configuration rejected" write_runtime wrong-audience vault_kms s3_object_lock [[ "$(rc env PATH="$WORK/bin:$PATH" bash "$PREFLIGHT" "$WORK/casan-prod.env")" -ne 0 ]] \ && pass "OIDC audience mismatch is refused" || fail "OIDC audience mismatch accepted" write_runtime casan-control-plane local_openssl local_hash_chain [[ "$(rc env PATH="$WORK/bin:$PATH" bash "$PREFLIGHT" "$WORK/casan-prod.env")" -ne 0 ]] \ && pass "production local signing and local ledger configuration is refused" \ || fail "production local trust fallback accepted" write_runtime casan-control-plane vault_kms s3_object_lock write_prod_env "$WORK/missing-idp-public.pem" [[ "$(rc env PATH="$WORK/bin:$PATH" bash "$PREFLIGHT" "$WORK/casan-prod.env")" -ne 0 ]] \ && pass "missing IdP verification key is refused" || fail "missing IdP key accepted" echo "===== PRODUCTION HANDOFF SUMMARY: PASS=$PASS FAIL=$FAIL =====" [[ "$FAIL" -eq 0 ]]