fix(core): authorize external symlink targets
This commit is contained in:
parent
d246597d1d
commit
1e3f8abf2d
2 changed files with 57 additions and 2 deletions
|
|
@ -155,6 +155,23 @@ export const Plugin = {
|
|||
prepared.push({ ...hunk, target, before: original.replace(/^\uFEFF/, ""), after: "" })
|
||||
return
|
||||
}
|
||||
if (!target.externalDirectory && !hunk.movePath) {
|
||||
const resolved = resolveTarget(location, yield* fs.resolve(target.canonical))
|
||||
if (resolved.externalDirectory) {
|
||||
yield* permission.assert({
|
||||
action: "external_directory",
|
||||
resources: [resolved.externalDirectory.resource],
|
||||
save: [resolved.externalDirectory.resource],
|
||||
metadata: {
|
||||
filepath: resolved.canonical,
|
||||
parentDir: resolved.externalDirectory.directory,
|
||||
},
|
||||
sessionID: context.sessionID,
|
||||
agent: context.agent,
|
||||
source,
|
||||
})
|
||||
}
|
||||
}
|
||||
const previous = updates.get(target.canonical)
|
||||
const original =
|
||||
previous ??
|
||||
|
|
|
|||
|
|
@ -825,7 +825,7 @@ describe("PatchTool", () => {
|
|||
),
|
||||
)
|
||||
|
||||
it.live("follows an internal symlink to an external file without external permission", () =>
|
||||
it.live("approves an external target before updating it through an internal symlink", () =>
|
||||
Effect.acquireUseRelease(
|
||||
Effect.promise(() => Promise.all([tmpdir(), tmpdir()])),
|
||||
([active, outside]) => {
|
||||
|
|
@ -844,7 +844,10 @@ describe("PatchTool", () => {
|
|||
call("*** Begin Patch\n*** Update File: link.txt\n@@\n-before\n+after\n*** End Patch"),
|
||||
),
|
||||
).toMatchObject({ type: "text" })
|
||||
expect(assertions.map((input) => input.action)).toEqual(["edit"])
|
||||
expect(assertions.map((input) => input.action)).toEqual(["external_directory", "edit"])
|
||||
expect(assertions[0]?.resources).toEqual([
|
||||
path.join(yield* Effect.promise(() => fs.realpath(outside.path)), "*").replaceAll("\\", "/"),
|
||||
])
|
||||
expect(yield* Effect.promise(() => fs.readFile(target, "utf8"))).toBe("after\n")
|
||||
}),
|
||||
),
|
||||
|
|
@ -858,6 +861,41 @@ describe("PatchTool", () => {
|
|||
),
|
||||
)
|
||||
|
||||
it.live("does not update an external symlink target when external permission is denied", () =>
|
||||
Effect.acquireUseRelease(
|
||||
Effect.promise(() => Promise.all([tmpdir(), tmpdir()])),
|
||||
([active, outside]) => {
|
||||
reset()
|
||||
if (process.platform === "win32") return Effect.void
|
||||
denyAction = "external_directory"
|
||||
const target = path.join(outside.path, "external.txt")
|
||||
const link = path.join(active.path, "link.txt")
|
||||
return Effect.promise(() => fs.writeFile(target, "before\n")).pipe(
|
||||
Effect.andThen(Effect.promise(() => fs.symlink(target, link))),
|
||||
Effect.andThen(
|
||||
withTool(active.path, (registry) =>
|
||||
Effect.gen(function* () {
|
||||
expect(
|
||||
yield* executeTool(
|
||||
registry,
|
||||
call("*** Begin Patch\n*** Update File: link.txt\n@@\n-before\n+after\n*** End Patch"),
|
||||
),
|
||||
).toMatchObject({ type: "error" })
|
||||
expect(assertions.map((input) => input.action)).toEqual(["external_directory"])
|
||||
expect(readsBeforeEditApproval).toBe(0)
|
||||
expect(yield* Effect.promise(() => fs.readFile(target, "utf8"))).toBe("before\n")
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
},
|
||||
([active, outside]) =>
|
||||
Effect.promise(() =>
|
||||
Promise.all([active[Symbol.asyncDispose](), outside[Symbol.asyncDispose]()]).then(() => undefined),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
it.live("approves a relative external target before reading and requests edit permission afterward", () =>
|
||||
Effect.acquireUseRelease(
|
||||
Effect.promise(() => Promise.all([tmpdir(), tmpdir()])),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue