mirror of
https://github.com/Sea-Haven-Industries/shoc-pr-review-runner.git
synced 2026-09-30 08:03:20 +00:00
Manually-dispatched GitHub Actions workflow that reviews SHOC pull requests in a clean environment: exact-head checkout of shoc-frontend-new and shoc-backend, clean build/test gates, a truthful evidence report, a single-shot Fireworks review, deterministic output validation, and published artifacts. The runner never writes to the product repositories or their pull requests. The review checklists move here from the reviewers' local Cursor commands so the instructions live outside both product repos. Phase 1 does not provision a database, start either application, or run live browser flows; the evidence report records those as NOT_RUN so a review cannot claim them. Security architecture: building a PR executes its author's code, so the workflow is split. The gates job runs that code holding no Fireworks key and revokes its App token first; the review job holds the key, executes no product code, and re-checks out this repo fresh. Product checkouts live outside the workspace, the App token is downscoped at mint time, gate results fail closed on any duplicate key, changed files are read from git objects rather than the filesystem, and the validator re-checks every claim against the gate table.
93 lines
3.7 KiB
Bash
Executable file
93 lines
3.7 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Pre-upload artifact hygiene (spec §25): scan every staged artifact for the
|
|
# run's secret values and for generic credential patterns, and fail the upload
|
|
# on any hit. Secrets must never reach console logs, evidence files, prompts, or
|
|
# uploaded artifacts.
|
|
#
|
|
# Matching is deliberately broader than a literal grep: log formatters wrap long
|
|
# values across lines, and encoders re-shape them, so each artifact is also
|
|
# scanned in a whitespace-stripped form and against derived encodings of each
|
|
# secret. Archives are refused rather than scanned opaquely.
|
|
#
|
|
# Reads: ARTIFACTS_DIR, plus whichever secrets are in scope for this job. At
|
|
# least one of GH_TOKEN / FIREWORKS_API_KEY must be present — an empty scan set
|
|
# would pass vacuously and produce a green signal that proves nothing.
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
# shellcheck source=lib.sh
|
|
source "$SCRIPT_DIR/lib.sh"
|
|
|
|
if [ -z "${GH_TOKEN:-}" ] && [ -z "${FIREWORKS_API_KEY:-}" ]; then
|
|
die "redaction check has no secrets to scan for — refusing to report clean (set GH_TOKEN and/or FIREWORKS_API_KEY)"
|
|
fi
|
|
|
|
hits=0
|
|
scanned=()
|
|
|
|
# Normalized copy of the artifact tree: newlines and spaces stripped, so a value
|
|
# wrapped across lines by a log formatter still matches.
|
|
norm_dir="$(mktemp -d)"
|
|
trap 'rm -rf "$norm_dir"' EXIT
|
|
while IFS= read -r -d '' f; do
|
|
case "$f" in
|
|
*.zip|*.gz|*.tgz|*.tar|*.bz2|*.xz|*.7z)
|
|
log "SECRET LEAK RISK: archive staged for upload cannot be scanned: $f"
|
|
hits=$((hits + 1))
|
|
continue
|
|
;;
|
|
esac
|
|
tr -d '\n\r \t' <"$f" >"$norm_dir/$(printf '%s' "$f" | md5sum | cut -d' ' -f1)" 2>/dev/null || true
|
|
done < <(find "$ARTIFACTS_DIR" -type f -print0)
|
|
|
|
# scan_value <label> <value> — checks the value and its common derived forms.
|
|
scan_value() {
|
|
local label="$1" value="$2"
|
|
[ -n "$value" ] || return 0
|
|
scanned+=("$label")
|
|
local -a forms=()
|
|
forms+=("$value")
|
|
forms+=("$(printf '%s' "$value" | base64 | tr -d '\n')")
|
|
forms+=("$(printf 'x-access-token:%s' "$value" | base64 | tr -d '\n')")
|
|
# URL-encoded form (only the characters that actually appear in tokens).
|
|
forms+=("$(printf '%s' "$value" | sed 's|/|%2F|g; s|+|%2B|g; s|=|%3D|g')")
|
|
local form found
|
|
for form in "${forms[@]}"; do
|
|
[ -n "$form" ] || continue
|
|
found="$(grep -rlF -- "$form" "$ARTIFACTS_DIR" 2>/dev/null || true)"
|
|
if [ -n "$found" ]; then
|
|
log "SECRET LEAK: $label found in artifact file(s):"
|
|
printf '%s\n' "$found" >&2
|
|
hits=$((hits + 1))
|
|
fi
|
|
# Whitespace-stripped scan catches line-wrapped occurrences.
|
|
found="$(grep -rlF -- "$(printf '%s' "$form" | tr -d '\n\r \t')" "$norm_dir" 2>/dev/null || true)"
|
|
if [ -n "$found" ]; then
|
|
log "SECRET LEAK: $label found (line-wrapped or whitespace-split) in a staged artifact"
|
|
hits=$((hits + 1))
|
|
fi
|
|
done
|
|
}
|
|
|
|
scan_value "GH_TOKEN (App installation token)" "${GH_TOKEN:-}"
|
|
scan_value "FIREWORKS_API_KEY" "${FIREWORKS_API_KEY:-}"
|
|
scan_value "SHOC_REVIEW_APP_PRIVATE_KEY" "${SHOC_REVIEW_APP_PRIVATE_KEY:-}"
|
|
|
|
# Generic credential patterns: catches secrets belonging to the PRODUCT repos
|
|
# (a PR touching .env or appsettings) that the runner knows nothing about.
|
|
generic_hits="$(grep -rlEI \
|
|
-e 'gh[pousr]_[A-Za-z0-9]{30,}' \
|
|
-e 'github_pat_[A-Za-z0-9_]{30,}' \
|
|
-e 'BEGIN [A-Z ]*PRIVATE KEY' \
|
|
-e 'AKIA[0-9A-Z]{16}' \
|
|
-e 'xox[baprs]-[A-Za-z0-9-]{10,}' \
|
|
"$ARTIFACTS_DIR" 2>/dev/null || true)"
|
|
if [ -n "$generic_hits" ]; then
|
|
log "SECRET LEAK: generic credential pattern found in artifact file(s):"
|
|
printf '%s\n' "$generic_hits" >&2
|
|
hits=$((hits + 1))
|
|
fi
|
|
|
|
if [ "$hits" -gt 0 ]; then
|
|
die "artifact redaction check failed: $hits leak indicator(s) — artifacts will not be uploaded"
|
|
fi
|
|
log "artifact redaction check clean (scanned for: ${scanned[*]} + generic credential patterns)"
|