mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 00:30:44 +00:00
fix: check effective UID (geteuid) in root guard
assertNotRoot only checked process.getuid(), so the guard was bypassed when the CLI was launched with a non-root real UID but an effective UID of 0 (e.g. via a setuid-root wrapper). In that context the process still has root write privileges and can cause the same state/config corruption the guard was added to prevent. Now checks both getuid() and geteuid() — either being 0 triggers the guard. Added three tests covering setuid-root scenarios.
This commit is contained in:
committed by
Sally O'Malley
parent
ad461c74cc
commit
6ec4e5cf4a
@@ -5,19 +5,22 @@ describe("assertNotRoot", () => {
|
||||
const exitSpy = vi.spyOn(process, "exit").mockImplementation(() => undefined as never);
|
||||
const stderrSpy = vi.spyOn(process.stderr, "write").mockImplementation(() => true);
|
||||
|
||||
// Save and restore real getuid so we can replace it per test.
|
||||
// Save and restore real getuid/geteuid so we can replace them per test.
|
||||
const realGetuid = process.getuid;
|
||||
const realGeteuid = process.geteuid;
|
||||
|
||||
beforeEach(() => {
|
||||
exitSpy.mockClear();
|
||||
stderrSpy.mockClear();
|
||||
process.getuid = realGetuid;
|
||||
process.geteuid = realGeteuid;
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
exitSpy.mockRestore();
|
||||
stderrSpy.mockRestore();
|
||||
process.getuid = realGetuid;
|
||||
process.geteuid = realGeteuid;
|
||||
});
|
||||
|
||||
it("exits with code 1 when uid is 0 and no env override", () => {
|
||||
@@ -50,6 +53,27 @@ describe("assertNotRoot", () => {
|
||||
expect(exitSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("exits when real uid is non-zero but effective uid is 0 (setuid-root)", () => {
|
||||
process.getuid = () => 1000;
|
||||
process.geteuid = () => 0;
|
||||
assertNotRoot({});
|
||||
expect(exitSpy).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it("does not exit when real uid is non-zero and effective uid is non-zero", () => {
|
||||
process.getuid = () => 1000;
|
||||
process.geteuid = () => 1000;
|
||||
assertNotRoot({});
|
||||
expect(exitSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not exit when euid is 0 but OPENCLAW_ALLOW_ROOT=1", () => {
|
||||
process.getuid = () => 1000;
|
||||
process.geteuid = () => 0;
|
||||
assertNotRoot({ OPENCLAW_ALLOW_ROOT: "1" });
|
||||
expect(exitSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not exit when getuid is undefined (Windows)", () => {
|
||||
process.getuid = undefined as unknown as typeof process.getuid;
|
||||
assertNotRoot({});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import process from "node:process";
|
||||
|
||||
/**
|
||||
* Block CLI execution when running as root (uid 0) unless explicitly opted in.
|
||||
* Block CLI execution when running as root (uid 0 or euid 0) unless explicitly opted in.
|
||||
*
|
||||
* Running as root causes:
|
||||
* - Separate state dir (/root/.openclaw/ vs /home/<user>/.openclaw/)
|
||||
@@ -12,7 +12,9 @@ export function assertNotRoot(env: NodeJS.ProcessEnv = process.env): void {
|
||||
if (typeof process.getuid !== "function") {
|
||||
return;
|
||||
}
|
||||
if (process.getuid() !== 0) {
|
||||
const uid = process.getuid();
|
||||
const euid = typeof process.geteuid === "function" ? process.geteuid() : uid;
|
||||
if (uid !== 0 && euid !== 0) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
|
||||
Reference in New Issue
Block a user