Skip to content

[luv-338] fix: canonicalize OpenCode + Pi tool-input arg keys#337

Merged
NiveditJain merged 3 commits intomainfrom
luv-338
May 10, 2026
Merged

[luv-338] fix: canonicalize OpenCode + Pi tool-input arg keys#337
NiveditJain merged 3 commits intomainfrom
luv-338

Conversation

@NiveditJain
Copy link
Copy Markdown
Member

@NiveditJain NiveditJain commented May 10, 2026

Summary

  • Bug observed: OpenCode session ses_1f0e1c55affeZUG6LBf95r6eRz (CWD /home/nivedit/dev-purge) successfully read /home/nivedit/claudeye — outside its CWD — via the read tool. block-read-outside-cwd did not deny it.
  • Root cause: The OpenCode shim canonicalizes the tool name (readRead) but forwards output.args verbatim. OpenCode delivers { filePath: "..." }; the policy reads ctx.toolInput.file_path, so getFilePath() returns "" and the empty-target short-circuit at src/hooks/builtin-policies.ts:799 allows the read.
  • Pi has the same family of bug with a different shape: Pi's read/write/edit use path. block-read-outside-cwd happens to work on Pi via an existing path fallback (builtin-policies.ts:796), but block-env-files and block-secrets-write only check file_path and were silently no-op'd.
  • Fix: mirror the existing *_TOOL_MAP pattern with per-tool input-key maps:
    • OPENCODE_TOOL_INPUT_MAP — Read/Write/Edit: filePathfile_path, plus oldString/newString/replaceAll for Edit
    • PI_TOOL_INPUT_MAP — Read/Write/Edit: pathfile_path (Pi nested edits-array doesn't flat-rename; no current builtin reads it)
  • Both maps are mirrored inline in their shims so .opencode/plugins/failproofai.mjs and pi-extension/index.ts stay self-contained. MCP mcp_* and any unmapped tool pass through unchanged.

Existing OpenCode/Pi users must reinstall their shim via failproofai policies --install --cli opencode / --cli pi to pick up the fix.

Test plan

  • bun run test:run — 1588 unit tests pass (added 5 OpenCode-shim tests + 6 Pi-shim tests covering filePath / path translation, all four Edit keys, mcp_* passthrough, after-hook canonicalization, and exported-map parity)
  • bun run test:e2e — 297 e2e tests pass (added regression in opencode-integration.e2e.test.ts: Read of a path outside cwd hits block-read-outside-cwd deny)
  • bun run lint — clean (one pre-existing unrelated warning)
  • bun run build — Next.js + dist build clean
  • Manually verify: regenerate dev shim with failproofai policies --install --cli opencode --scope project, start OpenCode session in a new CWD, ask to read a path outside the CWD → expect deny

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Fixed path-checking policies for OpenCode and Pi to correctly detect read/write/edit operations. Existing users must regenerate and reinstall shims for the fix to take effect.
  • Documentation

    • Updated configuration documentation to clarify tool integration and argument canonicalization behavior.
  • Tests

    • Added comprehensive test coverage for tool input canonicalization across integrations.

Review Change Stack

NiveditJain and others added 2 commits May 9, 2026 17:38
…-checking builtins fire

OpenCode's `read` slipped past `block-read-outside-cwd`: the shim
canonicalized the tool name (`read` → `Read`) but forwarded `output.args`
verbatim. OpenCode delivers `{ filePath }`; the policy reads
`ctx.toolInput.file_path`, so `getFilePath()` returned "" and the
target-empty short-circuit at builtin-policies.ts:799 allowed the read.

Same family of bug on Pi with a different mismatch shape: Pi's
read/write/edit use `path`. `block-read-outside-cwd` happens to work
on Pi via the existing `tool_input.path` fallback at builtin-policies.ts:796,
but `block-env-files` and `block-secrets-write` only check `file_path`
and were silently no-op'd.

Mirrors the TOOL_NAME_MAP pattern from #293 with two new per-tool
input-key maps keyed by canonical PascalCase tool name:

  • OPENCODE_TOOL_INPUT_MAP — Read/Write/Edit: filePath→file_path,
    plus oldString/newString/replaceAll for Edit
  • PI_TOOL_INPUT_MAP — Read/Write/Edit: path→file_path
    (Pi's nested edits[{oldText,newText}] array doesn't flat-rename,
    no current builtin reads it)

Both maps are mirrored inline in their shims so .opencode/plugins/
failproofai.mjs and pi-extension/index.ts stay self-contained. MCP
mcp_* and any unmapped tool pass through unchanged.

Existing users must re-run `failproofai policies --install --cli opencode`
/ `--cli pi` to regenerate their shims and pick up the fix.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…P in configuration.mdx

Mirror the existing tool-name canonicalization wording in the OpenCode
and Pi sections. Calls out which builtin policies start firing as a
result (block-read-outside-cwd / block-env-files / block-secrets-write
on OpenCode; block-env-files / block-secrets-write on Pi).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 10, 2026

📝 Walkthrough

Walkthrough

This PR fixes a mismatch between tool-input argument key formats across OpenCode and Pi integrations. Tool input keys from these CLIs (e.g., OpenCode's filePath, Pi's path) were not being converted to failproofai's expected schema (e.g., file_path), causing policy checks to not match read/write/edit operations and enforcement to be skipped.

Changes

Tool Input Canonicalization

Layer / File(s) Summary
Data Schemas
src/hooks/types.ts
Exports OPENCODE_TOOL_INPUT_MAP (camelCase to snake_case for Read/Write/Edit) and PI_TOOL_INPUT_MAP (path to file_path for Read/Write/Edit).
OpenCode Shim
src/hooks/integrations.ts
Generated shim includes canonicalizeToolInput helper and TOOL_INPUT_MAP constant; applies input canonicalization in tool.execute.before, tool.execute.after, and permission.ask handlers.
Pi Extension
pi-extension/index.ts
Introduces PI_TOOL_INPUT_MAP and canonicalizeToolInput helper; applies canonicalization in tool_call and tool_result event handlers before forwarding to policy.
Tests
__tests__/hooks/opencode-plugin-shim.test.ts, __tests__/hooks/pi-extension-shim.test.ts, __tests__/e2e/hooks/opencode-integration.e2e.test.ts
Comprehensive unit and E2E tests verify canonicalization of Read/Write/Edit key mappings, unknown tool passthrough, tool_result handling, and OPENCODE_TOOL_INPUT_MAP/PI_TOOL_INPUT_MAP parity.
Documentation
docs/configuration.mdx, CHANGELOG.md
Configuration guide updated to describe canonicalization behavior; changelog documents the fix for policies not triggering due to argument-shape mismatches and notes required shim regeneration.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 With camelCase tamed to snake_case grace,
Tool arguments find their rightful place,
Read, write, edit—now policies see clear,
No more lost requests; enforcement is here! 🔐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: fixing canonicalization of tool-input argument keys for OpenCode and Pi to ensure built-in policies work correctly.
Description check ✅ Passed The description provides a clear bug explanation, root cause analysis, fix implementation details, test coverage information, and reinstallation instructions for affected users.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Comment @coderabbitai help to get the list of available commands and usage tips.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
CHANGELOG.md (1)

3-7: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Update the changelog section date to today (2026-05-10).

The entry is under 0.0.10-beta.10 but the section header date is 2026-05-09; repository policy requires the current version section date to match today for the PR update.

Suggested patch
-## 0.0.10-beta.10 — 2026-05-09
+## 0.0.10-beta.10 — 2026-05-10

As per coding guidelines, "Every PR must include an update to CHANGELOG.md under the current '## — ' section matching package.json version and today's date."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CHANGELOG.md` around lines 3 - 7, Summary: The CHANGELOG.md entry for version
0.0.10-beta.10 has the wrong date (2026-05-09) and must be updated to today's
date. Fix: open CHANGELOG.md, find the header "## 0.0.10-beta.10 — 2026-05-09"
and change the date to "2026-05-10" so the section header matches the current PR
date; ensure the version string "0.0.10-beta.10" remains unchanged and save the
file.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@CHANGELOG.md`:
- Around line 3-7: Summary: The CHANGELOG.md entry for version 0.0.10-beta.10
has the wrong date (2026-05-09) and must be updated to today's date. Fix: open
CHANGELOG.md, find the header "## 0.0.10-beta.10 — 2026-05-09" and change the
date to "2026-05-10" so the section header matches the current PR date; ensure
the version string "0.0.10-beta.10" remains unchanged and save the file.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7b1f3c77-e04c-4ae2-855b-5ffca130aac4

📥 Commits

Reviewing files that changed from the base of the PR and between 9f0b14b and 61a515f.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • __tests__/e2e/hooks/opencode-integration.e2e.test.ts
  • __tests__/hooks/opencode-plugin-shim.test.ts
  • __tests__/hooks/pi-extension-shim.test.ts
  • docs/configuration.mdx
  • pi-extension/index.ts
  • src/hooks/integrations.ts
  • src/hooks/types.ts

@NiveditJain NiveditJain merged commit 4dc8004 into main May 10, 2026
9 checks passed
NiveditJain added a commit that referenced this pull request May 10, 2026
…zation for OpenCode/Pi (#340)

* [luv-340] fix: regenerate OpenCode dev shim + add handler-side canonicalization for OpenCode/Pi

Two-layer fix for `block-read-outside-cwd` silently no-op'ing on OpenCode
inside this repo. PR #337 added `OPENCODE_TOOL_INPUT_MAP` to the
production shim template at `src/hooks/integrations.ts:buildOpenCodePluginShim`,
but the dev shim at `.opencode/plugins/failproofai.mjs` is hand-maintained
and was never regenerated, so contributors running `opencode` from inside
this repo could still read paths outside the repo cwd — `getFilePath()`
returned "" (camelCase `filePath` didn't match the policy's snake_case
`file_path` lookup) and the target-empty short-circuit at
`src/hooks/builtin-policies.ts:799` returned allow.

Layer 1 (dev shim): regenerate `.opencode/plugins/failproofai.mjs` to
mirror the production template — `TOOL_NAME_MAP` + `TOOL_INPUT_MAP` +
idempotent `canonicalizeTool` / `canonicalizeToolInput`. Also pick up
the `applyDecision` async-await change from #318 that the dev shim had
been missing, so Stop / SubagentStop force-retry actually awaits the
SDK call before the plugin handler returns.

Layer 2 (handler): add per-CLI canonicalization for OpenCode + Pi in
`src/hooks/handler.ts` as defense-in-depth, so a user-scope shim that
pre-dates #337 automatically starts enforcing the moment failproofai
upgrades — without forcing a `failproofai policies --install --cli opencode`
re-run. The shim canonicalization stays in place; both passes are
idempotent because the per-CLI maps key on camelCase / Pi-shape and
don't match already-canonical snake_case input. New `canonicalizeToolInput`
helper is gated by `cli === "opencode" / "pi"` so Claude / Codex / Cursor /
Copilot / Gemini sessions are unaffected.

New unit tests in `__tests__/hooks/handler.test.ts` cover:
  - Stale-shim shapes (`read` + `filePath` for OpenCode, `read` + `path` for Pi)
  - Per-CLI map coverage parity (12 OpenCode + 6 Pi tool names, four Edit keys)
  - Idempotency (handler doesn't corrupt fresh-shim shapes)
  - Unknown-tool passthrough (MCP `mcp_*` tool names + args)
  - Per-CLI gate (Claude payloads with literal `filePath` keys are not rewritten)

Smoke-tested against the exact pattern from the bug report: an opencode
`read` with a camelCase `filePath` pointing outside the repo cwd is now
blocked with a clear `Access outside project directory blocked` deny
reason. In-cwd reads still pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* [luv-340] chore: align CHANGELOG date with UTC for 0.0.10-beta.11 section

CodeRabbit flagged the freshly-added section heading dated 2026-05-09
while the PR was opened at 2026-05-10T02:26Z UTC. Bump the date so the
heading is internally consistent with the prior `## 0.0.10-beta.10 —
2026-05-10` section and with the GH server clock that downstream
release tooling reads.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
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.

1 participant