feat(shell): allowlisted $VAR expansion, increment 6 — Track A complete (#34)#44
Merged
Conversation
…te (#34) WHAT: The safe-subset engine now expands whole-word variable references $VAR / ${VAR}. parse.rs gains Arg::Var(name) (read_variable: bare or braced name, [A-Za-z_][A-Za-z0-9_]*; a variable must be a STANDALONE word). shell_tool.rs holds a small secret-free VAR_ALLOWLIST (HOME, PWD, USER, TMPDIR, LANG, SHELL, ...); the executor expands an allowlisted var from the env as a single literal (no re-split / no re-glob of the value). WHY (security, the user's policy call): a *confined* engine must not splice secrets into arguments. So the allowlist is enforced in invoke BEFORE any spawn — a $VAR outside the set (e.g. $AWS_SECRET_KEY) is denied and nothing runs, even with full exec. Refused (documented follow-ups): a $VAR mixed into a larger word ($HOME/x), inside double quotes, in a redirect target, or in the program position; $(...) stays Dynamic-refused by design. No re-split/re-glob avoids the re-injection vector. TEST: parser (standalone var marked; mixed/quoted/invalid/bare refused; escaped \$ literal; var-in-redirect refused) + mocked (allowlisted var reaches spawner; non-allowlisted denied with nothing spawned; var-as-program denied) + real integration (echo $HOME == the env value). just check green. This completes Track A (#34): argv, pipes, redirects, &&/||/;, globbing, $VAR. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HMGPEApE4XfwgMhgFbRn6c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Increment 6 of #34 — the finale (ADR 0005 D3). The safe-subset engine now expands whole-word
$VAR/${VAR}for a small secret-free allowlist (your ratified policy: allowlist-only).What
parse.rs—Arg::Var(name)(read_variable: bare or braced,[A-Za-z_][A-Za-z0-9_]*; a variable must be a standalone word).shell_tool.rs—VAR_ALLOWLIST(HOME, PWD, USER, LOGNAME, TMPDIR, LANG, LC_ALL, SHELL, HOSTNAME, TERM, OLDPWD). The executor expands an allowlisted var from the env as a single literal (no re-split / no re-glob → no re-injection).Security (the policy call)
The allowlist is enforced in
invokebefore any spawn: a$VARoutside the set (e.g.$AWS_SECRET_KEY) is denied with nothing spawned, even under fullexec— a confined run can't splice a secret into an argument. Refused (documented follow-ups): a$VARmixed into a word ($HOME/x), inside double quotes, in a redirect target, or as the program name;$(…)stays Dynamic-refused by design.Testing (fully mocked + deep)
$refused; escaped\$literal; var-in-redirect refused.echo $HOME== the process'sHOME.Test plan
just checkgreen (fmt + clippy all-features & no-default-features + workspace tests).This completes Track A (#34): argv → pipes → redirects →
&&/||/;→ globbing →$VAR.🤖 Generated with Claude Code