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.
Notification body is exposed in process command lines (/proc/*/cmdline) — privacy leak + EDR false positives
Reported by Piemme99 · filed Aug 25, 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
Notification content is exposed in process command lines (/proc/*/cmdline)
Summary
The notifications plugin persists every notification by spawningbash -c … -- <paths> <json> …, where <json> is the **serialized
notification** (summary + body) passed as a positional argument. The content
is therefore visible in the process's argv, i.e. in /proc/<pid>/cmdline,
to any local process for the lifetime of the (short) job.
This is not an injection bug — the code is careful and correct on that
front: the JSON travels as $3/$json and is never re-parsed by the shell
(the in-code comment at persistPopupFile says exactly this). Bodies with
quotes/backticks/$() cannot break the command. The issue is purely that
arbitrary notification text ends up in a process command line.
Impact
- Privacy / info-leak. Notification bodies can contain sensitive data
(2FA codes, private DMs, email previews). Any unprivileged local process
can read them from /proc/*/cmdline while the persist/history job runs.
- Host-based EDR false positives. Endpoint agents (CrowdStrike Falcon,
etc.) match IOC patterns against process command lines. When a notification
body happens to contain attack-looking text — e.g. someone pastes a
command-injection PoC into a chat app that raises a desktop notification —
the persist job's argv contains that string and the EDR fires a
detection on an otherwise-benign local action. In a corp-managed
environment this generates SOC alerts and triage work. It also creates a
self-sustaining loop: the alert gets discussed in the same chat thread →
new notification → new persist job carrying the discussion text → new
alert.
Reproduction
- On an Omarchy shell, send a desktop notification whose body contains any
shell-metacharacter text (a command-injection-looking snippet — the kind of
thing that naturally appears when someone pastes a PoC into Slack/Discord).
2. While the notification is being persisted, observe the spawned process via
pgrep -af 'bash -c', or inspect
~/.local/state/omarchy/notifications/history/*.json — the same body text
is what was passed on the command line.
3. The body string is present in the process argv (/proc/<pid>/cmdline).
Note: Do Not Disturb does not mitigate this. DND-silenced notifications
still go through writeHistoryFile, which uses the samebash -c … <json> … argv-passing. DND only suppresses the visible toast.
Affected code
shell/plugins/notifications/Service.qml
persistPopupFile— `command = ["bash","-c", …, "--", popupStateDir,
imagesDir, serializePopup(...), …]` (JSON as an arg)
- writeHistoryFile — same pattern (also on the DND-silenced path)
Image copy from/to paths are less of a concern (they're internal file paths),
but the serialized JSON is arbitrary sender-controlled content.
Suggested fix
Keep passing paths as positional args (they're safe and needed), but feed
the serialized JSON payload via stdin instead of argv, then read it in
the script. Sketch:
# script reads the payload from stdin, not from "$json"
mkdir -p "$1" "$2" || exit 0
dir="$1" name="$4"
shift 4
# … copyImagesScript over the remaining from/to pairs …
cat > "$dir/$name" # payload arrives on stdinwith the QML side writing serializePopup(...) to the process's stdin
(Quickshell Process stdinEnabled + write, or an equivalentUtil.execDetached variant that accepts stdin). This removes notification
content from /proc/*/cmdline entirely, fixing both the privacy leak and the
EDR false positives, while preserving the existing injection-safe behaviour.
Environment
- Omarchy 4 "Quattro", Quickshell-based shell
shell/plugins/notifications/Service.qml
View raw text
# Notification content is exposed in process command lines (`/proc/*/cmdline`) ## Summary The notifications plugin persists every notification by spawning `bash -c … -- <paths> <json> …`, where `<json>` is the **serialized notification** (summary + body) passed as a positional argument. The content is therefore visible in the process's `argv`, i.e. in `/proc/<pid>/cmdline`, to any local process for the lifetime of the (short) job. This is **not an injection bug** — the code is careful and correct on that front: the JSON travels as `$3`/`$json` and is never re-parsed by the shell (the in-code comment at `persistPopupFile` says exactly this). Bodies with quotes/backticks/`$()` cannot break the command. The issue is purely that arbitrary notification text ends up in a process command line. ## Impact 1. **Privacy / info-leak.** Notification bodies can contain sensitive data (2FA codes, private DMs, email previews). Any unprivileged local process can read them from `/proc/*/cmdline` while the persist/history job runs. 2. **Host-based EDR false positives.** Endpoint agents (CrowdStrike Falcon, etc.) match IOC patterns against process command lines. When a notification body happens to contain attack-looking text — e.g. someone pastes a command-injection PoC into a chat app that raises a desktop notification — the persist job's `argv` contains that string and the EDR fires a detection on an otherwise-benign local action. In a corp-managed environment this generates SOC alerts and triage work. It also creates a self-sustaining loop: the alert gets discussed in the same chat thread → new notification → new persist job carrying the discussion text → new alert. ## Reproduction 1. On an Omarchy shell, send a desktop notification whose body contains any shell-metacharacter text (a command-injection-looking snippet — the kind of thing that naturally appears when someone pastes a PoC into Slack/Discord). 2. While the notification is being persisted, observe the spawned process via `pgrep -af 'bash -c'`, or inspect `~/.local/state/omarchy/notifications/history/*.json` — the same body text is what was passed on the command line. 3. The body string is present in the process `argv` (`/proc/<pid>/cmdline`). Note: **Do Not Disturb does not mitigate this.** DND-silenced notifications still go through `writeHistoryFile`, which uses the same `bash -c … <json> …` argv-passing. DND only suppresses the visible toast. ## Affected code `shell/plugins/notifications/Service.qml` - `persistPopupFile` — `command = ["bash","-c", …, "--", popupStateDir, imagesDir, serializePopup(...), …]` (JSON as an arg) - `writeHistoryFile` — same pattern (also on the DND-silenced path) Image copy from/to paths are less of a concern (they're internal file paths), but the serialized JSON is arbitrary sender-controlled content. ## Suggested fix Keep passing **paths** as positional args (they're safe and needed), but feed the **serialized JSON payload via stdin** instead of `argv`, then read it in the script. Sketch: ```sh # script reads the payload from stdin, not from "$json" mkdir -p "$1" "$2" || exit 0 dir="$1" name="$4" shift 4 # … copyImagesScript over the remaining from/to pairs … cat > "$dir/$name" # payload arrives on stdin ``` with the QML side writing `serializePopup(...)` to the process's stdin (Quickshell `Process` `stdinEnabled` + `write`, or an equivalent `Util.execDetached` variant that accepts stdin). This removes notification content from `/proc/*/cmdline` entirely, fixing both the privacy leak and the EDR false positives, while preserving the existing injection-safe behaviour. ## Environment - Omarchy 4 "Quattro", Quickshell-based shell - `shell/plugins/notifications/Service.qml`
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 08:44 UTC