chore: security review policy + operator runner, doc updates (EGB-677 stage 1)

Add .ship-policy.json (opts out AI adversarial/red-team/security-specialist
review; requires local operator sign-off) and test/run-security.sh (the
operator-local security regression subset). Document the policy in CLAUDE.md
and README, fix stale test counts (manifest.bats 41->58, total 174->191), and
update the storage-recursion note to reflect rekey/list now walking the full
project tree.
This commit is contained in:
Brian Majewski 2026-06-07 13:12:39 -07:00
parent c6ea724ddb
commit 588f290dcc
4 changed files with 121 additions and 4 deletions

65
test/run-security.sh Executable file
View file

@ -0,0 +1,65 @@
#!/usr/bin/env bash
# Local-only security regression suite. Uses attack-payload fixtures on purpose.
# Do NOT ask hosted AI agents to run this script or to perform equivalent red-team review.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"
if ! command -v bats >/dev/null 2>&1; then
echo "bats-core is required: brew install bats-core" >&2
exit 1
fi
# Subset of the full suite: adversarial regressions + explicit SECURITY tests +
# closely related path/injection rails. Keeps the run focused and fast.
FILTER='SECURITY|F1:|F2:|F3:|F4:|F5:|command injection does not execute|outside HOME is refused|symlinked target is refused|shell metacharacters|command-substitution|symlinked .secrets-store is skipped|symlinked .secrets-files is ignored|symlinked .secrets.json is refused'
echo "Security regression suite (operator-local only)"
echo "Repository policy: see .ship-policy.json"
echo ""
bats --filter "$FILTER" test/
echo ""
echo "All filtered security regression tests passed."
echo ""
if [ ! -t 0 ]; then
echo "Refusing non-interactive sign-off. Re-run in a terminal and complete operator certification." >&2
exit 1
fi
read -r -p "Operator name: " OPERATOR
if [ -z "${OPERATOR//[[:space:]]/}" ]; then
echo "Operator name is required." >&2
exit 1
fi
read -r -p "Type SIGNOFF to certify you ran this suite locally: " CONFIRM
if [ "$CONFIRM" != "SIGNOFF" ]; then
echo "Sign-off aborted (expected exactly SIGNOFF)." >&2
exit 1
fi
SIGNOFF_DIR="$ROOT/.gstack"
mkdir -p "$SIGNOFF_DIR"
COMMIT="$(git rev-parse HEAD 2>/dev/null || echo unknown)"
TS="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
SIGNOFF_FILE="$SIGNOFF_DIR/security-signoff.json"
# Escape operator name for JSON (minimal — names should not contain quotes).
OPERATOR_JSON="${OPERATOR//\\/\\\\}"
OPERATOR_JSON="${OPERATOR_JSON//\"/\\\"}"
cat >"$SIGNOFF_FILE" <<EOF
{
"operator": "$OPERATOR_JSON",
"signed_at": "$TS",
"commit": "$COMMIT",
"suite": "test/run-security.sh",
"filter": "$FILTER"
}
EOF
echo "Sign-off recorded at $SIGNOFF_FILE (gitignored — local only)."