From 8921233b5f68d3874e31cf8c4dc63868b822a253 Mon Sep 17 00:00:00 2001 From: latenighthackathon Date: Sun, 29 Mar 2026 14:54:33 -0500 Subject: [PATCH 1/2] sec(scripts): enforce config integrity check in non-root mode In non-root mode, verify_config_integrity() failure was logged as a warning but execution continued, allowing a tampered openclaw.json to be loaded. The root path correctly treats this as fatal. Make the non-root path consistent: exit 1 on integrity failure so a modified config is never silently accepted regardless of the execution mode. Closes #1013 --- scripts/nemoclaw-start.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/nemoclaw-start.sh b/scripts/nemoclaw-start.sh index 9e316b395..4c68e2925 100755 --- a/scripts/nemoclaw-start.sh +++ b/scripts/nemoclaw-start.sh @@ -295,7 +295,8 @@ if [ "$(id -u)" -ne 0 ]; then echo "[gateway] Running as non-root (uid=$(id -u)) — privilege separation disabled" export HOME=/sandbox if ! verify_config_integrity; then - echo "[SECURITY WARNING] Config integrity check failed — proceeding anyway (non-root mode)" + echo "[SECURITY] Config integrity check failed — refusing to start (non-root mode)" + exit 1 fi write_auth_profile From 1ce22ed3db74a705d8e0ae3d42252ef5562365de Mon Sep 17 00:00:00 2001 From: latenighthackathon Date: Mon, 30 Mar 2026 13:38:41 -0500 Subject: [PATCH 2/2] test(scripts): add regression tests for non-root config integrity exit Verify that the non-root code path exits on verify_config_integrity failure and does not contain the old "proceeding anyway" fallback. Also verify both root and non-root paths call verify_config_integrity. Signed-off-by: latenighthackathon --- test/nemoclaw-start.test.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/nemoclaw-start.test.js b/test/nemoclaw-start.test.js index 246b67cb9..24e0b2185 100644 --- a/test/nemoclaw-start.test.js +++ b/test/nemoclaw-start.test.js @@ -15,4 +15,24 @@ describe("nemoclaw-start non-root fallback", () => { expect(src).toMatch(/touch \/tmp\/gateway\.log/); expect(src).toMatch(/nohup "\$OPENCLAW" gateway run >\/tmp\/gateway\.log 2>&1 &/); }); + + it("exits on config integrity failure in non-root mode", () => { + const src = fs.readFileSync(START_SCRIPT, "utf-8"); + + // Non-root block must call verify_config_integrity and exit 1 on failure + expect(src).toMatch( + /if ! verify_config_integrity; then\s+.*exit 1/s, + ); + // Must not contain the old "proceeding anyway" fallback + expect(src).not.toMatch(/proceeding anyway/i); + }); + + it("calls verify_config_integrity in both root and non-root paths", () => { + const src = fs.readFileSync(START_SCRIPT, "utf-8"); + + // The function must be called at least twice: once in the non-root + // if-block and once in the root path below it. + const calls = src.match(/verify_config_integrity/g) || []; + expect(calls.length).toBeGreaterThanOrEqual(3); // definition + 2 call sites + }); });