fix(plugins): resolve relative executable paths against plugin root#627
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
Walkthrough
ChangesPlugin activation behavior
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
internal/plugins/activate_test.go (1)
553-558: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider using
t.Runfor subtest isolation.Using
t.Runper case would produce named subtests, making it easier to identify which input fails without reading the error message. This is a minor ergonomics improvement.♻️ Optional refactor
for _, tc := range cases { - got := expandPluginRootPath(tc.input, pluginDir) - if got != tc.want { - t.Errorf("expandPluginRootPath(%q) = %q, want %q", tc.input, got, tc.want) - } + t.Run(tc.input, func(t *testing.T) { + got := expandPluginRootPath(tc.input, pluginDir) + if got != tc.want { + t.Errorf("expandPluginRootPath(%q) = %q, want %q", tc.input, got, tc.want) + } + }) }🤖 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 `@internal/plugins/activate_test.go` around lines 553 - 558, Refactor the table-driven test around expandPluginRootPath to execute each case within a named t.Run subtest, using an appropriate case name or index, and keep the existing assertion inside the subtest for isolated, easily identifiable failures.
🤖 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.
Nitpick comments:
In `@internal/plugins/activate_test.go`:
- Around line 553-558: Refactor the table-driven test around
expandPluginRootPath to execute each case within a named t.Run subtest, using an
appropriate case name or index, and keep the existing assertion inside the
subtest for isolated, easily identifiable failures.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b95b6b32-d1ad-4b80-bf05-06ef9f5f124e
📒 Files selected for processing (2)
internal/plugins/activate.gointernal/plugins/activate_test.go
When a plugin registers a tool or a hook command as a relative path (e.g. ./tools/helper.sh), running it in the caller's workspace CWD allows local execution hijacking by placing a malicious script at that path in the untrusted workspace. Update expandPluginRootPath to resolve relative executable paths containing path separators to absolute paths inside the plugin's install directory.
fa7e665 to
2562bfb
Compare
The initial assignment to permission was always overwritten by one of the switch arms below it, including a case that just reassigned the same PermissionPrompt value the initializer already held. Flagged by ineffassign in CI.
Vasanthdev2004
left a comment
There was a problem hiding this comment.
Pulled this into a worktree against current main. The core fix anchors relative command paths (anything with a path separator) to the plugin's install directory via filepath.Join, so a relative executable can't be hijacked by resolving against the caller's CWD; absolute paths and bare binary names pass through unchanged, which is the right call. Build, vet, and the plugins tests all pass, including the new TestExpandPluginRootPathRelative.
One note: ../ escapes that climb out of the plugin root are accepted rather than rejected, but I think that's fine since a manifest author can already specify an arbitrary absolute path and untrusted project-scoped plugins are already dropped by the workspace-trust gate. Approve.
|
@gnanam1990 when you get a chance — small one: #627 anchors relative plugin exec paths to the plugin install dir so they can't resolve against the caller's CWD. I approved it; the one judgment call is that ../ escapes out of the plugin root are accepted rather than rejected. Worth a second look on the trust angle if you have a minute. |
|
Fixed the ineffassign failure on line 623: the initial permission assignment in toolSafety was always overwritten by the switch below it, including a case that just reassigned the same PermissionPrompt value. Removed the redundant initializer and let the switch be exhaustive with a default. |
Summary
Fixes a High-severity issue where relative command paths defined by plugins (e.g.
"command": "./tools/helper.sh") can be hijacked by malicious files placed at that same path inside an untrusted workspace.When a plugin tool runs,
RunWithOptionsexecutes the plugin command within the caller's workspace CWD. If the command is a relative path (e.g. starting with./or containing separators), Go'sos/execresolves it relative to the workspace directory.This PR resolves relative executable paths containing separators to absolute paths inside the plugin's install directory during activation.
Changes
internal/plugins/activate.goexpandPluginRootPath, check if the command path is not absolute (!filepath.IsAbsand!isWindowsAbs) and contains path separators. If so, resolve it to an absolute path under the plugin directory usingfilepath.Join.internal/plugins/activate_test.goTestExpandPluginRootPathRelativeto assert that relative executable paths are resolved, absolute paths are kept verbatim, and placeholders are correctly expanded.Test plan
go test ./internal/plugins/...— okSummary by CodeRabbit
/and\).