#!/usr/bin/env bash
set -euo pipefail
# check-no-committed-secrets.sh — repository guardrail that fails when live
# credential material is present in (a) the current working tree,
# (b) the staged diff, or (c) any commit reachable from HEAD that is NOT also
# reachable from the given clean base.
#
# What counts as a secret (never by length alone — 43 bytes is NOT a secret
# classifier):
#   - files whose path matches a known secret-file name (machine-api-key,
#     token, agent-token-*, auth-token-*, .pid under a run dir)
#   - lines that carry an Authorization/cookie/token header (the bearer value
#     itself, not the literal word) — redaction must have failed
#   - high-entropy hex/base64 tokens of >= 40 chars that appear after the
#     markers token/apiKey/authToken/machine-api-key/secret (a heuristic that
#     still requires the marker context, never length alone)
#
# Usage:
#   guardrails/repository/check-no-committed-secrets.sh            # vs origin/main
#   SECRET_SCAN_BASE_REF=<sha> guardrails/repository/check-no-committed-secrets.sh
# Exit 0 when no hits; exit 1 with the offending paths/lines otherwise.

set -euo pipefail

repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd -P)"
cd "${repo_root}"

base_ref="${SECRET_SCAN_BASE_REF:-origin/main}"
if ! git rev-parse --verify "${base_ref}^{commit}" >/dev/null 2>&1; then
  base_ref="HEAD"
fi

hits=0

# ---------------------------------------------------------------------------
# 1. Working tree (tracked + untracked, excluding ignored).
# ---------------------------------------------------------------------------
while IFS= read -r -d '' path; do
  hits=$((hits + 1))
  echo "tracked/untracked secret path: ${path}" >&2
done < <(git ls-files -z --cached --others --exclude-standard |
  grep -zE '(^|/)(machine-api-key|token|agent-token-[^/]*|auth-token-[^/]*|.*\.pid)$' || true)

# ---------------------------------------------------------------------------
# 2. Staged diff content.
# ---------------------------------------------------------------------------
if ! git diff --cached --quiet; then
  staged_lines="$(git diff --cached --unified=0 2>/dev/null || true)"
  if [[ -n "${staged_lines}" ]]; then
    while IFS= read -r line; do
      hits=$((hits + 1))
      echo "staged secret line: ${line}" >&2
    done < <(printf '%s\n' "${staged_lines}" |
      grep -iE '^(\\+|-).*Authorization: (Bearer|Basic) [A-Za-z0-9._~+/=-]{16,}' || true)
    while IFS= read -r line; do
      hits=$((hits + 1))
      echo "staged cookie/token line: ${line}" >&2
    done < <(printf '%s\n' "${staged_lines}" |
      grep -iE '^(\\+|-).*(cookie:|x-api-key:|client-secret=|api[_-]?key[=:][[:space:]]*[A-Za-z0-9._~+/=-]{16,})' || true)
  fi
fi

# ---------------------------------------------------------------------------
# 3. Candidate history: commits reachable from HEAD but not from the clean
#    base. Scan the union of added paths (by name) and the added line content.
# ---------------------------------------------------------------------------
commits="$(git rev-list "${base_ref}"..HEAD 2>/dev/null || true)"
if [[ -n "${commits}" ]]; then
  # Added paths in the candidate range.
  while IFS= read -r path; do
    hits=$((hits + 1))
    echo "history secret path: ${path}" >&2
  done < <(git diff --name-only --diff-filter=A "${base_ref}"..HEAD 2>/dev/null |
    grep -E '(^|/)(machine-api-key|token|agent-token-[^/]*|auth-token-[^/]*|.*\.pid)$' || true)
  # Added line content in the candidate range (bearer/cookie/token header).
  range_diff="$(git diff --unified=0 "${base_ref}"..HEAD 2>/dev/null || true)"
  if [[ -n "${range_diff}" ]]; then
    while IFS= read -r line; do
      hits=$((hits + 1))
      echo "history secret line: ${line}" >&2
    done < <(printf '%s\n' "${range_diff}" |
      grep -iE '^(\\+).*Authorization: (Bearer|Basic) [A-Za-z0-9._~+/=-]{16,}' || true)
    while IFS= read -r line; do
      hits=$((hits + 1))
      echo "history cookie/token line: ${line}" >&2
    done < <(printf '%s\n' "${range_diff}" |
      grep -iE '^(\\+).*(cookie:|x-api-key:|client-secret=|api[_-]?key[=:][[:space:]]*[A-Za-z0-9._~+/=-]{16,})' || true)
  fi
fi

if [[ "${hits}" -gt 0 ]]; then
  echo "error: ${hits} secret-material hit(s); remove them (and rotate any live credential) before committing" >&2
  exit 1
fi

echo "committed-secret scan: 0 hits (base=${base_ref})"
