fix(core): authorize external glob paths (#38714)

This commit is contained in:
Aiden Cline 2026-07-24 13:24:23 -05:00 committed by GitHub
commit 423fad730c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 115 additions and 19 deletions

View file

@ -7,6 +7,7 @@ import path from "path"
import { FileSystem } from "../filesystem"
import { FSUtil } from "@opencode-ai/util/fs-util"
import { Location } from "../location"
import { LocationMutation } from "../location-mutation"
import { Ripgrep } from "../ripgrep"
import { RelativePath } from "../schema"
import { PermissionV2 } from "../permission"
@ -45,6 +46,7 @@ export const Plugin = {
const fs = yield* FSUtil.Service
const ripgrep = yield* Ripgrep.Service
const location = yield* Location.Service
const mutation = yield* LocationMutation.Service
const permission = yield* PermissionV2.Service
yield* ctx.tool
@ -58,6 +60,16 @@ export const Plugin = {
output: Output,
execute: (input, context) =>
Effect.gen(function* () {
const source = { type: "tool" as const, messageID: context.messageID, callID: context.callID }
const target = yield* mutation.resolve({ path: input.path ?? ".", kind: "directory" })
const external = target.externalDirectory
if (external)
yield* permission.assert({
...LocationMutation.externalDirectoryPermission(external),
sessionID: context.sessionID,
agent: context.agent,
source,
})
yield* permission.assert({
action: name,
resources: [input.pattern],
@ -69,20 +81,20 @@ export const Plugin = {
},
sessionID: context.sessionID,
agent: context.agent,
source: { type: "tool", messageID: context.messageID, callID: context.callID },
source,
})
const cwd = path.resolve(location.directory, input.path ?? ".")
yield* fs
.stat(cwd)
.stat(target.canonical)
.pipe(
Effect.catchReason("PlatformError", "NotFound", () =>
Effect.fail(new ToolFailure({ message: `Search path does not exist: ${input.path ?? "."}` })),
),
)
const root = path.resolve(location.directory, input.path ?? ".")
const limit = input.limit ?? FileSystem.DEFAULT_SEARCH_LIMIT
const entries = yield* ripgrep
.glob({
cwd,
cwd: target.canonical,
pattern: input.pattern,
limit: limit + 1,
})
@ -91,7 +103,7 @@ export const Plugin = {
result.map((entry) =>
FileSystem.Entry.make({
...entry,
path: RelativePath.make(path.relative(location.directory, path.resolve(cwd, entry.path))),
path: RelativePath.make(path.relative(location.directory, path.resolve(root, entry.path))),
}),
),
),