Fail the suite on privileged writes through unquoted heredocs
An installer that writes a root-owned file through a heredoc with an unquoted delimiter (<<EOF rather than <<'EOF') has the installing user's shell expand the body first, so a user-controlled value is baked in as a literal. Send that into /etc and root later reads or executes a path the unprivileged user picked: a udev rule carrying $HOME/.local/share/omarchy/bin/... resolves through a symlink that user owns, so replacing the symlink gets their code run as root. Add the static check. A heredoc is flagged when its delimiter is unquoted, its body contains an install-time expansion (escaped \$VAR does not count, since that is left for a root daemon to expand at runtime), and its output reaches /etc, /usr, /opt, /srv, /boot or /var/lib via sudo tee, sudo dd, a redirect, or an install/cp/mv of the generated scratch file. Destinations written as variables are resolved from the file's own assignments. Sites that genuinely need install-time expansion declare it inline: # omarchy:heredoc-expands paths=none -- $servers is a validated IP list paths= is machine-checked against the expansions the scanner finds to be path-shaped, so this cannot become a rubber stamp: adding a $HOME/... to an already-annotated heredoc makes the declaration false and trips the check again. Path expansions anchored under a root-owned prefix, as in "/etc/systemd/system/$unit", are correctly not path-shaped. Annotate the sites the scan reports, each of which expands a scalar: DNS addresses in omarchy-dns, a literal PAM line in omarchy-setup-security-fingerprint, kernel cmdline parameters and usernames in omarchy-upgrade-to-quattro. omarchy-provision-owner expanded a unit name that was already a constant, so its delimiter is now quoted and the name hardcoded; the generated unit file is byte-identical. omarchy-windows-vm declares paths=storage,shared, the only site that interpolates a user-chosen path. Fixtures prove non-vacuity in both directions: the write routes other than a pipe into sudo tee, the shapes that must stay quiet, udev rules and a shutdown unit taken verbatim from this repository's history, and the rubber-stamp case where a paths=none annotation on a baked $HOME path still fails.
This commit is contained in:
@@ -597,6 +597,9 @@ preserve_kernel_cmdline_root() {
|
||||
fi
|
||||
|
||||
log "Preserving the kernel cmdline root parameters in $default_conf"
|
||||
# omarchy:heredoc-expands paths=none -- ${boot_params[*]} is the kernel
|
||||
# cmdline fragment assembled above from findmnt output (root=, rootflags=),
|
||||
# not a filesystem path this script controls.
|
||||
as_root tee -a "$default_conf" >/dev/null <<EOF
|
||||
# Written by omarchy-upgrade-to-quattro. The += drop-ins in
|
||||
# /etc/limine-entry-tool.d/ stop limine-entry-tool from reading
|
||||
@@ -1395,6 +1398,9 @@ EOF
|
||||
autologin_user=$(as_root awk -F= '/^User=/ { print $2; exit }' /etc/sddm.conf.d/autologin.conf 2>/dev/null || true)
|
||||
fi
|
||||
[[ -n ${autologin_user:-} ]] || autologin_user="$target_user"
|
||||
# omarchy:heredoc-expands paths=none -- $autologin_user is a username, read
|
||||
# back from the root-owned drop-in or falling back to $target_user. Same
|
||||
# mechanism as the old getty override: a name expands, no path does.
|
||||
cat <<EOF | as_root tee /etc/sddm.conf.d/autologin.conf >/dev/null
|
||||
[Autologin]
|
||||
User=$autologin_user
|
||||
@@ -1405,6 +1411,8 @@ EOF
|
||||
fi
|
||||
|
||||
as_root install -d -m 0755 -o sddm -g sddm /var/lib/sddm 2>/dev/null || as_root install -d -m 0755 /var/lib/sddm
|
||||
# omarchy:heredoc-expands paths=none -- $target_user is a username, not a
|
||||
# path; SDDM's state file records who logged in last.
|
||||
cat <<EOF | as_root tee /var/lib/sddm/state.conf >/dev/null
|
||||
[Last]
|
||||
Session=omarchy.desktop
|
||||
|
||||
Reference in New Issue
Block a user