Back to skill

Security audit

WorkbuddyOs

Security checks for vulnerabilities and agentic risk

Overview

This is a real WorkBuddy skinning tool, but its auto-install path persistently changes WorkBuddy startup and enables broad renderer debugging control.

Install only if you are comfortable with WorkBuddy being launched with a persistent local debugging port and with a LaunchAgent that automatically reinjects code. Prefer the one-shot apply/pause flow, avoid the auto-install scripts unless you explicitly need them, and verify that uninstall-auto.sh and uninstall-flag.sh fully remove the LaunchAgent and restore the original WorkBuddy executable.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (3)

T06 · System Persistence

Error
Location
scripts/install-auto.sh:28
Finding
Persistent LaunchAgent Automatically Executes the Renderer Injection Pipeline<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install-auto.sh:28-46`; supporting execution loop at `scripts/watch.sh:10-21` **Vulnerability Type**: Cross-session startup persistence **Risk Level**: High ### Complete Code Snippet From `scripts/install-auto.sh`: ```sh cat > "$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>${LABEL}</string> <key>ProgramArguments</key> <array><string>/bin/bash</string><string>$ROOT/scripts/watch.local.sh</string></array> <key>RunAtLoad</key><true/> <key>KeepAlive</key><true/> <key>StandardOutPath</key><string>$ROOT/scripts/watch.log</string> <key>StandardErrorPath</key><string>$ROOT/scripts/watch.log</string> </dict> </plist> EOF launchctl bootout "gui/$(id -u)/${LABEL}" 2>/dev/null || true launchctl bootstrap "gui/$(id -u)" "$PLIST" ``` From `scripts/watch.sh`: ```sh LAST_PID="" while true; do PID=$(pgrep -f "WorkBuddy.app/Contents/MacOS/Electron" | head -1) if [ -z "$PID" ]; then LAST_PID="" elif [ "$PID" != "$LAST_PID" ]; then if curl -s --max-time 1 "http://127.0.0.1:${PORT}/json/list" 2>/dev/null | grep -q "renderer/index.html"; then "$NODE" "$ROOT/src/apply.mjs" >>"$ROOT/scripts/watch.log" 2>&1 && LAST_PID=$PID fi fi sleep 2 done ``` ### Technical Analysis The installer creates `~/Library/LaunchAgents/com.workbuddy.skin.plist`, enables both `RunAtLoad` and `KeepAlive`, and immediately loads the service with `launchctl bootstrap`. The resulting process remains active across user sessions and repeatedly invokes `src/apply.mjs` when it detects a new WorkBuddy process. The LaunchAgent executes scripts directly from the project directory. Consequently, later modifications to `watch.local.sh`, `apply.mjs`, `inject.js`, or related payload files will be executed automatically under the logged-in user's accoun ...[truncated 1595 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the LaunchAgent and require explicit, per-session user invocation. 2. Do not use `KeepAlive` for a cosmetic theme operation. 3. If automatic startup is retained: - Require clear, informed user consent before installation. - Install a fixed, minimal payload in a protected application-support directory. - Verify cryptographic hashes or signatures before every execution. - Refuse to execute payload files that are writable by other users. - Use `RunAtLoad` only if strictly necessary and avoid an infinite polling loop. 4. Provide a complete uninstall procedure that unloads the service, removes the plist, stops active watcher processes, and removes generated files. 5. Display the exact persistence location and active status during installation. 6. Limit the persistent component to detecting startup; require fresh user approval before renderer injection. ]]>

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/install-flag.sh:8
Finding
WorkBuddy Executable Is Replaced with a Persistent Debugging Wrapper<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install-flag.sh:8-23` **Vulnerability Type**: Trusted executable replacement and tool hijacking **Risk Level**: High ### Complete Code Snippet ```sh APP="/Applications/WorkBuddy.app" BIN="$APP/Contents/MacOS/Electron" PORT=9223 [ -d "$APP" ] || { echo "WorkBuddy was not found" >&2; exit 1; } if [ -f "$BIN.real" ]; then exit 0 fi mv "$BIN" "$BIN.real" cat > "$BIN" <<EOF #!/bin/bash exec "\$(dirname "\$0")/Electron.real" --remote-debugging-port=$PORT "\$@" EOF chmod +x "$BIN" ``` ### Technical Analysis The script renames WorkBuddy's original Electron executable to `Electron.real` and writes a shell wrapper at the executable's original trusted path. All subsequent ordinary launches therefore execute project-controlled shell code before invoking the original binary. The wrapper permanently adds `--remote-debugging-port=9223`, exposing the Electron renderer through Chrome DevTools Protocol whenever WorkBuddy runs. This is broader than applying a visual theme and modifies the behavior of an installed application across sessions. Replacing a file inside an application bundle may also invalidate code-signing and integrity assumptions. The installer performs no signature verification before modification, no backup validation, no transactional rollback, and no check that `Electron.real` is the authentic original binary. ### Attack Path 1. The user runs `scripts/install-flag.sh` with sufficient permission to modify `/Applications/WorkBuddy.app`. 2. The legitimate `Contents/MacOS/Electron` executable is renamed to `Electron.real`. 3. A project-controlled shell script is written under the original executable name. 4. The user later starts WorkBuddy through a normal trusted shortcut or application launcher. 5. macOS executes the wrapper instead of the original executable. 6. The wrapper starts `Electron.real` with CDP enabled on the fixed port. 7. A local process can query the debugger target and issu ...[truncated 847 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not rename or replace executables inside a signed application bundle. 2. Launch a temporary, explicit development instance instead: - Use the original executable without altering it. - Select a random ephemeral debugging port. - Bind the debugger explicitly to loopback. - Use a separate temporary user-data directory where supported. - Terminate the debugging instance after the theme is applied. 3. Avoid enabling CDP during every normal application launch. 4. Verify the target application's identity and signature before interacting with it. 5. If a wrapper is unavoidable, place it outside the application bundle and require the user to invoke it explicitly. 6. Implement transactional rollback and verify that restoration targets the authentic original executable. 7. Update the documentation to clearly disclose application-bundle modification, code-signing implications, debugger exposure, required permissions, and rollback behavior. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
tools/cdp.mjs:6
Finding
Fixed CDP Endpoint and General-Purpose Utility Permit Arbitrary WorkBuddy Renderer Execution<![CDATA[ ## Vulnerability Details **File Location**: `tools/cdp.mjs:6-29`; debugger activation at `src/apply.mjs:49-62` **Vulnerability Type**: Excessive renderer-control capability **Risk Level**: High ### Complete Code Snippet From `tools/cdp.mjs`: ```js const PORT = process.env.WB_PORT || 9223; async function main() { let expr; if (process.argv[2] === '-f') expr = readFileSync(process.argv[3], 'utf8'); else expr = process.argv[2]; if (!expr) { console.error('usage: cdp.mjs <expr> | -f file'); process.exit(1); } const list = await (await fetch(`http://127.0.0.1:${PORT}/json/list`)).json(); const page = list.find(t => t.type === 'page' && t.url.includes('renderer/index.html')); if (!page) { console.error('renderer not found'); process.exit(1); } const ws = new WebSocket(page.webSocketDebuggerUrl); await new Promise((res, rej) => { ws.onopen = res; ws.onerror = rej; }); const result = await new Promise((resolve, reject) => { ws.onmessage = (ev) => { const msg = JSON.parse(ev.data); if (msg.id === 1) resolve(msg); }; ws.send(JSON.stringify({ id: 1, method: 'Runtime.evaluate', params: { expression: expr, returnByValue: true, awaitPromise: true }, })); setTimeout(() => reject(new Error('timeout')), 15000); }); ``` From `src/apply.mjs`: ```js spawn('open', ['-a', 'WorkBuddy', '--args', `--remote-debugging-port=${PORT}`], { detached: true, stdio: 'ignore' }).unref(); async function evaluate(page, expression) { const ws = new WebSocket(page.webSocketDebuggerUrl); await new Promise((res, rej) => { ws.onopen = res; ws.onerror = rej; }); const result = await new Promise((resolve, reject) => { ws.onmessage = ev => { const m = JSON.parse(ev.data); if (m.id === 1) resolve(m); }; ws.send(JSON.stringify({ id: 1, method: 'Runtime.evaluate', params: { expression, returnByValue: true } })); setTimeout(() => reject(new Error('evaluate timeout')) ...[truncated 2022 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the general-purpose `tools/cdp.mjs` evaluator from the distributed skill. 2. Replace arbitrary `Runtime.evaluate` input with a fixed, audited operation that only installs the required style and UI elements. 3. Generate a random ephemeral port for each explicit run instead of using port 9223. 4. Bind the debugging endpoint explicitly to loopback and verify the actual listening interface. 5. Shut down the debugging-enabled application instance or debugger endpoint immediately after injection. 6. Validate the exact target process, application signature, expected URL, and renderer identity before connecting. 7. Do not accept JavaScript from command-line arguments or arbitrary files. 8. If developer debugging functionality must remain, separate it from the end-user skill, disable it by default, and require an explicit development-only configuration. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (46)

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The script persistently replaces the app’s Electron binary with a wrapper that forces `--remote-debugging-port=9223` on every launch. Chrome/Electron DevTools remote debugging can expose browser/session internals and, if reachable by other local users or bridged network access, can enable inspection or manipulation of app contents well beyond normal user expectations.

Missing User Warnings

High
Confidence
98% confidence
Finding
The wrapper causes every future launch of the app to expose a remote debugging interface without a user-facing warning at the time of activation. Enabling this class of interface persistently expands the attack surface and may expose app data, session state, and script execution/inspection capabilities to unintended parties.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The script ensures WorkBuddy is relaunched with a Chrome DevTools remote debugging port, then connects to that port and executes arbitrary JavaScript via Runtime.evaluate inside the app context. That gives the script full ability to alter UI, inspect app state, and potentially access privileged in-app data or trigger unintended actions; in a local desktop app context this is a strong code-injection capability far beyond a simple theming operation.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
This script is explicitly designed to connect to a local Chrome DevTools Protocol endpoint, locate the WorkBuddy renderer, and execute attacker-supplied JavaScript via Runtime.evaluate. That creates arbitrary code execution inside the renderer context, which can access in-app state, DOM content, tokens, and potentially privileged bridges exposed by the app. The lack of any stated constrained purpose, allowlist, or safety boundary makes this dangerous in a skill context.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The README explicitly instructs users to install a persistent auto-injection mechanism, register a LaunchAgent, and modify the Electron startup path by renaming the original binary and inserting a forwarding wrapper with a remote debugging port. Even if intended only for theming, these steps weaken application and system integrity, create persistence, and normalize a technique that could be repurposed for unauthorized code injection or post-update breakage without clearly warning users about the security implications.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The skill explicitly instructs use of a CDP helper to execute arbitrary JavaScript in the WorkBuddy renderer while running the app in debug mode, but it does not clearly frame this as code execution against application state or explain the risks. In an agent setting, this can normalize unsafe execution patterns and could be repurposed to inspect sensitive data, alter app behavior, or perform unintended actions inside the desktop app context.

Intent-Code Divergence

Medium
Confidence
94% confidence
Finding
The header comment says the script registers a LaunchAgent to auto-start watch.sh at login, but the generated plist also sets KeepAlive=true and redirects stdout/stderr into a persistent log file. That is a stronger, continuously running behavior with filesystem writes, which goes beyond the narrower wording of simple login auto-start.

Session Persistence

Medium
Category
Rogue Agent
Content
chmod +x "$ROOT/scripts/watch.local.sh"

# 生成并加载 LaunchAgent
cat > "$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">
Confidence
93% confidence
Finding
At this point the script creates a LaunchAgent plist that will be used to establish login-session persistence. In an installer, persistence is security-sensitive because it ensures continued execution of another script on future logins, increasing the blast radius if that watcher script is modified, compromised, or behaves unexpectedly.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The installer immediately creates and loads a persistent LaunchAgent in the user session without an explicit confirmation step. In a skill/package context, silently establishing persistence reduces user awareness and can normalize autorun behavior, especially when paired with another script that will execute continuously at login.

Session Persistence

Medium
Category
Rogue Agent
Content
# 生成并加载 LaunchAgent
cat > "$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>${LABEL}</string>
Confidence
92% confidence
Finding
This is another trigger on the same plist-generation block and reflects the same persistence concern. The danger comes from automatic startup and continuous execution, not from the XML syntax itself.

Session Persistence

Medium
Category
Rogue Agent
Content
# 生成并加载 LaunchAgent
cat > "$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>${LABEL}</string>
Confidence
92% confidence
Finding
This is another trigger on the same plist-generation block and reflects the same persistence concern. The danger comes from automatic startup and continuous execution, not from the XML syntax itself.

Session Persistence

Medium
Category
Rogue Agent
Content
cat > "$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>${LABEL}</string>
  <key>ProgramArguments</key>
Confidence
90% confidence
Finding
The ProgramArguments entry binds launchd to execute the generated watch.local.sh automatically. This creates a durable autorun path for arbitrary future behavior in that script, which becomes more dangerous if the script can later be edited or replaced by another local process.

Session Persistence

Medium
Category
Rogue Agent
Content
EOF

launchctl bootout "gui/$(id -u)/${LABEL}" 2>/dev/null || true
launchctl bootstrap "gui/$(id -u)" "$PLIST"
echo "✓ 自动换肤已开启:今后正常打开 WorkBuddy 即可,皮肤会自动生效"
echo "· 日志:$ROOT/scripts/watch.log;卸载:scripts/uninstall-auto.sh"
Confidence
96% confidence
Finding
This line actively loads the LaunchAgent into the user’s launchd session, making the persistence mechanism effective immediately. In the context of an installer for a cosmetic modification, silently activating autorun behavior materially increases risk because code will continue running outside the user’s immediate action and may be overlooked.

Intent-Code Divergence

Medium
Confidence
93% confidence
Finding
The comments explicitly describe making the debugging port effective for every launch method by renaming the original Electron executable and inserting a forwarding wrapper. That persistence mechanism is invasive because it alters the trusted application bundle and silently changes runtime behavior in a way users and administrators may not detect.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script modifies an installed application under `/Applications` by moving the original executable and dropping in a replacement wrapper, but it provides no confirmation prompt before making the change. Silent modification of a signed app bundle can break trust assumptions, surprise users, and create a durable foothold for behavior they did not explicitly approve.

Session Persistence

Medium
Category
Rogue Agent
Content
# uninstall-auto.sh — 关闭自动换肤
set -e
LABEL="com.workbuddy.skin"
PLIST="$HOME/Library/LaunchAgents/${LABEL}.plist"
launchctl bootout "gui/$(id -u)/${LABEL}" 2>/dev/null || true
rm -f "$PLIST"
echo "✓ 自动换肤已关闭(当前皮肤仍在,WorkBuddy 重启后自然消失)"
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
# uninstall-auto.sh — 关闭自动换肤
set -e
LABEL="com.workbuddy.skin"
PLIST="$HOME/Library/LaunchAgents/${LABEL}.plist"
launchctl bootout "gui/$(id -u)/${LABEL}" 2>/dev/null || true
rm -f "$PLIST"
echo "✓ 自动换肤已关闭(当前皮肤仍在,WorkBuddy 重启后自然消失)"
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
# uninstall-auto.sh — 关闭自动换肤
set -e
LABEL="com.workbuddy.skin"
PLIST="$HOME/Library/LaunchAgents/${LABEL}.plist"
launchctl bootout "gui/$(id -u)/${LABEL}" 2>/dev/null || true
rm -f "$PLIST"
echo "✓ 自动换肤已关闭(当前皮肤仍在,WorkBuddy 重启后自然消失)"
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
# uninstall-auto.sh — 关闭自动换肤
set -e
LABEL="com.workbuddy.skin"
PLIST="$HOME/Library/LaunchAgents/${LABEL}.plist"
launchctl bootout "gui/$(id -u)/${LABEL}" 2>/dev/null || true
rm -f "$PLIST"
echo "✓ 自动换肤已关闭(当前皮肤仍在,WorkBuddy 重启后自然消失)"
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
# uninstall-auto.sh — 关闭自动换肤
set -e
LABEL="com.workbuddy.skin"
PLIST="$HOME/Library/LaunchAgents/${LABEL}.plist"
launchctl bootout "gui/$(id -u)/${LABEL}" 2>/dev/null || true
rm -f "$PLIST"
echo "✓ 自动换肤已关闭(当前皮肤仍在,WorkBuddy 重启后自然消失)"
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
# uninstall-auto.sh — 关闭自动换肤
set -e
LABEL="com.workbuddy.skin"
PLIST="$HOME/Library/LaunchAgents/${LABEL}.plist"
launchctl bootout "gui/$(id -u)/${LABEL}" 2>/dev/null || true
rm -f "$PLIST"
echo "✓ 自动换肤已关闭(当前皮肤仍在,WorkBuddy 重启后自然消失)"
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This shell script deletes and replaces an application executable using `rm -f` and `mv`, which is a safety-critical file operation. Although it prints a success message afterward, there is no prior warning, confirmation prompt, or explanatory comment disclosing that the script will modify files inside `/Applications` and overwrite the current executable.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
SQP-3 applies to all file types and includes language or locale policy violations in natural-language content. The operational comments are written entirely in Chinese, with no indication that language choice is optional or justified by a region-specific constraint, which can impose a locale on maintainers or operators.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This shell script is a code file, so SQP-2 applies to safety-relevant operations such as subprocess execution. When the target app is detected, the script automatically runs apply.mjs and redirects all output to a log file, but there is no confirmation prompt, user-visible print/log message, or explanatory warning in the script about this automatic action.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This script runs indefinitely, polls a local debugging-style HTTP endpoint, and automatically executes another script whenever it detects a matching renderer. That creates persistence and silent re-application behavior without user interaction in this file, which is risky because any compromise or unintended state change in the downstream apply script is repeatedly re-triggered.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
src/apply.mjs:21