Skip to content

fix(permission): prefer bash command prefix for approval grants#3815

Merged
esengine merged 2 commits into
esengine:main-v2from
JesonChou:fix/issue-3759-approval-scope-wrong
Jun 11, 2026
Merged

fix(permission): prefer bash command prefix for approval grants#3815
esengine merged 2 commits into
esengine:main-v2from
JesonChou:fix/issue-3759-approval-scope-wrong

Conversation

@JesonChou

@JesonChou JesonChou commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Reviewer feedback

# Request Status
1 Fully remove scope plumbing ✅ Done
2 Collapse TUI exact/prefix choices ✅ Done
3 Document edit widening + add test ✅ Done

Edit persistence scope (point 3): approving one edit_file call with "Always allow (save to config)" saves the rule as Edit — tool-wide, no path restriction. This covers all six file-mutation tools on any file across all future sessions. Deny rules still take precedence. This is intentional and matches the existing session-grant behaviour.


Fixes #3759 — the approval system remembered rules with full command arguments (e.g. "Bash(grep -r specificTerm .)"), causing every slightly different invocation to re-prompt within the same session.

Root cause: SessionGrantRuleForScope and RememberRuleForScope only applied BashCommandPrefix when scope==ApprovalScopePrefix, but the default "allow this session" path used ApprovalScopeExact — so the prefix was never used. Every command was stored literally.

Fix:

  • Both functions now always prefer BashCommandPrefix for bash when a safe prefix can be extracted, regardless of the scope parameter. Safe prefix means: >=2 words, no shell metacharacters, not dangerous. When no prefix is available (single word, shell syntax, dangerous command), the exact command is still stored as the fallback.
  • File mutation tools remain tool-wide ("Edit") — matching existing session-grant behaviour.
  • The now-unused scope parameter is removed from both function signatures; all callers updated.

Concrete example:
Before: "grep -r TODO ." approves -> stored "Bash(grep -r TODO .)" "grep -r FIXME ." -> prompts again (different subject) After: "grep -r TODO ." approves -> stored "Bash(grep -r:*)" "grep -r FIXME ." -> auto-allowed (matches prefix) "git log" -> still prompts (different command family)

@github-actions github-actions Bot added v2 Go rewrite (1.x) — main-v2 branch, active development tui Terminal UI / CLI (internal/cli, internal/control) agent Core agent loop (internal/agent, internal/control) labels Jun 10, 2026
@JesonChou

Copy link
Copy Markdown
Contributor Author

Other generic tools (search, web_fetch, grep, etc.) already returned the bare tool name in the old code and are unchanged. File mutation tools now use the same "Edit" grant for persistent rules that they already used for session grants — no new behavior, just alignment.

@esengine esengine left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — the root-cause analysis is exactly right, and preferring a safe bash prefix is the behaviour I want for #3759. The danger/shell-syntax guard in BashCommandPrefix keeps the widening safe, and the prefix-match tests are good. A few things to sort before I merge:

1. Finish removing the scope concept — right now it's only half-gone. The description says the unused scope parameter was removed and all callers updated, but it was only dropped from SessionGrantRuleForScope / RememberRuleForScope. approvalReply.scope, the scope parameter on Controller.ApproveWithScope, and the ApprovalScopeExact / ApprovalScopePrefix constants are still written but no longer read — requestApproval no longer consumes r.scope. Either fully remove the scope plumbing or keep it honoured; leaving it inert is dead code in a security-sensitive path.

2. The TUI still offers "exact" vs "prefix" keys that now produce the same rule. In chat_tui.go the session/persist handlers still pass ApprovalScopeExact vs ApprovalScopePrefix, but since the rule builder ignores scope, choosing the exact option silently stores the wider prefix grant. If we're committing to prefix-by-default (I'm fine with that), collapse those into a single "allow for this session" / "always allow" pair so the UI matches what's actually stored.

3. Confirm the persistent file-edit widening is intended. rememberRule("edit_file", "src/app.go") now returns Edit instead of Edit(src/app.go), so "Always allow (save to config)" on one edit persists a tool-wide Edit grant — every file edit in every future session auto-approves. That matches the in-session grant, but persisting a blanket edit-allow is a bigger step than the session one. I'm inclined to accept it, but please call it out in the description and add a test asserting the persisted rule is tool-wide, so it's a deliberate choice rather than a side effect.

Core direction is right. Get the scope cleanup done and the Edit widening documented + tested, and I'll run CI and merge.

@JesonChou

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review and the careful security analysis.

I’ll address all three points before the merge:

  1. Scope removal – You're right, the cleanup was incomplete. I'll fully remove the unused scope plumbing: delete approvalReply.scope, the scope parameter from Controller.ApproveWithScope, and the constants ApprovalScopeExact/ApprovalScopePrefix. The approval path will then have no dead code.
  2. TUI options – I'll collapse the "exact" vs "prefix" choices into a single "allow for this session" / "always allow" pair, since they now behave identically. The UI will accurately reflect the stored rule (prefix‑by‑default).
  3. Edit widening – I've confirmed this is intentional, but I agree it needs to be explicit. I'll update the PR description to highlight that persisting an edit approval creates a tool‑wide Edit grant. I'll also add a test that asserts the persisted rule has no path restriction, so future readers understand the behaviour is deliberate.

I'll push the cleanup, UI changes, and the additional test shortly. Once CI passes, feel free to merge.

Thanks again for the thorough review.

JesonChou added a commit to JesonChou/DeepSeek-Reasonix that referenced this pull request Jun 10, 2026
Address reviewer feedback on esengine#3815:

1. Remove the now-inert scope parameter from approvalReply, the
   defunct ApproveWithScope method, and the ApprovalScopeExact /
   ApprovalScopePrefix constants. All callers (desktop, serve, ACP
   dispatch, TUI) updated to use the single Approve method.

2. Collapse the TUI approval handler into a single set of options
   (allow once / session / persist) since prefix and exact scopes
   now produce identical rules. No more hasBashPrefix branching.

3. Add TestPersistedEditRuleIsToolWide asserting that a persisted
   "always allow" on one edit_file call creates a tool-wide Edit
   grant (no path restriction) covering all mutation tools on all
   files. This documents the deliberate choice.

All existing tests pass. Net -59 lines across 11 files.
@JesonChou JesonChou force-pushed the fix/issue-3759-approval-scope-wrong branch from e234b64 to b65fbd0 Compare June 10, 2026 13:31
@github-actions github-actions Bot added the desktop Wails desktop app (desktop/**) label Jun 10, 2026
JesonChou added a commit to JesonChou/DeepSeek-Reasonix that referenced this pull request Jun 10, 2026
Address reviewer feedback on esengine#3815:

1. Remove the now-inert scope parameter from approvalReply, the
   defunct ApproveWithScope method, and the ApprovalScopeExact /
   ApprovalScopePrefix constants. All callers (desktop, serve, ACP
   dispatch, TUI) updated to use the single Approve method.

2. Collapse the TUI approval handler and banner into a single set of
   options (allow once / session / persist / deny) since prefix and
   exact scopes now produce identical rules.  Preserved all deny
   key bindings (4/n/5/Esc).

3. Add TestPersistedEditRuleIsToolWide asserting that a persisted
   "always allow" on one edit_file call creates a tool-wide Edit
   grant (no path restriction) covering all mutation tools on all
   files. This documents the deliberate choice.

All existing tests pass.
@JesonChou JesonChou force-pushed the fix/issue-3759-approval-scope-wrong branch 2 times, most recently from 6c58c2e to b65fbd0 Compare June 10, 2026 13:58
Fixes esengine#3759 — the approval system remembered rules with full command
arguments (e.g. "Bash(grep -r specificTerm .)"), causing every
slightly different invocation to re-prompt within the same session.

Root cause: SessionGrantRuleForScope and RememberRuleForScope only
applied BashCommandPrefix when scope==ApprovalScopePrefix, but the
default "allow this session" path used ApprovalScopeExact — so the
prefix was never used.  Every command was stored literally.

Fix:
- Both functions now always prefer BashCommandPrefix for bash when a
  safe prefix can be extracted, regardless of the scope parameter.
  Safe prefix means: >=2 words, no shell metacharacters, not dangerous.
  When no prefix is available (single word, shell syntax, dangerous
  command), the exact command is still stored as the fallback.
- File mutation tools remain tool-wide ("Edit") — matching existing
  session-grant behaviour.
- The now-unused scope parameter is removed from both function
  signatures; all callers updated.

Concrete example:
  Before: "grep -r TODO ." approves -> stored "Bash(grep -r TODO .)"
          "grep -r FIXME ." -> prompts again (different subject)
  After:  "grep -r TODO ." approves -> stored "Bash(grep -r:*)"
          "grep -r FIXME ." -> auto-allowed (matches prefix)
          "git log" -> still prompts (different command family)
…s backend + frontend

Address reviewer feedback on esengine#3815:
1. Remove the inert scope parameter from approvalReply, Controller.ApproveWithScope,
   ApprovalScopeExact/ApprovalScopePrefix constants, and all callers.
2. Collapse TUI approval handler into single session/persist pair (no more
   hasBashPrefix branching). Restored numeric key "4" for deny.
3. Add TestPersistedEditRuleIsToolWide for the deliberate tool-wide Edit grant.
4. Clean up desktop Go (ApproveWithScope/ApproveTabWithScope), TypeScript bridge
   interface+mock, serve.go Scope field, and React ApprovalModal "prefix" arg.
5. Restore nil guard in bindApprove.
@JesonChou JesonChou force-pushed the fix/issue-3759-approval-scope-wrong branch from b65fbd0 to a0b4b77 Compare June 10, 2026 17:14
@JesonChou

Copy link
Copy Markdown
Contributor Author

All three points addressed:

  1. Scope fully removedapprovalReply.scope, ApproveWithScope, and the
    ApprovalScopeExact / ApprovalScopePrefix constants are deleted. Every
    caller uses the single Approve. 11 files, net -59 lines.

  2. TUI collapsedhandleApprovalKey no longer branches on hasBashPrefix.
    All options produce identical behaviour for every tool.

  3. Edit widening documented + testedTestPersistedEditRuleIsToolWide
    asserts tool-wide Edit with no path restriction. Function and test comments
    call out the deliberate choice. PR description updated with the note.

Full regression passes, three-way merge against upstream main-v2 is clean. @esengine

@JesonChou JesonChou requested a review from esengine June 10, 2026 17:40

@esengine esengine left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All three points addressed exactly as promised — the scope plumbing is fully gone end to end (controller, ACP dispatch/service, desktop bridge + dev mock, serve HTTP API), the TUI and modal now present a single session/persist pair that matches what actually gets stored, and TestPersistedEditRuleIsToolWide locks in the tool-wide Edit grant as a deliberate choice with both positive and negative matches. Nice, thorough cleanup.

Merging once CI is green.

@esengine esengine enabled auto-merge (squash) June 11, 2026 02:36
@esengine esengine merged commit 6ae1b57 into esengine:main-v2 Jun 11, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent Core agent loop (internal/agent, internal/control) desktop Wails desktop app (desktop/**) tui Terminal UI / CLI (internal/cli, internal/control) v2 Go rewrite (1.x) — main-v2 branch, active development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: 1.4版本,批准依然不对,重复提示需要批准 CLI

2 participants