#!/usr/bin/env bash

# This repository is public, and server/ describes real infrastructure. The
# rule that makes that safe has three parts, and each is pinned here because
# each fails silently:
#
#   1. No tracked file under server/ carries a secret. Compose files reference
#      secrets as ${VAR} interpolations resolved from the .env beside them on
#      the machine; .env.example names the variables with CHANGE_ME in place
#      of every value that matters.
#   2. .gitignore keeps .env and data/ out of server/containers/ even when a
#      cutover or a mistake puts one there. The live files belong in
#      ~/Server/<Name>/, outside the checkout entirely -- the ignore is a
#      seatbelt, and a seatbelt that got deleted should fail loudly.
#   3. Nothing named .env is tracked, full stop.

set -uo pipefail

repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
server_dir="$repo_dir/server"

findings=()
note() { findings+=("$1"); }

[[ -d "$server_dir" ]] || { printf 'compose secrets contract: no server/ directory\n' >&2; exit 1; }

# ── 1. Scanner fixtures and tracked content ──────────────────────────────────

scanner="$repo_dir/tests/server/scan-tracked-secrets.py"
fixtures_dir="$repo_dir/tests/server/fixtures/secrets"

if ! python3 "$scanner" "$fixtures_dir/clean" compose.yml .env.example README.md; then
    note 'the clean secret-scanning fixture was rejected'
fi

for fixture in compose.yml .env.example; do
    if output="$(python3 "$scanner" "$fixtures_dir/leaked" "$fixture" 2>&1)"; then
        note "the leaked $fixture fixture was accepted"
    elif [[ "$fixture" == compose.yml && "$output" != *'POSTGRES_PASSWORD'* ]]; then
        note 'the leaked compose fixture did not name POSTGRES_PASSWORD'
    elif [[ "$fixture" == .env.example && "$output" != *'API_TOKEN'* ]]; then
        note 'the leaked env fixture did not name API_TOKEN'
    fi
done

mapfile -t tracked_server_files < <(git -C "$repo_dir" ls-files 'server/**' 'server/*')
if ! output="$(python3 "$scanner" "$repo_dir" "${tracked_server_files[@]}" 2>&1)"; then
    while IFS= read -r hit; do
        [[ -n "$hit" ]] && note "$hit"
    done <<< "$output"
fi

# ── 2. The ignore still stands ───────────────────────────────────────────────
#
# check-ignore consults the real gitignore for a path that need not exist, so
# this asserts the rule rather than the current absence of violations.

git -C "$repo_dir" check-ignore -q 'server/containers/AnyService/.env' \
    || note '.gitignore no longer covers .env under server/containers/'
git -C "$repo_dir" check-ignore -q 'server/containers/AnyService/data/dump.sql' \
    || note '.gitignore no longer covers data/ under server/containers/'

# ── 3. No .env is tracked ────────────────────────────────────────────────────

while IFS= read -r tracked; do
    note "a live .env is tracked: $tracked"
done < <(git -C "$repo_dir" ls-files 'server/**/.env' 'server/.env')

# ── Report ───────────────────────────────────────────────────────────────────

if (( ${#findings[@]} > 0 )); then
    mapfile -t findings < <(printf '%s\n' "${findings[@]}" | sort -u)
    printf 'compose secrets contract: %d finding(s)\n' "${#findings[@]}" >&2
    printf '  - %s\n' "${findings[@]}" >&2
    exit 1
fi

printf 'compose secrets contract: PASS\n'
