Merge pull request #12008 from Kilo-Org/feat/sandbox-writable-paths-ui-test

feat(sandbox): widen writable-paths settings input and add coverage
This commit is contained in:
Marius
2026-07-07 15:55:16 +02:00
committed by GitHub
4 changed files with 127 additions and 53 deletions
@@ -242,6 +242,41 @@ describe("kilocode indexing config", () => {
})
})
describe("kilocode sandbox writable paths config", () => {
test("honors sandbox_writable_paths from global config only, ignoring project config", async () => {
await using globalTmp = await tmpdir()
await using tmp = await tmpdir({ git: true })
const prev = Global.Path.config
;(Global.Path as { config: string }).config = globalTmp.path
await clear()
await disposeAllInstances()
try {
await writeConfig(globalTmp.path, {
$schema: "https://app.kilo.ai/config.json",
experimental: { sandbox_writable_paths: ["/tmp/global"] },
})
// A project kilo.json must not widen the sandbox: its writable paths are dropped at merge time.
await writeConfig(tmp.path, {
experimental: { sandbox_writable_paths: ["/tmp/project"] },
})
await provideTestInstance({
directory: tmp.path,
fn: async () => {
const config = await load()
expect(config.experimental?.sandbox_writable_paths).toEqual(["/tmp/global"])
},
})
} finally {
;(Global.Path as { config: string }).config = prev
await clear()
await disposeAllInstances()
}
})
})
describe("custom provider model config", () => {
test("persists and removes reasoning across a global config reload", async () => {
await using globalTmp = await tmpdir()
@@ -234,4 +234,34 @@ describe("sandbox policy", () => {
expect(roots(ctx)).not.toContain(dirs.approved)
expect(Exit.isFailure(result)).toBe(true)
})
test("makes configured extra writable paths writable while unlisted paths stay denied", async () => {
await using tmp = await fixture()
const dirs = tmp.extra
const ctx = context(dirs.a, dirs.a, dirs)
const policy = profile(ctx, "deny", [dirs.approved])
const result = await Effect.runPromise(
Effect.all({
extra: runSandbox(policy, assertWrite(path.join(dirs.approved, "allowed.txt")).pipe(Effect.exit)),
other: runSandbox(policy, assertWrite(path.join(dirs.b, "denied.txt")).pipe(Effect.exit)),
}),
)
expect(policy.filesystem.allowWrite.map((rule) => rule.path)).toContain(dirs.approved)
expect(roots(ctx)).not.toContain(dirs.approved)
expect(Exit.isSuccess(result.extra)).toBe(true)
expect(Exit.isFailure(result.other)).toBe(true)
})
test("keeps .git denied inside a configured extra writable path", async () => {
await using tmp = await fixture()
const dirs = tmp.extra
const ctx = context(dirs.a, dirs.a, dirs)
const policy = profile(ctx, "deny", [dirs.approved])
const result = await Effect.runPromise(
runSandbox(policy, assertWrite(path.join(dirs.approved, ".git", "config")).pipe(Effect.exit)),
)
expect(Exit.isFailure(result)).toBe(true)
})
})