diff --git a/CHANGELOG.md b/CHANGELOG.md index e2ba6d40eea..640f7aaa288 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -108,6 +108,7 @@ Docs: https://docs.openclaw.ai - Gateway/Control UI origins: support wildcard `"*"` in `gateway.controlUi.allowedOrigins` for trusted remote access setups. Landed from contributor PR #31088 by @frankekn. Thanks @frankekn. - Cron/Isolated CLI timeout ratio: avoid reusing persisted CLI session IDs on fresh isolated cron runs so the fresh watchdog profile is used and jobs do not abort at roughly one-third of configured `timeoutSeconds`. (#30140) Thanks @ningding97. - Security/Sandbox media reads: eliminate sandbox media TOCTOU symlink-retarget escapes by enforcing root-scoped boundary-safe reads at attachment/image load time and consolidating shared safe-read helpers across sandbox media callsites. This ships in the next npm release. Thanks @tdjackey for reporting. +- Node host/service auth env: include `OPENCLAW_GATEWAY_TOKEN` in `openclaw node install` service environments (with `CLAWDBOT_GATEWAY_TOKEN` compatibility fallback) so installed node services keep remote gateway token auth across restart/reboot. Fixes #31041. Thanks @OneStepAt4time for reporting, @byungsker, @liuxiaopai-ai, and @vincentkoc. - Security/Subagents sandbox inheritance: block sandboxed sessions from spawning cross-agent subagents that would run unsandboxed, preventing runtime sandbox downgrade via `sessions_spawn agentId`. Thanks @tdjackey for reporting. - Security/Workspace safe writes: harden `writeFileWithinRoot` against symlink-retarget TOCTOU races by opening existing files without truncation, creating missing files with exclusive create, deferring truncation until post-open identity+boundary validation, and removing out-of-root create artifacts on blocked races; added regression tests for truncate/create race paths. This ships in the next npm release (`2026.3.1`). Thanks @tdjackey for reporting. - Security/Node metadata policy: harden node platform classification against Unicode confusables and switch unknown platform defaults to a conservative allowlist that excludes `system.run`/`system.which` unless explicitly allowlisted, preventing metadata canonicalization drift from broadening node command permissions. Thanks @tdjackey for reporting. diff --git a/src/daemon/service-env.test.ts b/src/daemon/service-env.test.ts index 95dee4ecc1d..9a13e81363e 100644 --- a/src/daemon/service-env.test.ts +++ b/src/daemon/service-env.test.ts @@ -364,6 +364,42 @@ describe("buildNodeServiceEnvironment", () => { expect(env.HOME).toBe("/home/user"); }); + it("passes through OPENCLAW_GATEWAY_TOKEN for node services", () => { + const env = buildNodeServiceEnvironment({ + env: { HOME: "/home/user", OPENCLAW_GATEWAY_TOKEN: " node-token " }, + }); + expect(env.OPENCLAW_GATEWAY_TOKEN).toBe("node-token"); + }); + + it("maps legacy CLAWDBOT_GATEWAY_TOKEN to OPENCLAW_GATEWAY_TOKEN for node services", () => { + const env = buildNodeServiceEnvironment({ + env: { HOME: "/home/user", CLAWDBOT_GATEWAY_TOKEN: " legacy-token " }, + }); + expect(env.OPENCLAW_GATEWAY_TOKEN).toBe("legacy-token"); + }); + + it("prefers OPENCLAW_GATEWAY_TOKEN over legacy CLAWDBOT_GATEWAY_TOKEN", () => { + const env = buildNodeServiceEnvironment({ + env: { + HOME: "/home/user", + OPENCLAW_GATEWAY_TOKEN: "openclaw-token", + CLAWDBOT_GATEWAY_TOKEN: "legacy-token", + }, + }); + expect(env.OPENCLAW_GATEWAY_TOKEN).toBe("openclaw-token"); + }); + + it("omits OPENCLAW_GATEWAY_TOKEN when both token env vars are empty", () => { + const env = buildNodeServiceEnvironment({ + env: { + HOME: "/home/user", + OPENCLAW_GATEWAY_TOKEN: " ", + CLAWDBOT_GATEWAY_TOKEN: " ", + }, + }); + expect(env.OPENCLAW_GATEWAY_TOKEN).toBeUndefined(); + }); + it("forwards proxy environment variables for node services", () => { const env = buildNodeServiceEnvironment({ env: { diff --git a/src/daemon/service-env.ts b/src/daemon/service-env.ts index 15c78521348..2ab274e7f74 100644 --- a/src/daemon/service-env.ts +++ b/src/daemon/service-env.ts @@ -279,6 +279,8 @@ export function buildNodeServiceEnvironment(params: { }): Record { const { env } = params; const platform = params.platform ?? process.platform; + const gatewayToken = + env.OPENCLAW_GATEWAY_TOKEN?.trim() || env.CLAWDBOT_GATEWAY_TOKEN?.trim() || undefined; const stateDir = env.OPENCLAW_STATE_DIR; const configPath = env.OPENCLAW_CONFIG_PATH; const tmpDir = env.TMPDIR?.trim() || os.tmpdir(); @@ -296,6 +298,7 @@ export function buildNodeServiceEnvironment(params: { NODE_EXTRA_CA_CERTS: nodeCaCerts, OPENCLAW_STATE_DIR: stateDir, OPENCLAW_CONFIG_PATH: configPath, + OPENCLAW_GATEWAY_TOKEN: gatewayToken, OPENCLAW_LAUNCHD_LABEL: resolveNodeLaunchAgentLabel(), OPENCLAW_SYSTEMD_UNIT: resolveNodeSystemdServiceName(), OPENCLAW_WINDOWS_TASK_NAME: resolveNodeWindowsTaskName(),