Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,16 @@ describe("Docker operation authority", () => {
);
});

it("fails closed when the fixed PATH has no Docker executable", () => {
expect(() =>
createDockerOperationAuthority("sandbox-lifecycle", {
HOME: "/tmp/nemoclaw-home",
DOCKER_HOST: "unix:///tmp/nemoclaw-docker.sock",
PATH: fakeExecutableRoot(),
}),
).toThrow("Docker operation could not resolve one absolute Docker executable");
});

it("rejects plaintext remote TCP before issuing a daemon command", () => {
const capture = contextCapture("unix:///var/run/docker.sock");

Expand Down
11 changes: 11 additions & 0 deletions src/lib/onboard/runtime-provider/docker-state-mutation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

import fs from "node:fs";
import path from "node:path";

import { afterEach, describe, expect, it, vi } from "vitest";
import {
Expand Down Expand Up @@ -51,6 +52,16 @@ afterEach(() => {
});

describe("Docker runtime-provider state mutation surface", () => {
it("uses one harness-owned absolute Docker executable", () => {
const runtime = harness();
runtime.authority.engine.capture(["version"]);
const executable = runtime.capture.mock.calls[0]?.[0] as string;

expect(path.isAbsolute(executable)).toBe(true);
expect(executable).toBe(fs.realpathSync(path.join(runtime.root, "bin", "docker")));
expect(runtime.context.environment).toMatchObject({ PATH: path.join(runtime.root, "bin") });
});

it("resolves one full labeled runtime and records authority only on synchronous acquire", () => {
const runtime = harness();
const surface = createDockerStateMutationSurface({
Expand Down
9 changes: 9 additions & 0 deletions test/helpers/docker-state-mutation-harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const DOCKER_STATE_MUTATION_PROJECTION_SHA256 = "b".repeat(64);
export const DOCKER_STATE_MUTATION_STATE_ROOT = "/sandbox/.hermes";
export const DOCKER_STATE_MUTATION_LIFECYCLE_GENERATION = "generation-7";
const SANDBOX_ID = "sandbox-alpha-id";
const DOCKER_EXECUTABLE_SOURCE = "#!/bin/sh\nexit 1\n";
const PODMAN_EXECUTABLE_BYTES = Buffer.from("qualified-podman-state-mutation", "utf8");
const PODMAN_SOCKET_AUTHORITY = {
directoryChain: [],
Expand Down Expand Up @@ -82,6 +83,13 @@ function temporaryRoot(): string {
return root;
}

function createDockerExecutableSearchPath(root: string): string {
const directory = path.join(root, "bin");
fs.mkdirSync(directory, { mode: 0o700 });
fs.writeFileSync(path.join(directory, "docker"), DOCKER_EXECUTABLE_SOURCE, { mode: 0o700 });
return directory;
}

export function cleanupDockerStateMutationRoots(): void {
for (const root of roots.splice(0)) fs.rmSync(root, { force: true, recursive: true });
}
Expand Down Expand Up @@ -375,6 +383,7 @@ function createContainerStateMutationHarness(
HOME: "/tmp/nemoclaw-home",
DOCKER_CONFIG: "/tmp/nemoclaw-docker",
DOCKER_HOST: "unix:///tmp/nemoclaw-docker.sock",
PATH: createDockerExecutableSearchPath(root),
}
: { HOME: "/tmp/nemoclaw-home" };
const dockerAuthority =
Expand Down
Loading