Browse Source

test(push-gate): behavioural suite — planted-secret catch, regex FP filter, gitleaks path

9 assertions over throwaway repos: 4 planted secret types caught
(AWS key, password assignment, private-key header, high-entropy token),
the regex false-positive filter holds (env refs/placeholders/fallbacks
stay clean), and the gitleaks integration path catches a high-entropy
GH token. Highest-blast-radius untested skill now has a net.

Orchestrator-hardened two lane defects before commit: sandbox was
created inside tests/ (TMPDIR=$HERE -> litter in the tracked tree; now
system mktemp) and the gitleaks fixture was a sequential low-entropy
string gitleaks does not detect (swapped for a real high-entropy token
verified to trip the scanner). Fixture tokens are split-literal
('ghp_' '...') so the committed file never self-trips push-gate.

Built by fleetflow lane robust/ra-pushgate (codex); orchestrator-committed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
0xDarkMatter 1 week ago
parent
commit
c292d85e4d
1 changed files with 133 additions and 0 deletions
  1. 133 0
      skills/push-gate/tests/run.sh

+ 133 - 0
skills/push-gate/tests/run.sh

@@ -0,0 +1,133 @@
+#!/usr/bin/env bash
+# Behavioural self-test for push-gate's secret scanner. Fully offline.
+
+set -uo pipefail
+
+HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+SKILL="$(dirname "$HERE")"
+SOURCE_SCANNER="$SKILL/scripts/scan-secrets.sh"
+SCANNER="$SOURCE_SCANNER"
+
+SB="$(mktemp -d)"
+trap 'rm -rf "$SB"' EXIT
+PASS=0
+FAIL=0
+SECRETS_CAUGHT=0
+
+# Use a byte-identical scanner in a temporary skill mirror, normalising only the
+# regex corpus so Git-for-Windows CRLF checkouts behave like Ubuntu CI.
+mkdir -p "$SB/skill/scripts" "$SB/skill/references"
+cp "$SOURCE_SCANNER" "$SB/skill/scripts/scan-secrets.sh"
+cp "$SKILL/references/secret-patterns.txt" "$SB/skill/references/secret-patterns.txt"
+sed -i 's/\r$//' "$SB/skill/references/secret-patterns.txt"
+cp "$SKILL/references/gitleaks-config.toml" "$SB/skill/references/gitleaks-config.toml"
+SCANNER="$SB/skill/scripts/scan-secrets.sh"
+
+ok() { PASS=$((PASS + 1)); printf '  PASS  %s\n' "$1"; }
+no() { FAIL=$((FAIL + 1)); printf '  FAIL  %s\n' "$1"; }
+expect_exit() {
+  if [[ "$2" == "$3" ]]; then ok "$1 (exit $3)"; else no "$1 (want $2 got $3)"; fi
+}
+expect_has() {
+  case "$3" in *"$2"*) ok "$1";; *) no "$1 (missing '$2')";; esac
+}
+
+mkdir -p "$SB/bin"
+cat > "$SB/bin/gitleaks" <<'EOF'
+#!/usr/bin/env bash
+exit 0
+EOF
+chmod +x "$SB/bin/gitleaks"
+STUB_PATH="$SB/bin:$PATH"
+
+new_repo() {
+  local repo="$1"
+  mkdir -p "$repo"
+  git -C "$repo" init -q -b main
+  git -C "$repo" config user.name push-gate-test
+  git -C "$repo" config user.email push-gate-test@example.invalid
+  printf '%s\n' 'baseline' > "$repo/README.md"
+  git -C "$repo" add README.md
+  git -C "$repo" commit -q -m 'test: baseline'
+  git -C "$repo" update-ref refs/remotes/origin/main HEAD
+}
+
+commit_file() {
+  local repo="$1" content="$2"
+  printf '%s\n' "$content" > "$repo/candidate.txt"
+  git -C "$repo" add candidate.txt
+  git -C "$repo" commit -q -m 'test: add candidate'
+}
+
+scan_stubbed() {
+  local repo="$1" output_file="$2"
+  (cd "$repo" && TMPDIR="$SB" PATH="$STUB_PATH" bash "$SCANNER" origin main) >"$output_file" 2>&1
+}
+
+echo "=== push-gate behavioural self-test ==="
+
+echo "-- contract --"
+bash -n "$SCANNER" 2>/dev/null && ok "bash -n scan-secrets.sh" || no "bash -n scan-secrets.sh"
+
+echo "-- clean diff --"
+repo="$SB/clean"
+new_repo "$repo"
+commit_file "$repo" 'message = "ordinary configuration"'
+scan_stubbed "$repo" "$SB/clean.out"; rc=$?
+expect_exit "clean committed diff reports clean" 0 "$rc"
+expect_has "clean verdict is reported" "secret scan CLEAN" "$(cat "$SB/clean.out")"
+
+echo "-- planted secrets --"
+names=("AWS access key" "generic password" "private-key header" "high-entropy token")
+values=(
+  "$(printf '%s%s' 'AKIA' 'IOSFODNN7EXAMPLZ')"
+  "$(printf '%s%s%s' 'password = "correct-' 'horse-battery-staple' '"')"
+  "$(printf '%s%s' '-----BEGIN RSA ' 'PRIVATE KEY-----')"
+  "$(printf '%s%s' 'sk-' 'abcdefghijklmnopqrstuvwxyz0123456789')"
+)
+
+for i in "${!names[@]}"; do
+  repo="$SB/secret-$i"
+  new_repo "$repo"
+  commit_file "$repo" "${values[$i]}"
+  scan_stubbed "$repo" "$SB/secret-$i.out"; rc=$?
+  if [[ "$rc" -eq 1 ]] && grep -q "SECRET-PATTERN MATCH" "$SB/secret-$i.out"; then
+    SECRETS_CAUGHT=$((SECRETS_CAUGHT + 1))
+    ok "${names[$i]} is caught by regex layer"
+  else
+    no "${names[$i]} is caught by regex layer (exit $rc)"
+    sed 's/^/        /' "$SB/secret-$i.out"
+  fi
+done
+
+echo "-- false-positive filter --"
+repo="$SB/false-positives"
+new_repo "$repo"
+printf '%s\n' \
+  'password = os.environ["PW"]' \
+  'password = "..."' \
+  'password = "${PASSWORD:-change-me-now}"' > "$repo/candidate.txt"
+git -C "$repo" add candidate.txt
+git -C "$repo" commit -q -m 'test: add references'
+scan_stubbed "$repo" "$SB/false-positives.out"; rc=$?
+expect_exit "env reference, placeholder, and shell fallback stay clean" 0 "$rc"
+
+echo "-- gitleaks integration --"
+if command -v gitleaks >/dev/null 2>&1; then
+  repo="$SB/gitleaks"
+  new_repo "$repo"
+  commit_file "$repo" "$(printf '%s%s' 'ghp_' '9xQ7zRtY2wodFb3KpL8mN0aBcDeFgHiJkLmN')"
+  (cd "$repo" && TMPDIR="$SB" bash "$SCANNER" origin main) >"$SB/gitleaks.out" 2>&1; rc=$?
+  if [[ "$rc" -eq 1 ]] && grep -q "SECRET DETECTED (gitleaks)" "$SB/gitleaks.out"; then
+    ok "known-bad blob is caught by installed gitleaks"
+  else
+    no "known-bad blob is caught by installed gitleaks (exit $rc)"
+  fi
+else
+  echo "  SKIP  gitleaks integration (gitleaks not installed)"
+fi
+
+echo ""
+echo "=== $PASS passed, $FAIL failed ==="
+[[ "$FAIL" -eq 0 ]] || exit 1
+exit 0