mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-16 09:55:29 +02:00
feat(security): receipt admin scripts + user git-ops (zero exceptions)
Wire the remaining shell egress through gstack-egress-lib.sh: - gstack-gbrain-mcp-verify: both JSON-RPC probe POSTs (initialize + tools/list) receipted fail-closed via payload files (hash == wire bytes). A refused receipt lands in the NETWORK class — no send. - gstack-security-dashboard / gstack-community-dashboard: the community-pulse GETs receipted fail-open (read-only stats must not break over an audit hiccup). - gstack-gbrain-supabase-provision: api_call receipted fail-closed. Each retry attempt hands the helper a fresh copy of the body file (the helper consumes its payload). The receipt hashes the request body only — the PAT never reaches the ledger or any log. Refusal exits 8 without retrying. - git-class sha256:null receipts, fail-open: gstack-artifacts-init (ls-remote, initial push, fetch/pull recovery, retry push), gstack-brain-restore (staging clone, existing-repo fetch), gstack-session-update (self-update pull). gstack-team-init needs no wiring: every git clone in it is inside an echoed instruction string, not an executed command. The lib now self-locates with shell builtins only (no dirname), so sourcing works under the whitelist-PATH test harnesses. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit b8c5e2055b21ab72878b3e46f8047782ee65a11c)
This commit is contained in:
@@ -69,6 +69,12 @@ set -euo pipefail
|
||||
|
||||
SUPABASE_API_BASE="${SUPABASE_API_BASE:-https://api.supabase.com}"
|
||||
API_VERSION="v1"
|
||||
|
||||
# Egress receipt helpers (_receipted_curl): receipt-before-send, fail-closed.
|
||||
# The receipt hashes the request body only — the PAT (Authorization header)
|
||||
# is never receipted or logged.
|
||||
. "$(cd "$(dirname "$0")" && pwd)/gstack-egress-lib.sh"
|
||||
SUPABASE_API_HOST="${SUPABASE_API_BASE#*://}"; SUPABASE_API_HOST="${SUPABASE_API_HOST%%/*}"
|
||||
DEFAULT_WAIT_TIMEOUT=180
|
||||
POLL_INTERVAL=5
|
||||
CURL_TIMEOUT=30
|
||||
@@ -135,11 +141,24 @@ api_call() {
|
||||
-H "Content-Type: application/json"
|
||||
-H "User-Agent: gstack-gbrain-supabase-provision"
|
||||
)
|
||||
# Receipted fail-closed. The retry loop reuses $body_file across
|
||||
# attempts, but the helper consumes its payload file — so each attempt
|
||||
# hands it a fresh copy (hash still equals the exact wire bytes; the
|
||||
# helper appends --data-binary @copy). Bodyless calls use --no-payload.
|
||||
local payload_arg="--no-payload"
|
||||
if [ -n "$body_file" ]; then
|
||||
curl_args+=(--data-binary "@$body_file")
|
||||
payload_arg=$(mktemp)
|
||||
cp "$body_file" "$payload_arg"
|
||||
fi
|
||||
local status
|
||||
if ! status=$(curl "${curl_args[@]}" "$url" 2>/dev/null); then
|
||||
local status rc=0
|
||||
status=$(_receipted_curl closed supabase-provision "$SUPABASE_API_HOST" "provision-api-call ($method $apipath)" "user ran gstack-gbrain-supabase-provision" "$payload_arg" \
|
||||
curl "${curl_args[@]}" "$url") || rc=$?
|
||||
if [ "$rc" -eq 3 ] && [ -z "$status" ]; then
|
||||
# Egress receipt refused — the send never happened (the helper's
|
||||
# problem/cause/fix message is already on stderr). Don't retry.
|
||||
exit 8
|
||||
fi
|
||||
if [ "$rc" -ne 0 ]; then
|
||||
# curl itself failed (network, timeout, etc.). Retry.
|
||||
if [ "$attempt" -ge "$max_attempts" ]; then
|
||||
die_net "network failure calling $method $apipath after $attempt attempts"
|
||||
|
||||
Reference in New Issue
Block a user