Repository navigation
Conversation
script(1) opens its transcript without O_NOFOLLOW, so the fixed name /tmp/omarchy-update.log let any other local account pre-plant a symlink there and have the whole update session written through to a file of their choosing, as the user running the update. The default umask also left the log world-readable, exposing the session transcript to every account on the machine. Stage it under the per-user runtime directory (0700), pre-created 0600, falling back to the state directory where Omarchy keeps everything else of its own when there is no session runtime dir. omarchy-update-analyze-logs resolves the same file, and a standalone run with no transcript behind it is now a quiet no-op rather than a grep error. Same class as the diagnostics and screen-recording staging fixes (omacom#8367, omacom#8372), in files neither covers.
There was a problem hiding this comment.
Pull request overview
Moves update transcripts from world-writable /tmp into private per-user storage.
Changes:
- Resolves runtime/state transcript paths consistently.
- Pre-creates transcripts with mode
0600. - Adds regression tests and updates documentation.
Tip
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
Reviewed changes
Copilot reviewed 2 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
bin/omarchy-update |
Securely stages the transcript. |
bin/omarchy-update-analyze-logs |
Reads the relocated transcript. |
test/shell.d/update-transcript-staging-test.sh |
Tests paths, permissions, and analysis. |
docs/update-process.md |
Documents transcript storage. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| - A failure should leave enough output in the update transcript | ||
| (`$XDG_RUNTIME_DIR/omarchy-update.log`, or | ||
| `${XDG_STATE_HOME:-~/.local/state}/omarchy/omarchy-update.log` without a | ||
| session) and the terminal transcript to debug. |
There was a problem hiding this comment.
The list item is now one line and names both paths.
| ```text | ||
| omarchy-update | ||
| ├─ ensure transcript logging through script(1) → /tmp/omarchy-update.log | ||
| ├─ ensure transcript logging through script(1) → $XDG_RUNTIME_DIR/omarchy-update.log |
There was a problem hiding this comment.
The Path 1 flow now shows the state-directory fallback when XDG_RUNTIME_DIR is unset, matching the scripts.
The high-level flow only named $XDG_RUNTIME_DIR, and the failure-output list item was hard-wrapped. Both now match the runtime/state resolution the scripts already use. Co-authored-by: Cursor <cursoragent@cursor.com>
|
hey, this is already covered in #12109 (update transcript out of shared /tmp). mind closing this as a duplicate? |
quattro moved the transcript re-exec below the sudo cleanup setup and passes OMARCHY_UPDATE_USER_PATH through it, and added the stay-awake row to the update docs. Keep both alongside the private transcript path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
omarchy-update now starts under a privileged Bash, checks OMARCHY_PATH against its own location, and re-execs through script(1) from a fixed PATH, so the stub on the caller's PATH was never the one it ran. Invoke the boundary copy the other update tests use and put the stub in its bin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Reviewed and run on a disposable Omarchy worker. The problem is real and this fixes it; it now merges cleanly against What the bug does on a stock install. On the worker, a second local account that plants Pushed to your branch
Tests, on the worker: Related: this also fixes #11204 (analyze-logs printing a raw grep error when there is no transcript). #12957 and #12109 fix the same bug in other ways: #12957 falls back to Second opinion: none. Codex Medium's daily review budget was spent before this item reached it, so only Claude Opus 5.5 checked this, including the choice between the three pull requests. Because of that it has not been marked Waiting on: a review by the second model, then the maintainer. Nothing is needed from you. |
|
Closing after maintainer review. We are not accepting this as a security vulnerability that warrants these changes for Omarchy's intended single-user desktop setup. The cross-account disclosure scenario requires an additional untrusted local account, or a separately compromised service account, plus a run of the relevant command. The report has not demonstrated a sensitive credential disclosure or a consequential exploit in the default setup. Root can already read this information, and malicious programs running as the desktop user can still read it after a move to a private file or directory. Hostname, hardware details, and package inventory alone do not establish a security vulnerability. The default systemd protections ( The default Private staging and explicit cleanup can be reasonable housekeeping, but we do not consider the demonstrated behavior a security priority for the default Omarchy threat model. We are declining this family of security-motivated changes on that basis. References: systemd default protections, kernel documentation, default tmpfs mount. The absence of O_NOFOLLOW in script(1) does not bypass the kernel protected_symlinks policy. A same-user symlink demonstration does not establish the claimed cross-user overwrite under default settings. The missing-log behavior in #11204 remains a separate functional issue. Omabot on behalf of DHH |
Summary
omarchy-updatelogs the whole update session throughscript(1)to the fixed name/tmp/omarchy-update.log, andomarchy-update-analyze-logsreads it back after the update.script(1)opens its transcript withoutO_NOFOLLOW— verified against util-linux on this machine: a symlink planted at the path is followed, and the target is truncated and written as the user running the update. With/tmpworld-writable, any other local account can pre-plant that symlink and have the update session written through to a file of their choosing. Independently, the default umask leaves the transcript world-readable in/tmp, exposing the session to every account on the machine.Same class as #8367 and #8372 (predictable
/tmpstaging that feeds a follow-on consumer), in files neither of those fixes covers.Change
${XDG_RUNTIME_DIR}/omarchy-update.log(per-user runtime dir, 0700), falling back to${XDG_STATE_HOME:-~/.local/state}/omarchy/omarchy-update.logwhen there is no session runtime dir — the same resolution [Security] Stage diagnostics where only their owner can read them #8370 uses for the diagnostics log.0600(install -m 600), soscript(1)truncates an already-private file instead of creating one at the umask.omarchy-update-analyze-logsresolves the identical path in both branches (it runs in the same environment, so the resolution agrees), and a standalone run with no transcript behind it is now a quiet no-op instead of grep errors.docs/update-process.mdupdated where the path was named.Scope
Local multi-user attack surface (symlink clobber as the updating user + world-readable transcript). Single-user machines are unaffected by the clobber; the readability half applies anywhere
/tmpis shared. No network or same-user primitive introduced or changed.Test
test/shell.d/update-transcript-staging-test.sh— stubsscriptso the wrapper runs without an update, and covers: the runtime-dir path, the file existing at 0600 beforescriptopens it, the no-runtime-dir fallback matching what analyze-logs reads, analyze-logs flagging (and clearing) the initramfs failure in both branches, the standalone no-op, and that neither script names the/tmppath anymore.Mutation-checked: restoring the
/tmppath inbin/omarchy-updatefails the suite; so does restoring it inomarchy-update-analyze-logs.battlenet-test,bin-style-test,update-sequence-test,update-lock-test,update-status-test, andinstalled-service-teststill pass.