Proof Portal
Omarchy
ProbeLabs65 findings · 95 requirementsA proof layer — requirements, tests and verified fixes — for two of Omarchy's subsystems: the application menu (launcher scripts, QML model, JSONC config, search and selection) and the lock screen (lock scripts, QML, PAM authentication). Scope is deliberately limited to those components of omacom/omarchy; the rest of the distribution is not covered.
omarchy-agent-crash splices unsanitized, attacker-reachable crash metadata into the AI-diagnosis prompt with no fencing
Reported by TaylorBurke · filed Oct 5, 2026 · open at source
At a glance
- 1 · What is claimed?
- Only the report text below — no validator has restated it as checkable claims yet.
- 2 · What does it affect?
- AreaOutside the audited area· Decided by agents · verified
- 3 · Is it reproducible?
- No validation has been run yet.
- 4 · What remains unclear?
- Not validated yet: no claims, no reproducer.
- 5 · What happens next?
- Decided by agents and verified by a second, independent agent — see the decision.
Original report
What happened
omarchy-crash-watch (enabled by default; install/user/first-run/enable-user-units.sh:22) reads COREDUMP_EXE straight from the systemd-coredump journal for any process the local user runs — a value fully controlled by whoever ran that process, since Linux filenames can contain almost any byte, including literal newlines. It passes exe to --exec omarchy-agent-crash "$pid" "$comm" "$exe" "$signal" as a safe, discrete argv word (bin/omarchy-crash-watch:45) — no shell/argv injection there.
But bin/omarchy-agent-crash:40 then splices that same raw string directly into a heredoc that becomes the AI agent's prompt, with no escaping, fencing, or "this is untrusted data" framing:
prompt=$(
cat <<PROMPT
...
binary: $exe
...
PROMPT
)
exec omarchy-agent --prompt "$prompt"omarchy-agent then launches whichever agent the user has set as their default (Omarchy ships with none picked; bin/omarchy-default-agent's own comment: "Omarchy picks no agent for you") with its permission-bypass flag for this "unattended" path — every entry in that case table gets one (--permission-mode auto/bypassPermissions, --dangerously-skip-permissions, --approve-for-me, --yolo, --allow-all, per agent), per the comment above the table: "agents launched from the keybinding or menu run unattended, so each one starts with its own spelling of 'don't stop to ask.'"
Net effect: any unprivileged local process can get a diagnostic AI agent to read attacker-chosen text as part of its prompt, with no human confirmation gate, just by crashing itself at a path of its own choosing and having the user click the resulting "Process crashed — Click to diagnose with AI" notification — regardless of which of the 13 supported agents that user has configured as default.
Why I'm filing this as a hardening suggestion, not a vulnerability report
I tested this against Claude Code specifically, because that's what was configured as the default agent on my test machine — not because it's an Omarchy default (there isn't one). Tried 5 different payloads: a bare injected instruction, authority framing (claiming to be the diagnose-crash skill's own documented step), field-blending (mimicking the report's own key: value structure), a fake ### SYSTEM / ### USER turn-boundary spoof, and a repeat of the strongest payload under --permission-mode bypassPermissions instead of auto (to rule out the permission flag itself as the deciding factor). All five were resisted — the model consistently identified the embedded text as data from the crash record rather than an instruction, e.g.:
"The process's command line and the name of a directory inside poc-crash-payload both contain text posing as a 'system report' instruction... That text came from the crashed program's own metadata, not from you or the skill, so I ignored it and ran nothing of the sort."So there's no working bypass today against this one agent, and per the program's own policy this doesn't cross a confirmed security boundary. But this is one model's behavior at one point in time — the same unsanitized splicing reaches whichever of the 13 supported agents a user has actually configured, and none of them get any sanitization or untrusted-data framing before this point. That's not something this code should rely on model alignment alone to cover.
Suggested direction
- Wrap
$comm/$exe/$signalin an explicit delimiter in the prompt template, with surrounding instruction text telling the agent these fields are unverified data from the crash record, not from the user. - Consider restricting
$exe/$commto printable characters (or otherwise normalizing) before they ever reach a prompt, the same discipline as sanitizing before any other sensitive sink. - Same bug class already fixed once in
omarchy-toggle-input-device(#8129, merged) — untrusted local device/process metadata reaching a code-execution-adjacent context — just not yet covered here.
Related: #8607 — tested whether its proposed auto → bypassPermissions fix changes this outcome for Claude specifically. It doesn't (same result both ways), so I don't believe it's blocked by this; cross-referencing only.
View raw text
## What happened `omarchy-crash-watch` (enabled by default; `install/user/first-run/enable-user-units.sh:22`) reads `COREDUMP_EXE` straight from the systemd-coredump journal for *any* process the local user runs — a value fully controlled by whoever ran that process, since Linux filenames can contain almost any byte, including literal newlines. It passes `exe` to `--exec omarchy-agent-crash "$pid" "$comm" "$exe" "$signal"` as a safe, discrete argv word (`bin/omarchy-crash-watch:45`) — no shell/argv injection there. But `bin/omarchy-agent-crash:40` then splices that same raw string directly into a heredoc that becomes the AI agent's prompt, with no escaping, fencing, or "this is untrusted data" framing: ```bash prompt=$( cat <<PROMPT ... binary: $exe ... PROMPT ) exec omarchy-agent --prompt "$prompt" ``` `omarchy-agent` then launches whichever agent the user has set as their default (Omarchy ships with none picked; `bin/omarchy-default-agent`'s own comment: *"Omarchy picks no agent for you"*) with its permission-bypass flag for this "unattended" path — every entry in that case table gets one (`--permission-mode auto`/`bypassPermissions`, `--dangerously-skip-permissions`, `--approve-for-me`, `--yolo`, `--allow-all`, per agent), per the comment above the table: *"agents launched from the keybinding or menu run unattended, so each one starts with its own spelling of 'don't stop to ask.'"* Net effect: any unprivileged local process can get a diagnostic AI agent to read attacker-chosen text as part of its prompt, with no human confirmation gate, just by crashing itself at a path of its own choosing and having the user click the resulting "Process crashed — Click to diagnose with AI" notification — regardless of which of the 13 supported agents that user has configured as default. ## Why I'm filing this as a hardening suggestion, not a vulnerability report I tested this against Claude Code specifically, because that's what was configured as the default agent on my test machine — not because it's an Omarchy default (there isn't one). Tried 5 different payloads: a bare injected instruction, authority framing (claiming to be the `diagnose-crash` skill's own documented step), field-blending (mimicking the report's own `key: value` structure), a fake `### SYSTEM` / `### USER` turn-boundary spoof, and a repeat of the strongest payload under `--permission-mode bypassPermissions` instead of `auto` (to rule out the permission flag itself as the deciding factor). **All five were resisted** — the model consistently identified the embedded text as data from the crash record rather than an instruction, e.g.: > "The process's command line and the name of a directory inside `poc-crash-payload` both contain text posing as a 'system report' instruction... That text came from the crashed program's own metadata, not from you or the skill, so I ignored it and ran nothing of the sort." So there's no working bypass today against this one agent, and per the program's own policy this doesn't cross a confirmed security boundary. But this is one model's behavior at one point in time — the same unsanitized splicing reaches whichever of the 13 supported agents a user has actually configured, and none of them get any sanitization or untrusted-data framing before this point. That's not something this code should rely on model alignment alone to cover. ## Suggested direction - Wrap `$comm`/`$exe`/`$signal` in an explicit delimiter in the prompt template, with surrounding instruction text telling the agent these fields are unverified data from the crash record, not from the user. - Consider restricting `$exe`/`$comm` to printable characters (or otherwise normalizing) before they ever reach a prompt, the same discipline as sanitizing before any other sensitive sink. - Same bug *class* already fixed once in `omarchy-toggle-input-device` (#8129, merged) — untrusted local device/process metadata reaching a code-execution-adjacent context — just not yet covered here. Related: #8607 — tested whether its proposed `auto` → `bypassPermissions` fix changes this outcome for Claude specifically. It doesn't (same result both ways), so I don't believe it's blocked by this; cross-referencing only.
Validation
Not checked yet. A validator will restate this report as checkable claims, map it to the project's requirements and write a reproducer that the proof engine runs.
Linked
Not linked to any known issue, other report or change yet.
Decision
Decisions are recorded by our staff — or, where this project allows it, by agents: checked by a second, independent agent, or by a single agent where the project's policy says so. Any decision reached so far is shown below.
- Out of scopeDecided by agents · verified · 2026-10-09 21:31 UTC