mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-11 04:34:32 +00:00
Sandbox: add shared bind-aware fs path resolver
This commit is contained in:
108
src/agents/sandbox/fs-paths.test.ts
Normal file
108
src/agents/sandbox/fs-paths.test.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { SandboxContext } from "./types.js";
|
||||
import {
|
||||
buildSandboxFsMounts,
|
||||
parseSandboxBindMount,
|
||||
resolveSandboxFsPathWithMounts,
|
||||
} from "./fs-paths.js";
|
||||
|
||||
function createSandbox(overrides?: Partial<SandboxContext>): SandboxContext {
|
||||
return {
|
||||
enabled: true,
|
||||
sessionKey: "sandbox:test",
|
||||
workspaceDir: "/tmp/workspace",
|
||||
agentWorkspaceDir: "/tmp/workspace",
|
||||
workspaceAccess: "rw",
|
||||
containerName: "openclaw-sbx-test",
|
||||
containerWorkdir: "/workspace",
|
||||
docker: {
|
||||
image: "openclaw-sandbox:bookworm-slim",
|
||||
containerPrefix: "openclaw-sbx-",
|
||||
network: "none",
|
||||
user: "1000:1000",
|
||||
workdir: "/workspace",
|
||||
readOnlyRoot: false,
|
||||
tmpfs: [],
|
||||
capDrop: [],
|
||||
seccompProfile: "",
|
||||
apparmorProfile: "",
|
||||
setupCommand: "",
|
||||
binds: [],
|
||||
dns: [],
|
||||
extraHosts: [],
|
||||
pidsLimit: 0,
|
||||
},
|
||||
tools: { allow: ["*"], deny: [] },
|
||||
browserAllowHostControl: false,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("parseSandboxBindMount", () => {
|
||||
it("parses bind mode and writeability", () => {
|
||||
expect(parseSandboxBindMount("/tmp/a:/workspace-a:ro")).toEqual({
|
||||
hostRoot: "/tmp/a",
|
||||
containerRoot: "/workspace-a",
|
||||
writable: false,
|
||||
});
|
||||
expect(parseSandboxBindMount("/tmp/b:/workspace-b:rw")).toEqual({
|
||||
hostRoot: "/tmp/b",
|
||||
containerRoot: "/workspace-b",
|
||||
writable: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveSandboxFsPathWithMounts", () => {
|
||||
it("maps mounted container absolute paths to host paths", () => {
|
||||
const sandbox = createSandbox({
|
||||
docker: {
|
||||
...createSandbox().docker,
|
||||
binds: ["/tmp/workspace-two:/workspace-two:ro"],
|
||||
},
|
||||
});
|
||||
const mounts = buildSandboxFsMounts(sandbox);
|
||||
const resolved = resolveSandboxFsPathWithMounts({
|
||||
filePath: "/workspace-two/docs/AGENTS.md",
|
||||
cwd: sandbox.workspaceDir,
|
||||
defaultWorkspaceRoot: sandbox.workspaceDir,
|
||||
defaultContainerRoot: sandbox.containerWorkdir,
|
||||
mounts,
|
||||
});
|
||||
|
||||
expect(resolved.hostPath).toBe("/tmp/workspace-two/docs/AGENTS.md");
|
||||
expect(resolved.containerPath).toBe("/workspace-two/docs/AGENTS.md");
|
||||
expect(resolved.relativePath).toBe("/workspace-two/docs/AGENTS.md");
|
||||
expect(resolved.writable).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps workspace-relative display paths for default workspace files", () => {
|
||||
const sandbox = createSandbox();
|
||||
const mounts = buildSandboxFsMounts(sandbox);
|
||||
const resolved = resolveSandboxFsPathWithMounts({
|
||||
filePath: "src/index.ts",
|
||||
cwd: sandbox.workspaceDir,
|
||||
defaultWorkspaceRoot: sandbox.workspaceDir,
|
||||
defaultContainerRoot: sandbox.containerWorkdir,
|
||||
mounts,
|
||||
});
|
||||
expect(resolved.hostPath).toBe("/tmp/workspace/src/index.ts");
|
||||
expect(resolved.containerPath).toBe("/workspace/src/index.ts");
|
||||
expect(resolved.relativePath).toBe("src/index.ts");
|
||||
expect(resolved.writable).toBe(true);
|
||||
});
|
||||
|
||||
it("preserves legacy sandbox-root error for outside paths", () => {
|
||||
const sandbox = createSandbox();
|
||||
const mounts = buildSandboxFsMounts(sandbox);
|
||||
expect(() =>
|
||||
resolveSandboxFsPathWithMounts({
|
||||
filePath: "/etc/passwd",
|
||||
cwd: sandbox.workspaceDir,
|
||||
defaultWorkspaceRoot: sandbox.workspaceDir,
|
||||
defaultContainerRoot: sandbox.containerWorkdir,
|
||||
mounts,
|
||||
}),
|
||||
).toThrow(/Path escapes sandbox root/);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user