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:
parent
c6ea724ddb
commit
588f290dcc
4 changed files with 121 additions and 4 deletions
65
test/run-security.sh
Executable file
65
test/run-security.sh
Executable 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)."
|
||||
Loading…
Add table
Add a link
Reference in a new issue