T06 · System Persistence
Error
- Location
- scripts/install.sh:45
- Finding
- Persistent LaunchAgent Runs the Queue Daemon Across User Sessions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install.sh:45-71` **Vulnerability Type**: Persistent user-level startup service **Risk Level**: High ### Vulnerable Code ```sh # ── 3. launchd plist ───────────────────────────────────────────────────────── echo " → Installing launchd plist: $PLIST" cat > "$PLIST" <<PLIST_EOF <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>ai.openclaw.queue-daemon</string> <key>ProgramArguments</key> <array> <string>/usr/bin/env</string> <string>node</string> <string>${QUEUE_DIR}/daemon.js</string> </array> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/> <key>StandardOutPath</key> <string>${QUEUE_DIR}/daemon.log</string> <key>StandardErrorPath</key> <string>${QUEUE_DIR}/daemon.log</string> </dict> </plist> PLIST_EOF launchctl unload "$PLIST" 2>/dev/null || true launchctl load "$PLIST" ``` ### Technical Analysis The manual installer creates a user LaunchAgent in `~/Library/LaunchAgents` and immediately loads it. `RunAtLoad` causes the daemon to start when the LaunchAgent is loaded or the user logs in, while unconditional `KeepAlive` causes launchd to restart the process after it exits. Persistent execution is related to the declared delayed-task functionality and is explicitly disclosed in `SKILL.md` and `public.json`. However, unconditional restart behavior grants the component durable, cross-session execution beyond a single Skill invocation. The installed program is a JavaScript file under the user's writable home directory: ```text ~/.openclaw/queue/daemon.js ``` Consequently, any process operating as the same user that replaces this file can alter the program subsequently executed and maintained by launchd. The project also provides no automated uninstall procedure to unload and remove the service. The Launch ...[truncated 1312 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Prefer OpenClaw's existing scheduler or another platform-managed delayed-task facility rather than installing a separate persistent daemon. 2. If launchd is required, avoid unconditional `KeepAlive`. Use an on-demand activation model, bounded execution, or a timer-based job that exits after processing. 3. Request explicit confirmation immediately before writing and loading the LaunchAgent, separately from general Skill installation consent. 4. Add a documented uninstall script that: - Uses `launchctl bootout` or the appropriate supported unload operation. - Removes the plist. - Removes the installed plugin and daemon files after user confirmation. - Preserves or securely deletes queue history according to user preference. 5. Use modern `launchctl bootstrap` and `bootout` commands with the correct per-user domain. 6. Restrict installation-directory and file permissions explicitly, and verify ownership before loading or updating the daemon. 7. Consider integrity verification or atomic, trusted update procedures so a replaced daemon file is not silently executed. 8. Clearly display the service's status, installed paths, restart policy, and removal command at installation time. ]]>
