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:
@@ -0,0 +1,12 @@
|
||||
if omarchy-battery-present; then
|
||||
# omarchy:heredoc-expands paths=none -- only interpolates the omarchy bin path
|
||||
cat <<EOF | sudo tee "/etc/udev/rules.d/99-power-profile.rules"
|
||||
SUBSYSTEM=="power_supply", ATTR{type}=="Mains", RUN+="/usr/bin/systemd-run --no-block --collect --unit=omarchy-power-profile --property=After=power-profiles-daemon.service $HOME/.local/share/omarchy/bin/omarchy-powerprofiles-set"
|
||||
SUBSYSTEM=="power_supply", ATTR{type}=="USB", RUN+="/usr/bin/systemd-run --no-block --collect --unit=omarchy-power-profile --property=After=power-profiles-daemon.service $HOME/.local/share/omarchy/bin/omarchy-powerprofiles-set"
|
||||
EOF
|
||||
|
||||
sudo systemctl enable power-profiles-daemon
|
||||
|
||||
sudo udevadm control --reload 2>/dev/null
|
||||
sudo udevadm trigger --subsystem-match=power_supply 2>/dev/null
|
||||
fi
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Two hops. The scan resolves omarchy_bin into helper, so the value it ends up
|
||||
# judging still carries an unresolved $HOME rather than a literal path.
|
||||
omarchy_bin="$HOME/.local/share/omarchy/bin"
|
||||
helper="$omarchy_bin/omarchy-agent"
|
||||
|
||||
# omarchy:heredoc-expands paths=none -- helper names the agent, no path is baked in
|
||||
cat <<EOF | sudo tee /etc/udev/rules.d/99-omarchy-agent.rules >/dev/null
|
||||
SUBSYSTEM=="power_supply", RUN+="$helper"
|
||||
EOF
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
# One hop between the expansion and the home path it carries. The token in the
|
||||
# heredoc has no slash and the value never resolves to a literal path, so a scan
|
||||
# that rescues unresolved values would exempt a unit baking the user's home into
|
||||
# /etc/systemd/system.
|
||||
helper="$HOME/.local/share/omarchy/bin/omarchy-agent"
|
||||
|
||||
# omarchy:heredoc-expands paths=none -- helper is just the agent command name
|
||||
cat <<EOF | sudo tee /etc/systemd/system/omarchy-agent.service >/dev/null
|
||||
[Service]
|
||||
ExecStart=$helper
|
||||
EOF
|
||||
@@ -0,0 +1,5 @@
|
||||
if true; then
|
||||
cat <<-EOF | sudo tee /etc/omarchy/indented.conf >/dev/null
|
||||
helper=$HOME/.local/share/omarchy/bin/omarchy-agent
|
||||
EOF
|
||||
fi
|
||||
@@ -0,0 +1,8 @@
|
||||
tmp=$(mktemp)
|
||||
|
||||
cat >"$tmp" <<EOF
|
||||
#!/bin/bash
|
||||
exec "$HOME/.local/share/omarchy/bin/omarchy-agent" "$@"
|
||||
EOF
|
||||
|
||||
sudo install -m 0755 "$tmp" /usr/local/bin/omarchy-agent-shim
|
||||
@@ -0,0 +1,4 @@
|
||||
# A plain redirect into /etc, no sudo: the command re-execs itself as root.
|
||||
cat >/etc/omarchy/agent.conf <<EOF
|
||||
helper=$HOME/.local/share/omarchy/bin/omarchy-agent
|
||||
EOF
|
||||
@@ -0,0 +1,3 @@
|
||||
sudo dd status=none of=/etc/omarchy/boot.conf <<EOF
|
||||
cmdline=$boot_params
|
||||
EOF
|
||||
@@ -0,0 +1,6 @@
|
||||
DROP_IN=/etc/systemd/system/omarchy-agent.service.d/override.conf
|
||||
|
||||
cat <<EOF | sudo tee "$DROP_IN" >/dev/null
|
||||
[Service]
|
||||
ExecStart=$OMARCHY_PATH/bin/omarchy-agent
|
||||
EOF
|
||||
@@ -0,0 +1,11 @@
|
||||
servers="1.1.1.1 9.9.9.9"
|
||||
|
||||
# omarchy:heredoc-expands paths=none -- $servers is a validated IP list, not a path
|
||||
cat <<EOF | sudo tee /etc/omarchy/dns.conf >/dev/null
|
||||
servers=$servers
|
||||
EOF
|
||||
|
||||
# omarchy:heredoc-expands paths=storage -- validated by valid_path and symlink-checked before use
|
||||
cat <<EOF | sudo tee /var/lib/omarchy/mounts.conf >/dev/null
|
||||
source=$storage:/storage
|
||||
EOF
|
||||
@@ -0,0 +1,7 @@
|
||||
resolved="RemoteCommand none"
|
||||
|
||||
grep -qvi '^remotecommand none$' <<<"$resolved" || true
|
||||
|
||||
sudo tee /etc/omarchy/plain.conf >/dev/null <<'EOF'
|
||||
ok=1
|
||||
EOF
|
||||
@@ -0,0 +1,3 @@
|
||||
cat <<EOF | sudo tee /etc/udev/rules.d/99-omarchy.rules >/dev/null
|
||||
SUBSYSTEM=="power_supply", ATTR{type}=="Mains", RUN+="/usr/bin/omarchy-powerprofiles-set"
|
||||
EOF
|
||||
@@ -0,0 +1,7 @@
|
||||
cat <<'EOF' | sudo tee /etc/udev/rules.d/99-omarchy.rules >/dev/null
|
||||
SUBSYSTEM=="power_supply", RUN+="/usr/bin/omarchy-powerprofiles-set $HOME"
|
||||
EOF
|
||||
|
||||
cat <<"XML" | sudo tee /etc/omarchy/agent.xml >/dev/null
|
||||
<config path="$HOME/.local/share/omarchy" />
|
||||
XML
|
||||
@@ -0,0 +1,11 @@
|
||||
unit=omarchy-agent.service
|
||||
|
||||
# "/etc/systemd/system/$unit" is a path, but one anchored where root already
|
||||
# owns everything, so paths=none is the truthful declaration -- the check must
|
||||
# not demand paths=unit here.
|
||||
# omarchy:heredoc-expands paths=none -- $unit is a unit name interpolated only into absolute /etc paths
|
||||
cat >"/etc/systemd/system/$unit" <<UNIT
|
||||
[Service]
|
||||
ExecStart=/usr/bin/omarchy-agent
|
||||
ExecStopPost=/usr/bin/rm -f /etc/systemd/system/$unit
|
||||
UNIT
|
||||
@@ -0,0 +1,6 @@
|
||||
# \$TERM stays literal so systemd expands it at runtime; nothing is baked in.
|
||||
cat <<EOF | sudo tee /etc/systemd/system/getty@tty1.service.d/override.conf >/dev/null
|
||||
[Service]
|
||||
Environment=TERM=\$TERM
|
||||
ExecStart=-/usr/bin/agetty --noclear %I \$TERM
|
||||
EOF
|
||||
@@ -0,0 +1,9 @@
|
||||
mkdir -p ~/.config/omarchy
|
||||
|
||||
cat >~/.config/omarchy/agent.conf <<EOF
|
||||
helper=$HOME/.local/share/omarchy/bin/omarchy-agent
|
||||
EOF
|
||||
|
||||
cat >"$HOME/.local/bin/omarchy-shim" <<EOF
|
||||
exec "$OMARCHY_PATH/bin/omarchy-agent" "$@"
|
||||
EOF
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
# The name is introduced with a packaged root-owned value and then reassigned to
|
||||
# one under the user's home. Judging the first assignment would read this as the
|
||||
# path it no longer holds.
|
||||
target=/usr/share/omarchy/bin/agent
|
||||
target="$HOME/.local/share/omarchy/bin/agent"
|
||||
|
||||
# omarchy:heredoc-expands paths=none -- target is the packaged agent path under /usr
|
||||
cat <<EOF | sudo tee /etc/systemd/system/omarchy-agent.service >/dev/null
|
||||
[Service]
|
||||
ExecStart=$target
|
||||
EOF
|
||||
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Install Plymouth package
|
||||
echo "Installing Plymouth..."
|
||||
yay -S --noconfirm --needed plymouth
|
||||
|
||||
# Skip if plymouth already exists for some reason
|
||||
if ! grep -q "plymouth" /etc/mkinitcpio.conf; then
|
||||
# Backup original mkinitcpio.conf just in case
|
||||
backup_timestamp=$(date +"%Y%m%d%H%M%S")
|
||||
sudo cp /etc/mkinitcpio.conf "/etc/mkinitcpio.conf.bak.${backup_timestamp}"
|
||||
|
||||
# Add plymouth to HOOKS array. Should be added:
|
||||
# - After 'base' and 'udev' (or 'systemd' if using systemd hook)
|
||||
# - Before 'encrypt' or 'sd-encrypt' if present
|
||||
|
||||
# Use sed to add plymouth in-place
|
||||
if grep -q "systemd" /etc/mkinitcpio.conf; then
|
||||
# Add after systemd
|
||||
sudo sed -i '/^HOOKS=/s/systemd/systemd plymouth/' /etc/mkinitcpio.conf
|
||||
elif grep -q "udev" /etc/mkinitcpio.conf; then
|
||||
# Add after udev
|
||||
sudo sed -i '/^HOOKS=/s/udev/udev plymouth/' /etc/mkinitcpio.conf
|
||||
else
|
||||
# Fallback: add after base
|
||||
sudo sed -i '/^HOOKS=/s/base/base plymouth/' /etc/mkinitcpio.conf
|
||||
fi
|
||||
fi
|
||||
|
||||
# Regenerate initramfs
|
||||
sudo mkinitcpio -P
|
||||
|
||||
# Add kernel parameters for Plymouth (systemd-boot only)
|
||||
if [ -d "/boot/loader/entries" ]; then
|
||||
echo "Detected systemd-boot"
|
||||
|
||||
for entry in /boot/loader/entries/*.conf; do
|
||||
if [ -f "$entry" ]; then
|
||||
# Skip fallback entries
|
||||
if [[ "$(basename "$entry")" == *"fallback"* ]]; then
|
||||
echo "Skipped: $(basename "$entry") (fallback entry)"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Skip if splash it already present for some reason
|
||||
if ! grep -q "splash" "$entry"; then
|
||||
sudo sed -i '/^options/ s/$/ splash quiet/' "$entry"
|
||||
else
|
||||
echo "Skipped: $(basename "$entry") (splash already present)"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo ""
|
||||
echo "systemd-boot not detected. Please manually add these kernel parameters:"
|
||||
echo " - splash (to see the graphical splash screen)"
|
||||
echo " - quiet (for silent boot)"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Touch .plymouth-sync-needed to signal rebuild on shutdown / reboot
|
||||
touch "$HOME/.config/omarchy/.plymouth-sync-needed"
|
||||
|
||||
# Create the systemd service
|
||||
sudo tee /etc/systemd/system/omarchy-plymouth-shutdown.service >/dev/null <<EOF
|
||||
[Unit]
|
||||
Description=Sync Plymouth Theme on Shutdown
|
||||
DefaultDependencies=yes
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=/bin/true
|
||||
ExecStop=$HOME/.local/share/omarchy/bin/omarchy-plymouth-shutdown-sync
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# Reload systemd and enable the service
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable omarchy-plymouth-shutdown.service
|
||||
sudo systemctl start omarchy-plymouth-shutdown.service
|
||||
@@ -0,0 +1,11 @@
|
||||
if omarchy-battery-present; then
|
||||
cat <<EOF | sudo tee "/etc/udev/rules.d/99-power-profile.rules"
|
||||
SUBSYSTEM=="power_supply", ATTR{type}=="Mains", RUN+="/usr/bin/systemd-run --no-block --collect --unit=omarchy-power-profile --property=After=power-profiles-daemon.service $HOME/.local/share/omarchy/bin/omarchy-powerprofiles-set"
|
||||
SUBSYSTEM=="power_supply", ATTR{type}=="USB", RUN+="/usr/bin/systemd-run --no-block --collect --unit=omarchy-power-profile --property=After=power-profiles-daemon.service $HOME/.local/share/omarchy/bin/omarchy-powerprofiles-set"
|
||||
EOF
|
||||
|
||||
sudo systemctl enable power-profiles-daemon
|
||||
|
||||
sudo udevadm control --reload 2>/dev/null
|
||||
sudo udevadm trigger --subsystem-match=power_supply 2>/dev/null
|
||||
fi
|
||||
@@ -0,0 +1,9 @@
|
||||
if omarchy-battery-present; then
|
||||
cat <<EOF | sudo tee "/etc/udev/rules.d/99-wifi-powersave.rules"
|
||||
SUBSYSTEM=="power_supply", ATTR{type}=="Mains", ATTR{online}=="0", RUN+="$HOME/.local/share/omarchy/bin/omarchy-wifi-powersave on"
|
||||
SUBSYSTEM=="power_supply", ATTR{type}=="Mains", ATTR{online}=="1", RUN+="$HOME/.local/share/omarchy/bin/omarchy-wifi-powersave off"
|
||||
EOF
|
||||
|
||||
sudo udevadm control --reload
|
||||
sudo udevadm trigger --subsystem-match=power_supply
|
||||
fi
|
||||
Reference in New Issue
Block a user