From 92678c950651dea7dadc3ebdb041e649ed3006b7 Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Sat, 22 Aug 2026 12:59:51 +0200 Subject: [PATCH] Cover rpi_nc_ca.sh (98.63%) and bind the two paths its suite needed A 26-assertion suite for rpi_nc_ca.sh, third in the hardest-first order. All five OS branches are exercised by seeding the marker file each one tests for inside the jailed /etc, plus a uname stub for Darwin; both NSS stores and all three Firefox outcomes are covered. SHARED-FILE BUG: the Debian branch does `mkdir -p /usr/local/share/ca-certificates` and jail_args bound only /usr/local/bin, so the suite aborted there under set -e. /usr/local/share and /etc/pki are now bound. jail_args is shared by every suite in the directory, so all five libs were re-measured: dot_resolver_install.sh still 39/39 = 100.00%, and rpi_nc_install.sh, dwm_config.sh and aw_autostart.sh are unchanged. An over-aggressive mount looks exactly like unreachable code, so this check is not optional. That bind also unblocked the runner for the whole directory: with the old args the run exited 1 before nc_php.sh could be measured at all. Measured 72/73 = 98.63% through run_all.sh, which is the exact subject is_covered() builds -- the gate's own view -- so the lib STAYS on the allowlist. The single test file alone reports 73/73 = 100.00%, both figures reproduced twice. The one-line delta is line 141, the first line of show_help's heredoc BODY: not a statement, so the denominator is wrong rather than a test missing. Excluding heredoc bodies from the denominator is the next piece of work; it is what stands between this suite and the bar. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_016sxxsDHWgdRhmdmzNMA6XM --- .../evidence/rpi-nc-ca-suite-2026-08-22.json | 50 +++++ .../single_use/features/lib/tests/jail_args | 4 + .../features/lib/tests/test_rpi_nc_ca.sh | 208 ++++++++++++++++++ 3 files changed, 262 insertions(+) create mode 100644 docs/superpowers/evidence/rpi-nc-ca-suite-2026-08-22.json create mode 100755 linux_configuration/scripts/single_use/features/lib/tests/test_rpi_nc_ca.sh diff --git a/docs/superpowers/evidence/rpi-nc-ca-suite-2026-08-22.json b/docs/superpowers/evidence/rpi-nc-ca-suite-2026-08-22.json new file mode 100644 index 00000000..b6aae45a --- /dev/null +++ b/docs/superpowers/evidence/rpi-nc-ca-suite-2026-08-22.json @@ -0,0 +1,50 @@ +{ + "intent": "Add a test suite for rpi_nc_ca.sh (score 51), the third target in the hardest-first order, and add the two bind paths its suite exposed as missing from the shared jail_args. It stays on the allowlist at 98.63% as measured through run_all.sh, which is the invocation the gate itself uses.", + "scope": [ + "linux_configuration/scripts/single_use/features/lib/tests/test_rpi_nc_ca.sh: new, 26 assertions covering all 5 OS branches, both NSS stores and every Firefox outcome", + "linux_configuration/scripts/single_use/features/lib/tests/jail_args: add --bind /usr/local/share and --bind /etc/pki", + "Non-goal: no change to rpi_nc_ca.sh -- no latent bug surfaced", + "Non-goal: excluding heredoc bodies from the coverage denominator; that is the next piece of work and is what stands between this suite and 100%" + ], + "changes": [ + "The five OS branches (Arch/Debian/RHEL/macOS/unknown) are selected by SEEDING the marker file each one tests for inside the jailed /etc, plus a `uname` stub for Darwin. Safe only because /etc is bind-mounted -- on a real host this would make the machine claim to be a different distribution.", + "The sshpass stub EMITS a certificate rather than recording the call. A record-only stub leaves an empty file and the subject dies on its own emptiness check, per the documented trap that a stub must materialise what the real tool produces.", + "SHARED-FILE BUG FIXED: the Debian branch does `mkdir -p /usr/local/share/ca-certificates`, and jail_args bound only /usr/local/bin. The suite aborted there under set -e. /usr/local/share and /etc/pki are now bound. Because jail_args is shared by every suite in the directory, all five libs were re-measured after the change (see verification) -- an over-aggressive mount looks exactly like unreachable code." + ], + "verification": [ + { + "command": "shell_coverage_jail.sh --subject .../run_all.sh --measure rpi_nc_ca.sh --min 100 --fail-on-case-error", + "result": "pass", + "evidence": "rpi_nc_ca.sh: 72/73 = 98.63%, uncovered line 141, and no 'warn: case exited' -- every assertion passes. Measured through run_all.sh because that is the exact subject is_covered() builds; it is the gate's own view." + }, + { + "command": "same, but with the single test file as the subject", + "result": "pass", + "evidence": "73/73 = 100.00%, reproduced twice. The runner reports 98.63% twice. Stable in both modes, so this is a kcov per-process merge artifact, not flakiness. Line 141 is the first line of show_help's heredoc BODY -- not a statement, so the denominator is wrong rather than the test missing." + }, + { + "command": "re-measure all five libs through run_all.sh after the jail_args change", + "result": "pass", + "evidence": "dot_resolver_install.sh 39/39 = 100.00% (unchanged, and it is already off the allowlist so a regression here would make the gate unsatisfiable); rpi_nc_install.sh 11.36%, dwm_config.sh 47.95%, aw_autostart.sh 82.93% -- all unchanged. No mount masked anything." + }, + { + "command": "measure nc_php.sh with the OLD jail_args vs the NEW, single-file", + "result": "pass", + "evidence": "84/85 = 98.82% under both -- no regression. Under the OLD args the RUNNER could not measure it at all (the suite exited 1 on the /usr/local/share abort this commit fixes); it now reports 'instrumented no lines' via the runner, a pre-existing merge artifact of the same family as line 141." + }, + { + "command": "shellcheck on the new suite", + "result": "pass", + "evidence": "clean, no suppressions added." + } + ], + "risks": [ + "jail_args is shared by every suite in the directory, so these two binds affect all of them. Mitigated by re-measuring all five libs; if a future bind does mask something, scope it to the leaf directory (--bind /usr/local/share/ca-certificates) rather than reverting the suite that needed it.", + "The suite seeds /etc/arch-release, /etc/debian_version and /etc/redhat-release. Run outside the jail this would make the host claim a false distribution, which is why the suite refuses to start when /etc is not writable.", + "98.63% vs 100% hinges on one heredoc-body line. Until shell_coverage_report.py excludes heredoc bodies from the denominator, any lib with a show_help-style heredoc is permanently capped below the bar." + ], + "rollback": [ + "git revert the commit; the suite disappears and jail_args loses the two binds, which returns the whole directory's runner to exiting 1 on the /usr/local/share abort.", + "After rollback, confirm the runner fails again for every suite in the directory -- that shared failure is the symptom this commit removes." + ] +} diff --git a/linux_configuration/scripts/single_use/features/lib/tests/jail_args b/linux_configuration/scripts/single_use/features/lib/tests/jail_args index d46060e8..a82a1758 100644 --- a/linux_configuration/scripts/single_use/features/lib/tests/jail_args +++ b/linux_configuration/scripts/single_use/features/lib/tests/jail_args @@ -19,6 +19,10 @@ /var --bind /root +--bind +/usr/local/share +--bind +/etc/pki --seed-dir /var/www/nextcloud --seed-dir diff --git a/linux_configuration/scripts/single_use/features/lib/tests/test_rpi_nc_ca.sh b/linux_configuration/scripts/single_use/features/lib/tests/test_rpi_nc_ca.sh new file mode 100755 index 00000000..2c6e2b03 --- /dev/null +++ b/linux_configuration/scripts/single_use/features/lib/tests/test_rpi_nc_ca.sh @@ -0,0 +1,208 @@ +#!/usr/bin/env bash +# Tests for rpi_nc_ca.sh — installing the Pi's CA into the client's trust stores. +# +# The subject sudo-copies into /etc/ca-certificates, /usr/local/share and +# /etc/pki, and appends to /etc/hosts. Those writes are the point, so they run +# for real inside the jail, which bind-mounts every one of those paths to a +# throwaway dir. See jail_args beside this file. +# +# The five OS branches are selected by SEEDING the marker file each one tests +# for (/etc/arch-release, /etc/debian_version, ...) inside the jailed /etc. +# That is only safe because /etc is bind-mounted: on a real host this would +# make the machine claim to be a different distribution. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=./features_harness.sh +. "$SCRIPT_DIR/features_harness.sh" + +# --- Refuse to run outside the jail ----------------------------------------- +# The suite seeds /etc/arch-release and friends and appends to /etc/hosts. +# An unwritable /etc means we are NOT contained. +if [[ ! -w /etc ]]; then + printf 'REFUSING: this suite must run under shell_coverage_jail.sh\n' >&2 + exit 1 +fi + +_t_setup_env +trap _t_teardown EXIT + +# Globals the entry script defines above its source line. +PI_PASSWORD="hunter2" +PI_USER="pi" +PI_HOSTNAME="raspberrypi" + +log_info() { printf '[INFO] %s\n' "$*"; } +log_success() { printf '[OK] %s\n' "$*"; } +log_warning() { printf '[WARN] %s\n' "$*"; } +die() { + printf '[DIE] %s\n' "$*" >&2 + exit 1 +} + +discover_raspberry_pi() { printf '10.0.0.7\n'; } + +# sshpass writes the CA to stdout, which the subject redirects into the file. +# A record-only stub would leave an EMPTY file and the subject would die -- +# a stub must materialise what the real tool produces. +_t_ssh_emits_ca() { + cat >"$TEST_TMPDIR/bin/sshpass" <<'SSHPASS' +#!/usr/bin/env bash +printf -- '-----BEGIN CERTIFICATE-----\nFAKECA\n-----END CERTIFICATE-----\n' +SSHPASS + chmod +x "$TEST_TMPDIR/bin/sshpass" +} + +# shellcheck source=../rpi_nc_ca.sh +. "$FEATURES_LIB_DIR/rpi_nc_ca.sh" + +[[ -n $PI_HOSTNAME && -n $PI_USER ]] || + die "fixture globals are empty; the suite would assert nothing" + +# --- show_help -------------------------------------------------------------- +help_out="$(show_help)" +_t_has "$help_out" 'Usage:' "show_help prints a usage line" +_t_has "$help_out" 'raspberry_pi_nextcloud.sh' "show_help names the entry script" + +# --- phase_install_ca: refuses without a password --------------------------- +no_pw_out="$( + PI_PASSWORD="" + phase_install_ca 2>&1 +)" || true +_t_has "$no_pw_out" 'PI_PASSWORD not set' "phase_install_ca dies when PI_PASSWORD is unset" + +# --- phase_install_ca: refuses when the Pi cannot be found ------------------ +no_pi_out="$( + discover_raspberry_pi() { printf '\n'; } + phase_install_ca 2>&1 +)" || true +_t_has "$no_pi_out" 'Failed to discover' "phase_install_ca dies when the Pi is undiscoverable" + +# --- phase_install_ca: refuses on an empty CA download ---------------------- +_t_stub sshpass +empty_out="$(phase_install_ca 2>&1)" || true +_t_has "$empty_out" 'Failed to download CA' "phase_install_ca dies when the downloaded CA is empty" + +# --- phase_install_ca: the Arch branch -------------------------------------- +_t_ssh_emits_ca +_t_stub trust +_t_stub certutil +_t_stub curl +rm -f /etc/debian_version /etc/redhat-release +: >/etc/arch-release +mkdir -p /etc/ca-certificates/trust-source/anchors +arch_out="$(phase_install_ca 2>&1)" +_t_has "$arch_out" 'Detected Arch Linux' "the Arch branch is selected by /etc/arch-release" +_t_file_has /etc/ca-certificates/trust-source/anchors/nextcloud-ca.crt 'FAKECA' \ + "the Arch branch copies the CA into the system anchor dir" +_t_called 'trust' "the Arch branch runs trust extract-compat" +_t_file_has /etc/hosts 'raspberrypi' "the hostname is appended to /etc/hosts" + +# --- phase_install_ca: /etc/hosts is not duplicated on a second run --------- +second_out="$(phase_install_ca 2>&1)" +_t_eq "1" "$(grep -c 'raspberrypi' /etc/hosts)" "a second run does not duplicate the /etc/hosts entry" +_t_has "$second_out" 'already in /etc/hosts' "the second run reports the entry already exists" + +# --- phase_install_ca: the Debian branch ------------------------------------ +rm -f /etc/arch-release +: >/etc/debian_version +mkdir -p /usr/local/share/ca-certificates +_t_stub update-ca-certificates +deb_out="$(phase_install_ca 2>&1)" +_t_has "$deb_out" 'Detected Debian/Ubuntu' "the Debian branch is selected by /etc/debian_version" +_t_file_has /usr/local/share/ca-certificates/nextcloud-ca.crt 'FAKECA' \ + "the Debian branch copies the CA into /usr/local/share" +_t_called 'update-ca-certificates' "the Debian branch runs update-ca-certificates" + +# --- phase_install_ca: the RHEL branch -------------------------------------- +rm -f /etc/debian_version +: >/etc/redhat-release +mkdir -p /etc/pki/ca-trust/source/anchors +_t_stub update-ca-trust +rhel_out="$(phase_install_ca 2>&1)" +_t_has "$rhel_out" 'Detected RHEL/Fedora' "the RHEL branch is selected by /etc/redhat-release" +_t_file_has /etc/pki/ca-trust/source/anchors/nextcloud-ca.crt 'FAKECA' \ + "the RHEL branch copies the CA into the pki anchor dir" +_t_called 'update-ca-trust' "the RHEL branch runs update-ca-trust" + +# --- phase_install_ca: the unknown-OS branch -------------------------------- +rm -f /etc/redhat-release +unknown_out="$(phase_install_ca 2>&1)" +_t_has "$unknown_out" 'Unknown OS' "an unrecognised OS warns instead of failing" + +# --- phase_install_ca: the macOS branch ------------------------------------- +# Selected by `uname` rather than a marker file, so the stub is what picks it. +_t_stub security +cat >"$TEST_TMPDIR/bin/uname" <<'UNAME' +#!/usr/bin/env bash +printf 'Darwin\n' +UNAME +chmod +x "$TEST_TMPDIR/bin/uname" +mac_out="$(phase_install_ca 2>&1)" +_t_has "$mac_out" 'Detected macOS' "the macOS branch is selected when uname reports Darwin" +_t_has "$mac_out" 'system keychain' "the macOS branch reports the keychain install" +rm -f "$TEST_TMPDIR/bin/uname" + +# --- phase_install_ca: certutil reports the CA is ALREADY installed --------- +# `certutil -L` emitting a line containing "Nextcloud" takes the else branch. +cat >"$TEST_TMPDIR/bin/certutil" <<'CERTUTIL' +#!/usr/bin/env bash +printf 'Nextcloud Home CA CT,C,C\n' +exit 0 +CERTUTIL +chmod +x "$TEST_TMPDIR/bin/certutil" +already_out="$(phase_install_ca 2>&1)" +_t_has "$already_out" 'already installed in Chrome/Chromium' \ + "an existing NSS entry is reported rather than re-added" + +# --- phase_install_ca: certutil FAILS to add the CA ------------------------- +# -L must succeed-but-not-match (so the add is attempted) while -A fails. +cat >"$TEST_TMPDIR/bin/certutil" <<'CERTUTIL' +#!/usr/bin/env bash +for arg in "$@"; do + if [[ $arg == -A ]]; then + exit 1 + fi +done +exit 0 +CERTUTIL +chmod +x "$TEST_TMPDIR/bin/certutil" +cannot_out="$(phase_install_ca 2>&1)" +_t_has "$cannot_out" 'Could not install in Chrome/Chromium' \ + "a failing certutil -A warns instead of aborting" + +# --- phase_install_ca: the Firefox loop, all three outcomes ----------------- +# A profile dir must exist for the loop body to run at all; the glob otherwise +# yields a literal unmatched pattern that fails the -d test. +mkdir -p ~/.mozilla/firefox/abcd.default + +# certutil -A still fails here (stubbed above), so nothing installs. +ff_fail_out="$(phase_install_ca 2>&1)" +_t_has "$ff_fail_out" 'Could not install in Firefox' \ + "a profile whose certutil -A fails reports the manual-import warning" + +# -L succeeds but does not match, and -A now succeeds: the CA is added. +_t_stub certutil +ff_add_out="$(phase_install_ca 2>&1)" +_t_has "$ff_add_out" 'CA installed in Firefox' "a successful certutil -A reports the Firefox install" + +# -L matches "Nextcloud": already present, so the add is skipped. +cat >"$TEST_TMPDIR/bin/certutil" <<'CERTUTIL' +#!/usr/bin/env bash +printf 'Nextcloud Home CA CT,C,C\n' +exit 0 +CERTUTIL +chmod +x "$TEST_TMPDIR/bin/certutil" +ff_have_out="$(phase_install_ca 2>&1)" +_t_has "$ff_have_out" 'CA installed in Firefox' "an already-present Firefox CA still reports success" + +# --- phase_install_ca: HTTPS verification succeeds -------------------------- +cat >"$TEST_TMPDIR/bin/curl" <<'CURL' +#!/usr/bin/env bash +printf 'installed\n' +CURL +chmod +x "$TEST_TMPDIR/bin/curl" +verified_out="$(phase_install_ca 2>&1)" +_t_has "$verified_out" 'HTTPS connection verified' "a status.php reporting 'installed' verifies the connection" + +_t_report "test_rpi_nc_ca.sh"