fix(permissions): match env-prefixed shell commands against saved permission rules#2850
Conversation
…ommands - Add dotAll flag to matchesCommandPattern for matching commands with embedded newlines - Support newline operators in SHELL_OPERATORS for splitCompoundCommand - Refactor getCommandRoot to skip leading VAR=value assignments - Add test coverage for multiline commands and env var prefixed commands Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
📋 Review SummaryThis PR addresses a critical permission matching bug where shell commands with leading environment variable assignments (e.g., 🔍 General Feedback
🎯 Specific Feedback🟢 Medium
🔵 Low
✅ Highlights
|
tanzhenxin
left a comment
There was a problem hiding this comment.
Nice fix for a real pain point — the root cause analysis is spot on. A couple of things I noticed:
rule-parser.ts, splitCompoundCommand
Adding \n to SHELL_OPERATORS breaks heredocs. Given a rule like Bash(python *):
python - <<'PY'
print(1)
PYsplitCompoundCommand() now splits this into three separate commands: python - <<'PY', print(1), and PY. A previously allowed heredoc starts prompting, and payloads like $(rm -rf /) that are inert inside the heredoc get flagged as dangerous standalone commands.
shell-utils.ts, getCommandRoot
The rewrite switches from a quote-aware regex to .split(/\s+/), which breaks commands with quoted paths containing spaces:
"C:\Program Files\foo\bar.exe" arg1
old: bar.exe ✓
new: Program ✗
This matters on Windows where spaces in paths are common.
Handle env-prefixed commands and quoted Windows paths consistently. Keep newline splitting heredoc-aware and avoid false heredoc detection in comments or arithmetic expressions.
… rewrite Remove ~350 lines of heredoc/comment/arithmetic parsing from splitCompoundCommand that were not needed to fix QwenLM#2846. Revert to the original main version, keeping only the core env-var stripping logic in matchesCommandPattern and getCommandRoot. This addresses both reviewer concerns: - heredoc breakage: no longer an issue since splitCompoundCommand is unchanged - Windows quoted paths: handled correctly by shell-quote parse in getCommandRoot
|
Took another pass after the follow-up changes. The two issues from the earlier review look addressed now: newline splitting is no longer part of the fix, and the quoted Windows path case is handled by the current This looks good to me now. |
tanzhenxin
left a comment
There was a problem hiding this comment.
Review
Fixes the asymmetry between rule extraction (tree-sitter AST correctly strips env vars) and rule matching (regex on raw command). Both matchesCommandPattern and getCommandRoot now normalize commands via shell-quote.parse() before matching. Previous review feedback (Windows path regression, heredoc breakage) has been fully addressed.
Verdict
APPROVE — Previous critical issues resolved. The fix is correct, focused, and well-tested. Minor observations (lossy quote-stripping in token reconstruction, duplicate regex constant, a few missing edge-case tests) are non-blocking. Nice work on the iterative refinement!
TLDR
Fix shell commands with leading environment variable assignments (for example
PYTHONPATH=/tmp python3 -c "...") failing to match saved permission rules. This bug caused repeated permission prompts even after the user selected "Always allow".Core fix: Normalize commands by stripping leading
VAR=valueassignments before permission pattern matching and before command-root extraction, so matching stays aligned with the rule extraction phase.Screenshots / Video Demo
N/A — no user-facing UI change; this is an internal permission matching fix.
Root Cause
There was an asymmetry between rule extraction and rule matching in the permission system:
extractCommandRules) uses tree-sitter AST parsing, correctly treatsPYTHONPATH=...as a variable assignment, extracts the real commandpython3, and saves a rule likepython3 *.matchesCommandPattern) previously matched against the raw command string, so the same rule failed when the command started withPYTHONPATH=.Final Fix
This PR keeps the fix narrow and focused:
1.
rule-parser.ts— Permission pattern matchingstripLeadingVariableAssignments()to normalize commands before matchingmatchesCommandPattern()aligned with the rule extraction behaviordotAllregex matching so commands with embedded newlines in arguments still match correctly2.
shell-utils.ts— Command root extractiongetCommandRoot()to skip leadingVAR=valueassignmentsshell-quoteparsing so quoted Windows paths with spaces are handled correctlyFiles Changed
packages/core/src/permissions/rule-parser.tspackages/core/src/utils/shell-utils.tspackages/core/src/permissions/permission-manager.test.tspackages/core/src/utils/shell-utils.test.tsReviewer Test Plan
Permission matching
npx vitest run packages/core/src/permissions/permission-manager.test.tsCommand root extraction
npx vitest run packages/core/src/utils/shell-utils.test.tsRegression
Testing Matrix
Linked issues / bugs
Fixes #2846
Related to #2669, #2723