From 26f3f823ac17940a1b0153619f6140f45d856e33 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Mon, 27 Apr 2026 13:00:26 -0500 Subject: refactor: verify GRUB_CMDLINE_LINUX seds via prepend_grub_cmdline_linux helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audited the ~10 silent sed -i sites in the installer against the verification-after pattern that landed for sshd_config last session. Triaged each by failure mode. The two GRUB_CMDLINE_LINUX seds in lib/btrfs.sh have a real silent-failure risk. If /etc/default/grub is missing or malformed and the sed pattern doesn't match, nothing happens. The kernel boots without cryptdevice=. The system can't unlock LUKS at boot. Added prepend_grub_cmdline_linux to lib/common.sh. Same shape as enable_sshd_root_login (sed, then grep, then error if the line wasn't modified). Replaced the two inline seds with helper calls. The HOOKS= seds in installer/archangel and lib/btrfs.sh (six total) don't need verification. A missing HOOKS= line makes mkinitcpio -P fail loudly downstream, so silent-replace failure can't reach a booted system. Added a one-line audit-rationale comment at each of the three locations so the next reader doesn't re-litigate the decision. The FILES= sed at lib/btrfs.sh:213 already self-heals via a sed-then-grep-then-append pattern, so no behavior change there. Filed a separate follow-up to lift that pattern into a named helper for clarity. Bats: 142 → 146. Four new tests in test_common.bats cover normal (empty cmdline, existing cmdline preserved, other lines preserved) and error (missing GRUB_CMDLINE_LINUX line). Lint clean. --- tests/unit/test_common.bats | 64 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'tests/unit') diff --git a/tests/unit/test_common.bats b/tests/unit/test_common.bats index 9d267ab..48efd15 100644 --- a/tests/unit/test_common.bats +++ b/tests/unit/test_common.bats @@ -405,3 +405,67 @@ Boot0001* ZFSBootMenu" ! grep -q 'PermitRootLogin' "$f" rm -f "$f" } + +############################# +# prepend_grub_cmdline_linux +############################# +# prepend_grub_cmdline_linux prepends a string to GRUB_CMDLINE_LINUX +# in /etc/default/grub. Errors loudly if the line isn't present, since +# silently doing nothing would leave the kernel without the parameter +# (e.g. cryptdevice= for LUKS — a missing prefix means the system +# can't unlock the root partition at boot). + +@test "prepend_grub_cmdline_linux prepends to an empty GRUB_CMDLINE_LINUX" { + local f + f=$(mktemp) + printf '%s\n' 'GRUB_CMDLINE_LINUX=""' > "$f" + + prepend_grub_cmdline_linux "cryptdevice=UUID=abc123:root " "$f" + + grep -qF 'GRUB_CMDLINE_LINUX="cryptdevice=UUID=abc123:root "' "$f" + rm -f "$f" +} + +@test "prepend_grub_cmdline_linux preserves text already inside GRUB_CMDLINE_LINUX" { + local f + f=$(mktemp) + printf '%s\n' 'GRUB_CMDLINE_LINUX="quiet splash"' > "$f" + + prepend_grub_cmdline_linux "cryptdevice=UUID=abc:root " "$f" + + grep -qF 'GRUB_CMDLINE_LINUX="cryptdevice=UUID=abc:root quiet splash"' "$f" + rm -f "$f" +} + +@test "prepend_grub_cmdline_linux preserves other lines in /etc/default/grub" { + local f + f=$(mktemp) + printf '%s\n' \ + 'GRUB_DEFAULT=0' \ + 'GRUB_TIMEOUT=5' \ + 'GRUB_CMDLINE_LINUX=""' \ + 'GRUB_DISABLE_RECOVERY=true' > "$f" + + prepend_grub_cmdline_linux "cryptdevice=UUID=abc:root " "$f" + + grep -qF 'GRUB_DEFAULT=0' "$f" + grep -qF 'GRUB_TIMEOUT=5' "$f" + grep -qF 'GRUB_CMDLINE_LINUX="cryptdevice=UUID=abc:root "' "$f" + grep -qF 'GRUB_DISABLE_RECOVERY=true' "$f" + rm -f "$f" +} + +@test "prepend_grub_cmdline_linux errors when GRUB_CMDLINE_LINUX line is absent" { + local f + f=$(mktemp) + printf '%s\n' \ + 'GRUB_DEFAULT=0' \ + 'GRUB_TIMEOUT=5' > "$f" + + error() { echo "ERROR: $*" >&2; return 1; } + run prepend_grub_cmdline_linux "cryptdevice=UUID=abc:root " "$f" + [ "$status" -ne 0 ] + [[ "$output" == *"GRUB_CMDLINE_LINUX"* ]] + ! grep -q 'cryptdevice' "$f" + rm -f "$f" +} -- cgit v1.2.3