fix(sandbox): honor explicit bind mounts over workspace defaults

Co-authored-by: tasaankaeris <tasaankaeris@users.noreply.github.com>
This commit is contained in:
Peter Steinberger
2026-02-22 20:31:40 +01:00
parent eefbf3dc5a
commit 6f895eb831
6 changed files with 126 additions and 7 deletions

View File

@@ -83,7 +83,7 @@ vi.mock("node:child_process", async (importOriginal) => {
};
});
function createSandboxConfig(dns: string[]): SandboxConfig {
function createSandboxConfig(dns: string[], binds?: string[]): SandboxConfig {
return {
mode: "all",
scope: "shared",
@@ -100,7 +100,7 @@ function createSandboxConfig(dns: string[]): SandboxConfig {
env: { LANG: "C.UTF-8" },
dns,
extraHosts: ["host.docker.internal:host-gateway"],
binds: ["/tmp/workspace:/workspace:rw"],
binds: binds ?? ["/tmp/workspace:/workspace:rw"],
},
browser: {
enabled: false,
@@ -189,4 +189,58 @@ describe("ensureSandboxContainer config-hash recreation", () => {
}),
);
});
it("applies custom binds after workspace mounts so overlapping binds can override", async () => {
const workspaceDir = "/tmp/workspace";
const cfg = createSandboxConfig(
["1.1.1.1"],
["/tmp/workspace-shared/USER.md:/workspace/USER.md:ro"],
);
const expectedHash = computeSandboxConfigHash({
docker: cfg.docker,
workspaceAccess: cfg.workspaceAccess,
workspaceDir,
agentWorkspaceDir: workspaceDir,
});
spawnState.inspectRunning = false;
spawnState.labelHash = "stale-hash";
registryMocks.readRegistry.mockResolvedValue({
entries: [
{
containerName: "oc-test-shared",
sessionKey: "shared",
createdAtMs: 1,
lastUsedAtMs: 0,
image: cfg.docker.image,
configHash: "stale-hash",
},
],
});
await ensureSandboxContainer({
sessionKey: "agent:main:session-1",
workspaceDir,
agentWorkspaceDir: workspaceDir,
cfg,
});
const createCall = spawnState.calls.find(
(call) => call.command === "docker" && call.args[0] === "create",
);
expect(createCall).toBeDefined();
expect(createCall?.args).toContain(`openclaw.configHash=${expectedHash}`);
const bindArgs: string[] = [];
const args = createCall?.args ?? [];
for (let i = 0; i < args.length; i += 1) {
if (args[i] === "-v" && typeof args[i + 1] === "string") {
bindArgs.push(args[i + 1]);
}
}
const workspaceMountIdx = bindArgs.indexOf("/tmp/workspace:/workspace");
const customMountIdx = bindArgs.indexOf("/tmp/workspace-shared/USER.md:/workspace/USER.md:ro");
expect(workspaceMountIdx).toBeGreaterThanOrEqual(0);
expect(customMountIdx).toBeGreaterThan(workspaceMountIdx);
});
});