Files
layue13 05f8fad38a
Gatekeep evals / eval-floor (push) Successful in 8s
Dogfood: run Gatekeep on this repo's PRs
Add .github/workflows/gatekeep-pr.yml with uses: ./ and
OPENROUTER_API_KEY from secrets. Forge-agnostic API helper for Gitea.
2026-10-26 20:40:51 +00:00

83 lines
2.9 KiB
Bash

#!/usr/bin/env bash
# Deterministic security checklist seed scanner (paths + diff hunks).
# Hits here are hard-fail regardless of model agreement (see policy.security_agreement).
set -euo pipefail
FILES_LIST="${1:?changed-files list}"
OUT="${2:?out json}"
ROOT="${GATEKEEP_ROOT:-.}"
REPO="${GITHUB_REPOSITORY:-}"
EVENT="${GITHUB_EVENT_PATH:-}"
hits=()
while IFS= read -r f; do
[[ -z "$f" ]] && continue
case "$f" in
.github/workflows/*|.github/actions/*)
hits+=("CI workflow tampering surface: $f")
;;
*secret*|*credential*|.env*|.env)
hits+=("Secrets/credential path touched: $f")
;;
esac
done < "$FILES_LIST"
# Diff scan for high-signal patterns (best-effort; forge-agnostic)
if [[ -n "$EVENT" && -f "$EVENT" && -n "$REPO" ]]; then
PR=$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["pull_request"]["number"])' "$EVENT")
ACTION_DIR="${GATEKEEP_ACTION:-$(cd "$(dirname "$0")/.." && pwd)}"
TMP=$(mktemp)
# Prefer .diff endpoint (works on Gitea + GitHub)
SERVER="${GITHUB_SERVER_URL:-}"
TOKEN="${GITHUB_TOKEN:-${GITEA_TOKEN:-}}"
if [[ -n "$SERVER" ]]; then
curl -sS -H "Authorization: token ${TOKEN}" \
"${SERVER%/}/${REPO}/pulls/${PR}.diff" -o "$TMP" 2>/dev/null \
|| curl -sS -H "Authorization: token ${TOKEN}" \
"${GITHUB_API_URL%/}/repos/${REPO}/pulls/${PR}.diff" -o "$TMP" 2>/dev/null \
|| true
fi
if [[ ! -s "$TMP" ]]; then
python3 "$ACTION_DIR/scripts/forge_api.py" GET "/repos/${REPO}/pulls/${PR}.diff" >"$TMP" 2>/dev/null || true
fi
DIFF=$(cat "$TMP" 2>/dev/null || true)
# Only look at added lines
ADDED=$(grep -E '^\+' "$TMP" | grep -vE '^\+\+\+' || true)
check() {
local name="$1" pat="$2"
if printf '%s\n' "$ADDED" | grep -Eiq -- "$pat"; then
hits+=("$name")
fi
}
check "Hardcoded secrets/tokens pattern" '(api[_-]?key|secret|token|password|private[_-]?key)\s*[:=]\s*['\''\"]?[A-Za-z0-9_\-]{16,}'
check "Prompt injection via repo instruction files" '(ignore (all )?(previous|above) instructions|you are now|system prompt)'
check "Possible SQL injection concat" '(execute\(|query\(|raw\().*\+|(SELECT|DROP|UNION).*\+'
check "Possible command injection" '(exec\(|spawn\(|os\.system\(|subprocess\.[a-z]+\([^)]*shell\s*=\s*True)'
check "Unsafe deserialization" '(pickle\.loads|yaml\.load\(|unserialize\(|ObjectInputStream)'
rm -f "$TMP"
fi
HARD=false
if ((${#hits[@]} > 0)); then
HARD=true
fi
python3 - "$OUT" "$HARD" "${hits[@]}" <<'PY'
import json,sys
out=sys.argv[1]
hard=sys.argv[2]=="true"
hits=sys.argv[3:]
json.dump({"hard_fail": hard, "hits": hits, "checklist": [
"hardcoded secrets/tokens",
"prompt injection via repo files",
"auth bypass",
"unauthorized API access",
"billing logic bypass",
"CI workflow tampering",
"unsafe deserialization",
"SQL/command injection"
]}, open(out,"w"), indent=2)
print(f"security-scan hard_fail={hard} hits={len(hits)}", file=sys.stderr)
PY