fix(secret-guard): treat env as a dump only when no command follows
`env -u VAR cmd` and `env FOO=bar cmd` exec a command with a modified environment and print nothing themselves. The old rule matched any `env` token followed anywhere later by a pipe into a filter, so it blocked the documented 1Password write path and forced a `(unset VAR; cmd)` workaround. False positives only — never a leak — but a guard people route around is a guard on its way out. The rule now denies only when everything after `env` is exhausted by options and VAR=value assignments. That is strictly the dumping shape, and it also closes a hole the two previous rules left open: `env FOO=bar` with no command word prints the whole environment and was allowed. Three new deny/allow pairs pin it, including the 1Password write shape that motivated the report. 20 cases now, up from 14, all passing. Closes #25 Bump-Type: patch
This commit is contained in:
+12
-5
@@ -50,11 +50,18 @@ if printf '%s' "$CMD" | grep -Eqi '(^|[;&|]|\s)op\s+document\s+get\b'; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Raw env/secret-file dumps (infra#228/#229 root cause).
|
# Raw env/secret-file dumps (infra#228/#229 root cause).
|
||||||
if printf '%s' "$CMD" | grep -Eqi '(^|[;&|]|\s)env(\s|$).*\|.*grep'; then
|
#
|
||||||
deny "Blocked: 'env | grep' prints matching values in full. Use an existence-only check: [ -n \"\$VAR\" ] && echo set"
|
# `env` is a dump only when nothing but options and VAR=value assignments
|
||||||
fi
|
# follow it. `env -u VAR cmd` and `env FOO=bar cmd` EXEC a command with a
|
||||||
if printf '%s' "$CMD" | grep -Eq '(^|[;&|])[[:space:]]*env[[:space:]]*($|[;&|])'; then
|
# modified environment and print nothing themselves — piping those into grep
|
||||||
deny "Blocked: bare 'env' prints every variable's value, including secrets. Use an existence-only check: [ -n \"\$VAR\" ] && echo set"
|
# filters the command's own output, not the environment (#25). The earlier
|
||||||
|
# pattern matched any `env` token followed anywhere later by a pipe into
|
||||||
|
# grep, which blocked the documented 1Password write path
|
||||||
|
# (`env -u OP_SERVICE_ACCOUNT_TOKEN op item create`) and cost a workaround.
|
||||||
|
# Denying only the "no command word follows" shape also catches `env FOO=bar`
|
||||||
|
# with no command, which the two previous rules let through.
|
||||||
|
if printf '%s' "$CMD" | grep -Eqi '(^|[;&|]|[[:space:]])env([[:space:]]+-[iu][[:space:]]*[A-Za-z_][A-Za-z0-9_]*|[[:space:]]+-[iu]|[[:space:]]+[A-Za-z_][A-Za-z0-9_]*=[^[:space:]]*)*[[:space:]]*($|[|;&])'; then
|
||||||
|
deny "Blocked: this 'env' prints every variable's value, including secrets (no command word follows it, so it dumps rather than execs). Use an existence-only check: [ -n \"\$VAR\" ] && echo set. Note 'env -u VAR cmd' and 'env FOO=bar cmd' are allowed — they run a command."
|
||||||
fi
|
fi
|
||||||
if printf '%s' "$CMD" | grep -Eqi '(^|[;&|]|\s)printenv\b'; then
|
if printf '%s' "$CMD" | grep -Eqi '(^|[;&|]|\s)printenv\b'; then
|
||||||
deny "Blocked: printenv dumps all env values. Use: [ -n \"\$VAR\" ] && echo set"
|
deny "Blocked: printenv dumps all env values. Use: [ -n \"\$VAR\" ] && echo set"
|
||||||
|
|||||||
@@ -61,6 +61,17 @@ expect deny 'op read "op://vault/item/field"' 'op read'
|
|||||||
expect deny 'env | grep -i gitea' 'env piped to grep'
|
expect deny 'env | grep -i gitea' 'env piped to grep'
|
||||||
expect deny 'env' 'bare env'
|
expect deny 'env' 'bare env'
|
||||||
expect deny 'printenv' 'printenv'
|
expect deny 'printenv' 'printenv'
|
||||||
|
expect deny 'env -i | grep -i token' 'env -i with no command is still a dump'
|
||||||
|
expect deny 'env FOO=bar' 'env with only assignments and no command is a dump'
|
||||||
|
|
||||||
|
# --- allow: `env VAR=x cmd` / `env -u VAR cmd` RUN a command (#25) ---
|
||||||
|
# These print nothing themselves; a pipe into grep filters the command's own
|
||||||
|
# output, not the environment. Blocking them cost a documented workaround
|
||||||
|
# ((unset VAR; cmd)) on the 1Password write path, which is the shape below.
|
||||||
|
expect allow 'env -u ANTHROPIC_API_KEY claude --debug -p "ok" 2>&1 | grep -i plugin' 'env -u VAR runs a command'
|
||||||
|
expect allow 'env FOO=bar ./script.sh | grep x' 'env VAR=x runs a command'
|
||||||
|
expect allow 'env -u OP_SERVICE_ACCOUNT_TOKEN op item create - < item.json' 'env -u on the 1P write path'
|
||||||
|
expect deny 'env -u FOO ./run.sh; env | grep -i secret' 'a real dump later in the same line still denies'
|
||||||
|
|
||||||
# --- deny: kubernetes secret material ---
|
# --- deny: kubernetes secret material ---
|
||||||
expect deny 'kubectl get secret brain-app -o yaml' 'kubectl get secret -o yaml'
|
expect deny 'kubectl get secret brain-app -o yaml' 'kubectl get secret -o yaml'
|
||||||
|
|||||||
Reference in New Issue
Block a user