Skip to content

fix: deny-by-default when ctx.ui.custom() returns undefined in RPC mode#25

Merged
aliou merged 2 commits intomainfrom
fix/rpc-permission-gate-bypass-v2
Apr 17, 2026
Merged

fix: deny-by-default when ctx.ui.custom() returns undefined in RPC mode#25
aliou merged 2 commits intomainfrom
fix/rpc-permission-gate-bypass-v2

Conversation

@aliou
Copy link
Copy Markdown
Owner

@aliou aliou commented Apr 17, 2026

Summary

Fixes #19. Rewrites PR #20 on top of current main (which now includes the path-access feature, causing massive conflicts on the old branch).

Bug: ctx.ui.custom() returns undefined in RPC mode (Pi's RPC runtime stubs it as async custom() { return undefined; }). The permission gate only checked for result === "deny", so undefined fell through and dangerous commands were silently allowed.

Fix:

  • Fall back to ctx.ui.select() (which works over the RPC protocol) when custom() returns undefined
  • If select() also returns undefined or an unrecognized value, deny by default
  • Extract select option strings to shared constants (SELECT_ALLOW_ONCE, SELECT_ALLOW_SESSION, SELECT_DENY)

Test coverage (14 tests, all passing):

Scenario custom() select() Expected
TUI deny "deny" not called blocked
TUI allow "allow" not called allowed
RPC: allow once undefined "Allow once" allowed
RPC: allow session undefined "Allow for session" allowed + session grant
RPC: deny undefined "Deny" blocked
RPC: both undefined undefined undefined blocked (deny-by-default)
RPC: unrecognized undefined "maybe" blocked (deny-by-default)
No UI (hasUI=false) n/a n/a blocked

Also adds vitest test infrastructure (test harness, spy-based context builders, custom matchers) for future test development.

Note

This PR was made by pi (synthetic/hf:zai-org/GLM-5.1)

aliou added 2 commits April 17, 2026 12:27
- Add test harness utilities (pi-context, pi-test-harness, matchers,
  theme, tmpdir, load-extension, pi-internal types)
- Add 14 unit tests for permission gate covering TUI, RPC fallback,
  and no-UI scenarios
- Update vitest.config.ts with alias and setupFiles
- Update tsconfig.json to include tests/ and vitest.config.ts
- Document test conventions in AGENTS.md
- Add development section to README.md
In RPC mode, ctx.ui.custom() returns undefined (Pi's RPC runtime stubs
it as async custom() { return undefined; }). The permission gate only
checked for result === 'deny', so undefined fell through and dangerous
commands were silently allowed.

Fall back to ctx.ui.select() (which works over RPC) when custom()
returns undefined. If select() also returns undefined or unrecognized,
deny by default.
@aliou aliou merged commit 6cd90b8 into main Apr 17, 2026
1 check passed
@aliou aliou deleted the fix/rpc-permission-gate-bypass-v2 branch April 17, 2026 12:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: dangerous commands silently allowed in RPC/headless mode

1 participant