diff --git a/tests/setup/readme-contract b/tests/setup/readme-contract index 9630956..cf0b643 100755 --- a/tests/setup/readme-contract +++ b/tests/setup/readme-contract @@ -49,25 +49,53 @@ require_bootstrap_doc() { grep -qiE "$pattern" <<<"$bootstrap_doc" || note "$explanation" } -require_bootstrap_doc 'verified target key' \ - 'the root bootstrap docs do not require a verified target key' -require_bootstrap_doc '700.*600|600.*700' \ - 'the root bootstrap docs do not state the exact SSH ownership and modes' -require_bootstrap_doc 'hardening (is )?unavailable.*(without|until).*key' \ - 'the root bootstrap docs do not say hardening is unavailable without a key' +assert_bootstrap_probe_rejected() { + local name="$1" pattern="$2" weakened_doc="$3" + if grep -qiE "$pattern" <<<"$weakened_doc"; then + note "the $name assertion accepts its weakened documentation probe" + fi +} + +target_key_requirement='target user owns[^.]*\.ssh[^.]*mode[^.]*([^0-9]|^)0700([^0-9]|$)[^.]*authorized_keys[^.]*mode[^.]*([^0-9]|^)0600([^0-9]|$)' +hardening_continues_requirement='hardening[[:space:]]+is[[:space:]]+unavailable[^.]*without[^.]*verified[^.]*key[^.]*install[[:space:]]+continues[^.]*without[[:space:]]+(it|SSH[[:space:]]+hardening)' +atomic_dropin_requirement='atomic[[:space:]]+same-directory[[:space:]]+drop-in' +rollback_requirement='validation[^.]*reload[^.]*fail[^.]*(restor|rollback)[^.]*previous[[:space:]]+drop-in' + +require_bootstrap_doc "$target_key_requirement" \ + 'the root bootstrap docs do not require target-user ownership with exact 0700/0600 SSH modes' +require_bootstrap_doc "$hardening_continues_requirement" \ + 'the root bootstrap docs do not say bootstrap continues without unavailable SSH hardening' require_bootstrap_doc 'sshd -t' \ 'the root bootstrap docs do not name sshd -t validation' -require_bootstrap_doc 'atomic.*same.directory|same.directory.*atomic' \ +require_bootstrap_doc "$atomic_dropin_requirement" \ 'the root bootstrap docs do not describe the atomic same-directory drop-in' require_bootstrap_doc 'detected (SSH )?unit.*reload|reload.*detected (SSH )?unit' \ 'the root bootstrap docs do not describe reloading the detected SSH unit' -require_bootstrap_doc 'restores? (the )?previous drop-in.*(validation|reload)|(validation|reload).*restores? (the )?previous drop-in' \ +require_bootstrap_doc "$rollback_requirement" \ 'the root bootstrap docs do not promise rollback on validation or reload failure' require_bootstrap_doc 'fixture contracts.*(these|this) (path|branch)|fixture contracts.*test' \ 'the root bootstrap docs do not limit proof to fixture contracts' require_bootstrap_doc 'no real daemon reload.*panama test --safe|panama test --safe.*no real daemon reload' \ 'the root bootstrap docs imply a live daemon reload under the safe suite' +# These prove the semantic assertions above reject the precise omissions they +# guard against. They mutate only the scoped documentation string; README.md +# itself remains the real input that must satisfy the contract. +assert_bootstrap_probe_rejected 'exact SSH modes' "$target_key_requirement" \ + "${bootstrap_doc//0700/700}" +assert_bootstrap_probe_rejected 'target-user ownership' "$target_key_requirement" \ + "${bootstrap_doc//target user owns/someone owns}" +assert_bootstrap_probe_rejected 'hardening availability' "$hardening_continues_requirement" \ + "${bootstrap_doc//unavailable/available}" +assert_bootstrap_probe_rejected 'hardening continuation' "$hardening_continues_requirement" \ + "${bootstrap_doc//continues/stops}" +assert_bootstrap_probe_rejected 'atomic drop-in' "$atomic_dropin_requirement" \ + "${bootstrap_doc//atomic /}" +assert_bootstrap_probe_rejected 'rollback after failure' "$rollback_requirement" \ + "${bootstrap_doc//restores /keeps }" +assert_bootstrap_probe_rejected 'rollback trigger' "$rollback_requirement" \ + "${bootstrap_doc//fails/works}" + if grep -qiE 'merely writes? (the )?(SSH )?(drop-in|file)|reload failure.*ignored|ignores? .*reload failure' <<<"$bootstrap_doc"; then note 'the root bootstrap docs weaken the transaction by treating the write or reload failure as harmless' fi