Skip to content

Reject shell substitutions in terminal tool commands#51689

Merged
rtfeldman merged 9 commits intomainfrom
SEC-264/env-var-injection-bypass
Mar 17, 2026
Merged

Reject shell substitutions in terminal tool commands#51689
rtfeldman merged 9 commits intomainfrom
SEC-264/env-var-injection-bypass

Conversation

@rtfeldman
Copy link
Copy Markdown
Contributor

@rtfeldman rtfeldman commented Mar 16, 2026

Harden the terminal tool's permission system to reject commands containing shell substitutions and interpolations ($VAR, ${VAR}, $(…), backticks, $((…)), <(…), >(…)) before they reach terminal creation.

Changes

Shell command parser (shell_command_parser)

  • Added structured terminal command-prefix extraction with env-var prefix support
  • Added parser-backed validation that classifies commands as Safe/Unsafe/Unknown
  • Extended normalized command extraction to include scalar env-var assignments in order
  • Preserved quoted assignment values when they contain whitespace or special characters

Pattern extraction (agent/pattern_extraction)

  • Updated terminal pattern extraction to use structured parser output
  • Included env-var prefixes in generated allow patterns
  • Normalized regex token boundaries to \s+ while preserving display whitespace

Tool permissions (agent/tool_permissions)

  • Added invalid-terminal-command rejection for forbidden substitutions/interpolations
  • Added unconditional allow-all bypass (global default Allow, or terminal-specific Allow with empty patterns)
  • Preserved hardcoded denial precedence over allow-all

Terminal tool (agent/tools/terminal_tool)

  • Updated tool description and input schema to explicitly prohibit shell substitutions
  • Added comprehensive SEC-264 regression test suite (20 new tests) covering:
    • All forbidden constructs (${HOME}, $1, $?, $$, $@, $(whoami), backticks, $((1+1)), <(ls), >(cat), env-prefix variants, multiline, nested)
    • Allow-all exception paths (global and terminal-specific)
    • Hardcoded-denial precedence
    • Env-prefix permission flow (matching, value mismatch rejection, multiple assignments, quoted whitespace)

Closes SEC-264

Release Notes:

  • Terminal tool permissions regexes can now match environment variables (e.g. FOO=bar cmd arg1 arg2)
  • If terminal tool permissions have active permissions regexes running on them, then bare interpolations like $FOO are disallowed for security, since regexes wouldn't be able to match on them.

@rtfeldman rtfeldman self-assigned this Mar 16, 2026
@cla-bot cla-bot Bot added the cla-signed The user has signed the Contributor License Agreement label Mar 16, 2026
@zed-community-bot zed-community-bot Bot added the staff Pull requests authored by a current member of Zed staff label Mar 16, 2026
Fix two security bypasses in shell command validation:

- CaseClause patterns (Vec<Word>) were not being validated, allowing
  command substitutions like $(whoami) in case patterns to slip through
  as Safe despite being executed by the shell at runtime.

- ArithmeticForClause only validated the body, ignoring initializer,
  condition, and updater expressions that can contain command
  substitutions. Now treated as unconditionally Unsafe, mirroring
  standalone Arithmetic.
Clarify the return type of normalize_assignment_for_command_prefix by
introducing a NormalizedAssignment enum with Included and Skipped
variants, replacing the confusing double-Option.
When a command has both Unsafe and Unknown parts, return Unsafe since
it is a more definitive and actionable signal for user-facing error
messages.
Replace wildcard catch-all arms with explicit matches for Word and
ProcessSubstitution variants so future enum additions cause compile
errors rather than silently passing through.
'Unsupported' more accurately describes what the variant means: the
command uses syntax we cannot analyze, not that the result is unknown
for some other reason.
@rtfeldman rtfeldman marked this pull request as ready for review March 17, 2026 03:49
@rtfeldman rtfeldman merged commit f3fb4e0 into main Mar 17, 2026
31 checks passed
@rtfeldman rtfeldman deleted the SEC-264/env-var-injection-bypass branch March 17, 2026 03:49
piper-of-dawn pushed a commit to piper-of-dawn/zed that referenced this pull request Apr 25, 2026
…51689)

Harden the terminal tool's permission system to reject commands
containing shell substitutions and interpolations (`$VAR`, `${VAR}`,
`$(…)`, backticks, `$((…))`, `<(…)`, `>(…)`) before they reach terminal
creation.

## Changes

### Shell command parser (`shell_command_parser`)
- Added structured terminal command-prefix extraction with env-var
prefix support
- Added parser-backed validation that classifies commands as
Safe/Unsafe/Unknown
- Extended normalized command extraction to include scalar env-var
assignments in order
- Preserved quoted assignment values when they contain whitespace or
special characters

### Pattern extraction (`agent/pattern_extraction`)
- Updated terminal pattern extraction to use structured parser output
- Included env-var prefixes in generated allow patterns
- Normalized regex token boundaries to `\s+` while preserving display
whitespace

### Tool permissions (`agent/tool_permissions`)
- Added invalid-terminal-command rejection for forbidden
substitutions/interpolations
- Added unconditional allow-all bypass (global default Allow, or
terminal-specific Allow with empty patterns)
- Preserved hardcoded denial precedence over allow-all

### Terminal tool (`agent/tools/terminal_tool`)
- Updated tool description and input schema to explicitly prohibit shell
substitutions
- Added comprehensive SEC-264 regression test suite (20 new tests)
covering:
- All forbidden constructs (`${HOME}`, `$1`, `$?`, `$$`, `$@`,
`$(whoami)`, backticks, `$((1+1))`, `<(ls)`, `>(cat)`, env-prefix
variants, multiline, nested)
  - Allow-all exception paths (global and terminal-specific)
  - Hardcoded-denial precedence
- Env-prefix permission flow (matching, value mismatch rejection,
multiple assignments, quoted whitespace)

Closes SEC-264

Release Notes:

- Terminal tool permissions regexes can now match environment variables
(e.g. `FOO=bar cmd arg1 arg2`)
- If terminal tool permissions have active permissions regexes running
on them, then bare interpolations like `$FOO` are disallowed for
security, since regexes wouldn't be able to match on them.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed The user has signed the Contributor License Agreement staff Pull requests authored by a current member of Zed staff

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant