fix: restart Windows gateway via Scheduled Task#38825
Conversation
🔒 Aisle Security AnalysisWe found 3 potential security issue(s) in this PR:
1. 🟠 Local privilege escalation via insecure temp .cmd helper for Windows scheduled-task relaunch
DescriptionThe Windows Scheduled Task relaunch helper writes a batch script into a temp directory and then executes it via a detached If the gateway runs as a privileged account (e.g., Scheduled Task as
This is a classic “temporary executable script in shared temp” issue. Vulnerable code: const scriptPath = path.join(
resolvePreferredOpenClawTmpDir(),
`openclaw-schtasks-restart-${randomUUID()}.cmd`,
);
...
fs.writeFileSync(scriptPath, `${buildScheduledTaskRestartScript(taskName)}\r\n`, "utf8");
const child = spawn("cmd.exe", ["/d", "/s", "/c", quotedScriptPath], {
detached: true,
stdio: "ignore",
windowsHide: true,
});Notes:
RecommendationAvoid writing an executable script into a potentially shared temp directory. Preferred fixes (pick one):
import { spawnSync } from "node:child_process";
for (let i = 0; i < TASK_RESTART_RETRY_LIMIT; i++) {
spawnSync("schtasks", ["/Run", "/TN", taskName], { stdio: "ignore" });
// exit early on success (check status/error)
await new Promise(r => setTimeout(r, TASK_RESTART_RETRY_DELAY_SEC * 1000));
}This keeps the operation in the same process and avoids a writable-on-disk executable artifact.
Additional hardening (defense-in-depth):
fs.writeFileSync(scriptPath, contents, { encoding: "utf8", flag: "wx" });
2. 🟡 Arbitrary Scheduled Task execution via OPENCLAW_WINDOWS_TASK_NAME override
DescriptionThe new Windows restart implementation (
Vulnerable code: const override = env.OPENCLAW_WINDOWS_TASK_NAME?.trim();
if (override) {
return override;
}
...
`schtasks /Run /TN ${quotedTaskName} >nul 2>&1`,RecommendationConstrain the task name used for restarts so it cannot be redirected to unrelated tasks. Recommended options (choose one):
Example strict validation: import { resolveGatewayWindowsTaskName } from "../daemon/constants.js";
function resolveWindowsTaskName(env: NodeJS.ProcessEnv): string {
const expected = resolveGatewayWindowsTaskName(env.OPENCLAW_PROFILE);
const override = env.OPENCLAW_WINDOWS_TASK_NAME?.trim();
if (override && override === expected) {
return override;
}
return expected;
}
Additionally, consider rejecting task names containing task-folder prefixes (e.g. starting with 3. 🔵 Windows command injection via unsafely-escaped scheduled task name in generated .cmd restart helper
DescriptionThe scheduled-task restart helper writes a
As a result, an attacker who can influence Vulnerable code: const quotedTaskName = quoteCmdScriptArg(taskName);
...
`schtasks /Run /TN ${quotedTaskName} >nul 2>&1`,Example exploit value:
This produces a script line that will execute RecommendationDo not embed potentially-variable strings directly into batch script command lines unless you use CMD-correct escaping/quoting. Safer options:
function assertSafeTaskName(name: string): void {
// allow: letters, numbers, space, dash, underscore, dot, parentheses
if (!/^[A-Za-z0-9 _.\-()]+$/.test(name)) {
throw new Error("Unsafe Windows task name");
}
}
function buildScheduledTaskRestartScript(taskName: string): string {
assertSafeTaskName(taskName);
return [
"@echo off",
"setlocal",
`schtasks /Run /TN "${taskName}" >nul 2>&1`,
...
].join("\r\n");
}
Additionally, add tests that execute the generated Analyzed PR: #38825 at commit Last updated on: 2026-03-07T13:48:22Z |
Greptile SummaryThis PR fixes Windows gateway restarts by routing them through Key changes:
Confidence Score: 4/5
Last reviewed commit: 5bcb017 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e56c6f02af
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1e169fbf67
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const scriptPath = path.join( | ||
| resolvePreferredOpenClawTmpDir(), | ||
| `openclaw-schtasks-restart-${randomUUID()}.cmd`, | ||
| ); | ||
| const quotedScriptPath = quoteCmdScriptArg(scriptPath); |
There was a problem hiding this comment.
Wrap temp script path derivation in restart error handling
relaunchGatewayScheduledTask computes scriptPath (and quotes it) before entering the try block, so failures from resolvePreferredOpenClawTmpDir() or quoteCmdScriptArg(scriptPath) escape as uncaught exceptions instead of returning { ok: false }. On Windows hosts with an unsafe/uncreatable temp dir, this propagates through triggerOpenClawRestart() and prevents the caller from taking its normal fallback path, which can break restart handling rather than reporting a controlled restart failure.
Useful? React with 👍 / 👎.
1e169fb to
a55cd45
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a55cd45b1d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return { | ||
| ok: true, | ||
| method: "schtasks", | ||
| tried: [`schtasks /Run /TN "${taskName}"`, `cmd.exe /d /s /c ${quotedScriptPath}`], |
There was a problem hiding this comment.
Verify Scheduled Task before reporting restart success
relaunchGatewayScheduledTask reports ok: true immediately after spawning detached cmd.exe, but the real schtasks /Run call happens later inside the helper script and can still fail (for example, when the task name is stale/renamed or the task was removed). In that failure mode restartGatewayProcessWithFreshPid() treats the restart as supervised and runGatewayLoop exits, so the gateway can go down without taking the in-process fallback path. Please gate success on a synchronous task probe (e.g. /Query or an immediate /Run check) before returning success.
Useful? React with 👍 / 👎.
…nclaw#38825) (cherry picked from commit 05c240f)
…nclaw#38825) (cherry picked from commit 05c240f)
Summary
Why
#38692 avoids the orphaned child, but this fixes the root cause: Windows had no explicit Scheduled Task restart path.
Closes #38508