Skip to content

Permission system splits heredoc/multi-line shell commands per line, breaking Bash(prefix) allow rules #9381

Description

@zwj-lambda

Environment

  • qwen-code version: 0.21.13

Problem

When a shell command embeds multi-line Python via heredoc (or any newline outside quotes),
the permission system splits the command per line and evaluates each line as an independent
command. Each line's first word (e.g. import, print) is treated as a command name;
per-line prompts appear and allow rules like Bash(python *) never match the whole command,
so it can never be auto-approved.

Reproduction

  1. Configure permissions.allow: ["Bash(python *)"].

  2. Run in ask/auto mode:

    python - <<'PY'
    import os
    print(os.getcwd())
    PY
  3. Observed: the command is split into ["python - <<'PY'", "import os", "print(os.getcwd())", "PY"];
    segments 2-3 match no allow rule and fall through to per-line ask/classifier evaluation.

Minimal repro of the splitter (logic from chunk-4S5337WV.js):

const SHELL_OPERATORS = ["&&", "||", ";;", "|&", "|", ";", "&", "\n"];
// splitCompoundCommandSegments("python - <<'PY'\nimport os\nprint(1)\nPY")
// -> ["python - <<'PY'", "import os", "print(1)", "PY"]

Root cause

In chunk-4S5337WV.js:

  • splitCompoundCommandSegments treats \n as a shell operator (SHELL_OPERATORS, ~line 43255).
  • The rule-evaluation path splitCompoundCommand() (~line 43342) splits the raw command
    WITHOUT stripping heredoc bodies.
  • The file-operation path walkCompoundCommand() (~line 47677) calls
    splitCompoundCommandSegments(stripHeredocBodies(command)) — heredoc bodies ARE stripped there.

The two paths are inconsistent: heredoc bodies are only stripped in the file-op path, not in
the permission rule-evaluation path.

Expected

python - <<'PY' ... PY (and any command whose newlines live inside quotes or a heredoc body)
is treated as a single command, so Bash(python *) allow rules match it.

Suggested fix

Strip heredoc bodies inside splitCompoundCommand(), mirroring walkCompoundCommand().

Metadata

Metadata

Assignees

No one assigned

    Labels

    category/securitySecurity and privacypriority/P2Medium - Moderately impactful, noticeable problemscope/shellShell command executiontype/bugSomething isn't working as expected

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions