Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -952,7 +952,7 @@ RUN chmod 444 /usr/local/lib/nemoclaw/sandbox-rlimits.sh \
&& if ! grep -q "sandbox-rlimits.sh" /etc/profile.d/nemoclaw-rlimits.sh 2>/dev/null; then \
printf '%s\n' \
'# NemoClaw sandbox resource limits — see sandbox-rlimits.sh (#2173)' \
'[ -f /usr/local/lib/nemoclaw/sandbox-rlimits.sh ] && . /usr/local/lib/nemoclaw/sandbox-rlimits.sh && harden_resource_limits --quiet && verify_resource_limits' \
'[ -f /usr/local/lib/nemoclaw/sandbox-rlimits.sh ] && . /usr/local/lib/nemoclaw/sandbox-rlimits.sh && harden_resource_limits --quiet && verify_resource_limits --quiet || true' \
> /etc/profile.d/nemoclaw-rlimits.sh \
&& chmod 444 /etc/profile.d/nemoclaw-rlimits.sh; \
fi \
Expand All @@ -969,7 +969,7 @@ RUN chmod 444 /usr/local/lib/nemoclaw/sandbox-rlimits.sh \
'[ -f /tmp/nemoclaw-proxy-env.sh ] && . /tmp/nemoclaw-proxy-env.sh' \
'' \
'# NemoClaw sandbox resource limits — see sandbox-rlimits.sh (#2173)' \
'[ -f /usr/local/lib/nemoclaw/sandbox-rlimits.sh ] && . /usr/local/lib/nemoclaw/sandbox-rlimits.sh && harden_resource_limits --quiet && verify_resource_limits' \
'[ -f /usr/local/lib/nemoclaw/sandbox-rlimits.sh ] && . /usr/local/lib/nemoclaw/sandbox-rlimits.sh && harden_resource_limits --quiet && verify_resource_limits --quiet || true' \
''; \
grep -Ev 'NemoClaw runtime proxy config|nemoclaw-proxy-env[.]sh|NemoClaw sandbox resource limits|sandbox-rlimits[.]sh' /etc/bash.bashrc || true; \
} > /etc/bash.bashrc.new \
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile.base
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ RUN printf '%s\n' \
RUN chmod 444 /usr/local/lib/nemoclaw/sandbox-rlimits.sh \
&& printf '%s\n' \
'# NemoClaw sandbox resource limits — see sandbox-rlimits.sh (#2173)' \
'[ -f /usr/local/lib/nemoclaw/sandbox-rlimits.sh ] && . /usr/local/lib/nemoclaw/sandbox-rlimits.sh && harden_resource_limits --quiet && verify_resource_limits' \
'[ -f /usr/local/lib/nemoclaw/sandbox-rlimits.sh ] && . /usr/local/lib/nemoclaw/sandbox-rlimits.sh && harden_resource_limits --quiet && verify_resource_limits --quiet || true' \
> /etc/profile.d/nemoclaw-rlimits.sh \
&& chmod 444 /etc/profile.d/nemoclaw-rlimits.sh \
&& printf '%s\n' \
Expand All @@ -179,7 +179,7 @@ RUN chmod 444 /usr/local/lib/nemoclaw/sandbox-rlimits.sh \
'[ -f /tmp/nemoclaw-proxy-env.sh ] && . /tmp/nemoclaw-proxy-env.sh' \
'' \
'# NemoClaw sandbox resource limits — see sandbox-rlimits.sh (#2173)' \
'[ -f /usr/local/lib/nemoclaw/sandbox-rlimits.sh ] && . /usr/local/lib/nemoclaw/sandbox-rlimits.sh && harden_resource_limits --quiet && verify_resource_limits' \
'[ -f /usr/local/lib/nemoclaw/sandbox-rlimits.sh ] && . /usr/local/lib/nemoclaw/sandbox-rlimits.sh && harden_resource_limits --quiet && verify_resource_limits --quiet || true' \
''; \
cat /etc/bash.bashrc; \
} > /etc/bash.bashrc.new \
Expand Down
4 changes: 2 additions & 2 deletions agents/hermes/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,13 @@ RUN chmod 755 /usr/local/bin/nemoclaw-start /usr/local/lib/nemoclaw/sandbox-init
&& mkdir -p /etc/profile.d \
&& printf '%s\n' \
'# NemoClaw sandbox resource limits — see sandbox-rlimits.sh (#2173)' \
'[ -f /usr/local/lib/nemoclaw/sandbox-rlimits.sh ] && . /usr/local/lib/nemoclaw/sandbox-rlimits.sh && harden_resource_limits --quiet && verify_resource_limits' \
'[ -f /usr/local/lib/nemoclaw/sandbox-rlimits.sh ] && . /usr/local/lib/nemoclaw/sandbox-rlimits.sh && harden_resource_limits --quiet && verify_resource_limits --quiet || true' \
> /etc/profile.d/nemoclaw-rlimits.sh \
&& chmod 444 /etc/profile.d/nemoclaw-rlimits.sh \
&& (chmod 644 /etc/bash.bashrc 2>/dev/null || true) \
&& { printf '%s\n' \
'# NemoClaw sandbox resource limits — see sandbox-rlimits.sh (#2173)' \
'[ -f /usr/local/lib/nemoclaw/sandbox-rlimits.sh ] && . /usr/local/lib/nemoclaw/sandbox-rlimits.sh && harden_resource_limits --quiet && verify_resource_limits' \
'[ -f /usr/local/lib/nemoclaw/sandbox-rlimits.sh ] && . /usr/local/lib/nemoclaw/sandbox-rlimits.sh && harden_resource_limits --quiet && verify_resource_limits --quiet || true' \
''; \
if [ -f /etc/bash.bashrc ]; then \
grep -Ev 'NemoClaw sandbox resource limits|sandbox-rlimits[.]sh' /etc/bash.bashrc || true; \
Expand Down
8 changes: 8 additions & 0 deletions test/e2e-scenario/live/sandbox-rlimits-connect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ function numericProbe(text: string, key: string): number {
return Number(match?.[1] ?? "NaN");
}

function expectNoRlimitStartupDiagnostics(text: string): void {
expect(text, "connect shell startup must not print rlimit security diagnostics").not.toMatch(
/\[SECURITY\].*(?:Effective|Could not set).*(?:nproc|nofile).*limit/iu,
);
}

function connectAcceptanceScript(cliPath: string, sandboxName: string): string {
const cli = JSON.stringify(cliPath);
const sandbox = JSON.stringify(sandboxName);
Expand Down Expand Up @@ -66,6 +72,7 @@ runConnectRlimitTest(
"bash -lc 'ulimit -u; ulimit -n'",
"bash -ic 'ulimit -u; ulimit -n'",
"ulimit -a",
"shell startup does not emit [SECURITY] rlimit diagnostics before user commands",
"for i in $(seq 1 5000); do sleep 60 & done 2>&1 | tail -5",
],
hermesCoverage:
Expand Down Expand Up @@ -127,6 +134,7 @@ runConnectRlimitTest(
expect(connect.exitCode, output).toBe(0);
expect(output).toContain("__NEMOCLAW_RLIMIT_CONNECT_BEGIN__");
expect(output).toContain("__NEMOCLAW_RLIMIT_CONNECT_END__");
expectNoRlimitStartupDiagnostics(output);

expect(numericProbe(output, "login_nproc")).toBeLessThanOrEqual(4096);
expect(numericProbe(output, "login_nofile")).toBeLessThanOrEqual(65536);
Expand Down
4 changes: 2 additions & 2 deletions test/sandbox-provisioning.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -902,7 +902,7 @@ describe("sandbox provisioning: unified .openclaw layout (#2227)", () => {
const rlimitLib = path.join(tmp, "sandbox-rlimits.sh");
const bashrc = path.join(tmp, "bash.bashrc");
const runtimeEnvShim = "[ -f /tmp/nemoclaw-proxy-env.sh ] && . /tmp/nemoclaw-proxy-env.sh";
const rlimitShim = `[ -f ${rlimitLib} ] && . ${rlimitLib} && harden_resource_limits --quiet && verify_resource_limits`;
const rlimitShim = `[ -f ${rlimitLib} ] && . ${rlimitLib} && harden_resource_limits --quiet && verify_resource_limits --quiet || true`;

try {
fs.mkdirSync(path.dirname(profileHook), { recursive: true });
Expand Down Expand Up @@ -946,7 +946,7 @@ describe("sandbox provisioning: unified .openclaw layout (#2227)", () => {
const rlimitLib = path.join(tmp, "sandbox-rlimits.sh");
const bashrc = path.join(tmp, "bash.bashrc");
const runtimeEnvShim = "[ -f /tmp/nemoclaw-proxy-env.sh ] && . /tmp/nemoclaw-proxy-env.sh";
const rlimitShim = `[ -f ${rlimitLib} ] && . ${rlimitLib} && harden_resource_limits --quiet && verify_resource_limits`;
const rlimitShim = `[ -f ${rlimitLib} ] && . ${rlimitLib} && harden_resource_limits --quiet && verify_resource_limits --quiet || true`;

try {
fs.mkdirSync(path.dirname(profileHook), { recursive: true });
Expand Down
37 changes: 35 additions & 2 deletions test/sandbox-rlimit-hooks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ function copyRlimitFixtureWithNprocLimit(rlimitLib: string, limit: number): void
}

function rlimitShim(rlimitLib: string): string {
return `[ -f ${rlimitLib} ] && . ${rlimitLib} && harden_resource_limits --quiet && verify_resource_limits`;
return `[ -f ${rlimitLib} ] && . ${rlimitLib} && harden_resource_limits --quiet && verify_resource_limits --quiet || true`;
}

type ProbeValues = Record<string, string | undefined>;
Expand Down Expand Up @@ -167,6 +167,36 @@ function expectSystemRlimitHookBypassesShadowedUlimit(hookPath: string): void {
expect(Number(values.nofile)).toBeLessThanOrEqual(65536);
}

function expectSystemRlimitHookIsSilentWhenVerificationFails(
hookPath: string,
rlimitLib: string,
): void {
fs.chmodSync(rlimitLib, 0o644);
fs.writeFileSync(
rlimitLib,
[
"harden_resource_limits() { :; }",
"verify_resource_limits() {",
' if [ "${1:-}" != "--quiet" ]; then',
' echo "[SECURITY] noisy verification failure" >&2',
" fi",
" return 1",
"}",
].join("\n"),
);
const probe = ["set -euo pipefail", `source ${JSON.stringify(hookPath)}`, 'printf "OK\\n"'].join(
"\n",
);
const result = spawnSync("bash", ["--noprofile", "--norc", "-c", probe], {
encoding: "utf-8",
timeout: 5000,
});

expect(result.status).toBe(0);
expect(result.stdout).toBe("OK\n");
expect(result.stderr).toBe("");
}

function expectRlimitLibIsPosixShSafe(rlimitLib: string): void {
const probe = [
"set -e",
Expand Down Expand Up @@ -388,6 +418,7 @@ describe("sandbox rlimit system hooks (#2173)", () => {
expectSystemRlimitHookEnforcesLimits(rlimitHook);
expectSystemRlimitHookEnforcesLimits(bashrc);
expectSystemRlimitHookBypassesShadowedUlimit(rlimitHook);
expectSystemRlimitHookIsSilentWhenVerificationFails(rlimitHook, rlimitLib);
} finally {
fs.rmSync(tmp, { recursive: true, force: true });
}
Expand Down Expand Up @@ -447,7 +478,7 @@ describe("sandbox rlimit system hooks (#2173)", () => {
"# NemoClaw runtime proxy config — see /tmp/nemoclaw-proxy-env.sh (#2704)",
"[ -f /tmp/nemoclaw-proxy-env.sh ] && . /tmp/nemoclaw-proxy-env.sh",
"# NemoClaw sandbox resource limits — see sandbox-rlimits.sh (#2173)",
"[ -f /usr/local/lib/nemoclaw/sandbox-rlimits.sh ] && . /usr/local/lib/nemoclaw/sandbox-rlimits.sh && harden_resource_limits --quiet && verify_resource_limits",
"[ -f /usr/local/lib/nemoclaw/sandbox-rlimits.sh ] && . /usr/local/lib/nemoclaw/sandbox-rlimits.sh && harden_resource_limits --quiet && verify_resource_limits --quiet || true",
].join("\n"),
);
const command = dockerRunCommandBetween(
Expand All @@ -467,6 +498,7 @@ describe("sandbox rlimit system hooks (#2173)", () => {
expect(occurrenceCount(bashrcBody, expectedRlimitShim)).toBe(1);
expectSystemRlimitHookEnforcesLimits(rlimitHook);
expectSystemRlimitHookEnforcesLimits(bashrc);
expectSystemRlimitHookIsSilentWhenVerificationFails(bashrc, rlimitLib);
} finally {
fs.rmSync(tmp, { recursive: true, force: true });
}
Expand Down Expand Up @@ -517,6 +549,7 @@ describe("sandbox rlimit system hooks (#2173)", () => {
expect(fs.readFileSync(bashrc, "utf-8")).toContain(expectedRlimitShim);
expectSystemRlimitHookEnforcesLimits(profileHook);
expectSystemRlimitHookEnforcesLimits(bashrc);
expectSystemRlimitHookIsSilentWhenVerificationFails(bashrc, rlimitLib);
} finally {
fs.rmSync(tmp, { recursive: true, force: true });
}
Expand Down
Loading