Description
isOpenclawDashboardReady() in bin/lib/onboard.js (line 2247) passes sandboxName directly into a shell command string without shellQuote():
const readyMatch = runCapture(
`openshell sandbox exec ${sandboxName} curl -sf http://localhost:18789/ 2>/dev/null || echo "no"`,
{ ignoreError: true },
);
Similarly, the DNS proxy setup at line 2276 passes GATEWAY_NAME unquoted:
run(
`bash "${path.join(SCRIPTS, "setup-dns-proxy.sh")}" ${GATEWAY_NAME} "${sandboxName}" 2>&1 || true`,
);
runCapture() and run() execute via bash -c, so any shell metacharacters in these values would be interpreted by the shell.
Current Mitigation
The sandbox name is validated upstream via regex during onboarding, which prevents exploitation through the normal flow. However:
- Defense-in-depth violation: The validation and shell execution are in different functions with no contract between them
- Inconsistency: The same file imports and uses
shellQuote() for other variables (lines 460, 1438, 3005) but omits it here
createSandbox() accepts a sandboxNameOverride parameter that bypasses promptValidatedSandboxName() entirely
Fix
Wrap both values with shellQuote(), which is already imported and used elsewhere in the file:
- `openshell sandbox exec ${sandboxName} curl -sf http://localhost:18789/ 2>/dev/null || echo "no"`,
+ `openshell sandbox exec ${shellQuote(sandboxName)} curl -sf http://localhost:18789/ 2>/dev/null || echo "no"`,
- `bash "${path.join(SCRIPTS, "setup-dns-proxy.sh")}" ${GATEWAY_NAME} "${sandboxName}" 2>&1 || true`,
+ `bash "${path.join(SCRIPTS, "setup-dns-proxy.sh")}" ${shellQuote(GATEWAY_NAME)} ${shellQuote(sandboxName)} 2>&1 || true`,
Description
isOpenclawDashboardReady()inbin/lib/onboard.js(line 2247) passessandboxNamedirectly into a shell command string withoutshellQuote():Similarly, the DNS proxy setup at line 2276 passes
GATEWAY_NAMEunquoted:runCapture()andrun()execute viabash -c, so any shell metacharacters in these values would be interpreted by the shell.Current Mitigation
The sandbox name is validated upstream via regex during onboarding, which prevents exploitation through the normal flow. However:
shellQuote()for other variables (lines 460, 1438, 3005) but omits it herecreateSandbox()accepts asandboxNameOverrideparameter that bypassespromptValidatedSandboxName()entirelyFix
Wrap both values with
shellQuote(), which is already imported and used elsewhere in the file: