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
1 change: 1 addition & 0 deletions agents/hermes/policy-additions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ filesystem_policy:
- /proc
- /dev/urandom
- /app
- /run/nemoclaw/managed-startup-ca-bundle.pem
- /etc
- /var/log
- /var/lib/dpkg # Allow package-version inspection without package mutation.
Expand Down
1 change: 1 addition & 0 deletions agents/hermes/policy-permissive.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ filesystem_policy:
- /proc
- /dev/urandom
- /app
- /run/nemoclaw/managed-startup-ca-bundle.pem
- /etc
- /var/log
- /var/lib/dpkg # Allow package-version inspection without package mutation.
Expand Down
1 change: 1 addition & 0 deletions agents/langchain-deepagents-code/policy-additions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ filesystem_policy:
- /proc
- /dev/urandom
- /app
- /run/nemoclaw/managed-startup-ca-bundle.pem
- /etc
- /var/log
- /var/lib/dpkg # Allow package-version inspection without package mutation.
Expand Down
1 change: 1 addition & 0 deletions agents/openclaw/policy-permissive.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ filesystem_policy:
- /proc
- /dev/urandom
- /app
- /run/nemoclaw/managed-startup-ca-bundle.pem
- /etc
- /var/log
- /var/lib/dpkg # Allow package-version inspection without package mutation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ filesystem_policy:
- /proc
- /dev/urandom
- /app
- /run/nemoclaw/managed-startup-ca-bundle.pem
- /etc
- /var/log
- /var/lib/dpkg # Allow package-version inspection without package mutation.
Expand Down
1 change: 1 addition & 0 deletions nemoclaw-blueprint/policies/openclaw-sandbox.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ filesystem_policy:
- /proc
- /dev/urandom
- /app
- /run/nemoclaw/managed-startup-ca-bundle.pem
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- /etc
- /var/log
- /var/lib/dpkg # Allow package-version inspection without package mutation.
Expand Down
96 changes: 78 additions & 18 deletions src/lib/onboard/initial-policy-real-policy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import YAML from "yaml";

import { SHIPPED_MANAGED_IMAGE_AGENTS } from "./managed-image/contract";
import { MANAGED_STARTUP_MERGED_CA_FILE } from "./managed-startup/image-runtime";
import { prepareInitialSandboxCreatePolicy } from "./initial-policy";

type PolicyRule = {
Expand Down Expand Up @@ -50,6 +52,20 @@ function repoPath(...segments: string[]): string {
return path.join(import.meta.dirname, "..", "..", "..", ...segments);
}

function normalizeFilesystemPolicyPath(policyPath: string): string {
return path.posix.normalize(policyPath).replace(/\/+$/, "") || "/";
}

function filesystemPolicyAncestors(policyPath: string): string[] {
const segments = normalizeFilesystemPolicyPath(policyPath).split("/").filter(Boolean);
return [
"/",
...segments
.slice(0, -1)
.map((_, index) => `/${segments.slice(0, index + 1).join("/")}`),
];
}

function readPreparedPolicy(prepared: {
policyPath: string;
cleanup?: () => boolean;
Expand All @@ -59,16 +75,68 @@ function readPreparedPolicy(prepared: {
}

describe("initial sandbox policy real preset merge", () => {
const shippingPolicyCases = [
{ path: ["nemoclaw-blueprint", "policies", "openclaw-sandbox.yaml"], agent: "openclaw" },
{
path: ["nemoclaw-blueprint", "policies", "openclaw-sandbox-permissive.yaml"],
agent: "openclaw",
const managedImagePolicyPathsByAgent = {
openclaw: [
["nemoclaw-blueprint", "policies", "openclaw-sandbox.yaml"],
["nemoclaw-blueprint", "policies", "openclaw-sandbox-permissive.yaml"],
["agents", "openclaw", "policy-permissive.yaml"],
],
hermes: [
["agents", "hermes", "policy-additions.yaml"],
["agents", "hermes", "policy-permissive.yaml"],
],
"langchain-deepagents-code": [
["agents", "langchain-deepagents-code", "policy-additions.yaml"],
],
} as const satisfies Record<
(typeof SHIPPED_MANAGED_IMAGE_AGENTS)[number],
readonly (readonly string[])[]
>;

const managedImagePolicyCases = SHIPPED_MANAGED_IMAGE_AGENTS.flatMap((agent) =>
managedImagePolicyPathsByAgent[agent].map((policyPath) => ({ path: policyPath, agent })),
);
const shippingPolicyCases = managedImagePolicyCases.filter(
({ agent }) => agent !== "langchain-deepagents-code",
);

it("covers the complete shipped managed startup CA policy matrix", () => {
const policyIdentities = managedImagePolicyCases.map(
({ path: policyPath, agent }) => `${agent}:${policyPath.join("/")}`,
);

expect(Object.keys(managedImagePolicyPathsByAgent)).toEqual([...SHIPPED_MANAGED_IMAGE_AGENTS]);
expect(policyIdentities).toHaveLength(6);
expect(new Set(policyIdentities).size).toBe(policyIdentities.length);
});

it.each(managedImagePolicyCases)(
"grants $agent policy $path exact read-only access to the managed startup CA bundle (#9360)",
(policyCase) => {
const prepared = prepareInitialSandboxCreatePolicy(repoPath(...policyCase.path), [], {
agentName: policyCase.agent,
});
const policy = readPreparedPolicy(prepared);
const readOnly = policy.filesystem_policy?.read_only ?? [];
const readWrite = policy.filesystem_policy?.read_write ?? [];
const normalizedReadOnly = readOnly.map(normalizeFilesystemPolicyPath);
const normalizedReadWrite = readWrite.map(normalizeFilesystemPolicyPath);
const managedCaAncestors = filesystemPolicyAncestors(MANAGED_STARTUP_MERGED_CA_FILE);

expect(readOnly, policyCase.path.join("/")).toContain(MANAGED_STARTUP_MERGED_CA_FILE);
expect(normalizedReadWrite, policyCase.path.join("/")).not.toContain(
MANAGED_STARTUP_MERGED_CA_FILE,
);
expect(
normalizedReadOnly.filter((candidate) => managedCaAncestors.includes(candidate)),
policyCase.path.join("/"),
).toEqual([]);
expect(
normalizedReadWrite.filter((candidate) => managedCaAncestors.includes(candidate)),
policyCase.path.join("/"),
).toEqual([]);
},
{ path: ["agents", "openclaw", "policy-permissive.yaml"], agent: "openclaw" },
{ path: ["agents", "hermes", "policy-additions.yaml"], agent: "hermes" },
{ path: ["agents", "hermes", "policy-permissive.yaml"], agent: "hermes" },
] as const;
);

it.each([
{
Expand Down Expand Up @@ -178,16 +246,8 @@ describe("initial sandbox policy real preset merge", () => {
},
);

const packageDatabasePolicyCases = [
...shippingPolicyCases,
{
path: ["agents", "langchain-deepagents-code", "policy-additions.yaml"],
agent: "langchain-deepagents-code",
},
] as const;

it.each(
packageDatabasePolicyCases.flatMap((policyCase) =>
managedImagePolicyCases.flatMap((policyCase) =>
["/", "/var", "/var/lib", "/var/lib/dpkg"].map((writableAncestor) => ({
policyCase,
writableAncestor,
Expand Down
Loading