fix(security): fail-closed XDG_RUNTIME_DIR and harden state file writes

Remove predictable /tmp/omarchy-key-visualizer.json fallback that exposed
captured key combinations and allowed symlink redirection when
XDG_RUNTIME_DIR is absent. Introduce fail-closed behaviour: disable
capture if XDG_RUNTIME_DIR is missing, equals /tmp, is not absolute,
not owned or not 0700.

Add secure_write with O_NOFOLLOW mitigation (test ! -L), atomic
tmp+rename and chmod 600 to prevent world-readable leaks and TOCTOU
races. Apply same guard to QML statePath/superPath and Panel nudge
writes (umask 077, chmod 600, symlink check).

Fixes security review at 790b07c0c9:
key-visualizer.lua:15-17,141-145
This commit is contained in:
felixzsh
2026-08-24 14:00:12 -05:00
parent 790b07c0c9
commit 89cd794708
3 changed files with 78 additions and 19 deletions
+6 -2
View File
@@ -105,7 +105,9 @@ Item {
readonly property string statePath: {
var runtime = Quickshell.env("XDG_RUNTIME_DIR")
return (runtime && runtime.length > 0 ? runtime : "/tmp") + "/omarchy-key-visualizer.json"
if (!runtime || runtime.length === 0) return ""
if (runtime === "/tmp") return ""
return runtime + "/omarchy-key-visualizer.json"
}
// Super-held flag written by the Lua capture hook. While Super is down the
@@ -114,7 +116,9 @@ Item {
property bool superHeld: false
readonly property string superPath: {
var runtime = Quickshell.env("XDG_RUNTIME_DIR")
return (runtime && runtime.length > 0 ? runtime : "/tmp") + "/omarchy-key-visualizer-super"
if (!runtime || runtime.length === 0) return ""
if (runtime === "/tmp") return ""
return runtime + "/omarchy-key-visualizer-super"
}
// True while the cursor hovers the card with Super held: the moment when the
// compositor's SUPER+mouse move/resize binds are temporarily unbound so the