Port of upstream grok-build 3794978 (snap6 "Reject ripgrep --hostname-bin
on always-safe and Auto routine paths"), widened where review showed the
same hole one layer down.
- `rg --pre` / `--hostname-bin` execute a program per file. The safe
list refused only `--pre`, and the Auto-mode routine classifier had
no rg flag check at all. One shared `rg_has_unsafe_flag` now refuses
both flags (bare or `=VALUE`) on a lowercased-basename head, scanning
every argument: rg's grammar (`--replace -- --pre`, `-ne --`) makes a
partial arity model bypassable, so a literal search for the flag name
costs a prompt instead of the auto-allow.
- The shell splitter handed allowlists undecoded source: a concatenation
(`--host""name-bin`, `''--hostname-bin`, `--p're'`) kept its quotes,
and double-quoted escapes (`"--host\<newline>name-bin"`) survived.
`PlainCommand` words are now the decoded argv (bash double-quote
rules, whole-node text minus the quotes so literal newlines and the
whitespace tree-sitter folds into the closing quote survive), with a
per-word `expandable` flag for unquoted glob/brace/tilde/backslash
syntax the shell would still expand (`--{pre,pre}`, a glob that can
match a file named `--pre`); the rg guard treats those as unsafe.
- Every prefix-based decision matches per argv element through one
matcher (built-in safe list, always-safe list, dangerous list, Auto
routine prefixes): a decoded word with inner whitespace (`rg `,
`rg /../payload`, one word spelled `git status`) can no longer pose
as a prefix. Saved grants match token for word (a grant saved from a
spaced argument prompts again). Saved denies keep the pre-0.1.15
check verbatim on a `legacy_words` spelling (raw content and raw
concatenations, wrappers peeled independently, space-boundary prefix)
so no deny narrows on upgrade.
Claude-Session: https://claude.ai/code/session_01RUqV8HVGXcAETyYbZ2tPQ4