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.
Web-planted wallpapers are decoded by multiple C parsers in the user session; a legal 254KB file costs 566MB RAM / 4.75s per decode
Reported by MohamedBurrito · filed Oct 3, 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?
- 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?
- Our staff decide whether the report is valid. Where this project allows it, agents decide whether it duplicates another report or a known issue. So far: partly in the audited area (menu) — decided by agents · verified; matches no known issue — decided by agents · verified. The outcome (valid, duplicate, not a bug, …) is not decided yet. See the decision.
Original report
System details
VMware virtual machine (SVGA adapter), omarchy 4.0.4-1, kernel 7.2.5-3-omarchy, ImageMagick 7.1.2-31 Q16-HDRI, libvips 8.18, Qt6 imageformat plugins (jp2/tiff/mng present)
What's wrong?
Summary
A silent Aether web link (aether://apply?wallpaper=<https-url>&silent=true — documented in docs/web-handler.md: "Aether does not add another confirmation") lets any web page plant an attacker-chosen image as the system's standing wallpaper, after validation by Aether's memory-safe Go decoders (internal/wallpaper/download.go: ValidateImageFile).
That same planted file is later parsed by multiple independent C parsers in the user session, none of which are covered by that validation boundary:
- ImageMagick —
bin/omarchy-bar-text-colorrunsmagick "$background_path[0]" ...on the planted wallpaper on every transparent-bar resample. - Qt/Quickshell image stack — the shell paints the desktop background from
~/.local/state/omarchy/current/background(Background.qml polls the symlink). - libvips —
omarchy-theme-switcher --preload(auto-run after everyomarchy theme setvia post_theme_commands) symlinks theme previews, andomarchy-menu-images: generate_thumbnail()runsvipsthumbnailon each — with content-sniffed loader dispatch (svgload/rsvg, jp2kload/OpenJPEG, gifload/libnsgif compiled in, registered asuntrusted). - ffmpegthumbnailer (ffmpeg) — for
preview.mp4/mkv/avitheme previews.
Aether's validation (Go) covers one format gate — the bytes are then re-parsed by parsers with a far wider attack surface, governed by neither that validation nor the ImageMagick policy.
Steps To Reproduce
Tested live on omarchy 4.0.4-1 (VM), 2026-10-03.
Step 1 — silent plant (no dialog anywhere):
aether --handle-url "aether://apply?wallpaper=<hosted-image-url>&silent=true"Observed: wallpaper replaced, md5 of the current/background target identical to the hosted file, desktop repainted with the attacker image (screenshot captured: uniform 243,1,244).
Step 2 — ImageMagick consumes the planted file:
/usr/bin/time -v omarchy-bar-text-color top 30 "#ffffff" "#000000"Observed: Maximum resident set size: 566424 kbytes, Elapsed: 4.75s — for a 254KB delivered file.
Step 3 — libvips content-sniffing proof (loader chosen by content, not extension):
printf '<?xml version="1.0"?>\n<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64"><rect width="64" height="64" fill="#ff00ff"/></svg>\n' > /tmp/fake.jpg
vipsthumbnail /tmp/fake.jpg -o /tmp/sniff-out.pngObserved: SVG content loaded via rsvg from a .jpg filename — output PNG produced.
Step 4 — picker thumbnail consumption of a planted theme preview:
strace -f -e trace=openat omarchy-menu-images --lazy-thumbnails ~/.config/omarchy/themes/<test-theme>Observed: multiple openat() calls on the planted preview.png and a derived thumbnail written to ~/.cache/omarchy/image-selector/.
Impact
- Today (no memory bug required): any web page can silently replace the desktop appearance (wallpaper, or a full theme via
as_omarchy_theme) — a consent violation by itself — and plant a file that is then repeatedly decoded by ImageMagick, Qt and libvips with the measured 2200× resource amplification on every theme change (noticeable on 8GB machines). - n-day window: when any memory-safety CVE lands in the four ImageMagick-allowed coders (GIF/JPEG/PNG/WEBP) or in any libvips/Qt loader reached by content sniffing, the proven delivery chain turns it into a one-click user-session RCE for everyone with a planted wallpaper — the file is already on disk and the decode repeats without any new interaction.
- Scope: user session (no root), no exfiltration channel demonstrated.
Suggested fixes (any combination)
- Parse once, share the result: have Aether export the palette sample it already computes in Go, so
omarchy-bar-text-colornever re-decodes the planted file. - At minimum: resource-cap the handoff (
MAGICK_MEMORY_LIMIT/MAGICK_TIME_LIMIT, libvips caps) for any file that originated from a web import. - Document this boundary next to the existing provenance note in
docs/theming.md.
Environment
omarchy 4.0.4-1, ImageMagick 7.1.2-31 Q16-HDRI (policy: read only GIF/JPEG/PNG/WEBP, delegates none), libvips 8.18 (svgload/jp2kload/gifload present), Qt6 imageformat plugins incl. libqjp2/libqtiff/libqmng. Verified live 2026-10-03.
View raw text
### System details VMware virtual machine (SVGA adapter), omarchy 4.0.4-1, kernel 7.2.5-3-omarchy, ImageMagick 7.1.2-31 Q16-HDRI, libvips 8.18, Qt6 imageformat plugins (jp2/tiff/mng present) ### What's wrong? ## Summary A silent Aether web link (`aether://apply?wallpaper=<https-url>&silent=true` — documented in docs/web-handler.md: *"Aether does not add another confirmation"*) lets any web page plant an attacker-chosen image as the system's standing wallpaper, after validation by Aether's **memory-safe Go decoders** (`internal/wallpaper/download.go: ValidateImageFile`). That same planted file is later parsed by **multiple independent C parsers** in the user session, none of which are covered by that validation boundary: 1. **ImageMagick** — `bin/omarchy-bar-text-color` runs `magick "$background_path[0]" ...` on the planted wallpaper on every transparent-bar resample. 2. **Qt/Quickshell image stack** — the shell paints the desktop background from `~/.local/state/omarchy/current/background` (Background.qml polls the symlink). 3. **libvips** — `omarchy-theme-switcher --preload` (auto-run after every `omarchy theme set` via post_theme_commands) symlinks theme previews, and `omarchy-menu-images: generate_thumbnail()` runs `vipsthumbnail` on each — with **content-sniffed loader dispatch** (svgload/rsvg, jp2kload/OpenJPEG, gifload/libnsgif compiled in, registered as `untrusted`). 4. **ffmpegthumbnailer (ffmpeg)** — for `preview.mp4/mkv/avi` theme previews. Aether's validation (Go) covers one format gate — the bytes are then re-parsed by parsers with a far wider attack surface, governed by neither that validation nor the ImageMagick policy. ## Steps To Reproduce Tested live on omarchy 4.0.4-1 (VM), 2026-10-03. **Step 1 — silent plant (no dialog anywhere):** ```bash aether --handle-url "aether://apply?wallpaper=<hosted-image-url>&silent=true" ``` Observed: wallpaper replaced, md5 of the `current/background` target identical to the hosted file, desktop repainted with the attacker image (screenshot captured: uniform 243,1,244). **Step 2 — ImageMagick consumes the planted file:** ```bash /usr/bin/time -v omarchy-bar-text-color top 30 "#ffffff" "#000000" ``` Observed: `Maximum resident set size: 566424 kbytes`, `Elapsed: 4.75s` — for a 254KB delivered file. **Step 3 — libvips content-sniffing proof (loader chosen by content, not extension):** ```bash printf '<?xml version="1.0"?>\n<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64"><rect width="64" height="64" fill="#ff00ff"/></svg>\n' > /tmp/fake.jpg vipsthumbnail /tmp/fake.jpg -o /tmp/sniff-out.png ``` Observed: SVG content loaded via rsvg from a `.jpg` filename — output PNG produced. **Step 4 — picker thumbnail consumption of a planted theme preview:** ```bash strace -f -e trace=openat omarchy-menu-images --lazy-thumbnails ~/.config/omarchy/themes/<test-theme> ``` Observed: multiple `openat()` calls on the planted `preview.png` and a derived thumbnail written to `~/.cache/omarchy/image-selector/`. ## Impact - **Today (no memory bug required):** any web page can silently replace the desktop appearance (wallpaper, or a full theme via `as_omarchy_theme`) — a consent violation by itself — and plant a file that is then repeatedly decoded by ImageMagick, Qt and libvips with the measured 2200× resource amplification on every theme change (noticeable on 8GB machines). - **n-day window:** when any memory-safety CVE lands in the four ImageMagick-allowed coders (GIF/JPEG/PNG/WEBP) or in any libvips/Qt loader reached by content sniffing, the proven delivery chain turns it into a one-click user-session RCE for everyone with a planted wallpaper — the file is already on disk and the decode repeats without any new interaction. - Scope: user session (no root), no exfiltration channel demonstrated. ## Suggested fixes (any combination) 1. **Parse once, share the result**: have Aether export the palette sample it already computes in Go, so `omarchy-bar-text-color` never re-decodes the planted file. 2. At minimum: resource-cap the handoff (`MAGICK_MEMORY_LIMIT`/`MAGICK_TIME_LIMIT`, libvips caps) for any file that originated from a web import. 3. Document this boundary next to the existing provenance note in `docs/theming.md`. ## Environment omarchy 4.0.4-1, ImageMagick 7.1.2-31 Q16-HDRI (policy: read only GIF/JPEG/PNG/WEBP, delegates none), libvips 8.18 (svgload/jp2kload/gifload present), Qt6 imageformat plugins incl. libqjp2/libqtiff/libqmng. Verified live 2026-10-03.
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
- Related PRpr/14281· confirmed
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.
- Decided by agents · verified: partly in the audited area (menu)
- Decided by agents · verified: matches no known issue
The outcome (valid, duplicate, not a bug, …) is not decided yet.