#!/usr/bin/env bash
# tests/creds-audit/run-tests.sh
#
# Harness for scripts/creds-audit.sh. Offline: builds synthetic creds folders +
# manifests under a tmp CREDS_ROOT and asserts the audit's verdict. Touches no real
# secret and no real home dir (CREDS_ROOT / MANIFEST_DIR are redirected to tmp).
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO="$(cd "$HERE/../.." && pwd)"
AUDIT="$REPO/scripts/creds-audit.sh"
P=0; F=0
ok() { echo "  [PASS] $1"; P=$((P+1)); }
no() { echo "  [FAIL] $1"; F=$((F+1)); }

[ -f "$AUDIT" ] || { echo "FAIL: $AUDIT not found"; exit 1; }

# fresh synthetic environment: CREDS_ROOT/<site>-creds + MANIFEST_DIR/<site>.manifest
SITE=testsite
mkenv() {  # mkenv -> echoes the tmp root; caller sets CREDS_ROOT/MANIFEST_DIR to it
  local root; root="$(mktemp -d)"
  mkdir -p "$root/${SITE}-creds" "$root/manifests"
  chmod 700 "$root/${SITE}-creds"
  printf 'a_secret 600 local\nb_key.pub 644 local\nc.env 600 somevm:/root/x.token\n' \
    > "$root/manifests/${SITE}.manifest"
  printf 'secret\n' > "$root/${SITE}-creds/a_secret"; chmod 600 "$root/${SITE}-creds/a_secret"
  printf 'pub\n'    > "$root/${SITE}-creds/b_key.pub"; chmod 644 "$root/${SITE}-creds/b_key.pub"
  printf 'tok\n'    > "$root/${SITE}-creds/c.env";     chmod 600 "$root/${SITE}-creds/c.env"
  echo "$root"
}
run() { CREDS_ROOT="$1" MANIFEST_DIR="$1/manifests" bash "$AUDIT" "$SITE" >"$1/out" 2>&1; echo $?; }

echo "=== creds-audit ==="

# T1: a complete, correct, non-sprawled folder -> CLEAN, exit 0
R="$(mkenv)"; rc=$(run "$R")
[ "$rc" = 0 ] && grep -q "CLEAN" "$R/out" && ok "T1 complete + correct folder -> CLEAN, exit 0" \
              || { no "T1 a correct folder should pass (rc=$rc)"; sed 's/^/       /' "$R/out"; }
rm -rf "$R"

# T2: a manifest-required file MISSING (the SEC-009 gap: a VM secret never pulled in)
R="$(mkenv)"; rm -f "$R/${SITE}-creds/c.env"; rc=$(run "$R")
[ "$rc" = 1 ] && grep -q "MISSING: c.env" "$R/out" && ok "T2 a manifested (VM-sourced) secret absent -> MISSING, exit 1" \
              || { no "T2 a missing manifest entry should fail (rc=$rc)"; sed 's/^/       /' "$R/out"; }
rm -rf "$R"

# T3: wrong permissions on a secret -> FAIL
R="$(mkenv)"; chmod 644 "$R/${SITE}-creds/a_secret"; rc=$(run "$R")
[ "$rc" = 1 ] && grep -q "a_secret mode 644" "$R/out" && ok "T3 a secret with loose mode (644) -> caught, exit 1" \
              || { no "T3 wrong mode should fail (rc=$rc)"; sed 's/^/       /' "$R/out"; }
rm -rf "$R"

# T4: an UNDECLARED file in the folder -> FAIL (spread: a secret nobody declared)
R="$(mkenv)"; printf 'x\n' > "$R/${SITE}-creds/stray.env"; chmod 600 "$R/${SITE}-creds/stray.env"; rc=$(run "$R")
[ "$rc" = 1 ] && grep -q "UNDECLARED file in folder: stray.env" "$R/out" && ok "T4 an undeclared file in the folder -> caught, exit 1" \
              || { no "T4 an undeclared file should fail (rc=$rc)"; sed 's/^/       /' "$R/out"; }
rm -rf "$R"

# T5: SPRAWL -- a sensitive file loose in CREDS_ROOT, outside any *-creds/ folder
R="$(mkenv)"; printf 'x\n' > "$R/loose.env"; chmod 600 "$R/loose.env"; rc=$(run "$R")
[ "$rc" = 1 ] && grep -q "SPRAWL: sensitive file loose" "$R/out" && ok "T5 a loose .env in the home root -> SPRAWL, exit 1" \
              || { no "T5 sprawl in the home root should fail (rc=$rc)"; sed 's/^/       /' "$R/out"; }
rm -rf "$R"

# T6: the folder itself not 0700 -> FAIL
R="$(mkenv)"; chmod 755 "$R/${SITE}-creds"; rc=$(run "$R")
[ "$rc" = 1 ] && grep -q "folder mode 755" "$R/out" && ok "T6 a world-listable creds folder (0755) -> caught, exit 1" \
              || { no "T6 a non-0700 folder should fail (rc=$rc)"; sed 's/^/       /' "$R/out"; }
rm -rf "$R"

# T8-T12: SPRAWL BLIND SPOTS the original six globs could not see (D-137 / SEC-023).
# Each fixture is a real exposure class, not a hypothetical: admin.pass is the SEC-020
# secret that sat undetected for 12 days, and admin-openrc is the exposure phase-03 is
# PREDICTED to create at Stage 5.
for probe in admin.pass:T8 id_ed25519:T9 tenant-ca.pem:T10 svc.apikey:T11 admin-openrc:T12 .admin.pass:T14 .svc_ed25519:T15; do
  fname="${probe%%:*}"; tag="${probe##*:}"
  R="$(mkenv)"; printf 'x\n' > "$R/$fname"; rc=$(run "$R")
  [ "$rc" = 1 ] && grep -q "SPRAWL: sensitive file loose" "$R/out" \
    && ok "$tag a loose '$fname' in the home root -> SPRAWL (was a blind spot)" \
    || { no "$tag '$fname' must be caught as sprawl (rc=$rc)"; sed 's/^/       /' "$R/out"; }
  rm -rf "$R"
done

# T13: widening must not make the check trigger-happy -- an ordinary file is NOT sprawl.
R="$(mkenv)"; printf 'notes\n' > "$R/README.md"; printf 'x\n' > "$R/notes.txt"; rc=$(run "$R")
[ "$rc" = 0 ] && ok "T13 ordinary non-secret files in the home root -> still CLEAN" \
              || { no "T13 widened globs must not false-positive (rc=$rc)"; sed 's/^/       /' "$R/out"; }
rm -rf "$R"

# T7: the audit reads NO secret content -- no content-reading verbs in the script
grep -qE '\b(cat|head|tail|less|more|od|xxd|hexdump)\b' "$AUDIT" \
  && no "T7 the audit contains a content-reading verb -- it must inspect metadata only" \
  || ok "T7 metadata-only: no cat/head/tail/od/xxd in the audit"

echo
if [ "$F" = 0 ]; then echo "creds-audit: $P/$P PASS"; exit 0; fi
echo "creds-audit: FAILURES: $F (passed $P)"; exit 1
