Skip to content

[Bug bounty] macOS launcher writes unescaped values into its launchd plist #181

Description

@Onui2

Bounty eligibility

  • I have signed up at monk.io with this GitHub account
  • I have used the product through the installed plugin and current Agent
  • I have starred this repo

Stage

connect

Coding agent

OpenAI Codex

Coding agent version

Codex CLI 0.145.0 in Codex desktop

Repro steps

The current macOS launcher writes dynamic values directly into XML text without
XML escaping.

  1. Check out Monk plugin v0.1.52, commit:

    6bdde78162eac7ebf845eb2f0e4add78945b90ce
    
  2. Put an executable fixture at a valid macOS path containing an ampersand:

    /tmp/agent&fixture/monk-agent
    
  3. Start from a fresh isolated HOME and run:

    MONK_AGENT_PATH='/tmp/agent&fixture/monk-agent' \
    MONK_AGENT_SKIP_SIGNIN_NUDGE=1 \
    scripts/start-monk-agent.sh
  4. Inspect:

    ~/Library/LaunchAgents/io.monk.agent.plist
    

    The ProgramArguments entry contains a raw XML ampersand:

    <string>/tmp/agent&fixture/monk-agent</string>
  5. Validate the plist with plutil -lint on macOS or another conforming
    XML/plist parser.

I used a bounded deterministic harness against the exact released script. It
stubbed curl and launchctl, so it opened no socket and created no real
launchd service. The stub recorded that the launcher reached
launchctl bootstrap, while Python plistlib parsed the exact generated file.

Affected result:

launcher exit: 0
launchctl bootstrap reached: true
raw ampersand present: true
&amp; present: false
plist parse: false
error: ExpatError: not well-formed (invalid token): line 9, column 74

Safe control, changing only agent&fixture to agent-fixture:

launcher exit: 0
launchctl bootstrap reached: true
plist parse: true

The same released launcher bytes appear in the root, packaged Monk plugin, and
Antigravity trees:

SHA-256:
F63392E61C6C56211E3348B5C0638FA5D12195B514DB6F64AA7736431226F654

Harness and proof:

  • harnesses/public-functional/launchd-plist-escaping.sh
  • harnesses/public-functional/run-launchd-plist-escaping.ps1
  • evidence/public-functional/launchd-plist-escaping-proof.json

Expected behavior

Every valid filesystem path and configured URL should produce a valid launchd
plist. Dynamic strings must be encoded as XML text before insertion.

Actual behavior

start_with_launchd() inserts agent_path, host, auth settings, plugin
version, launch client, PATH, and log path directly into a heredoc:

<string>$agent_path</string>

A valid value containing & or < makes the plist malformed. On macOS,
launchctl bootstrap consumes this file; a malformed plist prevents the
companion from being registered or started. This can affect a custom
MONK_AGENT_PATH, a PATH component, or an on-prem URL containing XML
metacharacters.

The launcher uses set -eu, so a real launchctl bootstrap rejection aborts
startup before the readiness result.

Suggested fix:

  1. Build the plist with plutil, PlistBuddy, or another structured plist API;
    or XML-escape every dynamic string before writing.
  2. Validate the completed plist with plutil -lint before replacing the active
    file.
  3. Compare decoded plist values in launchd_configured() instead of grepping
    raw XML text.
  4. Add regression cases containing & and <.

Severity (your guess)

major

A valid local path or on-prem configuration can completely block the macOS
companion startup. Removing the XML metacharacter is a workaround.

OS

macOS launchd path in plugin v0.1.52; exact branch exercised under Windows 11
WSL2 with deterministic command fixtures and standard-library plist validation.
No real macOS service was modified.

monkd version

Not involved. Failure occurs while registering the local companion with
launchd.

Target cloud

None

Integration (if relevant)

Monk plugin macOS launcher / launchd

Duplicate check

Fresh public search at 2026-07-30T06:10:07Z covered open and closed issues
through #180. Searches for plist, launchd XML, ampersand launcher,
MONK_AGENT_PATH special character, and LaunchAgents invalid found no
matching report.

Issue #180 mentions the launchd plist only to compare macOS restart behavior
with Linux. It does not cover XML serialization or malformed plist output.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions