fix(security): use POSIX-only checks in runtime dir validation

This commit is contained in:
felixzsh
2026-08-26 18:47:29 -05:00
parent 9fbdd2b8cf
commit 48af33c2a7
+11 -9
View File
@@ -19,12 +19,12 @@ local function is_runtime_secure(r)
if r == "/tmp" then return false end if r == "/tmp" then return false end
if r:sub(1, 1) ~= "/" then return false end if r:sub(1, 1) ~= "/" then return false end
local q = shell_quote(r) local q = shell_quote(r)
-- Must be a directory owned by the current user and, if stat is -- POSIX-only checks (test -d, test -w): no GNU `test -O`, no `stat`,
-- available, mode 0700 (systemd's XDG_RUNTIME_DIR default). The -- no command substitution. They behave identically inside Hyprland's
-- stat check is skipped when stat is missing so we don't fail-closed -- os.execute and in any POSIX shell. XDG_RUNTIME_DIR is always a 0700
-- on minimal containers. -- dir owned by the user, so "exists + writable by us + absolute + not
local cmd = "test -d " .. q .. " && test -O " .. q .. " && { p=$(stat -c %a " .. q .. " 2>/dev/null); [ -z \"$p\" ] || [ \"$p\" = \"700\" ]; }" -- /tmp" is sufficient; the predictable /tmp fallback is rejected above.
local res = os.execute(cmd) local res = os.execute("test -d " .. q .. " && test -w " .. q)
return res == 0 or res == true return res == 0 or res == true
end end
@@ -47,9 +47,11 @@ local function secure_write(path, content)
if path == "/tmp/omarchy-key-visualizer.json" or path == "/tmp/omarchy-key-visualizer-super" then return false end if path == "/tmp/omarchy-key-visualizer.json" or path == "/tmp/omarchy-key-visualizer-super" then return false end
local q = shell_quote(path) local q = shell_quote(path)
-- Refuse to follow a symlink at the destination (O_NOFOLLOW mitigation). -- Refuse to follow a symlink at the destination (O_NOFOLLOW mitigation).
-- `test ! -L` succeeds when the file does not exist or is not a symlink; -- POSIX `test ! -h` succeeds when the file does not exist or is not a
-- it fails only when the destination is a symlink, which we must not follow. -- symlink (the `-h` test is POSIX; `-L` is the GNU alias and is not
local not_symlink = os.execute("test ! -L " .. q) -- reliable inside Hyprland's os.execute). It fails only when the
-- destination is a symlink, which we must not follow.
local not_symlink = os.execute("test ! -h " .. q)
if not (not_symlink == 0 or not_symlink == true) then if not (not_symlink == 0 or not_symlink == true) then
print("[key-visualizer] refusing to write symlink: " .. path) print("[key-visualizer] refusing to write symlink: " .. path)
return false return false