mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-12 07:59:02 +02:00
fix(redact): close the remaining #1946 fail-opens — detection coverage + one-time consent
Two of #1946's reported gaps were still open after the v1.64 fail-closed work (the git-error and oversized-diff paths in bin/gstack-redact-prepush are already strict, chunked, and pinned by tests): 1. Detection fail-open: env.kv required an UPPERCASE name with an '=' assignment, so 'api_key=…', 'apiKey: "…"', and 'password: …' — the most common real config shapes — produced NO finding at all. The pattern is now case-insensitive, accepts ':' (YAML/JSON) as well as '=' assignment, and handles quoted JSON keys. It stays MEDIUM and entropy-gated per the calibration rule (a generic net that cries wolf gets bypassed), with pinned cases for each closed shape plus the placeholder/entropy negatives. 2. Install fail-open: nothing ever offered the guard, so a plain 'git push' scanned nothing and users believing themselves protected weren't. setup now asks ONCE for consent on a real interactive terminal (maintainer decision 6): an explicit answer is recorded to the existing redact_prepush_hook key and never re-asked; a timeout or non-interactive run changes nothing and keeps the hint-only posture. Default stays FALSE, and setup still never installs the hook itself — /ship owns the per-repo install (the wrong-repo invariant is pinned by the existing 'setup carries the hint only' test). Tests: per-shape pattern cases, prompt gating statics (key-absence + TTY + timed default-N read), timeout-persists-nothing, non-interactive stays hint-only with no key write, and recorded-answer-is-silent behavior runs. Contributes to #1946 (the pre-push guard's fail-closed scan paths landed in earlier releases; this closes the coverage and consent gaps it names). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
9af589bb73
commit
9c0de5fed1
@@ -2002,19 +2002,47 @@ if [ "$NO_TEAM_MODE" -eq 1 ] && [ -x "$SETTINGS_HOOK" ]; then
|
||||
"$SETTINGS_HOOK" remove-source --source plan-tune-cathedral 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# ─── Redact pre-push guard hint (#1946) ──────────────────────────────────────
|
||||
# ─── Redact pre-push guard consent (#1946) ───────────────────────────────────
|
||||
# The credential pre-push hook is per-REPO state — setup runs in the gstack
|
||||
# checkout, the wrong repo to install it into. /ship offers the install once
|
||||
# at the moment of relevance (first push) and silently installs in any repo
|
||||
# where redact_prepush_hook=true. This hint is setup's whole involvement.
|
||||
# Hint only when UNSET — an explicit "false" is a recorded decline and must
|
||||
# not be re-nagged on every setup run (adversarial review finding 11).
|
||||
# checkout, the wrong repo to install it into, so setup NEVER installs the
|
||||
# hook itself. /ship installs it silently in any repo where
|
||||
# redact_prepush_hook=true. What setup owns is CONSENT: on a real interactive
|
||||
# terminal it asks ONCE whether pushes should be scanned, recording the
|
||||
# answer to the existing redact_prepush_hook key (default stays false — a
|
||||
# timeout or non-interactive run changes nothing and keeps the hint-only
|
||||
# posture). An explicit answer is persisted and never re-asked; an explicit
|
||||
# "false" is a recorded decline (adversarial review finding 11).
|
||||
# `gstack-config get` defaults absent keys to "false", which is
|
||||
# indistinguishable from a decline — test key presence in the config file.
|
||||
_GSTACK_CFG_FILE="${GSTACK_HOME:-$HOME/.gstack}/config.yaml"
|
||||
if ! grep -q '^redact_prepush_hook:' "$_GSTACK_CFG_FILE" 2>/dev/null; then
|
||||
log ""
|
||||
log "Tip: gstack can block pushes containing credentials (per-repo git hook)."
|
||||
log " Enable once: gstack-config set redact_prepush_hook true — /ship"
|
||||
log " installs the hook automatically in every repo you ship from."
|
||||
if [ "$QUIET" -ne 1 ] && [ -t 0 ] && [ -t 1 ]; then
|
||||
_REDACT_PROMPT_TIMEOUT=10
|
||||
log ""
|
||||
log "Credential push guard: gstack can block pushes containing credentials"
|
||||
log "(a per-repo git pre-push hook; /ship installs it automatically in every"
|
||||
log "repo you ship from — nothing is installed right now)."
|
||||
printf "Enable the pre-push credential guard? [y/N] (default: N, auto-skips in %ss): " "$_REDACT_PROMPT_TIMEOUT"
|
||||
read -t "$_REDACT_PROMPT_TIMEOUT" -r _REDACT_REPLY </dev/tty 2>/dev/null || _REDACT_REPLY=""
|
||||
case "$_REDACT_REPLY" in
|
||||
y|Y)
|
||||
"$GSTACK_CONFIG" set redact_prepush_hook true 2>/dev/null || true
|
||||
log "Enabled. /ship will install the guard in each repo at first push."
|
||||
;;
|
||||
n|N)
|
||||
"$GSTACK_CONFIG" set redact_prepush_hook false 2>/dev/null || true
|
||||
log "Declined — recorded. Re-enable anytime: gstack-config set redact_prepush_hook true"
|
||||
;;
|
||||
*)
|
||||
# Timed out / empty: don't persist a decline — hint and ask next time.
|
||||
log ""
|
||||
log "Skipped for now. Enable anytime: gstack-config set redact_prepush_hook true"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
log ""
|
||||
log "Tip: gstack can block pushes containing credentials (per-repo git hook)."
|
||||
log " Enable once: gstack-config set redact_prepush_hook true — /ship"
|
||||
log " installs the hook automatically in every repo you ship from."
|
||||
fi
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user