T06 · System Persistence
- Location
scripts/deploy.sh:79- Finding
Persistent Gateway Startup Hook Promotes User-Writable JavaScript into the Control UI
- Content
View full analysis
{ if (event.type !== "gateway" || event.action !== "startup") return; // SECURITY: execFileSync with array args — no shell interpolation. // Script path is derived from __dirname (relative to this file), not user input. const script = join(__dirname, "inject.sh"); try { execFileSync("bash", [script], { timeout: 10_000, ``` #### `hooks/inject.sh:27-34` ```bash cp -f "$SRC" "$ASSET_DIR/voice-input.js" if ! grep -q "$MARKER" "$INDEX" 2>/dev/null; then # SECURITY: sed uses only hardcoded strings — no variable interpolation. sed -i 's|| \n |' "$INDEX" echo "[voice-input-inject] Injected into $INDEX" else echo "[voice-input-inject] Already present in $INDEX" ``` ### Technical Analysis Deployment creates a persistent OpenClaw gateway startup hook under `~/.openclaw/hooks/voice-input-inject`. Whenever the gateway emits a startup event, `handler.ts` invokes `inject.sh` through Bash. The script then copies `~/.openclaw/workspace/voice-input/voice-input.js` into the trusted OpenClaw Control UI asset directory and ensures that `index.html` loads it. Although `execFileSync` avoids shell-argument injection, it does not address the trust-boundary problem. The startup hook treats a workspace file as truste ...[truncated 2157 chars]- Remediation
View remediation
