Skip to content

Commit 18f12c6

Browse files
committed
fix(scripts): make config integrity check fatal in non-root mode
In nemoclaw-start.sh, the non-root code path caught verify_config_integrity failures and continued with a warning, bypassing the security model that protects openclaw.json from tampering. The root code path correctly treated the check as fatal (exits under set -euo pipefail). The integrity check is now fatal in both code paths. If the config hash doesn't match, the sandbox refuses to start regardless of whether it's running as root or non-root. Adds regression tests verifying: - The non-root block exits on integrity failure - No code path bypasses verify_config_integrity Fixes #1013
1 parent ba824a6 commit 18f12c6

2 files changed

Lines changed: 30 additions & 1 deletion

File tree

scripts/nemoclaw-start.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,8 @@ if [ "$(id -u)" -ne 0 ]; then
184184
echo "[gateway] Running as non-root (uid=$(id -u)) — privilege separation disabled"
185185
export HOME=/sandbox
186186
if ! verify_config_integrity; then
187-
echo "[SECURITY WARNING] Config integrity check failed — proceeding anyway (non-root mode)"
187+
echo "[SECURITY] Config integrity check failed — refusing to start (non-root mode)"
188+
exit 1
188189
fi
189190
write_auth_profile
190191

test/nemoclaw-start.test.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,31 @@ describe("nemoclaw-start non-root fallback", () => {
1616
expect(src).toMatch(/nohup "\$OPENCLAW" gateway run >\/tmp\/gateway\.log 2>&1 &/);
1717
});
1818
});
19+
20+
describe("config integrity check", () => {
21+
const src = fs.readFileSync(START_SCRIPT, "utf-8");
22+
23+
it("exits on integrity failure in non-root mode", () => {
24+
// Extract the outer non-root block. The trailing \nfi\n targets the
25+
// closing "fi" at column 0 — inner "fi" lines are indented (e.g. " fi")
26+
// so [\s\S]*? stops at the first unindented fi, selecting only the outer
27+
// block. This relies on consistent indentation in nemoclaw-start.sh.
28+
const nonRootMatch = src.match(
29+
/if \[ "\$\(id -u\)" -ne 0 \]; then\n([\s\S]*?)\nfi\n/
30+
);
31+
expect(nonRootMatch).not.toBeNull();
32+
const nonRootBlock = nonRootMatch[1];
33+
34+
// The block must NOT contain "proceeding anyway" — that was the old bypass
35+
expect(nonRootBlock).not.toMatch(/proceeding anyway/i);
36+
37+
// The block must exit on integrity failure
38+
expect(nonRootBlock).toMatch(/verify_config_integrity/);
39+
expect(nonRootBlock).toMatch(/exit 1/);
40+
});
41+
42+
it("does not bypass verify_config_integrity in any code path", () => {
43+
// No line should catch and ignore a verify_config_integrity failure
44+
expect(src).not.toMatch(/verify_config_integrity[\s\S]*?proceeding anyway/);
45+
});
46+
});

0 commit comments

Comments
 (0)