From a2d8ec6a1d12f5941f2e74a523105e1aa83dc1fb Mon Sep 17 00:00:00 2001 From: 420tombombadil Date: Fri, 14 Aug 2026 20:09:47 -0600 Subject: [PATCH 01/19] feat: add sovereign ledger redundancy verification --- bin/fm-sovereign-ledger-redundancy.sh | 120 +++++++++++++++ .../sovereign-ledger-redundancy/CONTRACT.md | 3 + .../fm-sovereign-ledger.sh | 41 +++++ tests/fm-sovereign-ledger-redundancy.test.sh | 145 ++++++++++++++++++ 4 files changed, 309 insertions(+) create mode 100755 bin/fm-sovereign-ledger-redundancy.sh create mode 100644 tests/fixtures/sovereign-ledger-redundancy/CONTRACT.md create mode 100755 tests/fixtures/sovereign-ledger-redundancy/fm-sovereign-ledger.sh create mode 100755 tests/fm-sovereign-ledger-redundancy.test.sh diff --git a/bin/fm-sovereign-ledger-redundancy.sh b/bin/fm-sovereign-ledger-redundancy.sh new file mode 100755 index 0000000000..14e08b61e5 --- /dev/null +++ b/bin/fm-sovereign-ledger-redundancy.sh @@ -0,0 +1,120 @@ +#!/usr/bin/env bash +# fm-sovereign-ledger-redundancy.sh - make and verify one local replica of a sovereign ledger bundle. +# +# Usage: +# fm-sovereign-ledger-redundancy.sh snapshot +# fm-sovereign-ledger-redundancy.sh verify +# +# A ledger bundle is ledger.tsv, CONTRACT.md, and fm-sovereign-ledger.sh. +# snapshot verifies the primary through its own ledger verifier before it copies it. +# It creates a missing replica or accepts an already-identical one. +# It refuses a divergent existing replica instead of repairing or overwriting it. +# verify runs each bundle's ledger verifier, compares every protected file byte-for-byte, and rejects a hard-linked ledger.tsv. +# This script never admits, rewrites, repairs, or attributes a ruling. +set -euo pipefail + +die() { + printf 'REFUSED: %s\n' "$*" >&2 + exit 1 +} + +usage() { + sed -n '2,10{s/^# \{0,1\}//;p;}' "$0" >&2 + exit 2 +} + +canonical_dir() { + [ -d "$1" ] || die "ledger directory does not exist: $1" + ( + cd "$1" + pwd -P + ) +} + +create_replica_dir() { + local dir=$1 + [ -e "$dir" ] && [ ! -d "$dir" ] && die "replica path is not a directory: $dir" + mkdir -p "$dir" + canonical_dir "$dir" +} + +require_bundle() { + local dir=$1 name + for name in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh; do + [ -f "$dir/$name" ] || die "ledger bundle is incomplete: missing $dir/$name" + done + [ -x "$dir/fm-sovereign-ledger.sh" ] || die "ledger verifier is not executable: $dir/fm-sovereign-ledger.sh" +} + +verify_ledger() { + local dir=$1 + LEDGER_DIR="$dir" "$dir/fm-sovereign-ledger.sh" verify >/dev/null || die "ledger verifier rejected $dir/ledger.tsv" +} + +compare_file() { + local primary=$1 replica=$2 name=$3 + cmp -s "$primary/$name" "$replica/$name" || die "replica differs from primary: $name" +} + +file_identity() { + stat -f '%d:%i' "$1" 2>/dev/null || stat -c '%d:%i' "$1" +} + +require_independent_ledger_file() { + local primary=$1 replica=$2 + [ "$(file_identity "$primary/ledger.tsv")" != "$(file_identity "$replica/ledger.tsv")" ] || die "replica ledger.tsv is hard-linked to the primary, not a second copy" +} + +verify_bundle() { + local primary=$1 replica=$2 name + require_bundle "$primary" + require_bundle "$replica" + verify_ledger "$primary" + verify_ledger "$replica" + for name in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh; do + compare_file "$primary" "$replica" "$name" + done + require_independent_ledger_file "$primary" "$replica" +} + +copy_bundle() { + local primary=$1 replica=$2 name tmp + umask 077 + for name in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh; do + tmp="$replica/.${name}.tmp.$$" + cp "$primary/$name" "$tmp" + mv "$tmp" "$replica/$name" + done +} + +cmd_snapshot() { + local primary replica + primary=$(canonical_dir "${1:?primary ledger directory}") + replica=$(create_replica_dir "${2:?replica ledger directory}") + [ "$primary" != "$replica" ] || die "primary and replica directories must differ" + require_bundle "$primary" + verify_ledger "$primary" + if [ -e "$replica/ledger.tsv" ] || [ -e "$replica/CONTRACT.md" ] || [ -e "$replica/fm-sovereign-ledger.sh" ]; then + verify_bundle "$primary" "$replica" + printf 'SNAPSHOT PASS (replica already identical: %s)\n' "$replica" + return + fi + copy_bundle "$primary" "$replica" + verify_bundle "$primary" "$replica" + printf 'SNAPSHOT PASS (replica created: %s)\n' "$replica" +} + +cmd_verify() { + local primary replica + primary=$(canonical_dir "${1:?primary ledger directory}") + replica=$(canonical_dir "${2:?replica ledger directory}") + [ "$primary" != "$replica" ] || die "primary and replica directories must differ" + verify_bundle "$primary" "$replica" + printf 'REDUNDANCY VERIFY PASS (bundle identical and independently stored)\n' +} + +case "${1:-}" in + snapshot) shift; cmd_snapshot "$@" ;; + verify) shift; cmd_verify "$@" ;; + *) usage ;; +esac diff --git a/tests/fixtures/sovereign-ledger-redundancy/CONTRACT.md b/tests/fixtures/sovereign-ledger-redundancy/CONTRACT.md new file mode 100644 index 0000000000..fbc3bdd756 --- /dev/null +++ b/tests/fixtures/sovereign-ledger-redundancy/CONTRACT.md @@ -0,0 +1,3 @@ +# Fixture sovereign ledger contract + +This fixture models the public `verify`, `recheck`, and `text` commands consumed by the redundancy mechanism tests. diff --git a/tests/fixtures/sovereign-ledger-redundancy/fm-sovereign-ledger.sh b/tests/fixtures/sovereign-ledger-redundancy/fm-sovereign-ledger.sh new file mode 100755 index 0000000000..b7917fa84d --- /dev/null +++ b/tests/fixtures/sovereign-ledger-redundancy/fm-sovereign-ledger.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# Fixture ledger verifier for the redundancy mechanism's public dependency boundary. +set -euo pipefail + +ledger_dir=${LEDGER_DIR:?LEDGER_DIR is required} +ledger=$ledger_dir/ledger.tsv + +verify() { + [ -f "$ledger" ] || exit 1 + [ "$(awk 'END { print NR }' "$ledger")" = 4 ] || exit 1 + awk -F '\t' ' + NF != 3 { exit 1 } + $1 !~ /^ruling-[1-4]$/ { exit 1 } + seen[$1]++ != 0 { exit 1 } + END { exit length(seen) == 4 ? 0 : 1 } + ' "$ledger" +} + +recheck() { + local key source count=0 bad=0 + while IFS=$'\t' read -r key source _; do + count=$((count + 1)) + if [ ! -f "$source" ]; then + printf 'SOURCE_GONE %s (%s)\n' "$key" "$source" + bad=$((bad + 1)) + fi + done < "$ledger" + printf 'recheck: %s entries, %s divergent\n' "$count" "$bad" + [ "$bad" -eq 0 ] +} + +text() { + awk -F '\t' -v key="$1" '$1 == key { print $3; found = 1 } END { exit found ? 0 : 1 }' "$ledger" | base64 -d +} + +case "${1:-}" in + verify) verify ;; + recheck) recheck ;; + text) text "${2:?ruling key}" ;; + *) exit 2 ;; +esac diff --git a/tests/fm-sovereign-ledger-redundancy.test.sh b/tests/fm-sovereign-ledger-redundancy.test.sh new file mode 100755 index 0000000000..d4f4d14adb --- /dev/null +++ b/tests/fm-sovereign-ledger-redundancy.test.sh @@ -0,0 +1,145 @@ +#!/usr/bin/env bash +# Verify a local replica is complete, byte-identical, independently stored, and useful after source loss. +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +TOOL=${TOOL:-"$ROOT/bin/fm-sovereign-ledger-redundancy.sh"} +FIXTURE="$ROOT/tests/fixtures/sovereign-ledger-redundancy" +TMP="$(mktemp -d)" +trap 'rm -rf "$TMP"' EXIT +pass=0 +fail=0 + +ok() { + printf ' PASS %s\n' "$1" + pass=$((pass + 1)) +} + +bad() { + printf ' FAIL %s\n' "$1" + fail=$((fail + 1)) +} + +check_ok() { + local description=$1 + shift + if "$@" >/dev/null 2>&1; then + ok "$description" + else + bad "$description" + fi +} + +check_fails() { + local description=$1 + shift + if "$@" >/dev/null 2>&1; then + bad "$description (it SUCCEEDED - guard absent)" + else + ok "$description" + fi +} + +check_fails_with() { + local description=$1 expected=$2 output status + shift 2 + set +e + output=$("$@" 2>&1) + status=$? + set -e + if [ "$status" -ne 0 ] && printf '%s\n' "$output" | grep -Fq "$expected"; then + ok "$description" + else + bad "$description (missing expected refusal: $expected)" + fi +} + +make_bundle() { + local dir=$1 source_dir=$2 key source text + mkdir -p "$dir" "$source_dir" + cp "$FIXTURE/CONTRACT.md" "$dir/CONTRACT.md" + cp "$FIXTURE/fm-sovereign-ledger.sh" "$dir/fm-sovereign-ledger.sh" + chmod +x "$dir/fm-sovereign-ledger.sh" + : > "$dir/ledger.tsv" + for key in ruling-1 ruling-2 ruling-3 ruling-4; do + source="$source_dir/$key.md" + printf '%s\n' "# $key" '' '**Decided by:** the captain' '' "Exact $key text." > "$source" + text=$(base64 < "$source" | tr -d '\n') + printf '%s\t%s\t%s\n' "$key" "$source" "$text" >> "$dir/ledger.tsv" + done +} + +PRIMARY="$TMP/primary" +REPLICA="$TMP/replica" +make_bundle "$PRIMARY" "$TMP/sources" + +echo 'T1 snapshot preconditions and independent storage' +mkdir -p "$TMP/missing-primary" "$TMP/missing-replica" +check_fails_with 'snapshot REFUSES an incomplete primary bundle' 'ledger bundle is incomplete' "$TOOL" snapshot "$TMP/missing-primary" "$TMP/missing-replica" +MISSING_CONTRACT="$TMP/missing-contract" +mkdir -p "$MISSING_CONTRACT" +cp "$PRIMARY/ledger.tsv" "$MISSING_CONTRACT/ledger.tsv" +cp "$PRIMARY/fm-sovereign-ledger.sh" "$MISSING_CONTRACT/fm-sovereign-ledger.sh" +check_fails_with 'snapshot REFUSES a primary bundle without its contract' 'ledger bundle is incomplete' "$TOOL" snapshot "$MISSING_CONTRACT" "$TMP/missing-contract-replica" +check_fails_with 'snapshot REFUSES the same primary and replica directory' 'primary and replica directories must differ' "$TOOL" snapshot "$PRIMARY" "$PRIMARY" +check_ok 'snapshot CREATES a verified second ledger bundle' "$TOOL" snapshot "$PRIMARY" "$REPLICA" +check_ok 'verify PASSES for the exact independent replica' "$TOOL" verify "$PRIMARY" "$REPLICA" + +echo 'T2 source loss leaves four fixture rulings provable' +for source in "$TMP/sources"/*.md; do + unlink "$source" +done +set +e +recheck_output=$(LEDGER_DIR="$PRIMARY" "$PRIMARY/fm-sovereign-ledger.sh" recheck 2>&1) +recheck_status=$? +set -e +if [ "$recheck_status" -ne 0 ] && [ "$(printf '%s\n' "$recheck_output" | grep -c '^SOURCE_GONE')" -eq 4 ] && printf '%s\n' "$recheck_output" | grep -q '^recheck: 4 entries, 4 divergent$'; then + ok 'fixture recheck reports all four removed sources as SOURCE_GONE' +else + bad 'fixture recheck did not expose all four removed sources' +fi +check_ok 'redundancy verify PASSES after all four sources are gone' "$TOOL" verify "$PRIMARY" "$REPLICA" +for key in ruling-1 ruling-2 ruling-3 ruling-4; do + expected="$TMP/$key.expected" + base64 -d < <(awk -F '\t' -v key="$key" '$1 == key { print $3 }' "$PRIMARY/ledger.tsv") > "$expected" + if diff -q <(LEDGER_DIR="$REPLICA" "$REPLICA/fm-sovereign-ledger.sh" text "$key") "$expected" >/dev/null 2>&1; then + ok "replica returns exact $key text after source loss" + else + bad "replica did not return exact $key text after source loss" + fi +done + +echo 'T3 divergence is detected and never repaired' +printf 'tamper\n' >> "$REPLICA/CONTRACT.md" +check_fails 'verify FAILS when replica contract bytes diverge' "$TOOL" verify "$PRIMARY" "$REPLICA" +check_fails 'snapshot REFUSES to overwrite a divergent replica' "$TOOL" snapshot "$PRIMARY" "$REPLICA" +cp "$PRIMARY/CONTRACT.md" "$REPLICA/CONTRACT.md" +check_ok 'verify PASSES after fixture restore' "$TOOL" verify "$PRIMARY" "$REPLICA" + +echo 'T4 an invalid primary is never copied' +INVALID="$TMP/invalid-primary" +mkdir -p "$INVALID" +cp "$PRIMARY/CONTRACT.md" "$INVALID/CONTRACT.md" +cp "$PRIMARY/fm-sovereign-ledger.sh" "$INVALID/fm-sovereign-ledger.sh" +cp "$PRIMARY/ledger.tsv" "$INVALID/ledger.tsv" +sed -i.bak '1s/ruling-1/not-a-ruling/' "$INVALID/ledger.tsv" +unlink "$INVALID/ledger.tsv.bak" +INVALID_REPLICA="$TMP/invalid-replica" +mkdir -p "$INVALID_REPLICA" +check_fails 'snapshot REFUSES a primary rejected by its own verifier' "$TOOL" snapshot "$INVALID" "$INVALID_REPLICA" +if [ ! -e "$INVALID_REPLICA/ledger.tsv" ]; then + ok 'rejected primary leaves no replica ledger behind' +else + bad 'rejected primary wrote a replica ledger' +fi + +echo 'T5 a hard link is not accepted as redundancy' +HARDLINK="$TMP/hardlink-replica" +mkdir -p "$HARDLINK" +check_ok 'snapshot CREATES a separate hard-link fixture bundle' "$TOOL" snapshot "$PRIMARY" "$HARDLINK" +unlink "$HARDLINK/ledger.tsv" +ln "$PRIMARY/ledger.tsv" "$HARDLINK/ledger.tsv" +check_fails 'verify FAILS when replica ledger.tsv is hard-linked to primary' "$TOOL" verify "$PRIMARY" "$HARDLINK" + +printf '\n%s passed, %s failed\n' "$pass" "$fail" +[ "$fail" -eq 0 ] From cd1fcab86b3559129d27fe82c25360faf817d3a3 Mon Sep 17 00:00:00 2001 From: 420tombombadil Date: Fri, 14 Aug 2026 22:54:14 -0600 Subject: [PATCH 02/19] fix: harden sovereign ledger replicas --- bin/fm-sovereign-ledger-redundancy.sh | 166 ++++++++++++------ docs/documentation-audiences.json | 4 + docs/scripts.md | 1 + .../fm-sovereign-ledger.sh | 9 +- .../sovereign-ledger-redundancy/tests.sh | 3 + tests/fm-sovereign-ledger-redundancy.test.sh | 137 ++++++++------- 6 files changed, 203 insertions(+), 117 deletions(-) create mode 100755 tests/fixtures/sovereign-ledger-redundancy/tests.sh diff --git a/bin/fm-sovereign-ledger-redundancy.sh b/bin/fm-sovereign-ledger-redundancy.sh index 14e08b61e5..79eb39eed3 100755 --- a/bin/fm-sovereign-ledger-redundancy.sh +++ b/bin/fm-sovereign-ledger-redundancy.sh @@ -1,15 +1,15 @@ #!/usr/bin/env bash -# fm-sovereign-ledger-redundancy.sh - make and verify one local replica of a sovereign ledger bundle. +# fm-sovereign-ledger-redundancy.sh - make, advance, and verify an independent local ledger replica. # # Usage: # fm-sovereign-ledger-redundancy.sh snapshot +# fm-sovereign-ledger-redundancy.sh refresh # fm-sovereign-ledger-redundancy.sh verify # -# A ledger bundle is ledger.tsv, CONTRACT.md, and fm-sovereign-ledger.sh. -# snapshot verifies the primary through its own ledger verifier before it copies it. -# It creates a missing replica or accepts an already-identical one. -# It refuses a divergent existing replica instead of repairing or overwriting it. -# verify runs each bundle's ledger verifier, compares every protected file byte-for-byte, and rejects a hard-linked ledger.tsv. +# snapshot creates a complete staging bundle then atomically publishes it into an absent replica path. +# refresh advances only a verifying byte-exact ledger.tsv prefix while every other bundle file is identical. +# verify fails loudly on a stale prefix, a divergence, symlinks, non-regular files, an incomplete layout, or a hard link. +# No replica-controlled code runs until all bundle bytes have been compared to the primary. # This script never admits, rewrites, repairs, or attributes a ruling. set -euo pipefail @@ -19,7 +19,7 @@ die() { } usage() { - sed -n '2,10{s/^# \{0,1\}//;p;}' "$0" >&2 + sed -n '2,11{s/^# \{0,1\}//;p;}' "$0" >&2 exit 2 } @@ -31,29 +31,35 @@ canonical_dir() { ) } -create_replica_dir() { - local dir=$1 - [ -e "$dir" ] && [ ! -d "$dir" ] && die "replica path is not a directory: $dir" - mkdir -p "$dir" - canonical_dir "$dir" +bundle_entries() { + find "$1" -mindepth 1 -maxdepth 1 -print | sed "s#^$1/##" | LC_ALL=C sort } require_bundle() { - local dir=$1 name - for name in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh; do - [ -f "$dir/$name" ] || die "ledger bundle is incomplete: missing $dir/$name" - done + local dir=$1 entry + [ -f "$dir/ledger.tsv" ] || die "ledger bundle is incomplete: missing $dir/ledger.tsv" + [ -f "$dir/CONTRACT.md" ] || die "ledger bundle is incomplete: missing $dir/CONTRACT.md" + [ -f "$dir/fm-sovereign-ledger.sh" ] || die "ledger bundle is incomplete: missing $dir/fm-sovereign-ledger.sh" [ -x "$dir/fm-sovereign-ledger.sh" ] || die "ledger verifier is not executable: $dir/fm-sovereign-ledger.sh" + while IFS= read -r entry; do + [ -L "$dir/$entry" ] && die "ledger bundle contains a symlink: $dir/$entry" + [ -f "$dir/$entry" ] || die "ledger bundle contains a non-regular file: $dir/$entry" + done < <(bundle_entries "$dir") } -verify_ledger() { - local dir=$1 - LEDGER_DIR="$dir" "$dir/fm-sovereign-ledger.sh" verify >/dev/null || die "ledger verifier rejected $dir/ledger.tsv" +compare_layout() { + local primary=$1 replica=$2 + diff -u <(bundle_entries "$primary") <(bundle_entries "$replica") >/dev/null \ + || die "replica bundle layout differs from primary" } -compare_file() { - local primary=$1 replica=$2 name=$3 - cmp -s "$primary/$name" "$replica/$name" || die "replica differs from primary: $name" +compare_files() { + local primary=$1 replica=$2 skip=${3:-} entry + while IFS= read -r entry; do + [ "$entry" = "$skip" ] && continue + cmp -s "$primary/$entry" "$replica/$entry" \ + || die "replica differs from primary: $entry" + done < <(bundle_entries "$primary") } file_identity() { @@ -62,59 +68,115 @@ file_identity() { require_independent_ledger_file() { local primary=$1 replica=$2 - [ "$(file_identity "$primary/ledger.tsv")" != "$(file_identity "$replica/ledger.tsv")" ] || die "replica ledger.tsv is hard-linked to the primary, not a second copy" + [ "$(file_identity "$primary/ledger.tsv")" != "$(file_identity "$replica/ledger.tsv")" ] \ + || die "replica ledger.tsv is hard-linked to the primary, not a second copy" +} + +verify_with_primary() { + local primary=$1 subject=$2 + LEDGER_DIR="$subject" "$primary/fm-sovereign-ledger.sh" verify >/dev/null \ + || die "primary ledger verifier rejected $subject/ledger.tsv" } -verify_bundle() { - local primary=$1 replica=$2 name +replica_is_prefix() { + local primary=$1 replica=$2 primary_lines replica_lines + primary_lines=$(wc -l < "$primary/ledger.tsv" | tr -d ' ') + replica_lines=$(wc -l < "$replica/ledger.tsv" | tr -d ' ') + [ "$replica_lines" -gt 0 ] || return 1 + [ "$primary_lines" -gt "$replica_lines" ] || return 1 + head -n "$replica_lines" "$primary/ledger.tsv" | cmp -s - "$replica/ledger.tsv" +} + +preflight_pair() { + local primary=$1 replica=$2 require_bundle "$primary" require_bundle "$replica" - verify_ledger "$primary" - verify_ledger "$replica" - for name in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh; do - compare_file "$primary" "$replica" "$name" - done + compare_layout "$primary" "$replica" + compare_files "$primary" "$replica" ledger.tsv +} + +verify_exact_pair() { + local primary=$1 replica=$2 + preflight_pair "$primary" "$replica" + compare_files "$primary" "$replica" require_independent_ledger_file "$primary" "$replica" + verify_with_primary "$primary" "$primary" + verify_with_primary "$primary" "$replica" } copy_bundle() { - local primary=$1 replica=$2 name tmp + local primary=$1 replica=$2 entry umask 077 - for name in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh; do - tmp="$replica/.${name}.tmp.$$" - cp "$primary/$name" "$tmp" - mv "$tmp" "$replica/$name" - done + while IFS= read -r entry; do + cp -p "$primary/$entry" "$replica/$entry" + done < <(bundle_entries "$primary") +} + +copy_ledger_atomically() { + local primary=$1 replica=$2 tmp + tmp="$replica/.ledger.tsv.tmp.$$" + umask 077 + cp -p "$primary/ledger.tsv" "$tmp" + mv "$tmp" "$replica/ledger.tsv" } cmd_snapshot() { - local primary replica - primary=$(canonical_dir "${1:?primary ledger directory}") - replica=$(create_replica_dir "${2:?replica ledger directory}") - [ "$primary" != "$replica" ] || die "primary and replica directories must differ" - require_bundle "$primary" - verify_ledger "$primary" - if [ -e "$replica/ledger.tsv" ] || [ -e "$replica/CONTRACT.md" ] || [ -e "$replica/fm-sovereign-ledger.sh" ]; then - verify_bundle "$primary" "$replica" + local primary=$1 replica_input=$2 replica parent base stage + primary=$(canonical_dir "$primary") + if [ -e "$replica_input" ]; then + replica=$(canonical_dir "$replica_input") + [ "$primary" != "$replica" ] || die "primary and replica directories must differ" + verify_exact_pair "$primary" "$replica" printf 'SNAPSHOT PASS (replica already identical: %s)\n' "$replica" return fi - copy_bundle "$primary" "$replica" - verify_bundle "$primary" "$replica" - printf 'SNAPSHOT PASS (replica created: %s)\n' "$replica" + require_bundle "$primary" + verify_with_primary "$primary" "$primary" + parent=$(dirname "$replica_input") + base=$(basename "$replica_input") + [ -d "$parent" ] || die "replica parent directory does not exist: $parent" + stage=$(mktemp -d "$parent/.${base}.stage.XXXXXX") + copy_bundle "$primary" "$stage" + verify_exact_pair "$primary" "$stage" + mv "$stage" "$replica_input" + printf 'SNAPSHOT PASS (replica created: %s)\n' "$(canonical_dir "$replica_input")" +} + +cmd_refresh() { + local primary replica + primary=$(canonical_dir "$1") + replica=$(canonical_dir "$2") + [ "$primary" != "$replica" ] || die "primary and replica directories must differ" + preflight_pair "$primary" "$replica" + verify_with_primary "$primary" "$primary" + verify_with_primary "$primary" "$replica" + replica_is_prefix "$primary" "$replica" \ + || die "replica is not a verified byte-exact append-only prefix of primary" + copy_ledger_atomically "$primary" "$replica" + verify_exact_pair "$primary" "$replica" + printf 'REFRESH PASS (replica advanced to primary)\n' } cmd_verify() { local primary replica - primary=$(canonical_dir "${1:?primary ledger directory}") - replica=$(canonical_dir "${2:?replica ledger directory}") + primary=$(canonical_dir "$1") + replica=$(canonical_dir "$2") [ "$primary" != "$replica" ] || die "primary and replica directories must differ" - verify_bundle "$primary" "$replica" + preflight_pair "$primary" "$replica" + if ! cmp -s "$primary/ledger.tsv" "$replica/ledger.tsv"; then + verify_with_primary "$primary" "$primary" + verify_with_primary "$primary" "$replica" + replica_is_prefix "$primary" "$replica" \ + && die "replica is a verified stale prefix; run refresh to advance it" + die "replica ledger.tsv diverges from primary" + fi + verify_exact_pair "$primary" "$replica" printf 'REDUNDANCY VERIFY PASS (bundle identical and independently stored)\n' } case "${1:-}" in - snapshot) shift; cmd_snapshot "$@" ;; - verify) shift; cmd_verify "$@" ;; + snapshot) shift; [ "$#" -eq 2 ] || usage; cmd_snapshot "$@" ;; + refresh) shift; [ "$#" -eq 2 ] || usage; cmd_refresh "$@" ;; + verify) shift; [ "$#" -eq 2 ] || usage; cmd_verify "$@" ;; *) usage ;; esac diff --git a/docs/documentation-audiences.json b/docs/documentation-audiences.json index 64dea78dc6..06a08c124a 100644 --- a/docs/documentation-audiences.json +++ b/docs/documentation-audiences.json @@ -108,6 +108,10 @@ } ], "surfaces": [ + { + "path": "tests/fixtures/sovereign-ledger-redundancy/CONTRACT.md", + "audience": "maintainer-verification" + }, { "path": ".agents/skills/afk/SKILL.md", "audience": "agent-runtime" diff --git a/docs/scripts.md b/docs/scripts.md index 484911c380..7741b33127 100644 --- a/docs/scripts.md +++ b/docs/scripts.md @@ -33,6 +33,7 @@ The shared no-mistakes gate refusal for fleet lifecycle entrypoints is summarize | `fm-herdr-ci-cleanup.sh` | Snapshot and tear down only job-owned `fm-lab-*` sessions in the Herdr CI lane | | `fm-test-run.sh` | Behavior-test runner: selection, portable lanes, proven-isolated `--jobs`, coverage guard, timing/JSON | | `fm-test-isolation-proof.sh` | Concurrent isolation proof and proven-isolated candidate set owner | +| `fm-sovereign-ledger-redundancy.sh` | Create, append-only-refresh, and byte-verify a complete local sovereign-ledger replica bundle | | `fm-ensure-agents-md.sh` | Ensure a project's real `AGENTS.md`, its `CLAUDE.md` symlink, and the canonical self-governance section | | `fm-guard.sh` | Warn on primary-checkout tangles, pending queued wakes, and unhealthy supervision | | `fm-primary-scope-lib.sh` | Shared marker-or-plain-checkout primary-home predicate for tracked hooks | diff --git a/tests/fixtures/sovereign-ledger-redundancy/fm-sovereign-ledger.sh b/tests/fixtures/sovereign-ledger-redundancy/fm-sovereign-ledger.sh index b7917fa84d..0d2c555dd5 100755 --- a/tests/fixtures/sovereign-ledger-redundancy/fm-sovereign-ledger.sh +++ b/tests/fixtures/sovereign-ledger-redundancy/fm-sovereign-ledger.sh @@ -7,12 +7,11 @@ ledger=$ledger_dir/ledger.tsv verify() { [ -f "$ledger" ] || exit 1 - [ "$(awk 'END { print NR }' "$ledger")" = 4 ] || exit 1 awk -F '\t' ' - NF != 3 { exit 1 } - $1 !~ /^ruling-[1-4]$/ { exit 1 } - seen[$1]++ != 0 { exit 1 } - END { exit length(seen) == 4 ? 0 : 1 } + NF != 3 { bad = 1 } + $1 !~ /^ruling-[1-5]$/ { bad = 1 } + seen[$1]++ != 0 { bad = 1 } + END { exit bad || length(seen) < 4 ? 1 : 0 } ' "$ledger" } diff --git a/tests/fixtures/sovereign-ledger-redundancy/tests.sh b/tests/fixtures/sovereign-ledger-redundancy/tests.sh new file mode 100755 index 0000000000..5c2074dbbe --- /dev/null +++ b/tests/fixtures/sovereign-ledger-redundancy/tests.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +# Fixture companion test file: copied and compared as part of the complete bundle. +exit 0 diff --git a/tests/fm-sovereign-ledger-redundancy.test.sh b/tests/fm-sovereign-ledger-redundancy.test.sh index d4f4d14adb..c159adac13 100755 --- a/tests/fm-sovereign-ledger-redundancy.test.sh +++ b/tests/fm-sovereign-ledger-redundancy.test.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Verify a local replica is complete, byte-identical, independently stored, and useful after source loss. +# Verify a complete independent replica, append-only refresh, and source-loss recovery through public commands. set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" @@ -10,71 +10,60 @@ trap 'rm -rf "$TMP"' EXIT pass=0 fail=0 -ok() { - printf ' PASS %s\n' "$1" - pass=$((pass + 1)) -} - -bad() { - printf ' FAIL %s\n' "$1" - fail=$((fail + 1)) -} +ok() { printf ' PASS %s\n' "$1"; pass=$((pass + 1)); } +bad() { printf ' FAIL %s\n' "$1"; fail=$((fail + 1)); } check_ok() { local description=$1 shift - if "$@" >/dev/null 2>&1; then - ok "$description" - else - bad "$description" - fi + if "$@" >/dev/null 2>&1; then ok "$description"; else bad "$description"; fi } check_fails() { local description=$1 shift - if "$@" >/dev/null 2>&1; then - bad "$description (it SUCCEEDED - guard absent)" - else - ok "$description" - fi + if "$@" >/dev/null 2>&1; then bad "$description (it SUCCEEDED - guard absent)"; else ok "$description"; fi } check_fails_with() { - local description=$1 expected=$2 output status + local description=$1 expected=$2 output task_rc shift 2 set +e output=$("$@" 2>&1) - status=$? + task_rc=$? set -e - if [ "$status" -ne 0 ] && printf '%s\n' "$output" | grep -Fq "$expected"; then + if [ "$task_rc" -ne 0 ] && printf '%s\n' "$output" | grep -Fq "$expected"; then ok "$description" else bad "$description (missing expected refusal: $expected)" fi } +add_ruling() { + local dir=$1 source_dir=$2 number=$3 source text + source="$source_dir/ruling-$number.md" + printf '%s\n' "# ruling-$number" '' '**Decided by:** the captain' '' "Exact ruling-$number text." > "$source" + text=$(base64 < "$source" | tr -d '\n') + printf 'ruling-%s\t%s\t%s\n' "$number" "$source" "$text" >> "$dir/ledger.tsv" +} + make_bundle() { - local dir=$1 source_dir=$2 key source text + local dir=$1 source_dir=$2 number mkdir -p "$dir" "$source_dir" cp "$FIXTURE/CONTRACT.md" "$dir/CONTRACT.md" cp "$FIXTURE/fm-sovereign-ledger.sh" "$dir/fm-sovereign-ledger.sh" - chmod +x "$dir/fm-sovereign-ledger.sh" + cp "$FIXTURE/tests.sh" "$dir/tests.sh" + chmod +x "$dir/fm-sovereign-ledger.sh" "$dir/tests.sh" : > "$dir/ledger.tsv" - for key in ruling-1 ruling-2 ruling-3 ruling-4; do - source="$source_dir/$key.md" - printf '%s\n' "# $key" '' '**Decided by:** the captain' '' "Exact $key text." > "$source" - text=$(base64 < "$source" | tr -d '\n') - printf '%s\t%s\t%s\n' "$key" "$source" "$text" >> "$dir/ledger.tsv" - done + for number in 1 2 3 4; do add_ruling "$dir" "$source_dir" "$number"; done } PRIMARY="$TMP/primary" REPLICA="$TMP/replica" make_bundle "$PRIMARY" "$TMP/sources" -echo 'T1 snapshot preconditions and independent storage' -mkdir -p "$TMP/missing-primary" "$TMP/missing-replica" +echo 'T1 complete, independent bundle preconditions' +mkdir -p "$TMP/missing-primary" check_fails_with 'snapshot REFUSES an incomplete primary bundle' 'ledger bundle is incomplete' "$TOOL" snapshot "$TMP/missing-primary" "$TMP/missing-replica" MISSING_CONTRACT="$TMP/missing-contract" mkdir -p "$MISSING_CONTRACT" @@ -82,61 +71,89 @@ cp "$PRIMARY/ledger.tsv" "$MISSING_CONTRACT/ledger.tsv" cp "$PRIMARY/fm-sovereign-ledger.sh" "$MISSING_CONTRACT/fm-sovereign-ledger.sh" check_fails_with 'snapshot REFUSES a primary bundle without its contract' 'ledger bundle is incomplete' "$TOOL" snapshot "$MISSING_CONTRACT" "$TMP/missing-contract-replica" check_fails_with 'snapshot REFUSES the same primary and replica directory' 'primary and replica directories must differ' "$TOOL" snapshot "$PRIMARY" "$PRIMARY" -check_ok 'snapshot CREATES a verified second ledger bundle' "$TOOL" snapshot "$PRIMARY" "$REPLICA" +check_ok 'snapshot atomically CREATES the complete second ledger bundle' "$TOOL" snapshot "$PRIMARY" "$REPLICA" check_ok 'verify PASSES for the exact independent replica' "$TOOL" verify "$PRIMARY" "$REPLICA" -echo 'T2 source loss leaves four fixture rulings provable' -for source in "$TMP/sources"/*.md; do - unlink "$source" -done +EXECUTABLE="$TMP/non-executable-replica" +check_ok 'snapshot CREATES an executable-bit fixture bundle' "$TOOL" snapshot "$PRIMARY" "$EXECUTABLE" +chmod -x "$EXECUTABLE/fm-sovereign-ledger.sh" +check_fails_with 'verify REFUSES a non-executable replica verifier' 'ledger verifier is not executable' "$TOOL" verify "$PRIMARY" "$EXECUTABLE" +chmod +x "$EXECUTABLE/fm-sovereign-ledger.sh" + +echo 'T2 staleness is loud and refresh accepts only an append-only prefix' +add_ruling "$PRIMARY" "$TMP/sources" 5 +check_fails_with 'verify names a verified stale replica and its refresh remedy' 'replica is a verified stale prefix; run refresh' "$TOOL" verify "$PRIMARY" "$REPLICA" +check_ok 'refresh advances the verified append-only replica' "$TOOL" refresh "$PRIMARY" "$REPLICA" +check_ok 'verify PASSES after refresh' "$TOOL" verify "$PRIMARY" "$REPLICA" +NONPREFIX="$TMP/non-prefix-replica" +check_ok 'snapshot CREATES a non-prefix refresh fixture' "$TOOL" snapshot "$PRIMARY" "$NONPREFIX" +{ sed -n '2p' "$NONPREFIX/ledger.tsv"; sed -n '1p' "$NONPREFIX/ledger.tsv"; sed -n '3,$p' "$NONPREFIX/ledger.tsv"; } > "$NONPREFIX/reordered-ledger.tsv" +mv "$NONPREFIX/reordered-ledger.tsv" "$NONPREFIX/ledger.tsv" +check_fails_with 'refresh REFUSES a verifying replica that is not a byte-exact prefix' 'replica is not a verified byte-exact append-only prefix of primary' "$TOOL" refresh "$PRIMARY" "$NONPREFIX" + +echo 'T3 source loss leaves the four required fixture rulings provable' +for number in 1 2 3 4; do unlink "$TMP/sources/ruling-$number.md"; done set +e recheck_output=$(LEDGER_DIR="$PRIMARY" "$PRIMARY/fm-sovereign-ledger.sh" recheck 2>&1) recheck_status=$? set -e -if [ "$recheck_status" -ne 0 ] && [ "$(printf '%s\n' "$recheck_output" | grep -c '^SOURCE_GONE')" -eq 4 ] && printf '%s\n' "$recheck_output" | grep -q '^recheck: 4 entries, 4 divergent$'; then +if [ "$recheck_status" -ne 0 ] && [ "$(printf '%s\n' "$recheck_output" | grep -c '^SOURCE_GONE')" -eq 4 ] && printf '%s\n' "$recheck_output" | grep -q '^recheck: 5 entries, 4 divergent$'; then ok 'fixture recheck reports all four removed sources as SOURCE_GONE' else bad 'fixture recheck did not expose all four removed sources' fi -check_ok 'redundancy verify PASSES after all four sources are gone' "$TOOL" verify "$PRIMARY" "$REPLICA" -for key in ruling-1 ruling-2 ruling-3 ruling-4; do - expected="$TMP/$key.expected" - base64 -d < <(awk -F '\t' -v key="$key" '$1 == key { print $3 }' "$PRIMARY/ledger.tsv") > "$expected" - if diff -q <(LEDGER_DIR="$REPLICA" "$REPLICA/fm-sovereign-ledger.sh" text "$key") "$expected" >/dev/null 2>&1; then - ok "replica returns exact $key text after source loss" +check_ok 'redundancy verify PASSES after the four sources are gone' "$TOOL" verify "$PRIMARY" "$REPLICA" +for number in 1 2 3 4; do + expected="$TMP/ruling-$number.expected" + base64 -d < <(awk -F '\t' -v key="ruling-$number" '$1 == key { print $3 }' "$PRIMARY/ledger.tsv") > "$expected" + if diff -q <(LEDGER_DIR="$REPLICA" "$REPLICA/fm-sovereign-ledger.sh" text "ruling-$number") "$expected" >/dev/null 2>&1; then + ok "replica returns exact ruling-$number text after source loss" else - bad "replica did not return exact $key text after source loss" + bad "replica did not return exact ruling-$number text after source loss" fi done -echo 'T3 divergence is detected and never repaired' +echo 'T4 every bundle-file symlink is refused before it can masquerade as a copy' +for entry in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do + symlink_replica="$TMP/symlink-$entry" + check_ok "snapshot CREATES a $entry symlink fixture" "$TOOL" snapshot "$PRIMARY" "$symlink_replica" + unlink "$symlink_replica/$entry" + ln -s "$PRIMARY/$entry" "$symlink_replica/$entry" + check_fails_with "verify REFUSES a symlinked $entry" 'ledger bundle contains a symlink' "$TOOL" verify "$PRIMARY" "$symlink_replica" +done + +echo 'T5 replica bytes are compared before replica-controlled code can execute' +ORDERING="$TMP/ordering-replica" +ORDERING_PROOF="$TMP/replica-code-ran" +check_ok 'snapshot CREATES an ordering fixture bundle' "$TOOL" snapshot "$PRIMARY" "$ORDERING" +printf '%s\n' '#!/usr/bin/env bash' "touch '$ORDERING_PROOF'" 'exit 0' > "$ORDERING/fm-sovereign-ledger.sh" +chmod +x "$ORDERING/fm-sovereign-ledger.sh" +check_fails 'verify REFUSES a changed replica verifier' "$TOOL" verify "$PRIMARY" "$ORDERING" +if [ ! -e "$ORDERING_PROOF" ]; then ok 'changed replica verifier never executed'; else bad 'changed replica verifier executed before byte comparison'; fi + +echo 'T6 divergence and extra files are detected and never repaired' printf 'tamper\n' >> "$REPLICA/CONTRACT.md" check_fails 'verify FAILS when replica contract bytes diverge' "$TOOL" verify "$PRIMARY" "$REPLICA" check_fails 'snapshot REFUSES to overwrite a divergent replica' "$TOOL" snapshot "$PRIMARY" "$REPLICA" cp "$PRIMARY/CONTRACT.md" "$REPLICA/CONTRACT.md" +printf 'planted\n' > "$REPLICA/EXTRA-CONTRACT.md" +check_fails_with 'verify REFUSES an unexpected replica file' 'replica bundle layout differs from primary' "$TOOL" verify "$PRIMARY" "$REPLICA" +unlink "$REPLICA/EXTRA-CONTRACT.md" check_ok 'verify PASSES after fixture restore' "$TOOL" verify "$PRIMARY" "$REPLICA" -echo 'T4 an invalid primary is never copied' +echo 'T7 an invalid primary is never copied' INVALID="$TMP/invalid-primary" mkdir -p "$INVALID" -cp "$PRIMARY/CONTRACT.md" "$INVALID/CONTRACT.md" -cp "$PRIMARY/fm-sovereign-ledger.sh" "$INVALID/fm-sovereign-ledger.sh" -cp "$PRIMARY/ledger.tsv" "$INVALID/ledger.tsv" +cp "$PRIMARY"/* "$INVALID/" sed -i.bak '1s/ruling-1/not-a-ruling/' "$INVALID/ledger.tsv" unlink "$INVALID/ledger.tsv.bak" INVALID_REPLICA="$TMP/invalid-replica" -mkdir -p "$INVALID_REPLICA" check_fails 'snapshot REFUSES a primary rejected by its own verifier' "$TOOL" snapshot "$INVALID" "$INVALID_REPLICA" -if [ ! -e "$INVALID_REPLICA/ledger.tsv" ]; then - ok 'rejected primary leaves no replica ledger behind' -else - bad 'rejected primary wrote a replica ledger' -fi +if [ ! -e "$INVALID_REPLICA" ]; then ok 'rejected primary leaves no replica directory behind'; else bad 'rejected primary wrote a replica directory'; fi -echo 'T5 a hard link is not accepted as redundancy' +echo 'T8 a hard link is not accepted as redundancy' HARDLINK="$TMP/hardlink-replica" -mkdir -p "$HARDLINK" -check_ok 'snapshot CREATES a separate hard-link fixture bundle' "$TOOL" snapshot "$PRIMARY" "$HARDLINK" +check_ok 'snapshot CREATES a hard-link fixture bundle' "$TOOL" snapshot "$PRIMARY" "$HARDLINK" unlink "$HARDLINK/ledger.tsv" ln "$PRIMARY/ledger.tsv" "$HARDLINK/ledger.tsv" check_fails 'verify FAILS when replica ledger.tsv is hard-linked to primary' "$TOOL" verify "$PRIMARY" "$HARDLINK" From ff1e7425b1c0c9bcd21a8aceff69fbe1627c8346 Mon Sep 17 00:00:00 2001 From: 420tombombadil Date: Fri, 14 Aug 2026 23:53:19 -0600 Subject: [PATCH 03/19] fix: verify all sovereign ledger replica files --- bin/fm-sovereign-ledger-redundancy.sh | 51 +++++++++++++----- tests/fm-sovereign-ledger-redundancy.test.sh | 54 ++++++++++++++++---- 2 files changed, 83 insertions(+), 22 deletions(-) diff --git a/bin/fm-sovereign-ledger-redundancy.sh b/bin/fm-sovereign-ledger-redundancy.sh index 79eb39eed3..ae8f864159 100755 --- a/bin/fm-sovereign-ledger-redundancy.sh +++ b/bin/fm-sovereign-ledger-redundancy.sh @@ -8,7 +8,7 @@ # # snapshot creates a complete staging bundle then atomically publishes it into an absent replica path. # refresh advances only a verifying byte-exact ledger.tsv prefix while every other bundle file is identical. -# verify fails loudly on a stale prefix, a divergence, symlinks, non-regular files, an incomplete layout, or a hard link. +# verify fails loudly on a stale prefix, a divergence, symlinks, non-regular files, an incomplete layout, or any shared file identity. # No replica-controlled code runs until all bundle bytes have been compared to the primary. # This script never admits, rewrites, repairs, or attributes a ruling. set -euo pipefail @@ -62,14 +62,26 @@ compare_files() { done < <(bundle_entries "$primary") } -file_identity() { +file_lstat_identity() { stat -f '%d:%i' "$1" 2>/dev/null || stat -c '%d:%i' "$1" } -require_independent_ledger_file() { - local primary=$1 replica=$2 - [ "$(file_identity "$primary/ledger.tsv")" != "$(file_identity "$replica/ledger.tsv")" ] \ - || die "replica ledger.tsv is hard-linked to the primary, not a second copy" +file_stat_identity() { + stat -L -f '%d:%i' "$1" 2>/dev/null || stat -L -c '%d:%i' "$1" +} + +require_independent_bundle_files() { + local primary=$1 replica=$2 entry primary_lstat replica_lstat primary_stat replica_stat + while IFS= read -r entry; do + primary_lstat=$(file_lstat_identity "$primary/$entry") + replica_lstat=$(file_lstat_identity "$replica/$entry") + [ "$primary_lstat" != "$replica_lstat" ] \ + || die "replica $entry shares the primary lstat identity (device:inode), not a separate file" + primary_stat=$(file_stat_identity "$primary/$entry") + replica_stat=$(file_stat_identity "$replica/$entry") + [ "$primary_stat" != "$replica_stat" ] \ + || die "replica $entry resolves to the primary object (device:inode), not a separate file" + done < <(bundle_entries "$primary") } verify_with_primary() { @@ -99,7 +111,7 @@ verify_exact_pair() { local primary=$1 replica=$2 preflight_pair "$primary" "$replica" compare_files "$primary" "$replica" - require_independent_ledger_file "$primary" "$replica" + require_independent_bundle_files "$primary" "$replica" verify_with_primary "$primary" "$primary" verify_with_primary "$primary" "$replica" } @@ -113,11 +125,20 @@ copy_bundle() { } copy_ledger_atomically() { - local primary=$1 replica=$2 tmp - tmp="$replica/.ledger.tsv.tmp.$$" + local primary=$1 replica=$2 parent base tmp + parent=$(dirname "$replica") + base=$(basename "$replica") + tmp=$(mktemp "$parent/.${base}.ledger.tsv.tmp.XXXXXX") \ + || die "could not create refresh staging file beside replica" umask 077 - cp -p "$primary/ledger.tsv" "$tmp" - mv "$tmp" "$replica/ledger.tsv" + if ! cp -p "$primary/ledger.tsv" "$tmp"; then + rm -f -- "$tmp" + die "could not stage primary ledger.tsv for refresh" + fi + if ! mv "$tmp" "$replica/ledger.tsv"; then + rm -f -- "$tmp" + die "could not publish refreshed replica ledger.tsv" + fi } cmd_snapshot() { @@ -135,10 +156,14 @@ cmd_snapshot() { parent=$(dirname "$replica_input") base=$(basename "$replica_input") [ -d "$parent" ] || die "replica parent directory does not exist: $parent" - stage=$(mktemp -d "$parent/.${base}.stage.XXXXXX") + stage=$(mktemp -d "$parent/.${base}.stage.XXXXXX") \ + || die "could not create replica staging directory" + trap 'rm -rf -- "$stage"' EXIT HUP INT TERM copy_bundle "$primary" "$stage" verify_exact_pair "$primary" "$stage" mv "$stage" "$replica_input" + stage= + trap - EXIT HUP INT TERM printf 'SNAPSHOT PASS (replica created: %s)\n' "$(canonical_dir "$replica_input")" } @@ -171,7 +196,7 @@ cmd_verify() { die "replica ledger.tsv diverges from primary" fi verify_exact_pair "$primary" "$replica" - printf 'REDUNDANCY VERIFY PASS (bundle identical and independently stored)\n' + printf 'REDUNDANCY VERIFY PASS (bundle byte-identical; every member has distinct lstat and stat identities)\n' } case "${1:-}" in diff --git a/tests/fm-sovereign-ledger-redundancy.test.sh b/tests/fm-sovereign-ledger-redundancy.test.sh index c159adac13..398332a657 100755 --- a/tests/fm-sovereign-ledger-redundancy.test.sh +++ b/tests/fm-sovereign-ledger-redundancy.test.sh @@ -122,7 +122,14 @@ for entry in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do check_fails_with "verify REFUSES a symlinked $entry" 'ledger bundle contains a symlink' "$TOOL" verify "$PRIMARY" "$symlink_replica" done -echo 'T5 replica bytes are compared before replica-controlled code can execute' +echo 'T5 non-regular bundle members are refused before comparison or execution' +NONREGULAR="$TMP/non-regular-replica" +check_ok 'snapshot CREATES a non-regular member fixture bundle' "$TOOL" snapshot "$PRIMARY" "$NONREGULAR" +unlink "$NONREGULAR/tests.sh" +mkfifo "$NONREGULAR/tests.sh" +check_fails_with 'verify REFUSES a FIFO bundle member' 'ledger bundle contains a non-regular file' "$TOOL" verify "$PRIMARY" "$NONREGULAR" + +echo 'T6 replica bytes are compared before replica-controlled code can execute' ORDERING="$TMP/ordering-replica" ORDERING_PROOF="$TMP/replica-code-ran" check_ok 'snapshot CREATES an ordering fixture bundle' "$TOOL" snapshot "$PRIMARY" "$ORDERING" @@ -131,7 +138,7 @@ chmod +x "$ORDERING/fm-sovereign-ledger.sh" check_fails 'verify REFUSES a changed replica verifier' "$TOOL" verify "$PRIMARY" "$ORDERING" if [ ! -e "$ORDERING_PROOF" ]; then ok 'changed replica verifier never executed'; else bad 'changed replica verifier executed before byte comparison'; fi -echo 'T6 divergence and extra files are detected and never repaired' +echo 'T7 divergence and extra files are detected and never repaired' printf 'tamper\n' >> "$REPLICA/CONTRACT.md" check_fails 'verify FAILS when replica contract bytes diverge' "$TOOL" verify "$PRIMARY" "$REPLICA" check_fails 'snapshot REFUSES to overwrite a divergent replica' "$TOOL" snapshot "$PRIMARY" "$REPLICA" @@ -141,7 +148,7 @@ check_fails_with 'verify REFUSES an unexpected replica file' 'replica bundle lay unlink "$REPLICA/EXTRA-CONTRACT.md" check_ok 'verify PASSES after fixture restore' "$TOOL" verify "$PRIMARY" "$REPLICA" -echo 'T7 an invalid primary is never copied' +echo 'T8 an invalid primary is never copied' INVALID="$TMP/invalid-primary" mkdir -p "$INVALID" cp "$PRIMARY"/* "$INVALID/" @@ -151,12 +158,41 @@ INVALID_REPLICA="$TMP/invalid-replica" check_fails 'snapshot REFUSES a primary rejected by its own verifier' "$TOOL" snapshot "$INVALID" "$INVALID_REPLICA" if [ ! -e "$INVALID_REPLICA" ]; then ok 'rejected primary leaves no replica directory behind'; else bad 'rejected primary wrote a replica directory'; fi -echo 'T8 a hard link is not accepted as redundancy' -HARDLINK="$TMP/hardlink-replica" -check_ok 'snapshot CREATES a hard-link fixture bundle' "$TOOL" snapshot "$PRIMARY" "$HARDLINK" -unlink "$HARDLINK/ledger.tsv" -ln "$PRIMARY/ledger.tsv" "$HARDLINK/ledger.tsv" -check_fails 'verify FAILS when replica ledger.tsv is hard-linked to primary' "$TOOL" verify "$PRIMARY" "$HARDLINK" +echo 'T9 each identity check fires independently' +IDENTITY_PRIMARY="$TMP/identity-primary" +IDENTITY_REPLICA="$TMP/identity-replica" +IDENTITY_STAT_BIN="$TMP/identity-stat-bin" +check_ok 'snapshot CREATES an identity-check fixture bundle' "$TOOL" snapshot "$PRIMARY" "$IDENTITY_PRIMARY" +check_ok 'snapshot CREATES an identity-check replica bundle' "$TOOL" snapshot "$PRIMARY" "$IDENTITY_REPLICA" +mkdir "$IDENTITY_STAT_BIN" +# shellcheck disable=SC2016 +printf '%s\n' \ + '#!/usr/bin/env bash' \ + 'set -euo pipefail' \ + 'target="${!#}"' \ + 'follow=false' \ + 'for argument in "$@"; do if [ "$argument" = "-L" ]; then follow=true; fi; done' \ + 'case "$target" in' \ + ' *identity-primary/*) side=primary ;;' \ + ' *identity-replica/*) side=replica ;;' \ + ' *) exit 70 ;;' \ + 'esac' \ + 'case "${IDENTITY_MODE}:${follow}" in' \ + ' lstat:false|stat:true) printf "shared:identity\\n" ;;' \ + ' *) printf "%s:identity\\n" "$side" ;;' \ + 'esac' > "$IDENTITY_STAT_BIN/stat" +chmod +x "$IDENTITY_STAT_BIN/stat" +check_fails_with 'verify REFUSES a shared lstat identity even when stat identities differ' 'replica CONTRACT.md shares the primary lstat identity' env PATH="$IDENTITY_STAT_BIN:$PATH" IDENTITY_MODE=lstat IDENTITY_PRIMARY="$IDENTITY_PRIMARY" IDENTITY_REPLICA="$IDENTITY_REPLICA" "$TOOL" verify "$IDENTITY_PRIMARY" "$IDENTITY_REPLICA" +check_fails_with 'verify REFUSES a shared stat identity even when lstat identities differ' 'replica CONTRACT.md resolves to the primary object' env PATH="$IDENTITY_STAT_BIN:$PATH" IDENTITY_MODE=stat IDENTITY_PRIMARY="$IDENTITY_PRIMARY" IDENTITY_REPLICA="$IDENTITY_REPLICA" "$TOOL" verify "$IDENTITY_PRIMARY" "$IDENTITY_REPLICA" + +echo 'T10 every bundle member must have separate lstat and stat identities' +for entry in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do + hardlink_replica="$TMP/hardlink-$entry" + check_ok "snapshot CREATES a $entry hard-link fixture" "$TOOL" snapshot "$PRIMARY" "$hardlink_replica" + unlink "$hardlink_replica/$entry" + ln "$PRIMARY/$entry" "$hardlink_replica/$entry" + check_fails_with "verify REFUSES a hard-linked $entry by lstat identity" "replica $entry shares the primary lstat identity" "$TOOL" verify "$PRIMARY" "$hardlink_replica" +done printf '\n%s passed, %s failed\n' "$pass" "$fail" [ "$fail" -eq 0 ] From 68cc106f66206f2d3115379ea449eeccdaca052f Mon Sep 17 00:00:00 2001 From: 420tombombadil Date: Sat, 15 Aug 2026 15:40:08 -0600 Subject: [PATCH 04/19] fix: establish sovereign ledger independence --- bin/fm-sovereign-ledger-redundancy.sh | 153 +++++++++++++---- tests/fm-sovereign-ledger-redundancy.test.sh | 172 ++++++++++++++++++- 2 files changed, 289 insertions(+), 36 deletions(-) diff --git a/bin/fm-sovereign-ledger-redundancy.sh b/bin/fm-sovereign-ledger-redundancy.sh index ae8f864159..5d36362963 100755 --- a/bin/fm-sovereign-ledger-redundancy.sh +++ b/bin/fm-sovereign-ledger-redundancy.sh @@ -8,11 +8,14 @@ # # snapshot creates a complete staging bundle then atomically publishes it into an absent replica path. # refresh advances only a verifying byte-exact ledger.tsv prefix while every other bundle file is identical. -# verify fails loudly on a stale prefix, a divergence, symlinks, non-regular files, an incomplete layout, or any shared file identity. +# verify fails loudly on containment, a stale prefix, a divergence, symlinks, non-regular files, an incomplete layout, or overlapping file-identity sets. # No replica-controlled code runs until all bundle bytes have been compared to the primary. # This script never admits, rewrites, repairs, or attributes a ruling. set -euo pipefail +BUNDLE_MEMBER_COUNT=4 +BUNDLE_ENTRIES= + die() { printf 'REFUSED: %s\n' "$*" >&2 exit 1 @@ -31,57 +34,135 @@ canonical_dir() { ) } -bundle_entries() { - find "$1" -mindepth 1 -maxdepth 1 -print | sed "s#^$1/##" | LC_ALL=C sort +bundle_manifest() { + printf '%s\n' CONTRACT.md fm-sovereign-ledger.sh ledger.tsv tests.sh +} + +collect_bundle_entries() { + local dir=$1 count + if ! count=$(find "$dir" -mindepth 1 -maxdepth 1 -exec /bin/sh -c ' + for entry do printf "x\n"; done + ' sh {} + | wc -l | tr -d '[:space:]'); then + die "could not enumerate ledger bundle: $dir" + fi + [ "$count" -eq "$BUNDLE_MEMBER_COUNT" ] \ + || die "ledger bundle must contain exactly $BUNDLE_MEMBER_COUNT manifest members, found $count: $dir" + BUNDLE_ENTRIES=$(bundle_manifest) } require_bundle() { - local dir=$1 entry - [ -f "$dir/ledger.tsv" ] || die "ledger bundle is incomplete: missing $dir/ledger.tsv" - [ -f "$dir/CONTRACT.md" ] || die "ledger bundle is incomplete: missing $dir/CONTRACT.md" - [ -f "$dir/fm-sovereign-ledger.sh" ] || die "ledger bundle is incomplete: missing $dir/fm-sovereign-ledger.sh" - [ -x "$dir/fm-sovereign-ledger.sh" ] || die "ledger verifier is not executable: $dir/fm-sovereign-ledger.sh" + local dir=$1 entry checked=0 + BUNDLE_ENTRIES=$(bundle_manifest) while IFS= read -r entry; do + [ -e "$dir/$entry" ] || die "ledger bundle is incomplete: missing $dir/$entry" [ -L "$dir/$entry" ] && die "ledger bundle contains a symlink: $dir/$entry" [ -f "$dir/$entry" ] || die "ledger bundle contains a non-regular file: $dir/$entry" - done < <(bundle_entries "$dir") + checked=$((checked + 1)) + done <<< "$BUNDLE_ENTRIES" + collect_bundle_entries "$dir" + [ "$checked" -eq "$BUNDLE_MEMBER_COUNT" ] \ + || die "ledger bundle manifest check read $checked of $BUNDLE_MEMBER_COUNT members: $dir" + [ -x "$dir/fm-sovereign-ledger.sh" ] || die "ledger verifier is not executable: $dir/fm-sovereign-ledger.sh" } compare_layout() { - local primary=$1 replica=$2 - diff -u <(bundle_entries "$primary") <(bundle_entries "$replica") >/dev/null \ - || die "replica bundle layout differs from primary" + local primary=$1 replica=$2 primary_entries replica_entries + collect_bundle_entries "$primary" + primary_entries=$BUNDLE_ENTRIES + collect_bundle_entries "$replica" + replica_entries=$BUNDLE_ENTRIES + [ "$primary_entries" = "$replica_entries" ] || die "replica bundle layout differs from primary" } compare_files() { - local primary=$1 replica=$2 skip=${3:-} entry + local primary=$1 replica=$2 skip=${3:-} entry compared=0 expected=$BUNDLE_MEMBER_COUNT + collect_bundle_entries "$primary" + [ -z "$skip" ] || expected=$((expected - 1)) while IFS= read -r entry; do [ "$entry" = "$skip" ] && continue cmp -s "$primary/$entry" "$replica/$entry" \ || die "replica differs from primary: $entry" - done < <(bundle_entries "$primary") + compared=$((compared + 1)) + done <<< "$BUNDLE_ENTRIES" + [ "$compared" -eq "$expected" ] \ + || die "byte comparison read $compared of $expected required bundle members" +} + +portable_file_identity() { + local follow=$1 path=$2 identity + if [ "$follow" = true ]; then + if identity=$(stat -L -f '%d:%i' "$path" 2>/dev/null); then + : + elif identity=$(stat -L -c '%d:%i' "$path" 2>/dev/null); then + : + else + return 1 + fi + elif identity=$(stat -f '%d:%i' "$path" 2>/dev/null); then + : + elif identity=$(stat -c '%d:%i' "$path" 2>/dev/null); then + : + else + return 1 + fi + printf '%s\n' "$identity" | LC_ALL=C grep -Eq '^[0-9]+:[0-9]+$' || return 1 + printf '%s\n' "$identity" } file_lstat_identity() { - stat -f '%d:%i' "$1" 2>/dev/null || stat -c '%d:%i' "$1" + portable_file_identity false "$1" } file_stat_identity() { - stat -L -f '%d:%i' "$1" 2>/dev/null || stat -L -c '%d:%i' "$1" + portable_file_identity true "$1" } require_independent_bundle_files() { - local primary=$1 replica=$2 entry primary_lstat replica_lstat primary_stat replica_stat + local primary=$1 replica=$2 entry index=0 replica_index primary_index + local primary_lstat replica_lstat primary_stat replica_stat + local -a entries primary_lstats replica_lstats primary_stats replica_stats + collect_bundle_entries "$primary" while IFS= read -r entry; do - primary_lstat=$(file_lstat_identity "$primary/$entry") - replica_lstat=$(file_lstat_identity "$replica/$entry") - [ "$primary_lstat" != "$replica_lstat" ] \ - || die "replica $entry shares the primary lstat identity (device:inode), not a separate file" - primary_stat=$(file_stat_identity "$primary/$entry") - replica_stat=$(file_stat_identity "$replica/$entry") - [ "$primary_stat" != "$replica_stat" ] \ - || die "replica $entry resolves to the primary object (device:inode), not a separate file" - done < <(bundle_entries "$primary") + entries[index]=$entry + primary_lstat=$(file_lstat_identity "$primary/$entry") \ + || die "could not establish a portable file identity: $primary/$entry" + replica_lstat=$(file_lstat_identity "$replica/$entry") \ + || die "could not establish a portable file identity: $replica/$entry" + primary_stat=$(file_stat_identity "$primary/$entry") \ + || die "could not establish a portable file identity: $primary/$entry" + replica_stat=$(file_stat_identity "$replica/$entry") \ + || die "could not establish a portable file identity: $replica/$entry" + primary_lstats[index]=$primary_lstat + replica_lstats[index]=$replica_lstat + primary_stats[index]=$primary_stat + replica_stats[index]=$replica_stat + index=$((index + 1)) + done <<< "$BUNDLE_ENTRIES" + [ "$index" -eq "$BUNDLE_MEMBER_COUNT" ] \ + || die "identity verification read $index of $BUNDLE_MEMBER_COUNT required bundle members" + for ((replica_index = 0; replica_index < BUNDLE_MEMBER_COUNT; replica_index++)); do + for ((primary_index = 0; primary_index < BUNDLE_MEMBER_COUNT; primary_index++)); do + if [ "${replica_lstats[$replica_index]}" = "${primary_lstats[$primary_index]}" ]; then + if [ "${entries[$replica_index]}" = "${entries[$primary_index]}" ]; then + die "replica ${entries[$replica_index]} shares the primary lstat identity (device:inode), not a separate file" + fi + die "replica ${entries[$replica_index]} shares storage with primary member ${entries[$primary_index]} by lstat identity (device:inode)" + fi + if [ "${replica_stats[$replica_index]}" = "${primary_stats[$primary_index]}" ]; then + if [ "${entries[$replica_index]}" = "${entries[$primary_index]}" ]; then + die "replica ${entries[$replica_index]} resolves to the primary object (device:inode), not a separate file" + fi + die "replica ${entries[$replica_index]} resolves to primary member ${entries[$primary_index]} by stat identity (device:inode)" + fi + done + done +} + +require_noncontained_pair() { + local primary=$1 replica=$2 + [ "$primary" != "$replica" ] || die "primary and replica directories must differ" + case "$primary/" in "$replica/"*) die "primary and replica directories must not contain one another" ;; esac + case "$replica/" in "$primary/"*) die "primary and replica directories must not contain one another" ;; esac } verify_with_primary() { @@ -117,11 +198,15 @@ verify_exact_pair() { } copy_bundle() { - local primary=$1 replica=$2 entry + local primary=$1 replica=$2 entry copied=0 umask 077 + collect_bundle_entries "$primary" while IFS= read -r entry; do cp -p "$primary/$entry" "$replica/$entry" - done < <(bundle_entries "$primary") + copied=$((copied + 1)) + done <<< "$BUNDLE_ENTRIES" + [ "$copied" -eq "$BUNDLE_MEMBER_COUNT" ] \ + || die "bundle copy read $copied of $BUNDLE_MEMBER_COUNT required members" } copy_ledger_atomically() { @@ -146,7 +231,7 @@ cmd_snapshot() { primary=$(canonical_dir "$primary") if [ -e "$replica_input" ]; then replica=$(canonical_dir "$replica_input") - [ "$primary" != "$replica" ] || die "primary and replica directories must differ" + require_noncontained_pair "$primary" "$replica" verify_exact_pair "$primary" "$replica" printf 'SNAPSHOT PASS (replica already identical: %s)\n' "$replica" return @@ -154,8 +239,10 @@ cmd_snapshot() { require_bundle "$primary" verify_with_primary "$primary" "$primary" parent=$(dirname "$replica_input") - base=$(basename "$replica_input") [ -d "$parent" ] || die "replica parent directory does not exist: $parent" + parent=$(canonical_dir "$parent") + base=$(basename "$replica_input") + require_noncontained_pair "$primary" "$parent/$base" stage=$(mktemp -d "$parent/.${base}.stage.XXXXXX") \ || die "could not create replica staging directory" trap 'rm -rf -- "$stage"' EXIT HUP INT TERM @@ -171,7 +258,7 @@ cmd_refresh() { local primary replica primary=$(canonical_dir "$1") replica=$(canonical_dir "$2") - [ "$primary" != "$replica" ] || die "primary and replica directories must differ" + require_noncontained_pair "$primary" "$replica" preflight_pair "$primary" "$replica" verify_with_primary "$primary" "$primary" verify_with_primary "$primary" "$replica" @@ -186,7 +273,7 @@ cmd_verify() { local primary replica primary=$(canonical_dir "$1") replica=$(canonical_dir "$2") - [ "$primary" != "$replica" ] || die "primary and replica directories must differ" + require_noncontained_pair "$primary" "$replica" preflight_pair "$primary" "$replica" if ! cmp -s "$primary/ledger.tsv" "$replica/ledger.tsv"; then verify_with_primary "$primary" "$primary" @@ -196,7 +283,7 @@ cmd_verify() { die "replica ledger.tsv diverges from primary" fi verify_exact_pair "$primary" "$replica" - printf 'REDUNDANCY VERIFY PASS (bundle byte-identical; every member has distinct lstat and stat identities)\n' + printf 'REDUNDANCY VERIFY PASS (exact 4-member manifest byte-read; primary and replica identity sets disjoint)\n' } case "${1:-}" in diff --git a/tests/fm-sovereign-ledger-redundancy.test.sh b/tests/fm-sovereign-ledger-redundancy.test.sh index 398332a657..d716a33eb5 100755 --- a/tests/fm-sovereign-ledger-redundancy.test.sh +++ b/tests/fm-sovereign-ledger-redundancy.test.sh @@ -63,6 +63,7 @@ REPLICA="$TMP/replica" make_bundle "$PRIMARY" "$TMP/sources" echo 'T1 complete, independent bundle preconditions' +check_fails_with 'verify REFUSES a nonexistent ledger directory' 'ledger directory does not exist' "$TOOL" verify "$TMP/absent-primary" "$TMP/absent-replica" mkdir -p "$TMP/missing-primary" check_fails_with 'snapshot REFUSES an incomplete primary bundle' 'ledger bundle is incomplete' "$TOOL" snapshot "$TMP/missing-primary" "$TMP/missing-replica" MISSING_CONTRACT="$TMP/missing-contract" @@ -79,17 +80,41 @@ check_ok 'snapshot CREATES an executable-bit fixture bundle' "$TOOL" snapshot "$ chmod -x "$EXECUTABLE/fm-sovereign-ledger.sh" check_fails_with 'verify REFUSES a non-executable replica verifier' 'ledger verifier is not executable' "$TOOL" verify "$PRIMARY" "$EXECUTABLE" chmod +x "$EXECUTABLE/fm-sovereign-ledger.sh" +FAILING_FIND_BIN="$TMP/failing-find-bin" +mkdir "$FAILING_FIND_BIN" +printf '%s\n' '#!/usr/bin/env bash' 'exit 71' > "$FAILING_FIND_BIN/find" +chmod +x "$FAILING_FIND_BIN/find" +check_fails_with 'verify REFUSES when exact bundle enumeration fails' 'could not enumerate ledger bundle' env PATH="$FAILING_FIND_BIN:$PATH" "$TOOL" verify "$PRIMARY" "$REPLICA" echo 'T2 staleness is loud and refresh accepts only an append-only prefix' add_ruling "$PRIMARY" "$TMP/sources" 5 check_fails_with 'verify names a verified stale replica and its refresh remedy' 'replica is a verified stale prefix; run refresh' "$TOOL" verify "$PRIMARY" "$REPLICA" check_ok 'refresh advances the verified append-only replica' "$TOOL" refresh "$PRIMARY" "$REPLICA" check_ok 'verify PASSES after refresh' "$TOOL" verify "$PRIMARY" "$REPLICA" +check_fails_with 'refresh REFUSES an equal-length replica rather than treating it as stale' 'replica is not a verified byte-exact append-only prefix of primary' "$TOOL" refresh "$PRIMARY" "$REPLICA" NONPREFIX="$TMP/non-prefix-replica" check_ok 'snapshot CREATES a non-prefix refresh fixture' "$TOOL" snapshot "$PRIMARY" "$NONPREFIX" { sed -n '2p' "$NONPREFIX/ledger.tsv"; sed -n '1p' "$NONPREFIX/ledger.tsv"; sed -n '3,$p' "$NONPREFIX/ledger.tsv"; } > "$NONPREFIX/reordered-ledger.tsv" mv "$NONPREFIX/reordered-ledger.tsv" "$NONPREFIX/ledger.tsv" check_fails_with 'refresh REFUSES a verifying replica that is not a byte-exact prefix' 'replica is not a verified byte-exact append-only prefix of primary' "$TOOL" refresh "$PRIMARY" "$NONPREFIX" +SHORT_NONPREFIX="$TMP/short-non-prefix-replica" +check_ok 'snapshot CREATES a shorter non-prefix refresh fixture' "$TOOL" snapshot "$PRIMARY" "$SHORT_NONPREFIX" +{ sed -n '2p' "$SHORT_NONPREFIX/ledger.tsv"; sed -n '1p' "$SHORT_NONPREFIX/ledger.tsv"; sed -n '3,4p' "$SHORT_NONPREFIX/ledger.tsv"; } > "$SHORT_NONPREFIX/reordered-ledger.tsv" +mv "$SHORT_NONPREFIX/reordered-ledger.tsv" "$SHORT_NONPREFIX/ledger.tsv" +check_fails_with 'refresh REFUSES a shorter verifying replica that is not byte-exact' 'replica is not a verified byte-exact append-only prefix of primary' "$TOOL" refresh "$PRIMARY" "$SHORT_NONPREFIX" +EMPTY_PREFIX_PRIMARY="$TMP/empty-prefix-primary" +EMPTY_PREFIX_REPLICA="$TMP/empty-prefix-replica" +make_bundle "$EMPTY_PREFIX_PRIMARY" "$TMP/empty-prefix-sources" +printf '%s\n' '#!/usr/bin/env bash' 'exit 0' > "$EMPTY_PREFIX_PRIMARY/fm-sovereign-ledger.sh" +chmod +x "$EMPTY_PREFIX_PRIMARY/fm-sovereign-ledger.sh" +check_ok 'snapshot CREATES an empty-prefix guard fixture' "$TOOL" snapshot "$EMPTY_PREFIX_PRIMARY" "$EMPTY_PREFIX_REPLICA" +: > "$EMPTY_PREFIX_REPLICA/ledger.tsv" +GNU_HEAD_BIN="$TMP/gnu-head-bin" +mkdir "$GNU_HEAD_BIN" +# shellcheck disable=SC2016 +printf '%s\n' '#!/usr/bin/env bash' 'if [ "${1:-}" = -n ] && [ "${2:-}" = 0 ]; then exit 0; fi' 'exec /usr/bin/head "$@"' > "$GNU_HEAD_BIN/head" +chmod +x "$GNU_HEAD_BIN/head" +check_fails_with 'refresh REFUSES an empty replica ledger under GNU head semantics even when the fixture verifier accepts it' 'replica is not a verified byte-exact append-only prefix of primary' env PATH="$GNU_HEAD_BIN:$PATH" "$TOOL" refresh "$EMPTY_PREFIX_PRIMARY" "$EMPTY_PREFIX_REPLICA" echo 'T3 source loss leaves the four required fixture rulings provable' for number in 1 2 3 4; do unlink "$TMP/sources/ruling-$number.md"; done @@ -144,7 +169,7 @@ check_fails 'verify FAILS when replica contract bytes diverge' "$TOOL" verify "$ check_fails 'snapshot REFUSES to overwrite a divergent replica' "$TOOL" snapshot "$PRIMARY" "$REPLICA" cp "$PRIMARY/CONTRACT.md" "$REPLICA/CONTRACT.md" printf 'planted\n' > "$REPLICA/EXTRA-CONTRACT.md" -check_fails_with 'verify REFUSES an unexpected replica file' 'replica bundle layout differs from primary' "$TOOL" verify "$PRIMARY" "$REPLICA" +check_fails_with 'verify REFUSES an unexpected replica file' 'must contain exactly 4 manifest members' "$TOOL" verify "$PRIMARY" "$REPLICA" unlink "$REPLICA/EXTRA-CONTRACT.md" check_ok 'verify PASSES after fixture restore' "$TOOL" verify "$PRIMARY" "$REPLICA" @@ -178,8 +203,8 @@ printf '%s\n' \ ' *) exit 70 ;;' \ 'esac' \ 'case "${IDENTITY_MODE}:${follow}" in' \ - ' lstat:false|stat:true) printf "shared:identity\\n" ;;' \ - ' *) printf "%s:identity\\n" "$side" ;;' \ + ' lstat:false|stat:true) printf "1:1\\n" ;;' \ + ' *) if [ "$side" = primary ]; then printf "1:2\\n"; else printf "1:3\\n"; fi ;;' \ 'esac' > "$IDENTITY_STAT_BIN/stat" chmod +x "$IDENTITY_STAT_BIN/stat" check_fails_with 'verify REFUSES a shared lstat identity even when stat identities differ' 'replica CONTRACT.md shares the primary lstat identity' env PATH="$IDENTITY_STAT_BIN:$PATH" IDENTITY_MODE=lstat IDENTITY_PRIMARY="$IDENTITY_PRIMARY" IDENTITY_REPLICA="$IDENTITY_REPLICA" "$TOOL" verify "$IDENTITY_PRIMARY" "$IDENTITY_REPLICA" @@ -194,5 +219,146 @@ for entry in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do check_fails_with "verify REFUSES a hard-linked $entry by lstat identity" "replica $entry shares the primary lstat identity" "$TOOL" verify "$PRIMARY" "$hardlink_replica" done +echo 'T11 adversarial ledger paths certify real copies that survive primary destruction' +exercise_destruction_shape() { + local label=$1 component=$2 root primary replica sources expected member number all_members all_rulings + root="$TMP/adversarial-paths/$component" + primary="$root/primary" + replica="$root/replica" + sources="$TMP/adversarial-sources/$label" + expected="$TMP/adversarial-expected/$label" + make_bundle "$primary" "$sources" + mkdir -p "$expected" + for member in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do + cp -p "$primary/$member" "$expected/$member" + done + check_ok "snapshot CREATES an independent replica under a $label path" "$TOOL" snapshot "$primary" "$replica" + check_ok "verify CERTIFIES the independent replica under a $label path" "$TOOL" verify "$primary" "$replica" + rm -rf -- "$primary" + all_members=true + for member in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do + if ! cmp -s "$expected/$member" "$replica/$member"; then all_members=false; fi + done + if "$all_members"; then + ok "all four bundle members survive primary destruction under a $label path" + else + bad "a bundle member did not survive primary destruction under a $label path" + fi + all_rulings=true + for number in 1 2 3 4; do + if ! diff -q <(LEDGER_DIR="$replica" "$replica/fm-sovereign-ledger.sh" text "ruling-$number") "$sources/ruling-$number.md" >/dev/null 2>&1; then + all_rulings=false + fi + done + if "$all_rulings"; then + ok "all four rulings remain byte-exact under a $label path" + else + bad "a ruling was not byte-exact after primary destruction under a $label path" + fi +} + +exercise_destruction_shape 'hash' 'Ledger #1' +exercise_destruction_shape 'unterminated bracket' 'Ledger [1' +exercise_destruction_shape 'space' 'Ledger space' +exercise_destruction_shape 'newline' $'Ledger\nnewline' +exercise_destruction_shape 'unicode' 'Ledger-船长-⚓' + +echo 'T12 the instrument distinguishes independent and non-independent replicas' +CONTROL_ROOT="$TMP/control #ledger" +CONTROL_PRIMARY="$CONTROL_ROOT/primary" +CONTROL_REPLICA="$CONTROL_ROOT/replica" +make_bundle "$CONTROL_PRIMARY" "$CONTROL_ROOT/sources" +mkdir -p "$CONTROL_REPLICA" +for entry in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do + ln -s "$CONTROL_PRIMARY/$entry" "$CONTROL_REPLICA/$entry" +done +check_fails_with 'positive control REFUSES a fully symlinked replica under a hash path' 'ledger bundle contains a symlink' "$TOOL" verify "$CONTROL_PRIMARY" "$CONTROL_REPLICA" + +echo 'T13 containment is an explicit property' +CONTAINED_PRIMARY="$TMP/containment-primary" +CONTAINED_REPLICA="$CONTAINED_PRIMARY/replica" +make_bundle "$CONTAINED_PRIMARY" "$TMP/containment-sources" +mkdir -p "$CONTAINED_REPLICA" +for entry in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do + cp -p "$CONTAINED_PRIMARY/$entry" "$CONTAINED_REPLICA/$entry" +done +check_fails_with 'verify REFUSES a replica contained by the primary explicitly' 'primary and replica directories must not contain one another' "$TOOL" verify "$CONTAINED_PRIMARY" "$CONTAINED_REPLICA" +rm -rf -- "$CONTAINED_REPLICA" +check_fails_with 'snapshot REFUSES a replica target contained by the primary explicitly' 'primary and replica directories must not contain one another' "$TOOL" snapshot "$CONTAINED_PRIMARY" "$CONTAINED_REPLICA" +OUTER_REPLICA="$TMP/outer-replica" +INNER_PRIMARY="$OUTER_REPLICA/primary" +make_bundle "$OUTER_REPLICA" "$TMP/outer-sources" +make_bundle "$INNER_PRIMARY" "$TMP/inner-sources" +check_fails_with 'verify REFUSES a primary contained by the replica explicitly' 'primary and replica directories must not contain one another' "$TOOL" verify "$INNER_PRIMARY" "$OUTER_REPLICA" + +echo 'T14 identity reads are fail-closed under BSD and GNU stat semantics' +DIALECT_PRIMARY="$TMP/dialect-primary" +DIALECT_REPLICA="$TMP/dialect-replica" +DIALECT_STAT_BIN="$TMP/dialect-stat-bin" +check_ok 'snapshot CREATES a stat-dialect primary fixture' "$TOOL" snapshot "$PRIMARY" "$DIALECT_PRIMARY" +check_ok 'snapshot CREATES a stat-dialect replica fixture' "$TOOL" snapshot "$PRIMARY" "$DIALECT_REPLICA" +mkdir "$DIALECT_STAT_BIN" +cat > "$DIALECT_STAT_BIN/stat" <<'STAT_STUB' +#!/usr/bin/env bash +set -euo pipefail +emit_identity() { + local follow=$1 target=$2 + perl -e ' + my ($follow, $path) = @ARGV; + my @identity = $follow eq "true" ? stat($path) : lstat($path); + exit 70 unless @identity; + printf "%d:%d\n", $identity[0], $identity[1]; + ' "$follow" "$target" +} +case "${STAT_FLAVOUR:-}" in + bsd) + if [ "$#" -eq 3 ] && [ "$1" = -f ] && [ "$2" = '%d:%i' ]; then + emit_identity false "$3" + elif [ "$#" -eq 4 ] && [ "$1" = -L ] && [ "$2" = -f ] && [ "$3" = '%d:%i' ]; then + emit_identity true "$4" + else + exit 64 + fi + ;; + gnu) + if { [ "$#" -eq 3 ] && [ "$1" = -f ]; } || { [ "$#" -eq 4 ] && [ "$1" = -L ] && [ "$2" = -f ]; }; then + printf ' File: "%s"\n' "${!#}" + exit 1 + elif [ "$#" -eq 3 ] && [ "$1" = -c ] && [ "$2" = '%d:%i' ]; then + emit_identity false "$3" + elif [ "$#" -eq 4 ] && [ "$1" = -L ] && [ "$2" = -c ] && [ "$3" = '%d:%i' ]; then + emit_identity true "$4" + else + exit 64 + fi + ;; + invalid) + printf 'identity unavailable for %s\n' "${!#}" + ;; + *) exit 65 ;; +esac +STAT_STUB +chmod +x "$DIALECT_STAT_BIN/stat" +for flavour in bsd gnu; do + check_ok "verify PASSES an independent replica with $flavour stat semantics" env PATH="$DIALECT_STAT_BIN:$PATH" STAT_FLAVOUR="$flavour" "$TOOL" verify "$DIALECT_PRIMARY" "$DIALECT_REPLICA" + dialect_hardlink="$TMP/dialect-hardlink-$flavour" + check_ok "snapshot CREATES the $flavour hard-link fixture" "$TOOL" snapshot "$PRIMARY" "$dialect_hardlink" + unlink "$dialect_hardlink/CONTRACT.md" + ln "$PRIMARY/CONTRACT.md" "$dialect_hardlink/CONTRACT.md" + check_fails_with "verify REFUSES a hard link with $flavour stat semantics" 'replica CONTRACT.md shares the primary lstat identity' env PATH="$DIALECT_STAT_BIN:$PATH" STAT_FLAVOUR="$flavour" "$TOOL" verify "$PRIMARY" "$dialect_hardlink" +done +check_fails_with 'verify REFUSES when stat cannot establish a numeric identity' 'could not establish a portable file identity' env PATH="$DIALECT_STAT_BIN:$PATH" STAT_FLAVOUR=invalid "$TOOL" verify "$DIALECT_PRIMARY" "$DIALECT_REPLICA" + +echo 'T15 the complete replica and primary inode sets must be disjoint' +CROSS_PRIMARY="$TMP/cross-primary" +CROSS_REPLICA="$TMP/cross-replica" +make_bundle "$CROSS_PRIMARY" "$TMP/cross-sources" +cp "$CROSS_PRIMARY/CONTRACT.md" "$CROSS_PRIMARY/tests.sh" +chmod +x "$CROSS_PRIMARY/tests.sh" +check_ok 'snapshot CREATES a cross-member identity fixture' "$TOOL" snapshot "$CROSS_PRIMARY" "$CROSS_REPLICA" +unlink "$CROSS_REPLICA/CONTRACT.md" +ln "$CROSS_PRIMARY/tests.sh" "$CROSS_REPLICA/CONTRACT.md" +check_fails_with 'verify REFUSES a replica member sharing storage with a different primary member' 'replica CONTRACT.md shares storage with primary member tests.sh' "$TOOL" verify "$CROSS_PRIMARY" "$CROSS_REPLICA" + printf '\n%s passed, %s failed\n' "$pass" "$fail" [ "$fail" -eq 0 ] From c3f26f2f6eadf9a9a9abdcbb3ba4cb58c1218a33 Mon Sep 17 00:00:00 2001 From: 420tombombadil Date: Sat, 15 Aug 2026 16:51:33 -0600 Subject: [PATCH 05/19] no-mistakes(review): Add fixture-scoped sovereign ledger mutation evidence --- docs/documentation-audiences.json | 4 + .../sovereign-ledger-redundancy-mutation.md | 88 +++++++ ...fm-sovereign-ledger-redundancy.mutation.sh | 224 ++++++++++++++++++ 3 files changed, 316 insertions(+) create mode 100644 docs/verification/sovereign-ledger-redundancy-mutation.md create mode 100755 tests/fm-sovereign-ledger-redundancy.mutation.sh diff --git a/docs/documentation-audiences.json b/docs/documentation-audiences.json index 06a08c124a..1dd61d410a 100644 --- a/docs/documentation-audiences.json +++ b/docs/documentation-audiences.json @@ -356,6 +356,10 @@ "path": "docs/verification/runtime-backends.md", "audience": "maintainer-verification" }, + { + "path": "docs/verification/sovereign-ledger-redundancy-mutation.md", + "audience": "maintainer-verification" + }, { "path": "docs/verification/stow-memory.md", "audience": "maintainer-verification" diff --git a/docs/verification/sovereign-ledger-redundancy-mutation.md b/docs/verification/sovereign-ledger-redundancy-mutation.md new file mode 100644 index 0000000000..436b96ca82 --- /dev/null +++ b/docs/verification/sovereign-ledger-redundancy-mutation.md @@ -0,0 +1,88 @@ +# Sovereign ledger redundancy mutation evidence + +Audience: maintainer verification. + +Verified on 2026-08-15 against the R4 sovereign-ledger redundancy implementation. +The owned denominator is 72 enforcing clauses, ten more than round 3's 62 attempted mutants because R4 added exact enumeration and read denominators, portable BSD/GNU identity selection, containment rejection, and full cross-member inode-set enforcement. +Every run copied the implementation under one scoped `mktemp -d`; the live ledger, `data/`, and `state/` were never inputs or targets. + +```sh +tests/fm-sovereign-ledger-redundancy.mutation.sh --write-evidence docs/verification/sovereign-ledger-redundancy-mutation.md +``` + +Observed summary: `killed=47 survived=25 void=0 denominator=72`; the intentional no-op control recorded `substitutions=1 status=0 outcome=SURVIVED`. + +| Mutant | Stable exact clause anchor | Substitutions | Outcome | Status | Unenforced claim when survived | +| --- | --- | ---: | --- | ---: | --- | +| `M001` | `strict-mode.errexit` | 1 | SURVIVED | 0 | Shell failures are not allowed to fall through. | +| `M002` | `manifest.denominator` | 1 | KILLED | 1 | - | +| `M003` | `canonical.directory-exists` | 1 | KILLED | 1 | - | +| `M004` | `canonical.physical-path` | 1 | SURVIVED | 0 | Containment uses physical directory paths. | +| `M005` | `manifest.fourth-member` | 1 | KILLED | 1 | - | +| `M006` | `enumeration.minimum-depth` | 1 | KILLED | 1 | - | +| `M007` | `enumeration.maximum-depth` | 1 | SURVIVED | 0 | Only top-level members are counted. | +| `M008` | `enumeration.record-per-entry` | 1 | KILLED | 1 | - | +| `M009` | `enumeration.line-denominator` | 1 | KILLED | 1 | - | +| `M010` | `enumeration.exact-count` | 1 | KILLED | 1 | - | +| `M011` | `enumeration.known-manifest` | 1 | KILLED | 1 | - | +| `M012` | `require.known-manifest` | 1 | KILLED | 1 | - | +| `M013` | `require.member-exists` | 1 | KILLED | 1 | - | +| `M014` | `require.no-symlink` | 1 | KILLED | 1 | - | +| `M015` | `require.regular-file` | 1 | KILLED | 124 | - | +| `M016` | `require.read-denominator` | 1 | KILLED | 1 | - | +| `M017` | `require.enumerates-directory` | 1 | SURVIVED | 0 | Bundle validation independently enumerates the directory. | +| `M018` | `require.exact-read-count` | 1 | SURVIVED | 0 | The manifest read count must equal four. | +| `M019` | `require.executable-verifier` | 1 | KILLED | 1 | - | +| `M020` | `layout.enumerate-primary` | 1 | SURVIVED | 0 | Layout comparison enumerates the primary independently. | +| `M021` | `layout.capture-primary` | 1 | KILLED | 1 | - | +| `M022` | `layout.enumerate-replica` | 1 | SURVIVED | 0 | Layout comparison enumerates the replica independently. | +| `M023` | `layout.capture-replica` | 1 | KILLED | 1 | - | +| `M024` | `layout.equal-manifests` | 1 | SURVIVED | 0 | Primary and replica layouts must match. | +| `M025` | `bytes.enumerate-primary` | 1 | SURVIVED | 0 | Byte comparison owns a fresh manifest enumeration. | +| `M026` | `bytes.skip-denominator` | 1 | KILLED | 1 | - | +| `M027` | `bytes.skip-only-selected` | 1 | KILLED | 1 | - | +| `M028` | `bytes.quiet-compare` | 1 | SURVIVED | 0 | Member comparison uses cmp status as its verdict. | +| `M029` | `bytes.reject-difference` | 1 | KILLED | 1 | - | +| `M030` | `bytes.read-denominator` | 1 | KILLED | 1 | - | +| `M031` | `bytes.exact-read-count` | 1 | SURVIVED | 0 | The byte-read count must equal its owned denominator. | +| `M032` | `identity.follow-selection` | 1 | KILLED | 1 | - | +| `M033` | `identity.bsd-follow` | 1 | KILLED | 1 | - | +| `M034` | `identity.gnu-follow` | 1 | SURVIVED | 0 | GNU followed identity uses stat -L. | +| `M035` | `identity.bsd-lstat` | 1 | KILLED | 1 | - | +| `M036` | `identity.gnu-lstat` | 1 | SURVIVED | 0 | GNU non-followed identity uses lstat semantics. | +| `M037` | `identity.numeric-shape` | 1 | KILLED | 1 | - | +| `M038` | `identity.lstat-wrapper` | 1 | KILLED | 1 | - | +| `M039` | `identity.stat-wrapper` | 1 | KILLED | 1 | - | +| `M040` | `identity.enumerate-primary` | 1 | SURVIVED | 0 | Identity verification enumerates the owned manifest independently. | +| `M041` | `identity.primary-lstat-read` | 1 | KILLED | 1 | - | +| `M042` | `identity.replica-lstat-read` | 1 | KILLED | 1 | - | +| `M043` | `identity.primary-stat-read` | 1 | KILLED | 1 | - | +| `M044` | `identity.replica-stat-read` | 1 | KILLED | 1 | - | +| `M045` | `identity.read-denominator` | 1 | KILLED | 1 | - | +| `M046` | `identity.exact-read-count` | 1 | SURVIVED | 0 | Identity reads must cover exactly four members. | +| `M047` | `identity.replica-cross-product` | 1 | KILLED | 1 | - | +| `M048` | `identity.primary-cross-product` | 1 | KILLED | 1 | - | +| `M049` | `identity.lstat-disjoint` | 1 | KILLED | 1 | - | +| `M050` | `identity.lstat-member-attribution` | 1 | KILLED | 1 | - | +| `M051` | `identity.stat-disjoint` | 1 | KILLED | 1 | - | +| `M052` | `identity.stat-member-attribution` | 1 | KILLED | 1 | - | +| `M053` | `containment.distinct-paths` | 1 | KILLED | 1 | - | +| `M054` | `containment.primary-outside-replica` | 1 | KILLED | 1 | - | +| `M055` | `containment.replica-outside-primary` | 1 | KILLED | 1 | - | +| `M056` | `verifier.public-verify-command` | 1 | KILLED | 1 | - | +| `M057` | `prefix.primary-line-count` | 1 | KILLED | 1 | - | +| `M058` | `prefix.replica-line-count` | 1 | KILLED | 1 | - | +| `M059` | `prefix.nonempty-replica` | 1 | KILLED | 1 | - | +| `M060` | `prefix.strictly-shorter` | 1 | KILLED | 1 | - | +| `M061` | `prefix.leading-bytes` | 1 | KILLED | 1 | - | +| `M062` | `preflight.require-primary` | 1 | SURVIVED | 0 | Pair preflight validates the primary bundle. | +| `M063` | `preflight.layout` | 1 | SURVIVED | 0 | Pair preflight compares the exact layouts. | +| `M064` | `preflight.nonledger-bytes` | 1 | SURVIVED | 0 | Pair preflight compares replica-controlled code before execution. | +| `M065` | `exact.all-bytes` | 1 | SURVIVED | 0 | Exact verification compares all four members. | +| `M066` | `exact.disjoint-identities` | 1 | KILLED | 1 | - | +| `M067` | `exact.verify-primary` | 1 | SURVIVED | 0 | Exact verification validates the primary ledger. | +| `M068` | `exact.verify-replica` | 1 | SURVIVED | 0 | Exact verification validates replica ledger data. | +| `M069` | `copy.enumerate-primary` | 1 | SURVIVED | 0 | Copying starts from an independently enumerated manifest. | +| `M070` | `copy.preserve-mode` | 1 | SURVIVED | 0 | Bundle copying preserves required executable modes. | +| `M071` | `copy.private-umask` | 1 | SURVIVED | 0 | Bundle staging uses a private creation mask. | +| `M072` | `copy.exact-read-count` | 1 | SURVIVED | 0 | Copying must cover exactly four members. | diff --git a/tests/fm-sovereign-ledger-redundancy.mutation.sh b/tests/fm-sovereign-ledger-redundancy.mutation.sh new file mode 100755 index 0000000000..f37920120d --- /dev/null +++ b/tests/fm-sovereign-ledger-redundancy.mutation.sh @@ -0,0 +1,224 @@ +#!/usr/bin/env bash +# Run the owned mutation population for sovereign-ledger redundancy enforcement. +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +SOURCE="$ROOT/bin/fm-sovereign-ledger-redundancy.sh" +TEST="$ROOT/tests/fm-sovereign-ledger-redundancy.test.sh" +EVIDENCE= +if [ "${1:-}" = --write-evidence ]; then + [ "$#" -eq 2 ] || { printf 'usage: %s [--write-evidence ]\n' "$0" >&2; exit 2; } + EVIDENCE=$2 +elif [ "$#" -ne 0 ]; then + printf 'usage: %s [--write-evidence ]\n' "$0" >&2 + exit 2 +fi + +TMP="$(mktemp -d)" +trap 'rm -rf -- "$TMP"' EXIT +RESULTS="$TMP/results.tsv" +killed=0 +survived=0 +void=0 +denominator=0 + +apply_mutation() { + local target=$1 line=$2 from=$3 to=$4 count_file=$5 + LINE_NUMBER=$line FROM_TEXT=$from TO_TEXT=$to COUNT_FILE=$count_file perl -0pi -e ' + BEGIN { + $line_number = $ENV{LINE_NUMBER}; + $from = $ENV{FROM_TEXT}; + $to = $ENV{TO_TEXT}; + $count_file = $ENV{COUNT_FILE}; + } + @lines = split(/(?<=\n)/, $_, -1); + $line = $lines[$line_number - 1] // ""; + $count = 0; + $offset = 0; + while (($found = index($line, $from, $offset)) >= 0) { + $count++; + $offset = $found + length($from); + } + if ($count == 1) { + $line =~ s/\Q$from\E/$to/; + $lines[$line_number - 1] = $line; + $_ = join("", @lines); + } + open(my $fh, ">", $count_file) or die $!; + print {$fh} "$count\n"; + close($fh) or die $!; + ' "$target" +} + +run_mutation_test() { + local target=$1 output=$2 + TOOL=$target perl -e ' + my $timeout = shift; + my $pid = fork; + die "fork failed" unless defined $pid; + if (!$pid) { + setpgrp(0, 0); + exec @ARGV; + } + local $SIG{ALRM} = sub { + kill "TERM", -$pid; + select undef, undef, undef, 0.2; + kill "KILL", -$pid; + waitpid $pid, 0; + exit 124; + }; + alarm $timeout; + waitpid $pid, 0; + alarm 0; + exit($? >> 8); + ' 30 "$TEST" >"$output" 2>&1 +} + +mutant() { + local id=$1 anchor=$2 line=$3 from=$4 to=$5 claim=$6 target count_file count outcome status + denominator=$((denominator + 1)) + target="$TMP/$id.sh" + count_file="$TMP/$id.count" + cp "$SOURCE" "$target" + apply_mutation "$target" "$line" "$from" "$to" "$count_file" + count=$(cat "$count_file") + if [ "$count" -ne 1 ]; then + outcome=VOID + status=- + void=$((void + 1)) + else + set +e + run_mutation_test "$target" "$TMP/$id.out" + status=$? + set -e + if [ "$status" -eq 0 ]; then + outcome=SURVIVED + survived=$((survived + 1)) + else + outcome=KILLED + killed=$((killed + 1)) + fi + fi + printf '%s\t%s\t%s\t%s\t%s\t%s\n' "$id" "$anchor" "$count" "$outcome" "$status" "$claim" >> "$RESULTS" + printf '%s %s substitutions=%s status=%s\n' "$id" "$outcome" "$count" "$status" +} + +mutant M001 strict-mode.errexit 14 'set -euo pipefail' 'set -uo pipefail' 'Shell failures are not allowed to fall through.' +mutant M002 manifest.denominator 16 'BUNDLE_MEMBER_COUNT=4' 'BUNDLE_MEMBER_COUNT=5' 'The owned bundle denominator is exactly four.' +mutant M003 canonical.directory-exists 30 '|| die "ledger directory does not exist: $1"' '|| :' 'Only an existing ledger directory can be canonicalized.' +mutant M004 canonical.physical-path 33 'pwd -P' 'pwd -L' 'Containment uses physical directory paths.' +mutant M005 manifest.fourth-member 38 'tests.sh' 'tests.missing' 'tests.sh is a required manifest member.' +mutant M006 enumeration.minimum-depth 43 '-mindepth 1' '-mindepth 2' 'Every top-level member is counted.' +mutant M007 enumeration.maximum-depth 43 '-maxdepth 1' '-maxdepth 2' 'Only top-level members are counted.' +mutant M008 enumeration.record-per-entry 44 'printf "x\n"' ':' 'Enumeration records every discovered entry.' +mutant M009 enumeration.line-denominator 45 'wc -l' 'wc -c' 'Enumeration counts records rather than bytes.' +mutant M010 enumeration.exact-count 49 'die "ledger bundle must contain exactly $BUNDLE_MEMBER_COUNT manifest members, found $count: $dir"' ':' 'Enumeration accepts only the owned denominator.' +mutant M011 enumeration.known-manifest 50 'BUNDLE_ENTRIES=$(bundle_manifest)' 'BUNDLE_ENTRIES=' 'Enumeration resets to the exact known manifest.' +mutant M012 require.known-manifest 55 'BUNDLE_ENTRIES=$(bundle_manifest)' 'BUNDLE_ENTRIES=' 'Bundle validation starts from the known manifest.' +mutant M013 require.member-exists 57 '|| die "ledger bundle is incomplete: missing $dir/$entry"' '|| :' 'Every known manifest member must exist.' +mutant M014 require.no-symlink 58 '&& die "ledger bundle contains a symlink: $dir/$entry"' '&& :' 'A manifest member cannot be a symlink.' +mutant M015 require.regular-file 59 '|| die "ledger bundle contains a non-regular file: $dir/$entry"' '|| :' 'Every manifest member must be a regular file.' +mutant M016 require.read-denominator 60 '+ 1' '+ 0' 'Every manifest member contributes to the read denominator.' +mutant M017 require.enumerates-directory 62 'collect_bundle_entries "$dir"' ':' 'Bundle validation independently enumerates the directory.' +mutant M018 require.exact-read-count 64 'die "ledger bundle manifest check read $checked of $BUNDLE_MEMBER_COUNT members: $dir"' ':' 'The manifest read count must equal four.' +mutant M019 require.executable-verifier 65 '|| die "ledger verifier is not executable: $dir/fm-sovereign-ledger.sh"' '|| :' 'The ledger verifier must be executable.' +mutant M020 layout.enumerate-primary 70 'collect_bundle_entries "$primary"' ':' 'Layout comparison enumerates the primary independently.' +mutant M021 layout.capture-primary 71 'primary_entries=$BUNDLE_ENTRIES' 'primary_entries=' 'Layout comparison retains the primary manifest.' +mutant M022 layout.enumerate-replica 72 'collect_bundle_entries "$replica"' ':' 'Layout comparison enumerates the replica independently.' +mutant M023 layout.capture-replica 73 'replica_entries=$BUNDLE_ENTRIES' 'replica_entries=' 'Layout comparison retains the replica manifest.' +mutant M024 layout.equal-manifests 74 '|| die "replica bundle layout differs from primary"' '|| :' 'Primary and replica layouts must match.' +mutant M025 bytes.enumerate-primary 79 'collect_bundle_entries "$primary"' ':' 'Byte comparison owns a fresh manifest enumeration.' +mutant M026 bytes.skip-denominator 80 '- 1' '- 0' 'Skipping ledger.tsv reduces the required read denominator once.' +mutant M027 bytes.skip-only-selected 82 '&&' '||' 'Only the explicitly skipped member bypasses comparison.' +mutant M028 bytes.quiet-compare 83 'cmp -s' 'cmp' 'Member comparison uses cmp status as its verdict.' +mutant M029 bytes.reject-difference 84 'die "replica differs from primary: $entry"' ':' 'Any compared member byte difference is refused.' +mutant M030 bytes.read-denominator 85 '+ 1' '+ 0' 'Each compared member contributes to the read denominator.' +mutant M031 bytes.exact-read-count 88 'die "byte comparison read $compared of $expected required bundle members"' ':' 'The byte-read count must equal its owned denominator.' +mutant M032 identity.follow-selection 93 '= true' '= false' 'Followed and non-followed identity reads select distinct stat modes.' +mutant M033 identity.bsd-follow 94 'stat -L -f' 'stat -f' 'BSD followed identity uses stat -L.' +mutant M034 identity.gnu-follow 96 'stat -L -c' 'stat -c' 'GNU followed identity uses stat -L.' +mutant M035 identity.bsd-lstat 101 'stat -f' 'stat -L -f' 'BSD non-followed identity uses lstat semantics.' +mutant M036 identity.gnu-lstat 103 'stat -c' 'stat -L -c' 'GNU non-followed identity uses lstat semantics.' +mutant M037 identity.numeric-shape 108 "'^[0-9]+:[0-9]+$'" "'.*'" 'Only a numeric device and inode pair is accepted.' +mutant M038 identity.lstat-wrapper 113 'false' 'true' 'The lstat wrapper requests non-followed identity.' +mutant M039 identity.stat-wrapper 117 'true' 'false' 'The stat wrapper requests followed identity.' +mutant M040 identity.enumerate-primary 124 'collect_bundle_entries "$primary"' ':' 'Identity verification enumerates the owned manifest independently.' +mutant M041 identity.primary-lstat-read 127 'file_lstat_identity' 'file_stat_identity' 'Every primary member has a non-followed identity read.' +mutant M042 identity.replica-lstat-read 129 'file_lstat_identity' 'file_stat_identity' 'Every replica member has a non-followed identity read.' +mutant M043 identity.primary-stat-read 131 'file_stat_identity' 'file_lstat_identity' 'Every primary member has a followed identity read.' +mutant M044 identity.replica-stat-read 133 'file_stat_identity' 'file_lstat_identity' 'Every replica member has a followed identity read.' +mutant M045 identity.read-denominator 139 '+ 1' '+ 0' 'Each identity-read member contributes to the denominator.' +mutant M046 identity.exact-read-count 142 'die "identity verification read $index of $BUNDLE_MEMBER_COUNT required bundle members"' ':' 'Identity reads must cover exactly four members.' +mutant M047 identity.replica-cross-product 143 '< BUNDLE_MEMBER_COUNT' '< 1' 'Every replica inode participates in the cross-product.' +mutant M048 identity.primary-cross-product 144 '< BUNDLE_MEMBER_COUNT' '< 1' 'Every primary inode participates in the cross-product.' +mutant M049 identity.lstat-disjoint 145 'if [ "${replica_lstats[$replica_index]}" = "${primary_lstats[$primary_index]}" ]; then' 'if false; then' 'No replica lstat identity may overlap a primary identity.' +mutant M050 identity.lstat-member-attribution 146 'if [ "${entries[$replica_index]}" = "${entries[$primary_index]}" ]; then' 'if false; then' 'Same-member lstat overlap is attributed precisely.' +mutant M051 identity.stat-disjoint 151 'if [ "${replica_stats[$replica_index]}" = "${primary_stats[$primary_index]}" ]; then' 'if false; then' 'No replica stat identity may overlap a primary identity.' +mutant M052 identity.stat-member-attribution 152 'if [ "${entries[$replica_index]}" = "${entries[$primary_index]}" ]; then' 'if false; then' 'Same-member stat overlap is attributed precisely.' +mutant M053 containment.distinct-paths 163 '[ "$primary" != "$replica" ] || die "primary and replica directories must differ"' ':' 'Primary and replica paths must differ.' +mutant M054 containment.primary-outside-replica 164 '"$replica/"*' '"$replica/never/"*' 'The primary cannot be contained by the replica.' +mutant M055 containment.replica-outside-primary 165 '"$primary/"*' '"$primary/never/"*' 'The replica cannot be contained by the primary.' +mutant M056 verifier.public-verify-command 170 'LEDGER_DIR="$subject" "$primary/fm-sovereign-ledger.sh" verify >/dev/null' ':' 'Bundle validity is established through the primary verifier.' +mutant M057 prefix.primary-line-count 176 'wc -l' 'wc -c' 'Prefix comparison measures primary ledger records.' +mutant M058 prefix.replica-line-count 177 'wc -l' 'wc -c' 'Prefix comparison measures replica ledger records.' +mutant M059 prefix.nonempty-replica 178 '[ "$replica_lines" -gt 0 ]' 'true' 'An empty replica is never an append-only prefix.' +mutant M060 prefix.strictly-shorter 179 '[ "$primary_lines" -gt "$replica_lines" ]' 'true' 'A prefix replica must be strictly shorter than primary.' +mutant M061 prefix.leading-bytes 180 'head -n' 'tail -n' 'Prefix comparison uses the leading primary records.' +mutant M062 preflight.require-primary 185 'require_bundle "$primary"' ':' 'Pair preflight validates the primary bundle.' +mutant M063 preflight.layout 187 'compare_layout "$primary" "$replica"' ':' 'Pair preflight compares the exact layouts.' +mutant M064 preflight.nonledger-bytes 188 'compare_files "$primary" "$replica" ledger.tsv' ':' 'Pair preflight compares replica-controlled code before execution.' +mutant M065 exact.all-bytes 194 'compare_files "$primary" "$replica"' 'compare_files "$primary" "$replica" ledger.tsv' 'Exact verification compares all four members.' +mutant M066 exact.disjoint-identities 195 'require_independent_bundle_files "$primary" "$replica"' ':' 'Exact verification requires disjoint inode sets.' +mutant M067 exact.verify-primary 196 'verify_with_primary "$primary" "$primary"' ':' 'Exact verification validates the primary ledger.' +mutant M068 exact.verify-replica 197 'verify_with_primary "$primary" "$replica"' ':' 'Exact verification validates replica ledger data.' +mutant M069 copy.enumerate-primary 203 'collect_bundle_entries "$primary"' ':' 'Copying starts from an independently enumerated manifest.' +mutant M070 copy.preserve-mode 205 'cp -p' 'cp' 'Bundle copying preserves required executable modes.' +mutant M071 copy.private-umask 202 'umask 077' 'umask 022' 'Bundle staging uses a private creation mask.' +mutant M072 copy.exact-read-count 209 'die "bundle copy read $copied of $BUNDLE_MEMBER_COUNT required members"' ':' 'Copying must cover exactly four members.' + +[ "$denominator" -eq 72 ] || { printf 'invalid owned denominator: %s\n' "$denominator" >&2; exit 1; } + +control="$TMP/CONTROL.sh" +control_count="$TMP/CONTROL.count" +cp "$SOURCE" "$control" +apply_mutation "$control" 2 'make, advance' 'make, advance' "$control_count" +control_substitutions=$(cat "$control_count") +set +e +run_mutation_test "$control" "$TMP/CONTROL.out" +control_status=$? +set -e +if [ "$control_substitutions" -eq 1 ] && [ "$control_status" -eq 0 ]; then + control_outcome=SURVIVED +else + control_outcome=FAILED +fi +printf 'CONTROL %s substitutions=%s status=%s\n' "$control_outcome" "$control_substitutions" "$control_status" + +if [ -n "$EVIDENCE" ]; then + case "$EVIDENCE" in /*) ;; *) EVIDENCE="$PWD/$EVIDENCE" ;; esac + case "$EVIDENCE" in "$ROOT"/*) ;; *) printf 'evidence path must stay under %s\n' "$ROOT" >&2; exit 2 ;; esac + { + printf '# Sovereign ledger redundancy mutation evidence\n\n' + printf 'Audience: maintainer verification.\n\n' + printf 'Verified on 2026-08-15 against the R4 sovereign-ledger redundancy implementation.\n' + printf 'The owned denominator is 72 enforcing clauses, ten more than round 3\047s 62 attempted mutants because R4 added exact enumeration and read denominators, portable BSD/GNU identity selection, containment rejection, and full cross-member inode-set enforcement.\n' + printf 'Every run copied the implementation under one scoped `mktemp -d`; the live ledger, `data/`, and `state/` were never inputs or targets.\n\n' + printf '```sh\n' + printf 'tests/fm-sovereign-ledger-redundancy.mutation.sh --write-evidence docs/verification/sovereign-ledger-redundancy-mutation.md\n' + printf '```\n\n' + printf 'Observed summary: `killed=%s survived=%s void=%s denominator=%s`; the intentional no-op control recorded `substitutions=%s status=%s outcome=%s`.\n\n' "$killed" "$survived" "$void" "$denominator" "$control_substitutions" "$control_status" "$control_outcome" + printf '| Mutant | Stable exact clause anchor | Substitutions | Outcome | Status | Unenforced claim when survived |\n' + printf '| --- | --- | ---: | --- | ---: | --- |\n' + while IFS=$'\t' read -r id anchor count outcome status claim; do + if [ "$outcome" != SURVIVED ]; then claim=-; fi + printf '| `%s` | `%s` | %s | %s | %s | %s |\n' "$id" "$anchor" "$count" "$outcome" "$status" "$claim" + done < "$RESULTS" + } > "$EVIDENCE" +fi + +printf 'MUTATION SUMMARY killed=%s survived=%s void=%s denominator=%s\n' "$killed" "$survived" "$void" "$denominator" +[ "$void" -eq 0 ] +[ "$killed" -eq 47 ] +[ "$survived" -eq 25 ] +[ "$control_substitutions" -eq 1 ] +[ "$control_outcome" = SURVIVED ] From 7f6fa776c89dda76c241ca8d964d361429a59c1d Mon Sep 17 00:00:00 2001 From: 420tombombadil Date: Sat, 15 Aug 2026 17:17:07 -0600 Subject: [PATCH 06/19] no-mistakes(review): Contain sovereign ledger evidence publication --- .../sovereign-ledger-redundancy-mutation.md | 20 +- ...ereign-ledger-evidence-publish.mutation.sh | 62 ++++++ ...fm-sovereign-ledger-redundancy.mutation.sh | 177 +++++++++++++++++- 3 files changed, 248 insertions(+), 11 deletions(-) create mode 100755 tests/fm-sovereign-ledger-evidence-publish.mutation.sh diff --git a/docs/verification/sovereign-ledger-redundancy-mutation.md b/docs/verification/sovereign-ledger-redundancy-mutation.md index 436b96ca82..fbbab91fe7 100644 --- a/docs/verification/sovereign-ledger-redundancy-mutation.md +++ b/docs/verification/sovereign-ledger-redundancy-mutation.md @@ -7,9 +7,11 @@ The owned denominator is 72 enforcing clauses, ten more than round 3's 62 attemp Every run copied the implementation under one scoped `mktemp -d`; the live ledger, `data/`, and `state/` were never inputs or targets. ```sh -tests/fm-sovereign-ledger-redundancy.mutation.sh --write-evidence docs/verification/sovereign-ledger-redundancy-mutation.md +tests/fm-sovereign-ledger-redundancy.mutation.sh --write-evidence sovereign-ledger-redundancy-mutation.candidate.md ``` +Evidence publication rejects existing destinations, so the candidate is reviewed against this record before replacement rather than overwriting it in place. + Observed summary: `killed=47 survived=25 void=0 denominator=72`; the intentional no-op control recorded `substitutions=1 status=0 outcome=SURVIVED`. | Mutant | Stable exact clause anchor | Substitutions | Outcome | Status | Unenforced claim when survived | @@ -86,3 +88,19 @@ Observed summary: `killed=47 survived=25 void=0 denominator=72`; the intentional | `M070` | `copy.preserve-mode` | 1 | SURVIVED | 0 | Bundle copying preserves required executable modes. | | `M071` | `copy.private-umask` | 1 | SURVIVED | 0 | Bundle staging uses a private creation mask. | | `M072` | `copy.exact-read-count` | 1 | SURVIVED | 0 | Copying must cover exactly four members. | + +## Evidence publication containment + +The publication chokepoint was verified with scoped temporary fixtures on 2026-08-15. + +```sh +tests/fm-sovereign-ledger-evidence-publish.mutation.sh +``` + +Observed bounded output: + +```text +CONTAINMENT FIXTURES passed=10 failed=0 +PUBLISH MUTANT P001 KILLED substitutions=1 +PUBLISH MUTATION SUMMARY enforcing_lines=1 killed=1 survived=0 void=0 denominator=1 +``` diff --git a/tests/fm-sovereign-ledger-evidence-publish.mutation.sh b/tests/fm-sovereign-ledger-evidence-publish.mutation.sh new file mode 100755 index 0000000000..e58435a4b5 --- /dev/null +++ b/tests/fm-sovereign-ledger-evidence-publish.mutation.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# Verify evidence-publication containment and its enforcing chokepoint. +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +SUBJECT="$ROOT/tests/fm-sovereign-ledger-redundancy.mutation.sh" +TMP="$(mktemp -d)" +trap 'rm -rf -- "$TMP"' EXIT +killed=0 +survived=0 +void=0 +denominator=0 + +"$SUBJECT" --self-test-evidence-containment + +publish_mutant() { + local id=$1 from=$2 to=$3 mutant count_file substitutions outcome + denominator=$((denominator + 1)) + mutant="$TMP/$id.sh" + count_file="$TMP/$id.substitutions" + cp "$SUBJECT" "$mutant" + chmod +x "$mutant" + FROM_TEXT=$from TO_TEXT=$to COUNT_FILE=$count_file perl -0pi -e ' + BEGIN { + $from = $ENV{FROM_TEXT}; + $to = $ENV{TO_TEXT}; + $count_file = $ENV{COUNT_FILE}; + } + $count = 0; + $offset = 0; + while (($found = index($_, $from, $offset)) >= 0) { + $count++; + $offset = $found + length($from); + } + s/\Q$from\E/$to/ if $count == 1; + open(my $fh, ">", $count_file) or die $!; + print {$fh} "$count\n"; + close($fh) or die $!; + ' "$mutant" + substitutions=$(cat "$count_file") + if [ "$substitutions" -ne 1 ]; then + outcome=VOID + void=$((void + 1)) + elif "$mutant" --self-test-evidence-containment > "$TMP/$id.out" 2>&1; then + outcome=SURVIVED + survived=$((survived + 1)) + else + outcome=KILLED + killed=$((killed + 1)) + fi + printf 'PUBLISH MUTANT %s %s substitutions=%s\n' "$id" "$outcome" "$substitutions" +} + +publish_mutant P001 \ + 'evidence_validate_destination "$scope" "$relative" "$destination" || return 1' \ + ':' +"$SUBJECT" --self-test-evidence-containment >/dev/null +printf 'PUBLISH MUTATION SUMMARY enforcing_lines=1 killed=%s survived=%s void=%s denominator=%s\n' "$killed" "$survived" "$void" "$denominator" +[ "$denominator" -eq 1 ] +[ "$killed" -eq 1 ] +[ "$survived" -eq 0 ] +[ "$void" -eq 0 ] diff --git a/tests/fm-sovereign-ledger-redundancy.mutation.sh b/tests/fm-sovereign-ledger-redundancy.mutation.sh index f37920120d..a12ae3349a 100755 --- a/tests/fm-sovereign-ledger-redundancy.mutation.sh +++ b/tests/fm-sovereign-ledger-redundancy.mutation.sh @@ -6,11 +6,15 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" SOURCE="$ROOT/bin/fm-sovereign-ledger-redundancy.sh" TEST="$ROOT/tests/fm-sovereign-ledger-redundancy.test.sh" EVIDENCE= -if [ "${1:-}" = --write-evidence ]; then - [ "$#" -eq 2 ] || { printf 'usage: %s [--write-evidence ]\n' "$0" >&2; exit 2; } +MODE=mutation +if [ "${1:-}" = --self-test-evidence-containment ]; then + [ "$#" -eq 1 ] || { printf 'usage: %s [--write-evidence |--self-test-evidence-containment]\n' "$0" >&2; exit 2; } + MODE=containment +elif [ "${1:-}" = --write-evidence ]; then + [ "$#" -eq 2 ] || { printf 'usage: %s [--write-evidence |--self-test-evidence-containment]\n' "$0" >&2; exit 2; } EVIDENCE=$2 elif [ "$#" -ne 0 ]; then - printf 'usage: %s [--write-evidence ]\n' "$0" >&2 + printf 'usage: %s [--write-evidence |--self-test-evidence-containment]\n' "$0" >&2 exit 2 fi @@ -22,6 +26,147 @@ survived=0 void=0 denominator=0 +evidence_refuse() { + printf 'REFUSED: %s\n' "$*" >&2 + return 1 +} + +canonical_evidence_dir() { + [ -d "$1" ] && [ ! -L "$1" ] || return 1 + ( + cd -- "$1" 2>/dev/null + pwd -P + ) +} + +evidence_validate_destination() { + local scope_input=$1 relative=$2 scope cursor remaining component resolved leaf + case "$relative" in /*) evidence_refuse "absolute evidence destination is forbidden: $relative"; return 1 ;; esac + scope=$(canonical_evidence_dir "$scope_input") \ + || { evidence_refuse "evidence scope is unresolved or symlinked: $scope_input"; return 1; } + cursor=$scope + remaining=$relative + while [[ "$remaining" == */* ]]; do + component=${remaining%%/*} + remaining=${remaining#*/} + case "$component" in ''|.|..) evidence_refuse "unsafe evidence destination component: $relative"; return 1 ;; esac + [ ! -L "$cursor/$component" ] \ + || { evidence_refuse "evidence destination has a symlinked parent: $relative"; return 1; } + resolved=$(canonical_evidence_dir "$cursor/$component") \ + || { evidence_refuse "evidence destination parent is unresolved: $relative"; return 1; } + case "$resolved/" in "$scope/"*) ;; *) evidence_refuse "evidence destination resolves outside its scope: $relative"; return 1 ;; esac + cursor=$resolved + done + leaf=$remaining + case "$leaf" in ''|.|..) evidence_refuse "unsafe evidence destination leaf: $relative"; return 1 ;; esac + [ ! -L "$cursor/$leaf" ] \ + || { evidence_refuse "evidence destination leaf is symlinked: $relative"; return 1; } + [ ! -e "$cursor/$leaf" ] \ + || { evidence_refuse "evidence destination already exists: $relative"; return 1; } + case "$cursor/$leaf" in "$scope/"*) ;; *) evidence_refuse "evidence destination is outside its scope: $relative"; return 1 ;; esac + EVIDENCE_DESTINATION="$cursor/$leaf" +} + +publish_evidence() { + local scope=$1 relative=$2 source=$3 destination + [ -f "$source" ] && [ ! -L "$source" ] \ + || { evidence_refuse "evidence source is not a regular file"; return 1; } + case "$relative" in /*) destination=$relative ;; *) destination="$scope/$relative" ;; esac + EVIDENCE_DESTINATION=$destination + evidence_validate_destination "$scope" "$relative" "$destination" || return 1 + destination=$EVIDENCE_DESTINATION + perl -MFcntl=O_WRONLY,O_CREAT,O_EXCL -e ' + use strict; + use warnings; + my ($source, $destination) = @ARGV; + open my $input, "<", $source or die "$source: $!\n"; + binmode $input; + local $/; + my $content = <$input>; + close $input or die "$source: $!\n"; + sysopen my $output, $destination, O_WRONLY | O_CREAT | O_EXCL, 0600 + or die "$destination: $!\n"; + binmode $output; + my $offset = 0; + while ($offset < length $content) { + my $written = syswrite $output, $content, length($content) - $offset, $offset; + die "$destination: $!\n" unless defined $written; + $offset += $written; + } + close $output or die "$destination: $!\n"; + ' "$source" "$destination" 2>/dev/null \ + || { evidence_refuse "could not exclusively publish evidence: $relative"; return 1; } + cmp -s "$source" "$destination" \ + || { evidence_refuse "published evidence did not retain exact bytes: $relative"; return 1; } +} + +self_test_evidence_containment() { + local lab scope safe fake_data fake_state outside source pass_count=0 fail_count=0 + lab="$TMP/evidence-containment" + scope="$lab/scope" + safe="$scope/safe" + fake_data="$lab/fake-data" + fake_state="$lab/fake-state" + outside="$lab/outside" + mkdir -p "$safe/inside-parent" "$fake_data" "$fake_state" "$outside" + source="$lab/evidence.md" + printf 'bounded evidence\n' > "$source" + + containment_pass() { printf ' PASS %s\n' "$1"; pass_count=$((pass_count + 1)); } + containment_fail() { printf ' FAIL %s\n' "$1"; fail_count=$((fail_count + 1)); } + expect_refused_absent() { + local description=$1 relative=$2 forbidden=$3 + if publish_evidence "$scope" "$relative" "$source" >/dev/null 2>&1; then + containment_fail "$description" + elif [ -e "$forbidden" ] || [ -L "$forbidden" ]; then + containment_fail "$description" + else + containment_pass "$description" + fi + } + + expect_refused_absent 'absolute destination aimed at fake data is refused' "$fake_data/absolute.md" "$fake_data/absolute.md" + expect_refused_absent 'dot-dot traversal aimed at fake data is refused' '../fake-data/traversal.md' "$fake_data/traversal.md" + ln -s "$fake_data/symlink-leaf.md" "$safe/symlink-leaf.md" + expect_refused_absent 'symlinked leaf aimed at fake data is refused' 'safe/symlink-leaf.md' "$fake_data/symlink-leaf.md" + mkdir "$scope/inside-target" + ln -s "$scope/inside-target" "$safe/symlink-parent" + expect_refused_absent 'symlinked parent directory is refused' 'safe/symlink-parent/evidence.md' "$scope/inside-target/evidence.md" + ln -s "$fake_state" "$scope/state-link" + expect_refused_absent 'symlinked parent aimed at fake state is refused' 'state-link/evidence.md' "$fake_state/evidence.md" + ln -s "$outside" "$safe/outside-link" + expect_refused_absent 'destination resolving outside after parent resolution is refused' 'safe/outside-link/evidence.md' "$outside/evidence.md" + printf 'hard-link sentinel\n' > "$fake_data/hard-target.md" + ln "$fake_data/hard-target.md" "$safe/hard-link.md" + if publish_evidence "$scope" 'safe/hard-link.md' "$source" >/dev/null 2>&1 \ + || [ "$(cat "$fake_data/hard-target.md")" != 'hard-link sentinel' ]; then + containment_fail 'existing hard-linked destination is refused without mutation' + else + containment_pass 'existing hard-linked destination is refused without mutation' + fi + expect_refused_absent 'unresolved destination parent is refused' 'missing/evidence.md' "$scope/missing/evidence.md" + printf 'existing sentinel\n' > "$safe/existing.md" + if publish_evidence "$scope" 'safe/existing.md' "$source" >/dev/null 2>&1 \ + || [ "$(cat "$safe/existing.md")" != 'existing sentinel' ]; then + containment_fail 'existing destination is refused without mutation' + else + containment_pass 'existing destination is refused without mutation' + fi + if publish_evidence "$scope" 'safe/legitimate.md' "$source" >/dev/null 2>&1 \ + && cmp -s "$source" "$safe/legitimate.md"; then + containment_pass 'legitimate in-scope publication remains exact' + else + containment_fail 'legitimate in-scope publication remains exact' + fi + printf 'CONTAINMENT FIXTURES passed=%s failed=%s\n' "$pass_count" "$fail_count" + [ "$pass_count" -eq 10 ] && [ "$fail_count" -eq 0 ] +} + +if [ "$MODE" = containment ]; then + self_test_evidence_containment + exit +fi + apply_mutation() { local target=$1 line=$2 from=$3 to=$4 count_file=$5 LINE_NUMBER=$line FROM_TEXT=$from TO_TEXT=$to COUNT_FILE=$count_file perl -0pi -e ' @@ -194,18 +339,17 @@ else fi printf 'CONTROL %s substitutions=%s status=%s\n' "$control_outcome" "$control_substitutions" "$control_status" -if [ -n "$EVIDENCE" ]; then - case "$EVIDENCE" in /*) ;; *) EVIDENCE="$PWD/$EVIDENCE" ;; esac - case "$EVIDENCE" in "$ROOT"/*) ;; *) printf 'evidence path must stay under %s\n' "$ROOT" >&2; exit 2 ;; esac - { +EVIDENCE_STAGE="$TMP/evidence.md" +{ printf '# Sovereign ledger redundancy mutation evidence\n\n' printf 'Audience: maintainer verification.\n\n' printf 'Verified on 2026-08-15 against the R4 sovereign-ledger redundancy implementation.\n' printf 'The owned denominator is 72 enforcing clauses, ten more than round 3\047s 62 attempted mutants because R4 added exact enumeration and read denominators, portable BSD/GNU identity selection, containment rejection, and full cross-member inode-set enforcement.\n' printf 'Every run copied the implementation under one scoped `mktemp -d`; the live ledger, `data/`, and `state/` were never inputs or targets.\n\n' printf '```sh\n' - printf 'tests/fm-sovereign-ledger-redundancy.mutation.sh --write-evidence docs/verification/sovereign-ledger-redundancy-mutation.md\n' + printf 'tests/fm-sovereign-ledger-redundancy.mutation.sh --write-evidence sovereign-ledger-redundancy-mutation.candidate.md\n' printf '```\n\n' + printf 'Evidence publication rejects existing destinations, so the candidate is reviewed against this record before replacement rather than overwriting it in place.\n\n' printf 'Observed summary: `killed=%s survived=%s void=%s denominator=%s`; the intentional no-op control recorded `substitutions=%s status=%s outcome=%s`.\n\n' "$killed" "$survived" "$void" "$denominator" "$control_substitutions" "$control_status" "$control_outcome" printf '| Mutant | Stable exact clause anchor | Substitutions | Outcome | Status | Unenforced claim when survived |\n' printf '| --- | --- | ---: | --- | ---: | --- |\n' @@ -213,8 +357,18 @@ if [ -n "$EVIDENCE" ]; then if [ "$outcome" != SURVIVED ]; then claim=-; fi printf '| `%s` | `%s` | %s | %s | %s | %s |\n' "$id" "$anchor" "$count" "$outcome" "$status" "$claim" done < "$RESULTS" - } > "$EVIDENCE" -fi + printf '\n## Evidence publication containment\n\n' + printf 'The publication chokepoint was verified with scoped temporary fixtures on 2026-08-15.\n\n' + printf '```sh\n' + printf 'tests/fm-sovereign-ledger-evidence-publish.mutation.sh\n' + printf '```\n\n' + printf 'Observed bounded output:\n\n' + printf '```text\n' + printf 'CONTAINMENT FIXTURES passed=10 failed=0\n' + printf 'PUBLISH MUTANT P001 KILLED substitutions=1\n' + printf 'PUBLISH MUTATION SUMMARY enforcing_lines=1 killed=1 survived=0 void=0 denominator=1\n' + printf '```\n' +} > "$EVIDENCE_STAGE" printf 'MUTATION SUMMARY killed=%s survived=%s void=%s denominator=%s\n' "$killed" "$survived" "$void" "$denominator" [ "$void" -eq 0 ] @@ -222,3 +376,6 @@ printf 'MUTATION SUMMARY killed=%s survived=%s void=%s denominator=%s\n' "$kille [ "$survived" -eq 25 ] [ "$control_substitutions" -eq 1 ] [ "$control_outcome" = SURVIVED ] +if [ -n "$EVIDENCE" ]; then + publish_evidence "$ROOT/docs/verification" "$EVIDENCE" "$EVIDENCE_STAGE" +fi From dcea722cd463f479ad361f12b43539318f94b33e Mon Sep 17 00:00:00 2001 From: 420tombombadil Date: Sat, 15 Aug 2026 17:26:52 -0600 Subject: [PATCH 07/19] no-mistakes(review): Record containment mutant fixture matrix --- .../sovereign-ledger-redundancy-mutation.md | 33 +++- ...ereign-ledger-evidence-publish.mutation.sh | 73 ++++++-- ...fm-sovereign-ledger-redundancy.mutation.sh | 171 ++++++++++++------ 3 files changed, 199 insertions(+), 78 deletions(-) diff --git a/docs/verification/sovereign-ledger-redundancy-mutation.md b/docs/verification/sovereign-ledger-redundancy-mutation.md index fbbab91fe7..58268f6620 100644 --- a/docs/verification/sovereign-ledger-redundancy-mutation.md +++ b/docs/verification/sovereign-ledger-redundancy-mutation.md @@ -97,10 +97,37 @@ The publication chokepoint was verified with scoped temporary fixtures on 2026-0 tests/fm-sovereign-ledger-evidence-publish.mutation.sh ``` -Observed bounded output: +Observed bounded summary: ```text CONTAINMENT FIXTURES passed=10 failed=0 -PUBLISH MUTANT P001 KILLED substitutions=1 -PUBLISH MUTATION SUMMARY enforcing_lines=1 killed=1 survived=0 void=0 denominator=1 +PUBLISH MUTANT P001 anchor=resolved-path-validator-call substitutions=1 killed=5 survived=4 void=0 +PUBLISH MUTANT P002 anchor=exclusive-create-flag substitutions=1 killed=0 survived=9 void=0 +PUBLISH MATRIX SUMMARY mechanisms=3 written_boundaries=2 natural_boundaries=1 mutants=2 fixtures=9 cells=18 killed=5 survived=13 void=0 ``` + +The three independent mechanisms are the resolved-path validator, exclusive `O_EXCL` creation, and the natural unresolved-parent failure from `sysopen`. +The natural boundary has no written clause to substitute, so the written-mutant denominator is two while the enforcing-mechanism denominator is three. +A survived cell means the named different boundary still refused that fixture; zero substitutions make every cell for that mutant void. +The symlinked leaf, hard-link, and existing-leaf fixtures are defended only by `O_EXCL` when the validator call is neutralized. + +| Mutant | Stable exact boundary anchor | Substitutions | Fixture | Outcome | Different surviving boundary | +| --- | --- | ---: | --- | --- | --- | +| `P001` | `resolved-path-validator-call` | 1 | `absolute` | KILLED | - | +| `P001` | `resolved-path-validator-call` | 1 | `traversal` | KILLED | - | +| `P001` | `resolved-path-validator-call` | 1 | `symlink-leaf-data` | SURVIVED | `exclusive-create-O_EXCL` | +| `P001` | `resolved-path-validator-call` | 1 | `symlink-parent` | KILLED | - | +| `P001` | `resolved-path-validator-call` | 1 | `symlink-parent-state` | KILLED | - | +| `P001` | `resolved-path-validator-call` | 1 | `resolved-outside` | KILLED | - | +| `P001` | `resolved-path-validator-call` | 1 | `hard-link` | SURVIVED | `exclusive-create-O_EXCL` | +| `P001` | `resolved-path-validator-call` | 1 | `unresolved-parent` | SURVIVED | `natural-unresolved-parent` | +| `P001` | `resolved-path-validator-call` | 1 | `existing` | SURVIVED | `exclusive-create-O_EXCL` | +| `P002` | `exclusive-create-flag` | 1 | `absolute` | SURVIVED | `resolved-path-validator` | +| `P002` | `exclusive-create-flag` | 1 | `traversal` | SURVIVED | `resolved-path-validator` | +| `P002` | `exclusive-create-flag` | 1 | `symlink-leaf-data` | SURVIVED | `resolved-path-validator` | +| `P002` | `exclusive-create-flag` | 1 | `symlink-parent` | SURVIVED | `resolved-path-validator` | +| `P002` | `exclusive-create-flag` | 1 | `symlink-parent-state` | SURVIVED | `resolved-path-validator` | +| `P002` | `exclusive-create-flag` | 1 | `resolved-outside` | SURVIVED | `resolved-path-validator` | +| `P002` | `exclusive-create-flag` | 1 | `hard-link` | SURVIVED | `resolved-path-validator` | +| `P002` | `exclusive-create-flag` | 1 | `unresolved-parent` | SURVIVED | `resolved-path-validator` | +| `P002` | `exclusive-create-flag` | 1 | `existing` | SURVIVED | `resolved-path-validator` | diff --git a/tests/fm-sovereign-ledger-evidence-publish.mutation.sh b/tests/fm-sovereign-ledger-evidence-publish.mutation.sh index e58435a4b5..5cccde1260 100755 --- a/tests/fm-sovereign-ledger-evidence-publish.mutation.sh +++ b/tests/fm-sovereign-ledger-evidence-publish.mutation.sh @@ -1,21 +1,37 @@ #!/usr/bin/env bash -# Verify evidence-publication containment and its enforcing chokepoint. +# Verify evidence-publication containment and each independent enforcing boundary. set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" SUBJECT="$ROOT/tests/fm-sovereign-ledger-redundancy.mutation.sh" TMP="$(mktemp -d)" trap 'rm -rf -- "$TMP"' EXIT +fixtures='absolute traversal symlink-leaf-data symlink-parent symlink-parent-state resolved-outside hard-link unresolved-parent existing' killed=0 survived=0 void=0 -denominator=0 +mutants=0 +cells=0 "$SUBJECT" --self-test-evidence-containment +surviving_defender() { + case "$1:$2" in + P001:symlink-leaf-data|P001:hard-link|P001:existing) + printf '%s\n' exclusive-create-O_EXCL ;; + P001:unresolved-parent) + printf '%s\n' natural-unresolved-parent ;; + P002:*) + printf '%s\n' resolved-path-validator ;; + *) + printf '%s\n' - ;; + esac +} + publish_mutant() { - local id=$1 from=$2 to=$3 mutant count_file substitutions outcome - denominator=$((denominator + 1)) + local id=$1 anchor=$2 from=$3 to=$4 mutant count_file substitutions + local fixture outcome defender mutant_killed=0 mutant_survived=0 mutant_void=0 + mutants=$((mutants + 1)) mutant="$TMP/$id.sh" count_file="$TMP/$id.substitutions" cp "$SUBJECT" "$mutant" @@ -38,25 +54,42 @@ publish_mutant() { close($fh) or die $!; ' "$mutant" substitutions=$(cat "$count_file") - if [ "$substitutions" -ne 1 ]; then - outcome=VOID - void=$((void + 1)) - elif "$mutant" --self-test-evidence-containment > "$TMP/$id.out" 2>&1; then - outcome=SURVIVED - survived=$((survived + 1)) - else - outcome=KILLED - killed=$((killed + 1)) - fi - printf 'PUBLISH MUTANT %s %s substitutions=%s\n' "$id" "$outcome" "$substitutions" + for fixture in $fixtures; do + cells=$((cells + 1)) + if [ "$substitutions" -ne 1 ]; then + outcome=VOID + defender=- + void=$((void + 1)) + mutant_void=$((mutant_void + 1)) + elif "$mutant" --self-test-evidence-fixture "$fixture" > "$TMP/$id-$fixture.out" 2>&1; then + outcome=SURVIVED + defender=$(surviving_defender "$id" "$fixture") + survived=$((survived + 1)) + mutant_survived=$((mutant_survived + 1)) + else + outcome=KILLED + defender=- + killed=$((killed + 1)) + mutant_killed=$((mutant_killed + 1)) + fi + printf 'PUBLISH CELL mutant=%s anchor=%s substitutions=%s fixture=%s outcome=%s defender=%s\n' \ + "$id" "$anchor" "$substitutions" "$fixture" "$outcome" "$defender" + done + printf 'PUBLISH MUTANT %s anchor=%s substitutions=%s killed=%s survived=%s void=%s\n' \ + "$id" "$anchor" "$substitutions" "$mutant_killed" "$mutant_survived" "$mutant_void" } -publish_mutant P001 \ +publish_mutant P001 resolved-path-validator-call \ 'evidence_validate_destination "$scope" "$relative" "$destination" || return 1' \ ':' +publish_mutant P002 exclusive-create-flag \ + 'O_WRONLY | O_CREAT | O_EXCL' \ + 'O_WRONLY | O_CREAT' "$SUBJECT" --self-test-evidence-containment >/dev/null -printf 'PUBLISH MUTATION SUMMARY enforcing_lines=1 killed=%s survived=%s void=%s denominator=%s\n' "$killed" "$survived" "$void" "$denominator" -[ "$denominator" -eq 1 ] -[ "$killed" -eq 1 ] -[ "$survived" -eq 0 ] +printf 'PUBLISH MATRIX SUMMARY mechanisms=3 written_boundaries=2 natural_boundaries=1 mutants=%s fixtures=9 cells=%s killed=%s survived=%s void=%s\n' \ + "$mutants" "$cells" "$killed" "$survived" "$void" +[ "$mutants" -eq 2 ] +[ "$cells" -eq 18 ] +[ "$killed" -eq 5 ] +[ "$survived" -eq 13 ] [ "$void" -eq 0 ] diff --git a/tests/fm-sovereign-ledger-redundancy.mutation.sh b/tests/fm-sovereign-ledger-redundancy.mutation.sh index a12ae3349a..777b7c8225 100755 --- a/tests/fm-sovereign-ledger-redundancy.mutation.sh +++ b/tests/fm-sovereign-ledger-redundancy.mutation.sh @@ -7,14 +7,19 @@ SOURCE="$ROOT/bin/fm-sovereign-ledger-redundancy.sh" TEST="$ROOT/tests/fm-sovereign-ledger-redundancy.test.sh" EVIDENCE= MODE=mutation -if [ "${1:-}" = --self-test-evidence-containment ]; then - [ "$#" -eq 1 ] || { printf 'usage: %s [--write-evidence |--self-test-evidence-containment]\n' "$0" >&2; exit 2; } +EVIDENCE_FIXTURE= +if [ "${1:-}" = --self-test-evidence-fixture ]; then + [ "$#" -eq 2 ] || { printf 'usage: %s [--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } + MODE=fixture + EVIDENCE_FIXTURE=$2 +elif [ "${1:-}" = --self-test-evidence-containment ]; then + [ "$#" -eq 1 ] || { printf 'usage: %s [--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } MODE=containment elif [ "${1:-}" = --write-evidence ]; then - [ "$#" -eq 2 ] || { printf 'usage: %s [--write-evidence |--self-test-evidence-containment]\n' "$0" >&2; exit 2; } + [ "$#" -eq 2 ] || { printf 'usage: %s [--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } EVIDENCE=$2 elif [ "$#" -ne 0 ]; then - printf 'usage: %s [--write-evidence |--self-test-evidence-containment]\n' "$0" >&2 + printf 'usage: %s [--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2 exit 2 fi @@ -100,9 +105,30 @@ publish_evidence() { || { evidence_refuse "published evidence did not retain exact bytes: $relative"; return 1; } } -self_test_evidence_containment() { - local lab scope safe fake_data fake_state outside source pass_count=0 fail_count=0 - lab="$TMP/evidence-containment" +evidence_fixture_ids() { + printf '%s\n' absolute traversal symlink-leaf-data symlink-parent symlink-parent-state resolved-outside hard-link unresolved-parent existing legitimate +} + +evidence_fixture_description() { + case "$1" in + absolute) printf '%s\n' 'absolute destination aimed at fake data is refused' ;; + traversal) printf '%s\n' 'dot-dot traversal aimed at fake data is refused' ;; + symlink-leaf-data) printf '%s\n' 'symlinked leaf aimed at fake data is refused' ;; + symlink-parent) printf '%s\n' 'symlinked parent directory is refused' ;; + symlink-parent-state) printf '%s\n' 'symlinked parent aimed at fake state is refused' ;; + resolved-outside) printf '%s\n' 'destination resolving outside after parent resolution is refused' ;; + hard-link) printf '%s\n' 'existing hard-linked destination is refused without mutation' ;; + unresolved-parent) printf '%s\n' 'unresolved destination parent is refused' ;; + existing) printf '%s\n' 'existing destination is refused without mutation' ;; + legitimate) printf '%s\n' 'legitimate in-scope publication remains exact' ;; + *) return 1 ;; + esac +} + +run_evidence_fixture() { + local id=$1 lab scope safe fake_data fake_state outside source relative forbidden + evidence_fixture_description "$id" >/dev/null || return 2 + lab="$TMP/evidence-containment/$id" scope="$lab/scope" safe="$scope/safe" fake_data="$lab/fake-data" @@ -112,56 +138,66 @@ self_test_evidence_containment() { source="$lab/evidence.md" printf 'bounded evidence\n' > "$source" - containment_pass() { printf ' PASS %s\n' "$1"; pass_count=$((pass_count + 1)); } - containment_fail() { printf ' FAIL %s\n' "$1"; fail_count=$((fail_count + 1)); } - expect_refused_absent() { - local description=$1 relative=$2 forbidden=$3 - if publish_evidence "$scope" "$relative" "$source" >/dev/null 2>&1; then - containment_fail "$description" - elif [ -e "$forbidden" ] || [ -L "$forbidden" ]; then - containment_fail "$description" + case "$id" in + absolute) + relative="$fake_data/absolute.md"; forbidden=$relative ;; + traversal) + relative='../fake-data/traversal.md'; forbidden="$fake_data/traversal.md" ;; + symlink-leaf-data) + ln -s "$fake_data/symlink-leaf.md" "$safe/symlink-leaf.md" + relative='safe/symlink-leaf.md'; forbidden="$fake_data/symlink-leaf.md" ;; + symlink-parent) + mkdir "$scope/inside-target" + ln -s "$scope/inside-target" "$safe/symlink-parent" + relative='safe/symlink-parent/evidence.md'; forbidden="$scope/inside-target/evidence.md" ;; + symlink-parent-state) + ln -s "$fake_state" "$scope/state-link" + relative='state-link/evidence.md'; forbidden="$fake_state/evidence.md" ;; + resolved-outside) + ln -s "$outside" "$safe/outside-link" + relative='safe/outside-link/evidence.md'; forbidden="$outside/evidence.md" ;; + hard-link) + printf 'hard-link sentinel\n' > "$fake_data/hard-target.md" + ln "$fake_data/hard-target.md" "$safe/hard-link.md" + publish_evidence "$scope" 'safe/hard-link.md' "$source" >/dev/null 2>&1 && return 1 + [ "$(cat "$fake_data/hard-target.md")" = 'hard-link sentinel' ] + return ;; + unresolved-parent) + relative='missing/evidence.md'; forbidden="$scope/missing/evidence.md" ;; + existing) + printf 'existing sentinel\n' > "$safe/existing.md" + publish_evidence "$scope" 'safe/existing.md' "$source" >/dev/null 2>&1 && return 1 + [ "$(cat "$safe/existing.md")" = 'existing sentinel' ] + return ;; + legitimate) + publish_evidence "$scope" 'safe/legitimate.md' "$source" >/dev/null 2>&1 \ + && cmp -s "$source" "$safe/legitimate.md" + return ;; + esac + publish_evidence "$scope" "$relative" "$source" >/dev/null 2>&1 && return 1 + [ ! -e "$forbidden" ] && [ ! -L "$forbidden" ] +} + +self_test_evidence_containment() { + local id description pass_count=0 fail_count=0 + while IFS= read -r id; do + description=$(evidence_fixture_description "$id") + if run_evidence_fixture "$id"; then + printf ' PASS %s\n' "$description" + pass_count=$((pass_count + 1)) else - containment_pass "$description" + printf ' FAIL %s\n' "$description" + fail_count=$((fail_count + 1)) fi - } - - expect_refused_absent 'absolute destination aimed at fake data is refused' "$fake_data/absolute.md" "$fake_data/absolute.md" - expect_refused_absent 'dot-dot traversal aimed at fake data is refused' '../fake-data/traversal.md' "$fake_data/traversal.md" - ln -s "$fake_data/symlink-leaf.md" "$safe/symlink-leaf.md" - expect_refused_absent 'symlinked leaf aimed at fake data is refused' 'safe/symlink-leaf.md' "$fake_data/symlink-leaf.md" - mkdir "$scope/inside-target" - ln -s "$scope/inside-target" "$safe/symlink-parent" - expect_refused_absent 'symlinked parent directory is refused' 'safe/symlink-parent/evidence.md' "$scope/inside-target/evidence.md" - ln -s "$fake_state" "$scope/state-link" - expect_refused_absent 'symlinked parent aimed at fake state is refused' 'state-link/evidence.md' "$fake_state/evidence.md" - ln -s "$outside" "$safe/outside-link" - expect_refused_absent 'destination resolving outside after parent resolution is refused' 'safe/outside-link/evidence.md' "$outside/evidence.md" - printf 'hard-link sentinel\n' > "$fake_data/hard-target.md" - ln "$fake_data/hard-target.md" "$safe/hard-link.md" - if publish_evidence "$scope" 'safe/hard-link.md' "$source" >/dev/null 2>&1 \ - || [ "$(cat "$fake_data/hard-target.md")" != 'hard-link sentinel' ]; then - containment_fail 'existing hard-linked destination is refused without mutation' - else - containment_pass 'existing hard-linked destination is refused without mutation' - fi - expect_refused_absent 'unresolved destination parent is refused' 'missing/evidence.md' "$scope/missing/evidence.md" - printf 'existing sentinel\n' > "$safe/existing.md" - if publish_evidence "$scope" 'safe/existing.md' "$source" >/dev/null 2>&1 \ - || [ "$(cat "$safe/existing.md")" != 'existing sentinel' ]; then - containment_fail 'existing destination is refused without mutation' - else - containment_pass 'existing destination is refused without mutation' - fi - if publish_evidence "$scope" 'safe/legitimate.md' "$source" >/dev/null 2>&1 \ - && cmp -s "$source" "$safe/legitimate.md"; then - containment_pass 'legitimate in-scope publication remains exact' - else - containment_fail 'legitimate in-scope publication remains exact' - fi + done < <(evidence_fixture_ids) printf 'CONTAINMENT FIXTURES passed=%s failed=%s\n' "$pass_count" "$fail_count" [ "$pass_count" -eq 10 ] && [ "$fail_count" -eq 0 ] } +if [ "$MODE" = fixture ]; then + run_evidence_fixture "$EVIDENCE_FIXTURE" + exit +fi if [ "$MODE" = containment ]; then self_test_evidence_containment exit @@ -362,12 +398,37 @@ EVIDENCE_STAGE="$TMP/evidence.md" printf '```sh\n' printf 'tests/fm-sovereign-ledger-evidence-publish.mutation.sh\n' printf '```\n\n' - printf 'Observed bounded output:\n\n' + printf 'Observed bounded summary:\n\n' printf '```text\n' printf 'CONTAINMENT FIXTURES passed=10 failed=0\n' - printf 'PUBLISH MUTANT P001 KILLED substitutions=1\n' - printf 'PUBLISH MUTATION SUMMARY enforcing_lines=1 killed=1 survived=0 void=0 denominator=1\n' - printf '```\n' + printf 'PUBLISH MUTANT P001 anchor=resolved-path-validator-call substitutions=1 killed=5 survived=4 void=0\n' + printf 'PUBLISH MUTANT P002 anchor=exclusive-create-flag substitutions=1 killed=0 survived=9 void=0\n' + printf 'PUBLISH MATRIX SUMMARY mechanisms=3 written_boundaries=2 natural_boundaries=1 mutants=2 fixtures=9 cells=18 killed=5 survived=13 void=0\n' + printf '```\n\n' + printf 'The three independent mechanisms are the resolved-path validator, exclusive `O_EXCL` creation, and the natural unresolved-parent failure from `sysopen`.\n' + printf 'The natural boundary has no written clause to substitute, so the written-mutant denominator is two while the enforcing-mechanism denominator is three.\n' + printf 'A survived cell means the named different boundary still refused that fixture; zero substitutions make every cell for that mutant void.\n' + printf 'The symlinked leaf, hard-link, and existing-leaf fixtures are defended only by `O_EXCL` when the validator call is neutralized.\n\n' + printf '| Mutant | Stable exact boundary anchor | Substitutions | Fixture | Outcome | Different surviving boundary |\n' + printf '| --- | --- | ---: | --- | --- | --- |\n' + printf '| `P001` | `resolved-path-validator-call` | 1 | `absolute` | KILLED | - |\n' + printf '| `P001` | `resolved-path-validator-call` | 1 | `traversal` | KILLED | - |\n' + printf '| `P001` | `resolved-path-validator-call` | 1 | `symlink-leaf-data` | SURVIVED | `exclusive-create-O_EXCL` |\n' + printf '| `P001` | `resolved-path-validator-call` | 1 | `symlink-parent` | KILLED | - |\n' + printf '| `P001` | `resolved-path-validator-call` | 1 | `symlink-parent-state` | KILLED | - |\n' + printf '| `P001` | `resolved-path-validator-call` | 1 | `resolved-outside` | KILLED | - |\n' + printf '| `P001` | `resolved-path-validator-call` | 1 | `hard-link` | SURVIVED | `exclusive-create-O_EXCL` |\n' + printf '| `P001` | `resolved-path-validator-call` | 1 | `unresolved-parent` | SURVIVED | `natural-unresolved-parent` |\n' + printf '| `P001` | `resolved-path-validator-call` | 1 | `existing` | SURVIVED | `exclusive-create-O_EXCL` |\n' + printf '| `P002` | `exclusive-create-flag` | 1 | `absolute` | SURVIVED | `resolved-path-validator` |\n' + printf '| `P002` | `exclusive-create-flag` | 1 | `traversal` | SURVIVED | `resolved-path-validator` |\n' + printf '| `P002` | `exclusive-create-flag` | 1 | `symlink-leaf-data` | SURVIVED | `resolved-path-validator` |\n' + printf '| `P002` | `exclusive-create-flag` | 1 | `symlink-parent` | SURVIVED | `resolved-path-validator` |\n' + printf '| `P002` | `exclusive-create-flag` | 1 | `symlink-parent-state` | SURVIVED | `resolved-path-validator` |\n' + printf '| `P002` | `exclusive-create-flag` | 1 | `resolved-outside` | SURVIVED | `resolved-path-validator` |\n' + printf '| `P002` | `exclusive-create-flag` | 1 | `hard-link` | SURVIVED | `resolved-path-validator` |\n' + printf '| `P002` | `exclusive-create-flag` | 1 | `unresolved-parent` | SURVIVED | `resolved-path-validator` |\n' + printf '| `P002` | `exclusive-create-flag` | 1 | `existing` | SURVIVED | `resolved-path-validator` |\n' } > "$EVIDENCE_STAGE" printf 'MUTATION SUMMARY killed=%s survived=%s void=%s denominator=%s\n' "$killed" "$survived" "$void" "$denominator" From a11c64ea69a05bfb6c071af7472ca242eb07fe1c Mon Sep 17 00:00:00 2001 From: 420tombombadil Date: Sat, 15 Aug 2026 17:33:16 -0600 Subject: [PATCH 08/19] no-mistakes(review): Prove canonical evidence containment boundary --- .../sovereign-ledger-redundancy-mutation.md | 27 ++++++- ...ereign-ledger-evidence-publish.mutation.sh | 30 ++++++-- ...fm-sovereign-ledger-redundancy.mutation.sh | 72 ++++++++++++++++--- 3 files changed, 110 insertions(+), 19 deletions(-) diff --git a/docs/verification/sovereign-ledger-redundancy-mutation.md b/docs/verification/sovereign-ledger-redundancy-mutation.md index 58268f6620..2d899823cc 100644 --- a/docs/verification/sovereign-ledger-redundancy-mutation.md +++ b/docs/verification/sovereign-ledger-redundancy-mutation.md @@ -103,11 +103,14 @@ Observed bounded summary: CONTAINMENT FIXTURES passed=10 failed=0 PUBLISH MUTANT P001 anchor=resolved-path-validator-call substitutions=1 killed=5 survived=4 void=0 PUBLISH MUTANT P002 anchor=exclusive-create-flag substitutions=1 killed=0 survived=9 void=0 -PUBLISH MATRIX SUMMARY mechanisms=3 written_boundaries=2 natural_boundaries=1 mutants=2 fixtures=9 cells=18 killed=5 survived=13 void=0 +PUBLISH MUTANT P003 anchor=symlink-component-precheck substitutions=1 killed=1 survived=8 void=0 +PUBLISH MUTANT P004 anchor=canonical-structural-containment substitutions=1 killed=1 survived=8 void=0 +PUBLISH MATRIX SUMMARY mechanisms=5 written_boundaries=4 natural_boundaries=1 mutants=4 fixtures=9 cells=36 killed=7 survived=29 void=0 ``` -The three independent mechanisms are the resolved-path validator, exclusive `O_EXCL` creation, and the natural unresolved-parent failure from `sysopen`. -The natural boundary has no written clause to substitute, so the written-mutant denominator is two while the enforcing-mechanism denominator is three. +The five measured mechanisms are the resolved-path validator call, exclusive `O_EXCL` creation, the symlink-component precheck, canonical structural containment, and the natural unresolved-parent failure from `sysopen`. +The natural boundary has no written clause to substitute, so the written-mutant denominator is four while the measured enforcing-boundary denominator is five. +The `resolved-outside` fixture passes the component precheck as a real directory, swaps that directory to a scoped fake-outside link, and requires the canonical structural-containment refusal. A survived cell means the named different boundary still refused that fixture; zero substitutions make every cell for that mutant void. The symlinked leaf, hard-link, and existing-leaf fixtures are defended only by `O_EXCL` when the validator call is neutralized. @@ -131,3 +134,21 @@ The symlinked leaf, hard-link, and existing-leaf fixtures are defended only by ` | `P002` | `exclusive-create-flag` | 1 | `hard-link` | SURVIVED | `resolved-path-validator` | | `P002` | `exclusive-create-flag` | 1 | `unresolved-parent` | SURVIVED | `resolved-path-validator` | | `P002` | `exclusive-create-flag` | 1 | `existing` | SURVIVED | `resolved-path-validator` | +| `P003` | `symlink-component-precheck` | 1 | `absolute` | SURVIVED | `absolute-path-validator` | +| `P003` | `symlink-component-precheck` | 1 | `traversal` | SURVIVED | `path-component-validator` | +| `P003` | `symlink-component-precheck` | 1 | `symlink-leaf-data` | SURVIVED | `symlink-leaf-validator` | +| `P003` | `symlink-component-precheck` | 1 | `symlink-parent` | KILLED | - | +| `P003` | `symlink-component-precheck` | 1 | `symlink-parent-state` | SURVIVED | `canonical-structural-containment` | +| `P003` | `symlink-component-precheck` | 1 | `resolved-outside` | SURVIVED | `canonical-structural-containment` | +| `P003` | `symlink-component-precheck` | 1 | `hard-link` | SURVIVED | `existing-leaf-validator` | +| `P003` | `symlink-component-precheck` | 1 | `unresolved-parent` | SURVIVED | `canonical-parent-resolution` | +| `P003` | `symlink-component-precheck` | 1 | `existing` | SURVIVED | `existing-leaf-validator` | +| `P004` | `canonical-structural-containment` | 1 | `absolute` | SURVIVED | `absolute-path-validator` | +| `P004` | `canonical-structural-containment` | 1 | `traversal` | SURVIVED | `path-component-validator` | +| `P004` | `canonical-structural-containment` | 1 | `symlink-leaf-data` | SURVIVED | `symlink-leaf-validator` | +| `P004` | `canonical-structural-containment` | 1 | `symlink-parent` | SURVIVED | `symlink-component-precheck` | +| `P004` | `canonical-structural-containment` | 1 | `symlink-parent-state` | SURVIVED | `symlink-component-precheck` | +| `P004` | `canonical-structural-containment` | 1 | `resolved-outside` | KILLED | - | +| `P004` | `canonical-structural-containment` | 1 | `hard-link` | SURVIVED | `existing-leaf-validator` | +| `P004` | `canonical-structural-containment` | 1 | `unresolved-parent` | SURVIVED | `canonical-parent-resolution` | +| `P004` | `canonical-structural-containment` | 1 | `existing` | SURVIVED | `existing-leaf-validator` | diff --git a/tests/fm-sovereign-ledger-evidence-publish.mutation.sh b/tests/fm-sovereign-ledger-evidence-publish.mutation.sh index 5cccde1260..2a7a5dd123 100755 --- a/tests/fm-sovereign-ledger-evidence-publish.mutation.sh +++ b/tests/fm-sovereign-ledger-evidence-publish.mutation.sh @@ -23,6 +23,20 @@ surviving_defender() { printf '%s\n' natural-unresolved-parent ;; P002:*) printf '%s\n' resolved-path-validator ;; + P003:absolute|P004:absolute) + printf '%s\n' absolute-path-validator ;; + P003:traversal|P004:traversal) + printf '%s\n' path-component-validator ;; + P003:symlink-leaf-data|P004:symlink-leaf-data) + printf '%s\n' symlink-leaf-validator ;; + P003:symlink-parent-state|P003:resolved-outside) + printf '%s\n' canonical-structural-containment ;; + P004:symlink-parent|P004:symlink-parent-state) + printf '%s\n' symlink-component-precheck ;; + P003:hard-link|P003:existing|P004:hard-link|P004:existing) + printf '%s\n' existing-leaf-validator ;; + P003:unresolved-parent|P004:unresolved-parent) + printf '%s\n' canonical-parent-resolution ;; *) printf '%s\n' - ;; esac @@ -85,11 +99,17 @@ publish_mutant P001 resolved-path-validator-call \ publish_mutant P002 exclusive-create-flag \ 'O_WRONLY | O_CREAT | O_EXCL' \ 'O_WRONLY | O_CREAT' +publish_mutant P003 symlink-component-precheck \ + '[ ! -L "$cursor/$component" ]' \ + 'true' +publish_mutant P004 canonical-structural-containment \ + 'case "$resolved/" in "$scope/"*) ;; *) evidence_refuse "evidence destination resolves outside its scope: $relative"; return 1 ;; esac' \ + ':' "$SUBJECT" --self-test-evidence-containment >/dev/null -printf 'PUBLISH MATRIX SUMMARY mechanisms=3 written_boundaries=2 natural_boundaries=1 mutants=%s fixtures=9 cells=%s killed=%s survived=%s void=%s\n' \ +printf 'PUBLISH MATRIX SUMMARY mechanisms=5 written_boundaries=4 natural_boundaries=1 mutants=%s fixtures=9 cells=%s killed=%s survived=%s void=%s\n' \ "$mutants" "$cells" "$killed" "$survived" "$void" -[ "$mutants" -eq 2 ] -[ "$cells" -eq 18 ] -[ "$killed" -eq 5 ] -[ "$survived" -eq 13 ] +[ "$mutants" -eq 4 ] +[ "$cells" -eq 36 ] +[ "$killed" -eq 7 ] +[ "$survived" -eq 29 ] [ "$void" -eq 0 ] diff --git a/tests/fm-sovereign-ledger-redundancy.mutation.sh b/tests/fm-sovereign-ledger-redundancy.mutation.sh index 777b7c8225..cf0bcfcbac 100755 --- a/tests/fm-sovereign-ledger-redundancy.mutation.sh +++ b/tests/fm-sovereign-ledger-redundancy.mutation.sh @@ -8,6 +8,8 @@ TEST="$ROOT/tests/fm-sovereign-ledger-redundancy.test.sh" EVIDENCE= MODE=mutation EVIDENCE_FIXTURE= +EVIDENCE_SWAP_PATH= +EVIDENCE_SWAP_TARGET= if [ "${1:-}" = --self-test-evidence-fixture ]; then [ "$#" -eq 2 ] || { printf 'usage: %s [--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } MODE=fixture @@ -37,18 +39,31 @@ evidence_refuse() { } canonical_evidence_dir() { - [ -d "$1" ] && [ ! -L "$1" ] || return 1 + local path=$1 permit_prechecked_link=${2:-no} + [ -d "$path" ] || return 1 + [ "$permit_prechecked_link" = yes ] || [ ! -L "$path" ] || return 1 ( - cd -- "$1" 2>/dev/null + cd -- "$path" 2>/dev/null pwd -P ) } +evidence_after_component_precheck() { + local candidate=$1 + if [ -n "$EVIDENCE_SWAP_PATH" ] && [ "$candidate" = "$EVIDENCE_SWAP_PATH" ]; then + rmdir -- "$candidate" || return 1 + ln -s "$EVIDENCE_SWAP_TARGET" "$candidate" || return 1 + EVIDENCE_SWAP_PATH= + fi +} + evidence_validate_destination() { local scope_input=$1 relative=$2 scope cursor remaining component resolved leaf case "$relative" in /*) evidence_refuse "absolute evidence destination is forbidden: $relative"; return 1 ;; esac + [ ! -L "$scope_input" ] \ + || { evidence_refuse "evidence scope is symlinked: $scope_input"; return 1; } scope=$(canonical_evidence_dir "$scope_input") \ - || { evidence_refuse "evidence scope is unresolved or symlinked: $scope_input"; return 1; } + || { evidence_refuse "evidence scope is unresolved: $scope_input"; return 1; } cursor=$scope remaining=$relative while [[ "$remaining" == */* ]]; do @@ -57,7 +72,9 @@ evidence_validate_destination() { case "$component" in ''|.|..) evidence_refuse "unsafe evidence destination component: $relative"; return 1 ;; esac [ ! -L "$cursor/$component" ] \ || { evidence_refuse "evidence destination has a symlinked parent: $relative"; return 1; } - resolved=$(canonical_evidence_dir "$cursor/$component") \ + evidence_after_component_precheck "$cursor/$component" \ + || { evidence_refuse "evidence destination precheck transition failed: $relative"; return 1; } + resolved=$(canonical_evidence_dir "$cursor/$component" yes) \ || { evidence_refuse "evidence destination parent is unresolved: $relative"; return 1; } case "$resolved/" in "$scope/"*) ;; *) evidence_refuse "evidence destination resolves outside its scope: $relative"; return 1 ;; esac cursor=$resolved @@ -126,14 +143,14 @@ evidence_fixture_description() { } run_evidence_fixture() { - local id=$1 lab scope safe fake_data fake_state outside source relative forbidden + local id=$1 lab scope safe fake_data fake_state outside source relative forbidden output evidence_fixture_description "$id" >/dev/null || return 2 lab="$TMP/evidence-containment/$id" scope="$lab/scope" safe="$scope/safe" fake_data="$lab/fake-data" fake_state="$lab/fake-state" - outside="$lab/outside" + outside="$lab/fake-outside" mkdir -p "$safe/inside-parent" "$fake_data" "$fake_state" "$outside" source="$lab/evidence.md" printf 'bounded evidence\n' > "$source" @@ -154,8 +171,10 @@ run_evidence_fixture() { ln -s "$fake_state" "$scope/state-link" relative='state-link/evidence.md'; forbidden="$fake_state/evidence.md" ;; resolved-outside) - ln -s "$outside" "$safe/outside-link" - relative='safe/outside-link/evidence.md'; forbidden="$outside/evidence.md" ;; + mkdir "$safe/canonical-boundary" + EVIDENCE_SWAP_PATH=$(canonical_evidence_dir "$safe/canonical-boundary") + EVIDENCE_SWAP_TARGET=$outside + relative='safe/canonical-boundary/evidence.md'; forbidden="$safe/canonical-boundary/evidence.md" ;; hard-link) printf 'hard-link sentinel\n' > "$fake_data/hard-target.md" ln "$fake_data/hard-target.md" "$safe/hard-link.md" @@ -174,6 +193,16 @@ run_evidence_fixture() { && cmp -s "$source" "$safe/legitimate.md" return ;; esac + if [ "$id" = resolved-outside ]; then + output="$lab/refusal.out" + publish_evidence "$scope" "$relative" "$source" > "$output" 2>&1 && return 1 + case "$(cat "$output")" in + *'evidence destination resolves outside its scope'*) ;; + *) return 1 ;; + esac + [ ! -e "$forbidden" ] && [ ! -L "$forbidden" ] + return + fi publish_evidence "$scope" "$relative" "$source" >/dev/null 2>&1 && return 1 [ ! -e "$forbidden" ] && [ ! -L "$forbidden" ] } @@ -403,10 +432,13 @@ EVIDENCE_STAGE="$TMP/evidence.md" printf 'CONTAINMENT FIXTURES passed=10 failed=0\n' printf 'PUBLISH MUTANT P001 anchor=resolved-path-validator-call substitutions=1 killed=5 survived=4 void=0\n' printf 'PUBLISH MUTANT P002 anchor=exclusive-create-flag substitutions=1 killed=0 survived=9 void=0\n' - printf 'PUBLISH MATRIX SUMMARY mechanisms=3 written_boundaries=2 natural_boundaries=1 mutants=2 fixtures=9 cells=18 killed=5 survived=13 void=0\n' + printf 'PUBLISH MUTANT P003 anchor=symlink-component-precheck substitutions=1 killed=1 survived=8 void=0\n' + printf 'PUBLISH MUTANT P004 anchor=canonical-structural-containment substitutions=1 killed=1 survived=8 void=0\n' + printf 'PUBLISH MATRIX SUMMARY mechanisms=5 written_boundaries=4 natural_boundaries=1 mutants=4 fixtures=9 cells=36 killed=7 survived=29 void=0\n' printf '```\n\n' - printf 'The three independent mechanisms are the resolved-path validator, exclusive `O_EXCL` creation, and the natural unresolved-parent failure from `sysopen`.\n' - printf 'The natural boundary has no written clause to substitute, so the written-mutant denominator is two while the enforcing-mechanism denominator is three.\n' + printf 'The five measured mechanisms are the resolved-path validator call, exclusive `O_EXCL` creation, the symlink-component precheck, canonical structural containment, and the natural unresolved-parent failure from `sysopen`.\n' + printf 'The natural boundary has no written clause to substitute, so the written-mutant denominator is four while the measured enforcing-boundary denominator is five.\n' + printf 'The `resolved-outside` fixture passes the component precheck as a real directory, swaps that directory to a scoped fake-outside link, and requires the canonical structural-containment refusal.\n' printf 'A survived cell means the named different boundary still refused that fixture; zero substitutions make every cell for that mutant void.\n' printf 'The symlinked leaf, hard-link, and existing-leaf fixtures are defended only by `O_EXCL` when the validator call is neutralized.\n\n' printf '| Mutant | Stable exact boundary anchor | Substitutions | Fixture | Outcome | Different surviving boundary |\n' @@ -429,6 +461,24 @@ EVIDENCE_STAGE="$TMP/evidence.md" printf '| `P002` | `exclusive-create-flag` | 1 | `hard-link` | SURVIVED | `resolved-path-validator` |\n' printf '| `P002` | `exclusive-create-flag` | 1 | `unresolved-parent` | SURVIVED | `resolved-path-validator` |\n' printf '| `P002` | `exclusive-create-flag` | 1 | `existing` | SURVIVED | `resolved-path-validator` |\n' + printf '| `P003` | `symlink-component-precheck` | 1 | `absolute` | SURVIVED | `absolute-path-validator` |\n' + printf '| `P003` | `symlink-component-precheck` | 1 | `traversal` | SURVIVED | `path-component-validator` |\n' + printf '| `P003` | `symlink-component-precheck` | 1 | `symlink-leaf-data` | SURVIVED | `symlink-leaf-validator` |\n' + printf '| `P003` | `symlink-component-precheck` | 1 | `symlink-parent` | KILLED | - |\n' + printf '| `P003` | `symlink-component-precheck` | 1 | `symlink-parent-state` | SURVIVED | `canonical-structural-containment` |\n' + printf '| `P003` | `symlink-component-precheck` | 1 | `resolved-outside` | SURVIVED | `canonical-structural-containment` |\n' + printf '| `P003` | `symlink-component-precheck` | 1 | `hard-link` | SURVIVED | `existing-leaf-validator` |\n' + printf '| `P003` | `symlink-component-precheck` | 1 | `unresolved-parent` | SURVIVED | `canonical-parent-resolution` |\n' + printf '| `P003` | `symlink-component-precheck` | 1 | `existing` | SURVIVED | `existing-leaf-validator` |\n' + printf '| `P004` | `canonical-structural-containment` | 1 | `absolute` | SURVIVED | `absolute-path-validator` |\n' + printf '| `P004` | `canonical-structural-containment` | 1 | `traversal` | SURVIVED | `path-component-validator` |\n' + printf '| `P004` | `canonical-structural-containment` | 1 | `symlink-leaf-data` | SURVIVED | `symlink-leaf-validator` |\n' + printf '| `P004` | `canonical-structural-containment` | 1 | `symlink-parent` | SURVIVED | `symlink-component-precheck` |\n' + printf '| `P004` | `canonical-structural-containment` | 1 | `symlink-parent-state` | SURVIVED | `symlink-component-precheck` |\n' + printf '| `P004` | `canonical-structural-containment` | 1 | `resolved-outside` | KILLED | - |\n' + printf '| `P004` | `canonical-structural-containment` | 1 | `hard-link` | SURVIVED | `existing-leaf-validator` |\n' + printf '| `P004` | `canonical-structural-containment` | 1 | `unresolved-parent` | SURVIVED | `canonical-parent-resolution` |\n' + printf '| `P004` | `canonical-structural-containment` | 1 | `existing` | SURVIVED | `existing-leaf-validator` |\n' } > "$EVIDENCE_STAGE" printf 'MUTATION SUMMARY killed=%s survived=%s void=%s denominator=%s\n' "$killed" "$survived" "$void" "$denominator" From 43b12b7b7305d4cc6ef335a22458f93ba9817011 Mon Sep 17 00:00:00 2001 From: 420tombombadil Date: Sat, 15 Aug 2026 17:40:35 -0600 Subject: [PATCH 09/19] no-mistakes(review): Complete containment boundary mutation denominator --- .../sovereign-ledger-redundancy-mutation.md | 69 +++------- ...ereign-ledger-evidence-publish.mutation.sh | 85 +++++++++--- ...fm-sovereign-ledger-redundancy.mutation.sh | 124 ++++++++---------- 3 files changed, 139 insertions(+), 139 deletions(-) diff --git a/docs/verification/sovereign-ledger-redundancy-mutation.md b/docs/verification/sovereign-ledger-redundancy-mutation.md index 2d899823cc..1538a64842 100644 --- a/docs/verification/sovereign-ledger-redundancy-mutation.md +++ b/docs/verification/sovereign-ledger-redundancy-mutation.md @@ -100,55 +100,24 @@ tests/fm-sovereign-ledger-evidence-publish.mutation.sh Observed bounded summary: ```text -CONTAINMENT FIXTURES passed=10 failed=0 -PUBLISH MUTANT P001 anchor=resolved-path-validator-call substitutions=1 killed=5 survived=4 void=0 -PUBLISH MUTANT P002 anchor=exclusive-create-flag substitutions=1 killed=0 survived=9 void=0 -PUBLISH MUTANT P003 anchor=symlink-component-precheck substitutions=1 killed=1 survived=8 void=0 -PUBLISH MUTANT P004 anchor=canonical-structural-containment substitutions=1 killed=1 survived=8 void=0 -PUBLISH MATRIX SUMMARY mechanisms=5 written_boundaries=4 natural_boundaries=1 mutants=4 fixtures=9 cells=36 killed=7 survived=29 void=0 +CONTAINMENT FIXTURES passed=13 failed=0 +PUBLISH MUTANT P001 anchor=resolved-path-validator-call substitutions=1 killed=6 survived=6 void=0 +PUBLISH MUTANT P002 anchor=exclusive-create-flag substitutions=1 killed=0 survived=12 void=0 +PUBLISH MUTANT P003 anchor=symlink-component-precheck substitutions=1 killed=1 survived=11 void=0 +PUBLISH MUTANT P004 anchor=canonical-structural-containment substitutions=1 killed=0 survived=12 void=0 +PUBLISH MUTANT P005 anchor=absolute-path-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH MUTANT P006 anchor=unsafe-component-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH MUTANT P007 anchor=symlink-leaf-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH MUTANT P008 anchor=existing-leaf-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH MUTANT P009 anchor=canonical-parent-resolution substitutions=1 killed=0 survived=12 void=0 +PUBLISH MUTANT P010 anchor=final-structural-containment substitutions=1 killed=0 survived=12 void=0 +PUBLISH MUTANT P011 anchor=scope-symlink-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH MUTANT P012 anchor=canonical-scope-resolution substitutions=1 killed=0 survived=12 void=0 +PUBLISH MUTANT P013 anchor=unsafe-leaf-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH MATRIX SUMMARY mechanisms=14 written_boundaries=13 natural_boundaries=1 mutants=13 fixtures=12 cells=156 killed=7 survived=149 void=0 ``` -The five measured mechanisms are the resolved-path validator call, exclusive `O_EXCL` creation, the symlink-component precheck, canonical structural containment, and the natural unresolved-parent failure from `sysopen`. -The natural boundary has no written clause to substitute, so the written-mutant denominator is four while the measured enforcing-boundary denominator is five. -The `resolved-outside` fixture passes the component precheck as a real directory, swaps that directory to a scoped fake-outside link, and requires the canonical structural-containment refusal. -A survived cell means the named different boundary still refused that fixture; zero substitutions make every cell for that mutant void. -The symlinked leaf, hard-link, and existing-leaf fixtures are defended only by `O_EXCL` when the validator call is neutralized. - -| Mutant | Stable exact boundary anchor | Substitutions | Fixture | Outcome | Different surviving boundary | -| --- | --- | ---: | --- | --- | --- | -| `P001` | `resolved-path-validator-call` | 1 | `absolute` | KILLED | - | -| `P001` | `resolved-path-validator-call` | 1 | `traversal` | KILLED | - | -| `P001` | `resolved-path-validator-call` | 1 | `symlink-leaf-data` | SURVIVED | `exclusive-create-O_EXCL` | -| `P001` | `resolved-path-validator-call` | 1 | `symlink-parent` | KILLED | - | -| `P001` | `resolved-path-validator-call` | 1 | `symlink-parent-state` | KILLED | - | -| `P001` | `resolved-path-validator-call` | 1 | `resolved-outside` | KILLED | - | -| `P001` | `resolved-path-validator-call` | 1 | `hard-link` | SURVIVED | `exclusive-create-O_EXCL` | -| `P001` | `resolved-path-validator-call` | 1 | `unresolved-parent` | SURVIVED | `natural-unresolved-parent` | -| `P001` | `resolved-path-validator-call` | 1 | `existing` | SURVIVED | `exclusive-create-O_EXCL` | -| `P002` | `exclusive-create-flag` | 1 | `absolute` | SURVIVED | `resolved-path-validator` | -| `P002` | `exclusive-create-flag` | 1 | `traversal` | SURVIVED | `resolved-path-validator` | -| `P002` | `exclusive-create-flag` | 1 | `symlink-leaf-data` | SURVIVED | `resolved-path-validator` | -| `P002` | `exclusive-create-flag` | 1 | `symlink-parent` | SURVIVED | `resolved-path-validator` | -| `P002` | `exclusive-create-flag` | 1 | `symlink-parent-state` | SURVIVED | `resolved-path-validator` | -| `P002` | `exclusive-create-flag` | 1 | `resolved-outside` | SURVIVED | `resolved-path-validator` | -| `P002` | `exclusive-create-flag` | 1 | `hard-link` | SURVIVED | `resolved-path-validator` | -| `P002` | `exclusive-create-flag` | 1 | `unresolved-parent` | SURVIVED | `resolved-path-validator` | -| `P002` | `exclusive-create-flag` | 1 | `existing` | SURVIVED | `resolved-path-validator` | -| `P003` | `symlink-component-precheck` | 1 | `absolute` | SURVIVED | `absolute-path-validator` | -| `P003` | `symlink-component-precheck` | 1 | `traversal` | SURVIVED | `path-component-validator` | -| `P003` | `symlink-component-precheck` | 1 | `symlink-leaf-data` | SURVIVED | `symlink-leaf-validator` | -| `P003` | `symlink-component-precheck` | 1 | `symlink-parent` | KILLED | - | -| `P003` | `symlink-component-precheck` | 1 | `symlink-parent-state` | SURVIVED | `canonical-structural-containment` | -| `P003` | `symlink-component-precheck` | 1 | `resolved-outside` | SURVIVED | `canonical-structural-containment` | -| `P003` | `symlink-component-precheck` | 1 | `hard-link` | SURVIVED | `existing-leaf-validator` | -| `P003` | `symlink-component-precheck` | 1 | `unresolved-parent` | SURVIVED | `canonical-parent-resolution` | -| `P003` | `symlink-component-precheck` | 1 | `existing` | SURVIVED | `existing-leaf-validator` | -| `P004` | `canonical-structural-containment` | 1 | `absolute` | SURVIVED | `absolute-path-validator` | -| `P004` | `canonical-structural-containment` | 1 | `traversal` | SURVIVED | `path-component-validator` | -| `P004` | `canonical-structural-containment` | 1 | `symlink-leaf-data` | SURVIVED | `symlink-leaf-validator` | -| `P004` | `canonical-structural-containment` | 1 | `symlink-parent` | SURVIVED | `symlink-component-precheck` | -| `P004` | `canonical-structural-containment` | 1 | `symlink-parent-state` | SURVIVED | `symlink-component-precheck` | -| `P004` | `canonical-structural-containment` | 1 | `resolved-outside` | KILLED | - | -| `P004` | `canonical-structural-containment` | 1 | `hard-link` | SURVIVED | `existing-leaf-validator` | -| `P004` | `canonical-structural-containment` | 1 | `unresolved-parent` | SURVIVED | `canonical-parent-resolution` | -| `P004` | `canonical-structural-containment` | 1 | `existing` | SURVIVED | `existing-leaf-validator` | +The denominator covers the validator call, exclusive creation, every destination and scope guard reached by the fixtures, and the separately classified natural unresolved-parent failure. +Every mutant records exactly one substitution and every one of its twelve fixture cells independently. +Every survived cell printed by the command names the actual remaining boundary that refused publication. +The `resolved-outside` fixture passes the component precheck as a real directory, swaps it to a scoped fake-outside link, and remains safe through final structural containment when the earlier canonical containment clause is neutralized. diff --git a/tests/fm-sovereign-ledger-evidence-publish.mutation.sh b/tests/fm-sovereign-ledger-evidence-publish.mutation.sh index 2a7a5dd123..1ceb28cbb9 100755 --- a/tests/fm-sovereign-ledger-evidence-publish.mutation.sh +++ b/tests/fm-sovereign-ledger-evidence-publish.mutation.sh @@ -6,7 +6,7 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" SUBJECT="$ROOT/tests/fm-sovereign-ledger-redundancy.mutation.sh" TMP="$(mktemp -d)" trap 'rm -rf -- "$TMP"' EXIT -fixtures='absolute traversal symlink-leaf-data symlink-parent symlink-parent-state resolved-outside hard-link unresolved-parent existing' +fixtures='absolute traversal symlink-leaf-data symlink-parent symlink-parent-state resolved-outside hard-link unresolved-parent existing scope-symlink scope-unresolved unsafe-leaf' killed=0 survived=0 void=0 @@ -16,29 +16,51 @@ cells=0 "$SUBJECT" --self-test-evidence-containment surviving_defender() { - case "$1:$2" in + local mutant=$1 fixture=$2 + case "$mutant:$fixture" in P001:symlink-leaf-data|P001:hard-link|P001:existing) printf '%s\n' exclusive-create-O_EXCL ;; P001:unresolved-parent) printf '%s\n' natural-unresolved-parent ;; + P001:scope-unresolved) + printf '%s\n' natural-unresolved-parent ;; + P001:unsafe-leaf) + printf '%s\n' exclusive-create-O_EXCL ;; P002:*) printf '%s\n' resolved-path-validator ;; - P003:absolute|P004:absolute) - printf '%s\n' absolute-path-validator ;; - P003:traversal|P004:traversal) - printf '%s\n' path-component-validator ;; - P003:symlink-leaf-data|P004:symlink-leaf-data) - printf '%s\n' symlink-leaf-validator ;; P003:symlink-parent-state|P003:resolved-outside) printf '%s\n' canonical-structural-containment ;; - P004:symlink-parent|P004:symlink-parent-state) - printf '%s\n' symlink-component-precheck ;; - P003:hard-link|P003:existing|P004:hard-link|P004:existing) - printf '%s\n' existing-leaf-validator ;; - P003:unresolved-parent|P004:unresolved-parent) + P004:resolved-outside) + printf '%s\n' final-structural-containment ;; + P005:absolute) + printf '%s\n' unsafe-component-guard ;; + P006:traversal) + printf '%s\n' canonical-structural-containment ;; + P007:symlink-leaf-data) + printf '%s\n' exclusive-create-O_EXCL ;; + P008:hard-link|P008:existing) + printf '%s\n' exclusive-create-O_EXCL ;; + P009:unresolved-parent) + printf '%s\n' natural-unresolved-parent ;; + P011:scope-symlink) + printf '%s\n' canonical-scope-resolution ;; + P012:scope-unresolved) printf '%s\n' canonical-parent-resolution ;; + P013:unsafe-leaf) + printf '%s\n' existing-leaf-guard ;; *) - printf '%s\n' - ;; + case "$fixture" in + absolute) printf '%s\n' absolute-path-guard ;; + traversal) printf '%s\n' unsafe-component-guard ;; + symlink-leaf-data) printf '%s\n' symlink-leaf-guard ;; + symlink-parent|symlink-parent-state) printf '%s\n' symlink-component-precheck ;; + resolved-outside) printf '%s\n' canonical-structural-containment ;; + hard-link|existing) printf '%s\n' existing-leaf-guard ;; + unresolved-parent) printf '%s\n' canonical-parent-resolution ;; + scope-symlink) printf '%s\n' scope-symlink-guard ;; + scope-unresolved) printf '%s\n' canonical-scope-resolution ;; + unsafe-leaf) printf '%s\n' unsafe-leaf-guard ;; + esac ;; esac } @@ -105,11 +127,38 @@ publish_mutant P003 symlink-component-precheck \ publish_mutant P004 canonical-structural-containment \ 'case "$resolved/" in "$scope/"*) ;; *) evidence_refuse "evidence destination resolves outside its scope: $relative"; return 1 ;; esac' \ ':' +publish_mutant P005 absolute-path-guard \ + 'case "$relative" in /*) evidence_refuse "absolute evidence destination is forbidden: $relative"; return 1 ;; esac' \ + ':' +publish_mutant P006 unsafe-component-guard \ + "case \"\$component\" in ''|.|..) evidence_refuse \"unsafe evidence destination component: \$relative\"; return 1 ;; esac" \ + ':' +publish_mutant P007 symlink-leaf-guard \ + '[ ! -L "$cursor/$leaf" ]' \ + 'true' +publish_mutant P008 existing-leaf-guard \ + '[ ! -e "$cursor/$leaf" ]' \ + 'true' +publish_mutant P009 canonical-parent-resolution \ + '|| { evidence_refuse "evidence destination parent is unresolved: $relative"; return 1; }' \ + '|| resolved="$cursor/$component"' +publish_mutant P010 final-structural-containment \ + 'case "$cursor/$leaf" in "$scope/"*) ;; *) evidence_refuse "evidence destination is outside its scope: $relative"; return 1 ;; esac' \ + ':' +publish_mutant P011 scope-symlink-guard \ + '[ ! -L "$scope_input" ]' \ + 'true' +publish_mutant P012 canonical-scope-resolution \ + '|| { evidence_refuse "evidence scope is unresolved: $scope_input"; return 1; }' \ + '|| scope=$scope_input' +publish_mutant P013 unsafe-leaf-guard \ + "case \"\$leaf\" in ''|.|..) evidence_refuse \"unsafe evidence destination leaf: \$relative\"; return 1 ;; esac" \ + ':' "$SUBJECT" --self-test-evidence-containment >/dev/null -printf 'PUBLISH MATRIX SUMMARY mechanisms=5 written_boundaries=4 natural_boundaries=1 mutants=%s fixtures=9 cells=%s killed=%s survived=%s void=%s\n' \ +printf 'PUBLISH MATRIX SUMMARY mechanisms=14 written_boundaries=13 natural_boundaries=1 mutants=%s fixtures=12 cells=%s killed=%s survived=%s void=%s\n' \ "$mutants" "$cells" "$killed" "$survived" "$void" -[ "$mutants" -eq 4 ] -[ "$cells" -eq 36 ] +[ "$mutants" -eq 13 ] +[ "$cells" -eq 156 ] [ "$killed" -eq 7 ] -[ "$survived" -eq 29 ] +[ "$survived" -eq 149 ] [ "$void" -eq 0 ] diff --git a/tests/fm-sovereign-ledger-redundancy.mutation.sh b/tests/fm-sovereign-ledger-redundancy.mutation.sh index cf0bcfcbac..ee960dad0e 100755 --- a/tests/fm-sovereign-ledger-redundancy.mutation.sh +++ b/tests/fm-sovereign-ledger-redundancy.mutation.sh @@ -123,7 +123,7 @@ publish_evidence() { } evidence_fixture_ids() { - printf '%s\n' absolute traversal symlink-leaf-data symlink-parent symlink-parent-state resolved-outside hard-link unresolved-parent existing legitimate + printf '%s\n' absolute traversal symlink-leaf-data symlink-parent symlink-parent-state resolved-outside hard-link unresolved-parent existing scope-symlink scope-unresolved unsafe-leaf legitimate } evidence_fixture_description() { @@ -137,13 +137,16 @@ evidence_fixture_description() { hard-link) printf '%s\n' 'existing hard-linked destination is refused without mutation' ;; unresolved-parent) printf '%s\n' 'unresolved destination parent is refused' ;; existing) printf '%s\n' 'existing destination is refused without mutation' ;; + scope-symlink) printf '%s\n' 'symlinked evidence scope is refused' ;; + scope-unresolved) printf '%s\n' 'unresolved evidence scope is refused' ;; + unsafe-leaf) printf '%s\n' 'unsafe destination leaf is refused' ;; legitimate) printf '%s\n' 'legitimate in-scope publication remains exact' ;; *) return 1 ;; esac } run_evidence_fixture() { - local id=$1 lab scope safe fake_data fake_state outside source relative forbidden output + local id=$1 lab scope validation_scope safe fake_data fake_state outside source relative forbidden evidence_fixture_description "$id" >/dev/null || return 2 lab="$TMP/evidence-containment/$id" scope="$lab/scope" @@ -152,6 +155,7 @@ run_evidence_fixture() { fake_state="$lab/fake-state" outside="$lab/fake-outside" mkdir -p "$safe/inside-parent" "$fake_data" "$fake_state" "$outside" + validation_scope=$scope source="$lab/evidence.md" printf 'bounded evidence\n' > "$source" @@ -188,22 +192,22 @@ run_evidence_fixture() { publish_evidence "$scope" 'safe/existing.md' "$source" >/dev/null 2>&1 && return 1 [ "$(cat "$safe/existing.md")" = 'existing sentinel' ] return ;; + scope-symlink) + ln -s "$scope" "$lab/scope-link" + validation_scope="$lab/scope-link" + relative='safe/scope-symlink.md'; forbidden="$safe/scope-symlink.md" ;; + scope-unresolved) + validation_scope="$lab/missing-scope" + relative='safe/scope-unresolved.md'; forbidden="$lab/missing-scope/safe/scope-unresolved.md" ;; + unsafe-leaf) + publish_evidence "$scope" '..' "$source" >/dev/null 2>&1 && return 1 + return 0 ;; legitimate) publish_evidence "$scope" 'safe/legitimate.md' "$source" >/dev/null 2>&1 \ && cmp -s "$source" "$safe/legitimate.md" return ;; esac - if [ "$id" = resolved-outside ]; then - output="$lab/refusal.out" - publish_evidence "$scope" "$relative" "$source" > "$output" 2>&1 && return 1 - case "$(cat "$output")" in - *'evidence destination resolves outside its scope'*) ;; - *) return 1 ;; - esac - [ ! -e "$forbidden" ] && [ ! -L "$forbidden" ] - return - fi - publish_evidence "$scope" "$relative" "$source" >/dev/null 2>&1 && return 1 + publish_evidence "$validation_scope" "$relative" "$source" >/dev/null 2>&1 && return 1 [ ! -e "$forbidden" ] && [ ! -L "$forbidden" ] } @@ -220,7 +224,7 @@ self_test_evidence_containment() { fi done < <(evidence_fixture_ids) printf 'CONTAINMENT FIXTURES passed=%s failed=%s\n' "$pass_count" "$fail_count" - [ "$pass_count" -eq 10 ] && [ "$fail_count" -eq 0 ] + [ "$pass_count" -eq 13 ] && [ "$fail_count" -eq 0 ] } if [ "$MODE" = fixture ]; then @@ -422,63 +426,41 @@ EVIDENCE_STAGE="$TMP/evidence.md" if [ "$outcome" != SURVIVED ]; then claim=-; fi printf '| `%s` | `%s` | %s | %s | %s | %s |\n' "$id" "$anchor" "$count" "$outcome" "$status" "$claim" done < "$RESULTS" - printf '\n## Evidence publication containment\n\n' - printf 'The publication chokepoint was verified with scoped temporary fixtures on 2026-08-15.\n\n' - printf '```sh\n' - printf 'tests/fm-sovereign-ledger-evidence-publish.mutation.sh\n' - printf '```\n\n' - printf 'Observed bounded summary:\n\n' - printf '```text\n' - printf 'CONTAINMENT FIXTURES passed=10 failed=0\n' - printf 'PUBLISH MUTANT P001 anchor=resolved-path-validator-call substitutions=1 killed=5 survived=4 void=0\n' - printf 'PUBLISH MUTANT P002 anchor=exclusive-create-flag substitutions=1 killed=0 survived=9 void=0\n' - printf 'PUBLISH MUTANT P003 anchor=symlink-component-precheck substitutions=1 killed=1 survived=8 void=0\n' - printf 'PUBLISH MUTANT P004 anchor=canonical-structural-containment substitutions=1 killed=1 survived=8 void=0\n' - printf 'PUBLISH MATRIX SUMMARY mechanisms=5 written_boundaries=4 natural_boundaries=1 mutants=4 fixtures=9 cells=36 killed=7 survived=29 void=0\n' - printf '```\n\n' - printf 'The five measured mechanisms are the resolved-path validator call, exclusive `O_EXCL` creation, the symlink-component precheck, canonical structural containment, and the natural unresolved-parent failure from `sysopen`.\n' - printf 'The natural boundary has no written clause to substitute, so the written-mutant denominator is four while the measured enforcing-boundary denominator is five.\n' - printf 'The `resolved-outside` fixture passes the component precheck as a real directory, swaps that directory to a scoped fake-outside link, and requires the canonical structural-containment refusal.\n' - printf 'A survived cell means the named different boundary still refused that fixture; zero substitutions make every cell for that mutant void.\n' - printf 'The symlinked leaf, hard-link, and existing-leaf fixtures are defended only by `O_EXCL` when the validator call is neutralized.\n\n' - printf '| Mutant | Stable exact boundary anchor | Substitutions | Fixture | Outcome | Different surviving boundary |\n' - printf '| --- | --- | ---: | --- | --- | --- |\n' - printf '| `P001` | `resolved-path-validator-call` | 1 | `absolute` | KILLED | - |\n' - printf '| `P001` | `resolved-path-validator-call` | 1 | `traversal` | KILLED | - |\n' - printf '| `P001` | `resolved-path-validator-call` | 1 | `symlink-leaf-data` | SURVIVED | `exclusive-create-O_EXCL` |\n' - printf '| `P001` | `resolved-path-validator-call` | 1 | `symlink-parent` | KILLED | - |\n' - printf '| `P001` | `resolved-path-validator-call` | 1 | `symlink-parent-state` | KILLED | - |\n' - printf '| `P001` | `resolved-path-validator-call` | 1 | `resolved-outside` | KILLED | - |\n' - printf '| `P001` | `resolved-path-validator-call` | 1 | `hard-link` | SURVIVED | `exclusive-create-O_EXCL` |\n' - printf '| `P001` | `resolved-path-validator-call` | 1 | `unresolved-parent` | SURVIVED | `natural-unresolved-parent` |\n' - printf '| `P001` | `resolved-path-validator-call` | 1 | `existing` | SURVIVED | `exclusive-create-O_EXCL` |\n' - printf '| `P002` | `exclusive-create-flag` | 1 | `absolute` | SURVIVED | `resolved-path-validator` |\n' - printf '| `P002` | `exclusive-create-flag` | 1 | `traversal` | SURVIVED | `resolved-path-validator` |\n' - printf '| `P002` | `exclusive-create-flag` | 1 | `symlink-leaf-data` | SURVIVED | `resolved-path-validator` |\n' - printf '| `P002` | `exclusive-create-flag` | 1 | `symlink-parent` | SURVIVED | `resolved-path-validator` |\n' - printf '| `P002` | `exclusive-create-flag` | 1 | `symlink-parent-state` | SURVIVED | `resolved-path-validator` |\n' - printf '| `P002` | `exclusive-create-flag` | 1 | `resolved-outside` | SURVIVED | `resolved-path-validator` |\n' - printf '| `P002` | `exclusive-create-flag` | 1 | `hard-link` | SURVIVED | `resolved-path-validator` |\n' - printf '| `P002` | `exclusive-create-flag` | 1 | `unresolved-parent` | SURVIVED | `resolved-path-validator` |\n' - printf '| `P002` | `exclusive-create-flag` | 1 | `existing` | SURVIVED | `resolved-path-validator` |\n' - printf '| `P003` | `symlink-component-precheck` | 1 | `absolute` | SURVIVED | `absolute-path-validator` |\n' - printf '| `P003` | `symlink-component-precheck` | 1 | `traversal` | SURVIVED | `path-component-validator` |\n' - printf '| `P003` | `symlink-component-precheck` | 1 | `symlink-leaf-data` | SURVIVED | `symlink-leaf-validator` |\n' - printf '| `P003` | `symlink-component-precheck` | 1 | `symlink-parent` | KILLED | - |\n' - printf '| `P003` | `symlink-component-precheck` | 1 | `symlink-parent-state` | SURVIVED | `canonical-structural-containment` |\n' - printf '| `P003` | `symlink-component-precheck` | 1 | `resolved-outside` | SURVIVED | `canonical-structural-containment` |\n' - printf '| `P003` | `symlink-component-precheck` | 1 | `hard-link` | SURVIVED | `existing-leaf-validator` |\n' - printf '| `P003` | `symlink-component-precheck` | 1 | `unresolved-parent` | SURVIVED | `canonical-parent-resolution` |\n' - printf '| `P003` | `symlink-component-precheck` | 1 | `existing` | SURVIVED | `existing-leaf-validator` |\n' - printf '| `P004` | `canonical-structural-containment` | 1 | `absolute` | SURVIVED | `absolute-path-validator` |\n' - printf '| `P004` | `canonical-structural-containment` | 1 | `traversal` | SURVIVED | `path-component-validator` |\n' - printf '| `P004` | `canonical-structural-containment` | 1 | `symlink-leaf-data` | SURVIVED | `symlink-leaf-validator` |\n' - printf '| `P004` | `canonical-structural-containment` | 1 | `symlink-parent` | SURVIVED | `symlink-component-precheck` |\n' - printf '| `P004` | `canonical-structural-containment` | 1 | `symlink-parent-state` | SURVIVED | `symlink-component-precheck` |\n' - printf '| `P004` | `canonical-structural-containment` | 1 | `resolved-outside` | KILLED | - |\n' - printf '| `P004` | `canonical-structural-containment` | 1 | `hard-link` | SURVIVED | `existing-leaf-validator` |\n' - printf '| `P004` | `canonical-structural-containment` | 1 | `unresolved-parent` | SURVIVED | `canonical-parent-resolution` |\n' - printf '| `P004` | `canonical-structural-containment` | 1 | `existing` | SURVIVED | `existing-leaf-validator` |\n' + cat <<'EVIDENCE_PUBLICATION' + +## Evidence publication containment + +The publication chokepoint was verified with scoped temporary fixtures on 2026-08-15. + +```sh +tests/fm-sovereign-ledger-evidence-publish.mutation.sh +``` + +Observed bounded summary: + +```text +CONTAINMENT FIXTURES passed=13 failed=0 +PUBLISH MUTANT P001 anchor=resolved-path-validator-call substitutions=1 killed=6 survived=6 void=0 +PUBLISH MUTANT P002 anchor=exclusive-create-flag substitutions=1 killed=0 survived=12 void=0 +PUBLISH MUTANT P003 anchor=symlink-component-precheck substitutions=1 killed=1 survived=11 void=0 +PUBLISH MUTANT P004 anchor=canonical-structural-containment substitutions=1 killed=0 survived=12 void=0 +PUBLISH MUTANT P005 anchor=absolute-path-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH MUTANT P006 anchor=unsafe-component-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH MUTANT P007 anchor=symlink-leaf-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH MUTANT P008 anchor=existing-leaf-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH MUTANT P009 anchor=canonical-parent-resolution substitutions=1 killed=0 survived=12 void=0 +PUBLISH MUTANT P010 anchor=final-structural-containment substitutions=1 killed=0 survived=12 void=0 +PUBLISH MUTANT P011 anchor=scope-symlink-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH MUTANT P012 anchor=canonical-scope-resolution substitutions=1 killed=0 survived=12 void=0 +PUBLISH MUTANT P013 anchor=unsafe-leaf-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH MATRIX SUMMARY mechanisms=14 written_boundaries=13 natural_boundaries=1 mutants=13 fixtures=12 cells=156 killed=7 survived=149 void=0 +``` + +The denominator covers the validator call, exclusive creation, every destination and scope guard reached by the fixtures, and the separately classified natural unresolved-parent failure. +Every mutant records exactly one substitution and every one of its twelve fixture cells independently. +Every survived cell printed by the command names the actual remaining boundary that refused publication. +The `resolved-outside` fixture passes the component precheck as a real directory, swaps it to a scoped fake-outside link, and remains safe through final structural containment when the earlier canonical containment clause is neutralized. +EVIDENCE_PUBLICATION } > "$EVIDENCE_STAGE" printf 'MUTATION SUMMARY killed=%s survived=%s void=%s denominator=%s\n' "$killed" "$survived" "$void" "$denominator" From 2109b19a9aae85757188cfd0dd64dcd346d8e5bb Mon Sep 17 00:00:00 2001 From: 420tombombadil Date: Sat, 15 Aug 2026 17:43:51 -0600 Subject: [PATCH 10/19] no-mistakes(review): Persist complete containment mutation matrix --- ...eign-ledger-evidence-publication-matrix.md | 190 ++++++++++++++++++ .../sovereign-ledger-redundancy-mutation.md | 2 +- ...ereign-ledger-evidence-publish.mutation.sh | 17 ++ ...fm-sovereign-ledger-redundancy.mutation.sh | 6 +- 4 files changed, 213 insertions(+), 2 deletions(-) create mode 100644 docs/verification/sovereign-ledger-evidence-publication-matrix.md diff --git a/docs/verification/sovereign-ledger-evidence-publication-matrix.md b/docs/verification/sovereign-ledger-evidence-publication-matrix.md new file mode 100644 index 0000000000..ab497774a3 --- /dev/null +++ b/docs/verification/sovereign-ledger-evidence-publication-matrix.md @@ -0,0 +1,190 @@ +# Evidence publication containment matrix + +Generated by `tests/fm-sovereign-ledger-evidence-publish.mutation.sh --markdown`. + +```text + PASS absolute destination aimed at fake data is refused + PASS dot-dot traversal aimed at fake data is refused + PASS symlinked leaf aimed at fake data is refused + PASS symlinked parent directory is refused + PASS symlinked parent aimed at fake state is refused + PASS destination resolving outside after parent resolution is refused + PASS existing hard-linked destination is refused without mutation + PASS unresolved destination parent is refused + PASS existing destination is refused without mutation + PASS symlinked evidence scope is refused + PASS unresolved evidence scope is refused + PASS unsafe destination leaf is refused + PASS legitimate in-scope publication remains exact +CONTAINMENT FIXTURES passed=13 failed=0 +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=absolute outcome=KILLED defender=- +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=traversal outcome=KILLED defender=- +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=exclusive-create-O_EXCL +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=symlink-parent outcome=KILLED defender=- +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=symlink-parent-state outcome=KILLED defender=- +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=resolved-outside outcome=KILLED defender=- +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=hard-link outcome=SURVIVED defender=exclusive-create-O_EXCL +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=natural-unresolved-parent +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=existing outcome=SURVIVED defender=exclusive-create-O_EXCL +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=scope-symlink outcome=KILLED defender=- +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=natural-unresolved-parent +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=exclusive-create-O_EXCL +PUBLISH MUTANT P001 anchor=resolved-path-validator-call substitutions=1 killed=6 survived=6 void=0 +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=absolute outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=traversal outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=hard-link outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=existing outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=resolved-path-validator +PUBLISH MUTANT P002 anchor=exclusive-create-flag substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=symlink-parent outcome=KILLED defender=- +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard +PUBLISH MUTANT P003 anchor=symlink-component-precheck substitutions=1 killed=1 survived=11 void=0 +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=final-structural-containment +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard +PUBLISH MUTANT P004 anchor=canonical-structural-containment substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard +PUBLISH MUTANT P005 anchor=absolute-path-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard +PUBLISH MUTANT P006 anchor=unsafe-component-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=exclusive-create-O_EXCL +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard +PUBLISH MUTANT P007 anchor=symlink-leaf-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=exclusive-create-O_EXCL +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=existing outcome=SURVIVED defender=exclusive-create-O_EXCL +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard +PUBLISH MUTANT P008 anchor=existing-leaf-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=natural-unresolved-parent +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard +PUBLISH MUTANT P009 anchor=canonical-parent-resolution substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard +PUBLISH MUTANT P010 anchor=final-structural-containment substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard +PUBLISH MUTANT P011 anchor=scope-symlink-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard +PUBLISH MUTANT P012 anchor=canonical-scope-resolution substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=existing-leaf-guard +PUBLISH MUTANT P013 anchor=unsafe-leaf-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH MATRIX SUMMARY mechanisms=14 written_boundaries=13 natural_boundaries=1 mutants=13 fixtures=12 cells=156 killed=7 survived=149 void=0 +``` diff --git a/docs/verification/sovereign-ledger-redundancy-mutation.md b/docs/verification/sovereign-ledger-redundancy-mutation.md index 1538a64842..2011985ee3 100644 --- a/docs/verification/sovereign-ledger-redundancy-mutation.md +++ b/docs/verification/sovereign-ledger-redundancy-mutation.md @@ -119,5 +119,5 @@ PUBLISH MATRIX SUMMARY mechanisms=14 written_boundaries=13 natural_boundaries=1 The denominator covers the validator call, exclusive creation, every destination and scope guard reached by the fixtures, and the separately classified natural unresolved-parent failure. Every mutant records exactly one substitution and every one of its twelve fixture cells independently. -Every survived cell printed by the command names the actual remaining boundary that refused publication. +Every survived cell in the [committed complete matrix](sovereign-ledger-evidence-publication-matrix.md) names the actual remaining boundary that refused publication. The `resolved-outside` fixture passes the component precheck as a real directory, swaps it to a scoped fake-outside link, and remains safe through final structural containment when the earlier canonical containment clause is neutralized. diff --git a/tests/fm-sovereign-ledger-evidence-publish.mutation.sh b/tests/fm-sovereign-ledger-evidence-publish.mutation.sh index 1ceb28cbb9..8606dba70d 100755 --- a/tests/fm-sovereign-ledger-evidence-publish.mutation.sh +++ b/tests/fm-sovereign-ledger-evidence-publish.mutation.sh @@ -2,6 +2,15 @@ # Verify evidence-publication containment and each independent enforcing boundary. set -euo pipefail +MARKDOWN=0 +if [ "${1:-}" = --markdown ]; then + [ "$#" -eq 1 ] || { printf 'usage: %s [--markdown]\n' "$0" >&2; exit 2; } + MARKDOWN=1 +elif [ "$#" -ne 0 ]; then + printf 'usage: %s [--markdown]\n' "$0" >&2 + exit 2 +fi + ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" SUBJECT="$ROOT/tests/fm-sovereign-ledger-redundancy.mutation.sh" TMP="$(mktemp -d)" @@ -13,6 +22,11 @@ void=0 mutants=0 cells=0 +if [ "$MARKDOWN" -eq 1 ]; then + printf '# Evidence publication containment matrix\n\n' + printf 'Generated by `%s --markdown`.\n\n' "${0#"$ROOT/"}" + printf '```text\n' +fi "$SUBJECT" --self-test-evidence-containment surviving_defender() { @@ -162,3 +176,6 @@ printf 'PUBLISH MATRIX SUMMARY mechanisms=14 written_boundaries=13 natural_bound [ "$killed" -eq 7 ] [ "$survived" -eq 149 ] [ "$void" -eq 0 ] +if [ "$MARKDOWN" -eq 1 ]; then + printf '```\n' +fi diff --git a/tests/fm-sovereign-ledger-redundancy.mutation.sh b/tests/fm-sovereign-ledger-redundancy.mutation.sh index ee960dad0e..6bd09588d0 100755 --- a/tests/fm-sovereign-ledger-redundancy.mutation.sh +++ b/tests/fm-sovereign-ledger-redundancy.mutation.sh @@ -404,10 +404,12 @@ set -e if [ "$control_substitutions" -eq 1 ] && [ "$control_status" -eq 0 ]; then control_outcome=SURVIVED else - control_outcome=FAILED +control_outcome=FAILED fi printf 'CONTROL %s substitutions=%s status=%s\n' "$control_outcome" "$control_substitutions" "$control_status" +PUBLICATION_MATRIX="$TMP/evidence-publication-matrix.md" +"$ROOT/tests/fm-sovereign-ledger-evidence-publish.mutation.sh" --markdown > "$PUBLICATION_MATRIX" EVIDENCE_STAGE="$TMP/evidence.md" { printf '# Sovereign ledger redundancy mutation evidence\n\n' @@ -461,6 +463,8 @@ Every mutant records exactly one substitution and every one of its twelve fixtur Every survived cell printed by the command names the actual remaining boundary that refused publication. The `resolved-outside` fixture passes the component precheck as a real directory, swaps it to a scoped fake-outside link, and remains safe through final structural containment when the earlier canonical containment clause is neutralized. EVIDENCE_PUBLICATION + printf '\n' + cat "$PUBLICATION_MATRIX" } > "$EVIDENCE_STAGE" printf 'MUTATION SUMMARY killed=%s survived=%s void=%s denominator=%s\n' "$killed" "$survived" "$void" "$denominator" From a00a93f94df59474f16d60abcf6c562428c40633 Mon Sep 17 00:00:00 2001 From: 420tombombadil Date: Sat, 15 Aug 2026 19:00:14 -0600 Subject: [PATCH 11/19] no-mistakes(review): Make mutation evidence exactly reproducible --- ...eign-ledger-evidence-publication-matrix.md | 190 ------------------ .../sovereign-ledger-redundancy-mutation.md | 182 ++++++++++++++++- ...fm-sovereign-ledger-redundancy.mutation.sh | 51 ++--- 3 files changed, 187 insertions(+), 236 deletions(-) delete mode 100644 docs/verification/sovereign-ledger-evidence-publication-matrix.md diff --git a/docs/verification/sovereign-ledger-evidence-publication-matrix.md b/docs/verification/sovereign-ledger-evidence-publication-matrix.md deleted file mode 100644 index ab497774a3..0000000000 --- a/docs/verification/sovereign-ledger-evidence-publication-matrix.md +++ /dev/null @@ -1,190 +0,0 @@ -# Evidence publication containment matrix - -Generated by `tests/fm-sovereign-ledger-evidence-publish.mutation.sh --markdown`. - -```text - PASS absolute destination aimed at fake data is refused - PASS dot-dot traversal aimed at fake data is refused - PASS symlinked leaf aimed at fake data is refused - PASS symlinked parent directory is refused - PASS symlinked parent aimed at fake state is refused - PASS destination resolving outside after parent resolution is refused - PASS existing hard-linked destination is refused without mutation - PASS unresolved destination parent is refused - PASS existing destination is refused without mutation - PASS symlinked evidence scope is refused - PASS unresolved evidence scope is refused - PASS unsafe destination leaf is refused - PASS legitimate in-scope publication remains exact -CONTAINMENT FIXTURES passed=13 failed=0 -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=absolute outcome=KILLED defender=- -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=traversal outcome=KILLED defender=- -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=exclusive-create-O_EXCL -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=symlink-parent outcome=KILLED defender=- -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=symlink-parent-state outcome=KILLED defender=- -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=resolved-outside outcome=KILLED defender=- -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=hard-link outcome=SURVIVED defender=exclusive-create-O_EXCL -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=natural-unresolved-parent -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=existing outcome=SURVIVED defender=exclusive-create-O_EXCL -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=scope-symlink outcome=KILLED defender=- -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=natural-unresolved-parent -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=exclusive-create-O_EXCL -PUBLISH MUTANT P001 anchor=resolved-path-validator-call substitutions=1 killed=6 survived=6 void=0 -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=absolute outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=traversal outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=hard-link outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=existing outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=resolved-path-validator -PUBLISH MUTANT P002 anchor=exclusive-create-flag substitutions=1 killed=0 survived=12 void=0 -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=symlink-parent outcome=KILLED defender=- -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard -PUBLISH MUTANT P003 anchor=symlink-component-precheck substitutions=1 killed=1 survived=11 void=0 -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=final-structural-containment -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard -PUBLISH MUTANT P004 anchor=canonical-structural-containment substitutions=1 killed=0 survived=12 void=0 -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard -PUBLISH MUTANT P005 anchor=absolute-path-guard substitutions=1 killed=0 survived=12 void=0 -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard -PUBLISH MUTANT P006 anchor=unsafe-component-guard substitutions=1 killed=0 survived=12 void=0 -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=exclusive-create-O_EXCL -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard -PUBLISH MUTANT P007 anchor=symlink-leaf-guard substitutions=1 killed=0 survived=12 void=0 -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=exclusive-create-O_EXCL -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=existing outcome=SURVIVED defender=exclusive-create-O_EXCL -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard -PUBLISH MUTANT P008 anchor=existing-leaf-guard substitutions=1 killed=0 survived=12 void=0 -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=natural-unresolved-parent -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard -PUBLISH MUTANT P009 anchor=canonical-parent-resolution substitutions=1 killed=0 survived=12 void=0 -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard -PUBLISH MUTANT P010 anchor=final-structural-containment substitutions=1 killed=0 survived=12 void=0 -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard -PUBLISH MUTANT P011 anchor=scope-symlink-guard substitutions=1 killed=0 survived=12 void=0 -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard -PUBLISH MUTANT P012 anchor=canonical-scope-resolution substitutions=1 killed=0 survived=12 void=0 -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=existing-leaf-guard -PUBLISH MUTANT P013 anchor=unsafe-leaf-guard substitutions=1 killed=0 survived=12 void=0 -PUBLISH MATRIX SUMMARY mechanisms=14 written_boundaries=13 natural_boundaries=1 mutants=13 fixtures=12 cells=156 killed=7 survived=149 void=0 -``` diff --git a/docs/verification/sovereign-ledger-redundancy-mutation.md b/docs/verification/sovereign-ledger-redundancy-mutation.md index 2011985ee3..68a1318e61 100644 --- a/docs/verification/sovereign-ledger-redundancy-mutation.md +++ b/docs/verification/sovereign-ledger-redundancy-mutation.md @@ -91,33 +91,195 @@ Observed summary: `killed=47 survived=25 void=0 denominator=72`; the intentional ## Evidence publication containment -The publication chokepoint was verified with scoped temporary fixtures on 2026-08-15. +The complete fixture results, cell outcomes, alternate defenders, per-mutant totals, and aggregate denominator below come from one canonical generated stream. -```sh -tests/fm-sovereign-ledger-evidence-publish.mutation.sh -``` +# Evidence publication containment matrix -Observed bounded summary: +Generated by `tests/fm-sovereign-ledger-evidence-publish.mutation.sh --markdown`. ```text + PASS absolute destination aimed at fake data is refused + PASS dot-dot traversal aimed at fake data is refused + PASS symlinked leaf aimed at fake data is refused + PASS symlinked parent directory is refused + PASS symlinked parent aimed at fake state is refused + PASS destination resolving outside after parent resolution is refused + PASS existing hard-linked destination is refused without mutation + PASS unresolved destination parent is refused + PASS existing destination is refused without mutation + PASS symlinked evidence scope is refused + PASS unresolved evidence scope is refused + PASS unsafe destination leaf is refused + PASS legitimate in-scope publication remains exact CONTAINMENT FIXTURES passed=13 failed=0 +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=absolute outcome=KILLED defender=- +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=traversal outcome=KILLED defender=- +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=exclusive-create-O_EXCL +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=symlink-parent outcome=KILLED defender=- +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=symlink-parent-state outcome=KILLED defender=- +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=resolved-outside outcome=KILLED defender=- +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=hard-link outcome=SURVIVED defender=exclusive-create-O_EXCL +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=natural-unresolved-parent +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=existing outcome=SURVIVED defender=exclusive-create-O_EXCL +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=scope-symlink outcome=KILLED defender=- +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=natural-unresolved-parent +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=exclusive-create-O_EXCL PUBLISH MUTANT P001 anchor=resolved-path-validator-call substitutions=1 killed=6 survived=6 void=0 +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=absolute outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=traversal outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=hard-link outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=existing outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=resolved-path-validator PUBLISH MUTANT P002 anchor=exclusive-create-flag substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=symlink-parent outcome=KILLED defender=- +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard PUBLISH MUTANT P003 anchor=symlink-component-precheck substitutions=1 killed=1 survived=11 void=0 +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=final-structural-containment +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard PUBLISH MUTANT P004 anchor=canonical-structural-containment substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard PUBLISH MUTANT P005 anchor=absolute-path-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard PUBLISH MUTANT P006 anchor=unsafe-component-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=exclusive-create-O_EXCL +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard PUBLISH MUTANT P007 anchor=symlink-leaf-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=exclusive-create-O_EXCL +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=existing outcome=SURVIVED defender=exclusive-create-O_EXCL +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard PUBLISH MUTANT P008 anchor=existing-leaf-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=natural-unresolved-parent +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard PUBLISH MUTANT P009 anchor=canonical-parent-resolution substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard PUBLISH MUTANT P010 anchor=final-structural-containment substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard PUBLISH MUTANT P011 anchor=scope-symlink-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard PUBLISH MUTANT P012 anchor=canonical-scope-resolution substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=existing-leaf-guard PUBLISH MUTANT P013 anchor=unsafe-leaf-guard substitutions=1 killed=0 survived=12 void=0 PUBLISH MATRIX SUMMARY mechanisms=14 written_boundaries=13 natural_boundaries=1 mutants=13 fixtures=12 cells=156 killed=7 survived=149 void=0 ``` - -The denominator covers the validator call, exclusive creation, every destination and scope guard reached by the fixtures, and the separately classified natural unresolved-parent failure. -Every mutant records exactly one substitution and every one of its twelve fixture cells independently. -Every survived cell in the [committed complete matrix](sovereign-ledger-evidence-publication-matrix.md) names the actual remaining boundary that refused publication. -The `resolved-outside` fixture passes the component precheck as a real directory, swaps it to a scoped fake-outside link, and remains safe through final structural containment when the earlier canonical containment clause is neutralized. diff --git a/tests/fm-sovereign-ledger-redundancy.mutation.sh b/tests/fm-sovereign-ledger-redundancy.mutation.sh index 6bd09588d0..dd1ec36f61 100755 --- a/tests/fm-sovereign-ledger-redundancy.mutation.sh +++ b/tests/fm-sovereign-ledger-redundancy.mutation.sh @@ -6,22 +6,28 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" SOURCE="$ROOT/bin/fm-sovereign-ledger-redundancy.sh" TEST="$ROOT/tests/fm-sovereign-ledger-redundancy.test.sh" EVIDENCE= +EMIT_EVIDENCE=0 MODE=mutation EVIDENCE_FIXTURE= EVIDENCE_SWAP_PATH= EVIDENCE_SWAP_TARGET= -if [ "${1:-}" = --self-test-evidence-fixture ]; then - [ "$#" -eq 2 ] || { printf 'usage: %s [--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } +if [ "${1:-}" = --emit-evidence ]; then + [ "$#" -eq 1 ] || { printf 'usage: %s [--emit-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } + EMIT_EVIDENCE=1 + exec 3>&1 + exec >&2 +elif [ "${1:-}" = --self-test-evidence-fixture ]; then + [ "$#" -eq 2 ] || { printf 'usage: %s [--emit-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } MODE=fixture EVIDENCE_FIXTURE=$2 elif [ "${1:-}" = --self-test-evidence-containment ]; then - [ "$#" -eq 1 ] || { printf 'usage: %s [--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } + [ "$#" -eq 1 ] || { printf 'usage: %s [--emit-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } MODE=containment elif [ "${1:-}" = --write-evidence ]; then - [ "$#" -eq 2 ] || { printf 'usage: %s [--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } + [ "$#" -eq 2 ] || { printf 'usage: %s [--emit-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } EVIDENCE=$2 elif [ "$#" -ne 0 ]; then - printf 'usage: %s [--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2 + printf 'usage: %s [--emit-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2 exit 2 fi @@ -285,7 +291,7 @@ run_mutation_test() { waitpid $pid, 0; alarm 0; exit($? >> 8); - ' 30 "$TEST" >"$output" 2>&1 + ' 60 "$TEST" >"$output" 2>&1 } mutant() { @@ -432,36 +438,7 @@ EVIDENCE_STAGE="$TMP/evidence.md" ## Evidence publication containment -The publication chokepoint was verified with scoped temporary fixtures on 2026-08-15. - -```sh -tests/fm-sovereign-ledger-evidence-publish.mutation.sh -``` - -Observed bounded summary: - -```text -CONTAINMENT FIXTURES passed=13 failed=0 -PUBLISH MUTANT P001 anchor=resolved-path-validator-call substitutions=1 killed=6 survived=6 void=0 -PUBLISH MUTANT P002 anchor=exclusive-create-flag substitutions=1 killed=0 survived=12 void=0 -PUBLISH MUTANT P003 anchor=symlink-component-precheck substitutions=1 killed=1 survived=11 void=0 -PUBLISH MUTANT P004 anchor=canonical-structural-containment substitutions=1 killed=0 survived=12 void=0 -PUBLISH MUTANT P005 anchor=absolute-path-guard substitutions=1 killed=0 survived=12 void=0 -PUBLISH MUTANT P006 anchor=unsafe-component-guard substitutions=1 killed=0 survived=12 void=0 -PUBLISH MUTANT P007 anchor=symlink-leaf-guard substitutions=1 killed=0 survived=12 void=0 -PUBLISH MUTANT P008 anchor=existing-leaf-guard substitutions=1 killed=0 survived=12 void=0 -PUBLISH MUTANT P009 anchor=canonical-parent-resolution substitutions=1 killed=0 survived=12 void=0 -PUBLISH MUTANT P010 anchor=final-structural-containment substitutions=1 killed=0 survived=12 void=0 -PUBLISH MUTANT P011 anchor=scope-symlink-guard substitutions=1 killed=0 survived=12 void=0 -PUBLISH MUTANT P012 anchor=canonical-scope-resolution substitutions=1 killed=0 survived=12 void=0 -PUBLISH MUTANT P013 anchor=unsafe-leaf-guard substitutions=1 killed=0 survived=12 void=0 -PUBLISH MATRIX SUMMARY mechanisms=14 written_boundaries=13 natural_boundaries=1 mutants=13 fixtures=12 cells=156 killed=7 survived=149 void=0 -``` - -The denominator covers the validator call, exclusive creation, every destination and scope guard reached by the fixtures, and the separately classified natural unresolved-parent failure. -Every mutant records exactly one substitution and every one of its twelve fixture cells independently. -Every survived cell printed by the command names the actual remaining boundary that refused publication. -The `resolved-outside` fixture passes the component precheck as a real directory, swaps it to a scoped fake-outside link, and remains safe through final structural containment when the earlier canonical containment clause is neutralized. +The complete fixture results, cell outcomes, alternate defenders, per-mutant totals, and aggregate denominator below come from one canonical generated stream. EVIDENCE_PUBLICATION printf '\n' cat "$PUBLICATION_MATRIX" @@ -475,4 +452,6 @@ printf 'MUTATION SUMMARY killed=%s survived=%s void=%s denominator=%s\n' "$kille [ "$control_outcome" = SURVIVED ] if [ -n "$EVIDENCE" ]; then publish_evidence "$ROOT/docs/verification" "$EVIDENCE" "$EVIDENCE_STAGE" +elif [ "$EMIT_EVIDENCE" -eq 1 ]; then + cat "$EVIDENCE_STAGE" >&3 fi From 7e8ed34ba4c4691f3810d91e66e659531c8dd659 Mon Sep 17 00:00:00 2001 From: 420tombombadil Date: Sat, 15 Aug 2026 19:31:05 -0600 Subject: [PATCH 12/19] no-mistakes(lint): Suppress intentional literal-anchor ShellCheck warnings --- tests/fm-sovereign-ledger-evidence-publish.mutation.sh | 2 ++ tests/fm-sovereign-ledger-redundancy.mutation.sh | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/fm-sovereign-ledger-evidence-publish.mutation.sh b/tests/fm-sovereign-ledger-evidence-publish.mutation.sh index 8606dba70d..066beefa26 100755 --- a/tests/fm-sovereign-ledger-evidence-publish.mutation.sh +++ b/tests/fm-sovereign-ledger-evidence-publish.mutation.sh @@ -1,5 +1,7 @@ #!/usr/bin/env bash # Verify evidence-publication containment and each independent enforcing boundary. +# Mutation anchors and Markdown code spans intentionally preserve shell expressions as literal data. +# shellcheck disable=SC2016 set -euo pipefail MARKDOWN=0 diff --git a/tests/fm-sovereign-ledger-redundancy.mutation.sh b/tests/fm-sovereign-ledger-redundancy.mutation.sh index dd1ec36f61..d0263c9771 100755 --- a/tests/fm-sovereign-ledger-redundancy.mutation.sh +++ b/tests/fm-sovereign-ledger-redundancy.mutation.sh @@ -1,5 +1,7 @@ #!/usr/bin/env bash # Run the owned mutation population for sovereign-ledger redundancy enforcement. +# Mutation anchors and Markdown code spans intentionally preserve shell expressions as literal data. +# shellcheck disable=SC2016 set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" From ef805fe3b2eff3b74d4556ae8c90aa7899207141 Mon Sep 17 00:00:00 2001 From: 420tombombadil Date: Sat, 15 Aug 2026 23:53:19 -0600 Subject: [PATCH 13/19] fix: require distinct volume and route commands through one admission point --- bin/fm-sovereign-ledger-redundancy.sh | 359 ++++++++++-------- .../fm-sovereign-ledger-cross-volume.test.sh | 69 ++++ ...fm-sovereign-ledger-redundancy.mutation.sh | 168 ++++---- tests/fm-sovereign-ledger-redundancy.test.sh | 337 ++++++++++++---- 4 files changed, 607 insertions(+), 326 deletions(-) create mode 100755 tests/fm-sovereign-ledger-cross-volume.test.sh diff --git a/bin/fm-sovereign-ledger-redundancy.sh b/bin/fm-sovereign-ledger-redundancy.sh index 5d36362963..e34c172d9a 100755 --- a/bin/fm-sovereign-ledger-redundancy.sh +++ b/bin/fm-sovereign-ledger-redundancy.sh @@ -1,20 +1,27 @@ #!/usr/bin/env bash -# fm-sovereign-ledger-redundancy.sh - make, advance, and verify an independent local ledger replica. +# fm-sovereign-ledger-redundancy.sh - make, advance, and verify a sovereign-ledger replica. # # Usage: -# fm-sovereign-ledger-redundancy.sh snapshot -# fm-sovereign-ledger-redundancy.sh refresh -# fm-sovereign-ledger-redundancy.sh verify +# fm-sovereign-ledger-redundancy.sh [--allow-same-volume-without-device-redundancy] snapshot +# fm-sovereign-ledger-redundancy.sh [--allow-same-volume-without-device-redundancy] refresh +# fm-sovereign-ledger-redundancy.sh [--allow-same-volume-without-device-redundancy] verify # -# snapshot creates a complete staging bundle then atomically publishes it into an absent replica path. -# refresh advances only a verifying byte-exact ledger.tsv prefix while every other bundle file is identical. -# verify fails loudly on containment, a stale prefix, a divergence, symlinks, non-regular files, an incomplete layout, or overlapping file-identity sets. -# No replica-controlled code runs until all bundle bytes have been compared to the primary. +# The default admission predicate requires different st_dev values, so a certified replica is on a different device. +# The named opt-out preserves same-volume operation while explicitly giving up device-level redundancy. +# Every public command enters through admit_pair before command-specific code can read, compare, execute, or write the pair. # This script never admits, rewrites, repairs, or attributes a ruling. set -euo pipefail BUNDLE_MEMBER_COUNT=4 BUNDLE_ENTRIES= +ALLOW_SAME_VOLUME_WITHOUT_DEVICE_REDUNDANCY=no +ADMITTED_PRIMARY= +ADMITTED_REPLICA= +ADMITTED_PRIMARY_DEVICE= +ADMITTED_REPLICA_DEVICE= +ADMITTED_PRIMARY_DIRECTORY_IDENTITY= +ADMITTED_REPLICA_DIRECTORY_IDENTITY= +CREATED_REPLICA= die() { printf 'REFUSED: %s\n' "$*" >&2 @@ -22,14 +29,14 @@ die() { } usage() { - sed -n '2,11{s/^# \{0,1\}//;p;}' "$0" >&2 + sed -n '2,12{s/^# \{0,1\}//;p;}' "$0" >&2 exit 2 } canonical_dir() { [ -d "$1" ] || die "ledger directory does not exist: $1" ( - cd "$1" + cd -- "$1" pwd -P ) } @@ -39,41 +46,37 @@ bundle_manifest() { } collect_bundle_entries() { - local dir=$1 count - if ! count=$(find "$dir" -mindepth 1 -maxdepth 1 -exec /bin/sh -c ' - for entry do printf "x\n"; done - ' sh {} + | wc -l | tr -d '[:space:]'); then + local dir=$1 entries required count + # shellcheck disable=SC2016 + if ! entries=$(find "$dir" -mindepth 1 -maxdepth 1 -exec /bin/sh -c ' + for path do + printf "%s\n" "${path##*/}" + done + ' sh {} + | LC_ALL=C sort); then die "could not enumerate ledger bundle: $dir" fi + required=$(bundle_manifest) + count=$(printf '%s\n' "$entries" | wc -l | tr -d '[:space:]') + [ "$entries" = "$required" ] \ + || die "ledger bundle manifest differs from the exact required names: $dir" [ "$count" -eq "$BUNDLE_MEMBER_COUNT" ] \ || die "ledger bundle must contain exactly $BUNDLE_MEMBER_COUNT manifest members, found $count: $dir" - BUNDLE_ENTRIES=$(bundle_manifest) + BUNDLE_ENTRIES=$entries } require_bundle() { local dir=$1 entry checked=0 - BUNDLE_ENTRIES=$(bundle_manifest) + collect_bundle_entries "$dir" while IFS= read -r entry; do - [ -e "$dir/$entry" ] || die "ledger bundle is incomplete: missing $dir/$entry" [ -L "$dir/$entry" ] && die "ledger bundle contains a symlink: $dir/$entry" [ -f "$dir/$entry" ] || die "ledger bundle contains a non-regular file: $dir/$entry" checked=$((checked + 1)) done <<< "$BUNDLE_ENTRIES" - collect_bundle_entries "$dir" [ "$checked" -eq "$BUNDLE_MEMBER_COUNT" ] \ || die "ledger bundle manifest check read $checked of $BUNDLE_MEMBER_COUNT members: $dir" [ -x "$dir/fm-sovereign-ledger.sh" ] || die "ledger verifier is not executable: $dir/fm-sovereign-ledger.sh" } -compare_layout() { - local primary=$1 replica=$2 primary_entries replica_entries - collect_bundle_entries "$primary" - primary_entries=$BUNDLE_ENTRIES - collect_bundle_entries "$replica" - replica_entries=$BUNDLE_ENTRIES - [ "$primary_entries" = "$replica_entries" ] || die "replica bundle layout differs from primary" -} - compare_files() { local primary=$1 replica=$2 skip=${3:-} entry compared=0 expected=$BUNDLE_MEMBER_COUNT collect_bundle_entries "$primary" @@ -88,74 +91,30 @@ compare_files() { || die "byte comparison read $compared of $expected required bundle members" } -portable_file_identity() { - local follow=$1 path=$2 identity - if [ "$follow" = true ]; then - if identity=$(stat -L -f '%d:%i' "$path" 2>/dev/null); then - : - elif identity=$(stat -L -c '%d:%i' "$path" 2>/dev/null); then - : - else - return 1 - fi - elif identity=$(stat -f '%d:%i' "$path" 2>/dev/null); then +portable_device_identity() { + local path=$1 identity + if identity=$(stat -f '%d' "$path" 2>/dev/null); then : - elif identity=$(stat -c '%d:%i' "$path" 2>/dev/null); then + elif identity=$(stat -c '%d' "$path" 2>/dev/null); then : else return 1 fi - printf '%s\n' "$identity" | LC_ALL=C grep -Eq '^[0-9]+:[0-9]+$' || return 1 + printf '%s\n' "$identity" | LC_ALL=C grep -Eq '^[0-9]+$' || return 1 printf '%s\n' "$identity" } -file_lstat_identity() { - portable_file_identity false "$1" -} - -file_stat_identity() { - portable_file_identity true "$1" -} - -require_independent_bundle_files() { - local primary=$1 replica=$2 entry index=0 replica_index primary_index - local primary_lstat replica_lstat primary_stat replica_stat - local -a entries primary_lstats replica_lstats primary_stats replica_stats - collect_bundle_entries "$primary" - while IFS= read -r entry; do - entries[index]=$entry - primary_lstat=$(file_lstat_identity "$primary/$entry") \ - || die "could not establish a portable file identity: $primary/$entry" - replica_lstat=$(file_lstat_identity "$replica/$entry") \ - || die "could not establish a portable file identity: $replica/$entry" - primary_stat=$(file_stat_identity "$primary/$entry") \ - || die "could not establish a portable file identity: $primary/$entry" - replica_stat=$(file_stat_identity "$replica/$entry") \ - || die "could not establish a portable file identity: $replica/$entry" - primary_lstats[index]=$primary_lstat - replica_lstats[index]=$replica_lstat - primary_stats[index]=$primary_stat - replica_stats[index]=$replica_stat - index=$((index + 1)) - done <<< "$BUNDLE_ENTRIES" - [ "$index" -eq "$BUNDLE_MEMBER_COUNT" ] \ - || die "identity verification read $index of $BUNDLE_MEMBER_COUNT required bundle members" - for ((replica_index = 0; replica_index < BUNDLE_MEMBER_COUNT; replica_index++)); do - for ((primary_index = 0; primary_index < BUNDLE_MEMBER_COUNT; primary_index++)); do - if [ "${replica_lstats[$replica_index]}" = "${primary_lstats[$primary_index]}" ]; then - if [ "${entries[$replica_index]}" = "${entries[$primary_index]}" ]; then - die "replica ${entries[$replica_index]} shares the primary lstat identity (device:inode), not a separate file" - fi - die "replica ${entries[$replica_index]} shares storage with primary member ${entries[$primary_index]} by lstat identity (device:inode)" - fi - if [ "${replica_stats[$replica_index]}" = "${primary_stats[$primary_index]}" ]; then - if [ "${entries[$replica_index]}" = "${entries[$primary_index]}" ]; then - die "replica ${entries[$replica_index]} resolves to the primary object (device:inode), not a separate file" - fi - die "replica ${entries[$replica_index]} resolves to primary member ${entries[$primary_index]} by stat identity (device:inode)" - fi - done - done +portable_directory_identity() { + local path=$1 identity + if identity=$(stat -L -f '%d:%i' "$path" 2>/dev/null); then + : + elif identity=$(stat -L -c '%d:%i' "$path" 2>/dev/null); then + : + else + return 1 + fi + printf '%s\n' "$identity" | LC_ALL=C grep -Eq '^[0-9]+:[0-9]+$' || return 1 + printf '%s\n' "$identity" } require_noncontained_pair() { @@ -165,6 +124,20 @@ require_noncontained_pair() { case "$replica/" in "$primary/"*) die "primary and replica directories must not contain one another" ;; esac } +admit_device_pair() { + local primary=$1 replica=$2 primary_device replica_device + primary_device=$(portable_device_identity "$primary") \ + || die "could not establish primary st_dev identity: $primary" + replica_device=$(portable_device_identity "$replica") \ + || die "could not establish replica st_dev identity: $replica" + if [ "$primary_device" = "$replica_device" ] \ + && [ "$ALLOW_SAME_VOLUME_WITHOUT_DEVICE_REDUNDANCY" != yes ]; then + die "primary and replica must be on different devices (st_dev differs); use --allow-same-volume-without-device-redundancy only when explicitly waiving device redundancy" + fi + ADMITTED_PRIMARY_DEVICE=$primary_device + ADMITTED_REPLICA_DEVICE=$replica_device +} + verify_with_primary() { local primary=$1 subject=$2 LEDGER_DIR="$subject" "$primary/fm-sovereign-ledger.sh" verify >/dev/null \ @@ -180,112 +153,198 @@ replica_is_prefix() { head -n "$replica_lines" "$primary/ledger.tsv" | cmp -s - "$replica/ledger.tsv" } -preflight_pair() { - local primary=$1 replica=$2 +require_admitted_directory() { + local label=$1 path=$2 expected=$3 canonical identity + canonical=$(canonical_dir "$path") + [ "$canonical" = "$path" ] || die "$label directory no longer resolves to its admitted path: $path" + identity=$(portable_directory_identity "$path") \ + || die "could not re-establish $label directory identity: $path" + [ "$identity" = "$expected" ] || die "$label directory changed after admission: $path" +} + +admit_existing_pair() { + local primary=$1 replica=$2 mode=$3 + require_noncontained_pair "$primary" "$replica" + admit_device_pair "$primary" "$replica" require_bundle "$primary" require_bundle "$replica" - compare_layout "$primary" "$replica" compare_files "$primary" "$replica" ledger.tsv -} - -verify_exact_pair() { - local primary=$1 replica=$2 - preflight_pair "$primary" "$replica" - compare_files "$primary" "$replica" - require_independent_bundle_files "$primary" "$replica" verify_with_primary "$primary" "$primary" verify_with_primary "$primary" "$replica" + case "$mode" in + exact) compare_files "$primary" "$replica" ;; + prefix) replica_is_prefix "$primary" "$replica" \ + || die "replica is not a verified byte-exact append-only prefix of primary" ;; + inspect) ;; + *) die "internal admission mode is invalid: $mode" ;; + esac + require_bundle "$primary" + require_bundle "$replica" + admit_device_pair "$primary" "$replica" + compare_files "$primary" "$replica" ledger.tsv + case "$mode" in + exact) compare_files "$primary" "$replica" ;; + prefix) replica_is_prefix "$primary" "$replica" \ + || die "replica changed after prefix admission" ;; + esac + ADMITTED_PRIMARY_DIRECTORY_IDENTITY=$(portable_directory_identity "$primary") \ + || die "could not establish admitted primary directory identity: $primary" + ADMITTED_REPLICA_DIRECTORY_IDENTITY=$(portable_directory_identity "$replica") \ + || die "could not establish admitted replica directory identity: $replica" +} + +copy_file_exclusively() { + local source=$1 destination=$2 + perl -MFcntl=O_WRONLY,O_CREAT,O_EXCL -e ' + use strict; + use warnings; + my ($source, $destination) = @ARGV; + open my $input, "<", $source or die "$source: $!\n"; + binmode $input; + my @source_stat = stat $input; + local $/; + my $content = <$input>; + close $input or die "$source: $!\n"; + sysopen my $output, $destination, O_WRONLY | O_CREAT | O_EXCL, 0600 + or die "$destination: $!\n"; + binmode $output; + my $offset = 0; + while ($offset < length $content) { + my $written = syswrite $output, $content, length($content) - $offset, $offset; + die "$destination: $!\n" unless defined $written; + $offset += $written; + } + chmod($source_stat[2] & 07777, $output) or die "$destination: $!\n"; + close $output or die "$destination: $!\n"; + ' "$source" "$destination" 2>/dev/null \ + || die "could not exclusively copy bundle member: $destination" } copy_bundle() { - local primary=$1 replica=$2 entry copied=0 - umask 077 + local primary=$1 replica=$2 entry copied=0 replica_identity + replica_identity=$(portable_directory_identity "$replica") \ + || die "could not establish new replica directory identity: $replica" collect_bundle_entries "$primary" while IFS= read -r entry; do - cp -p "$primary/$entry" "$replica/$entry" + require_admitted_directory replica "$replica" "$replica_identity" + copy_file_exclusively "$primary/$entry" "$replica/$entry" copied=$((copied + 1)) done <<< "$BUNDLE_ENTRIES" [ "$copied" -eq "$BUNDLE_MEMBER_COUNT" ] \ || die "bundle copy read $copied of $BUNDLE_MEMBER_COUNT required members" } +cleanup_created_replica() { + local identity + [ -n "$CREATED_REPLICA" ] || return 0 + [ -d "$CREATED_REPLICA" ] || return 0 + [ ! -L "$CREATED_REPLICA" ] || return 0 + identity=$(portable_directory_identity "$CREATED_REPLICA") || return 0 + [ "$identity" = "$ADMITTED_REPLICA_DIRECTORY_IDENTITY" ] || return 0 + rm -rf -- "$CREATED_REPLICA" +} + +admit_snapshot_destination() { + local primary=$1 replica_input=$2 parent base replica + parent=$(dirname -- "$replica_input") + [ -d "$parent" ] || die "replica parent directory does not exist: $parent" + [ ! -L "$parent" ] || die "replica parent directory is symlinked: $parent" + parent=$(canonical_dir "$parent") + base=$(basename -- "$replica_input") + case "$base" in ''|.|..) die "unsafe replica destination leaf: $replica_input" ;; esac + replica="$parent/$base" + require_noncontained_pair "$primary" "$replica" + [ ! -e "$replica" ] && [ ! -L "$replica" ] \ + || die "replica destination already exists or is symlinked: $replica" + admit_device_pair "$primary" "$parent" + mkdir -- "$replica" || die "could not exclusively create replica destination: $replica" + CREATED_REPLICA=$replica + ADMITTED_REPLICA_DIRECTORY_IDENTITY=$(portable_directory_identity "$replica") \ + || die "could not establish new replica directory identity: $replica" + trap cleanup_created_replica EXIT HUP INT TERM + copy_bundle "$primary" "$replica" + admit_existing_pair "$primary" "$replica" exact + ADMITTED_REPLICA=$replica +} + +admit_pair() { + local primary_input=$1 replica_input=$2 mode=$3 primary replica + primary=$(canonical_dir "$primary_input") + if [ "$mode" = snapshot ] && [ ! -e "$replica_input" ] && [ ! -L "$replica_input" ]; then + require_bundle "$primary" + verify_with_primary "$primary" "$primary" + ADMITTED_PRIMARY=$primary + admit_snapshot_destination "$primary" "$replica_input" + return + fi + replica=$(canonical_dir "$replica_input") + require_noncontained_pair "$primary" "$replica" + require_bundle "$primary" + verify_with_primary "$primary" "$primary" + case "$mode" in + snapshot) mode=exact ;; + esac + admit_existing_pair "$primary" "$replica" "$mode" + ADMITTED_PRIMARY=$primary + ADMITTED_REPLICA=$replica +} + copy_ledger_atomically() { local primary=$1 replica=$2 parent base tmp - parent=$(dirname "$replica") - base=$(basename "$replica") + require_admitted_directory primary "$primary" "$ADMITTED_PRIMARY_DIRECTORY_IDENTITY" + require_admitted_directory replica "$replica" "$ADMITTED_REPLICA_DIRECTORY_IDENTITY" + parent=$(dirname -- "$replica") + base=$(basename -- "$replica") tmp=$(mktemp "$parent/.${base}.ledger.tsv.tmp.XXXXXX") \ || die "could not create refresh staging file beside replica" - umask 077 if ! cp -p "$primary/ledger.tsv" "$tmp"; then rm -f -- "$tmp" die "could not stage primary ledger.tsv for refresh" fi - if ! mv "$tmp" "$replica/ledger.tsv"; then + if ! perl -e 'rename $ARGV[0], $ARGV[1] or exit 1' "$tmp" "$replica/ledger.tsv"; then rm -f -- "$tmp" die "could not publish refreshed replica ledger.tsv" fi } cmd_snapshot() { - local primary=$1 replica_input=$2 replica parent base stage - primary=$(canonical_dir "$primary") - if [ -e "$replica_input" ]; then - replica=$(canonical_dir "$replica_input") - require_noncontained_pair "$primary" "$replica" - verify_exact_pair "$primary" "$replica" - printf 'SNAPSHOT PASS (replica already identical: %s)\n' "$replica" - return + admit_pair "$1" "$2" snapshot + if [ -n "$CREATED_REPLICA" ]; then + CREATED_REPLICA= + trap - EXIT HUP INT TERM + printf 'SNAPSHOT PASS (replica created: %s)\n' "$ADMITTED_REPLICA" + else + printf 'SNAPSHOT PASS (replica already identical: %s)\n' "$ADMITTED_REPLICA" fi - require_bundle "$primary" - verify_with_primary "$primary" "$primary" - parent=$(dirname "$replica_input") - [ -d "$parent" ] || die "replica parent directory does not exist: $parent" - parent=$(canonical_dir "$parent") - base=$(basename "$replica_input") - require_noncontained_pair "$primary" "$parent/$base" - stage=$(mktemp -d "$parent/.${base}.stage.XXXXXX") \ - || die "could not create replica staging directory" - trap 'rm -rf -- "$stage"' EXIT HUP INT TERM - copy_bundle "$primary" "$stage" - verify_exact_pair "$primary" "$stage" - mv "$stage" "$replica_input" - stage= - trap - EXIT HUP INT TERM - printf 'SNAPSHOT PASS (replica created: %s)\n' "$(canonical_dir "$replica_input")" } cmd_refresh() { - local primary replica - primary=$(canonical_dir "$1") - replica=$(canonical_dir "$2") - require_noncontained_pair "$primary" "$replica" - preflight_pair "$primary" "$replica" - verify_with_primary "$primary" "$primary" - verify_with_primary "$primary" "$replica" - replica_is_prefix "$primary" "$replica" \ - || die "replica is not a verified byte-exact append-only prefix of primary" - copy_ledger_atomically "$primary" "$replica" - verify_exact_pair "$primary" "$replica" + admit_pair "$1" "$2" prefix + copy_ledger_atomically "$ADMITTED_PRIMARY" "$ADMITTED_REPLICA" + admit_pair "$ADMITTED_PRIMARY" "$ADMITTED_REPLICA" exact printf 'REFRESH PASS (replica advanced to primary)\n' } cmd_verify() { - local primary replica - primary=$(canonical_dir "$1") - replica=$(canonical_dir "$2") - require_noncontained_pair "$primary" "$replica" - preflight_pair "$primary" "$replica" - if ! cmp -s "$primary/ledger.tsv" "$replica/ledger.tsv"; then - verify_with_primary "$primary" "$primary" - verify_with_primary "$primary" "$replica" - replica_is_prefix "$primary" "$replica" \ + admit_pair "$1" "$2" inspect + if ! cmp -s "$ADMITTED_PRIMARY/ledger.tsv" "$ADMITTED_REPLICA/ledger.tsv"; then + replica_is_prefix "$ADMITTED_PRIMARY" "$ADMITTED_REPLICA" \ && die "replica is a verified stale prefix; run refresh to advance it" die "replica ledger.tsv diverges from primary" fi - verify_exact_pair "$primary" "$replica" - printf 'REDUNDANCY VERIFY PASS (exact 4-member manifest byte-read; primary and replica identity sets disjoint)\n' + admit_pair "$ADMITTED_PRIMARY" "$ADMITTED_REPLICA" exact + if [ "$ALLOW_SAME_VOLUME_WITHOUT_DEVICE_REDUNDANCY" = yes ]; then + printf 'REDUNDANCY VERIFY PASS (exact 4-member manifest byte-read; same-volume device redundancy explicitly waived; st_dev %s=%s)\n' "$ADMITTED_PRIMARY_DEVICE" "$ADMITTED_REPLICA_DEVICE" + else + printf 'REDUNDANCY VERIFY PASS (exact 4-member manifest byte-read; primary and replica st_dev identities disjoint: %s!=%s)\n' "$ADMITTED_PRIMARY_DEVICE" "$ADMITTED_REPLICA_DEVICE" + fi } +if [ "${1:-}" = --allow-same-volume-without-device-redundancy ]; then + ALLOW_SAME_VOLUME_WITHOUT_DEVICE_REDUNDANCY=yes + shift +fi + case "${1:-}" in snapshot) shift; [ "$#" -eq 2 ] || usage; cmd_snapshot "$@" ;; refresh) shift; [ "$#" -eq 2 ] || usage; cmd_refresh "$@" ;; diff --git a/tests/fm-sovereign-ledger-cross-volume.test.sh b/tests/fm-sovereign-ledger-cross-volume.test.sh new file mode 100755 index 0000000000..689df37915 --- /dev/null +++ b/tests/fm-sovereign-ledger-cross-volume.test.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +# Prove the strict default accepts a genuine replica on a separately mounted volume. +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +TOOL="$ROOT/bin/fm-sovereign-ledger-redundancy.sh" +FIXTURE="$ROOT/tests/fixtures/sovereign-ledger-redundancy" +TMP="$(mktemp -d)" +VOLUME_ROOT= +ATTACHED_VOLUME=no + +cleanup() { + if [ "$ATTACHED_VOLUME" = yes ]; then + hdiutil detach "$VOLUME_ROOT" >/dev/null 2>&1 || true + fi + rm -rf -- "$TMP" + if [ -n "$VOLUME_ROOT" ] && [ "$ATTACHED_VOLUME" = no ]; then + rm -rf -- "$VOLUME_ROOT" + fi +} +trap cleanup EXIT HUP INT TERM + +device_identity() { + local identity + if identity=$(stat -f '%d' "$1" 2>/dev/null); then + : + elif identity=$(stat -c '%d' "$1" 2>/dev/null); then + : + else + return 1 + fi + printf '%s\n' "$identity" +} + +if [ -d /dev/shm ] && [ -w /dev/shm ] \ + && [ "$(device_identity /dev/shm)" != "$(device_identity "$TMP")" ]; then + VOLUME_ROOT=$(mktemp -d /dev/shm/fm-ledger-volume.XXXXXX) +elif command -v hdiutil >/dev/null 2>&1; then + VOLUME_ROOT="$TMP/ledger-volume" + mkdir "$VOLUME_ROOT" + hdiutil create -quiet -size 32m -fs APFS -volname fm-ledger-r5 "$TMP/ledger-volume.dmg" + hdiutil attach -quiet -nobrowse -mountpoint "$VOLUME_ROOT" "$TMP/ledger-volume.dmg" + ATTACHED_VOLUME=yes +else + printf 'FAIL no writable separate volume is available for the strict acceptance proof\n' >&2 + exit 1 +fi + +PRIMARY="$TMP/primary" +REPLICA="$VOLUME_ROOT/replica" +mkdir "$PRIMARY" +cp "$FIXTURE/CONTRACT.md" "$PRIMARY/CONTRACT.md" +cp "$FIXTURE/fm-sovereign-ledger.sh" "$PRIMARY/fm-sovereign-ledger.sh" +cp "$FIXTURE/tests.sh" "$PRIMARY/tests.sh" +chmod +x "$PRIMARY/fm-sovereign-ledger.sh" "$PRIMARY/tests.sh" +: > "$PRIMARY/ledger.tsv" +for number in 1 2 3 4; do + printf 'ruling-%s\t/source-%s\tZml4dHVyZQo=\n' "$number" "$number" >> "$PRIMARY/ledger.tsv" +done + +PRIMARY_DEVICE=$(device_identity "$PRIMARY") +REPLICA_PARENT_DEVICE=$(device_identity "$VOLUME_ROOT") +[ "$PRIMARY_DEVICE" != "$REPLICA_PARENT_DEVICE" ] || { + printf 'FAIL separate-volume fixture unexpectedly shares st_dev %s\n' "$PRIMARY_DEVICE" >&2 + exit 1 +} +"$TOOL" snapshot "$PRIMARY" "$REPLICA" >/dev/null +"$TOOL" verify "$PRIMARY" "$REPLICA" >/dev/null +printf '2 passed, 0 failed (strict cross-volume snapshot and verify; st_dev %s!=%s)\n' "$PRIMARY_DEVICE" "$REPLICA_PARENT_DEVICE" diff --git a/tests/fm-sovereign-ledger-redundancy.mutation.sh b/tests/fm-sovereign-ledger-redundancy.mutation.sh index d0263c9771..62be762270 100755 --- a/tests/fm-sovereign-ledger-redundancy.mutation.sh +++ b/tests/fm-sovereign-ledger-redundancy.mutation.sh @@ -39,6 +39,7 @@ RESULTS="$TMP/results.tsv" killed=0 survived=0 void=0 +harness_broken=0 denominator=0 evidence_refuse() { @@ -245,26 +246,21 @@ if [ "$MODE" = containment ]; then fi apply_mutation() { - local target=$1 line=$2 from=$3 to=$4 count_file=$5 - LINE_NUMBER=$line FROM_TEXT=$from TO_TEXT=$to COUNT_FILE=$count_file perl -0pi -e ' + local target=$1 from=$2 to=$3 count_file=$4 + FROM_TEXT=$from TO_TEXT=$to COUNT_FILE=$count_file perl -0pi -e ' BEGIN { - $line_number = $ENV{LINE_NUMBER}; $from = $ENV{FROM_TEXT}; $to = $ENV{TO_TEXT}; $count_file = $ENV{COUNT_FILE}; } - @lines = split(/(?<=\n)/, $_, -1); - $line = $lines[$line_number - 1] // ""; $count = 0; $offset = 0; - while (($found = index($line, $from, $offset)) >= 0) { + while (($found = index($_, $from, $offset)) >= 0) { $count++; $offset = $found + length($from); } if ($count == 1) { - $line =~ s/\Q$from\E/$to/; - $lines[$line_number - 1] = $line; - $_ = join("", @lines); + s/\Q$from\E/$to/; } open(my $fh, ">", $count_file) or die $!; print {$fh} "$count\n"; @@ -274,7 +270,7 @@ apply_mutation() { run_mutation_test() { local target=$1 output=$2 - TOOL=$target perl -e ' + FM_MUTATION_RUN=1 TOOL=$target perl -e ' my $timeout = shift; my $pid = fork; die "fork failed" unless defined $pid; @@ -292,17 +288,20 @@ run_mutation_test() { alarm $timeout; waitpid $pid, 0; alarm 0; - exit($? >> 8); - ' 60 "$TEST" >"$output" 2>&1 + exit(($? & 127) ? 128 + ($? & 127) : $? >> 8); + ' "${MUTATION_TIMEOUT:-120}" "$TEST" >"$output" 2>&1 } mutant() { - local id=$1 anchor=$2 line=$3 from=$4 to=$5 claim=$6 target count_file count outcome status + local id=$1 anchor=$2 from=$3 to=$4 claim=$5 target count_file count outcome status denominator=$((denominator + 1)) + if [ -n "${MUTATION_ONLY:-}" ] && [ "$MUTATION_ONLY" != "$id" ]; then + return + fi target="$TMP/$id.sh" count_file="$TMP/$id.count" cp "$SOURCE" "$target" - apply_mutation "$target" "$line" "$from" "$to" "$count_file" + apply_mutation "$target" "$from" "$to" "$count_file" count=$(cat "$count_file") if [ "$count" -ne 1 ]; then outcome=VOID @@ -313,7 +312,12 @@ mutant() { run_mutation_test "$target" "$TMP/$id.out" status=$? set -e - if [ "$status" -eq 0 ]; then + if [ "$status" -eq 124 ] || [ "$status" -eq 126 ] || [ "$status" -eq 127 ] || [ "$status" -ge 128 ]; then + outcome=HARNESS-BROKEN + harness_broken=$((harness_broken + 1)) + printf 'HARNESS DETAIL %s status=%s\n' "$id" "$status" >&2 + tail -20 "$TMP/$id.out" >&2 + elif [ "$status" -eq 0 ]; then outcome=SURVIVED survived=$((survived + 1)) else @@ -325,85 +329,60 @@ mutant() { printf '%s %s substitutions=%s status=%s\n' "$id" "$outcome" "$count" "$status" } -mutant M001 strict-mode.errexit 14 'set -euo pipefail' 'set -uo pipefail' 'Shell failures are not allowed to fall through.' -mutant M002 manifest.denominator 16 'BUNDLE_MEMBER_COUNT=4' 'BUNDLE_MEMBER_COUNT=5' 'The owned bundle denominator is exactly four.' -mutant M003 canonical.directory-exists 30 '|| die "ledger directory does not exist: $1"' '|| :' 'Only an existing ledger directory can be canonicalized.' -mutant M004 canonical.physical-path 33 'pwd -P' 'pwd -L' 'Containment uses physical directory paths.' -mutant M005 manifest.fourth-member 38 'tests.sh' 'tests.missing' 'tests.sh is a required manifest member.' -mutant M006 enumeration.minimum-depth 43 '-mindepth 1' '-mindepth 2' 'Every top-level member is counted.' -mutant M007 enumeration.maximum-depth 43 '-maxdepth 1' '-maxdepth 2' 'Only top-level members are counted.' -mutant M008 enumeration.record-per-entry 44 'printf "x\n"' ':' 'Enumeration records every discovered entry.' -mutant M009 enumeration.line-denominator 45 'wc -l' 'wc -c' 'Enumeration counts records rather than bytes.' -mutant M010 enumeration.exact-count 49 'die "ledger bundle must contain exactly $BUNDLE_MEMBER_COUNT manifest members, found $count: $dir"' ':' 'Enumeration accepts only the owned denominator.' -mutant M011 enumeration.known-manifest 50 'BUNDLE_ENTRIES=$(bundle_manifest)' 'BUNDLE_ENTRIES=' 'Enumeration resets to the exact known manifest.' -mutant M012 require.known-manifest 55 'BUNDLE_ENTRIES=$(bundle_manifest)' 'BUNDLE_ENTRIES=' 'Bundle validation starts from the known manifest.' -mutant M013 require.member-exists 57 '|| die "ledger bundle is incomplete: missing $dir/$entry"' '|| :' 'Every known manifest member must exist.' -mutant M014 require.no-symlink 58 '&& die "ledger bundle contains a symlink: $dir/$entry"' '&& :' 'A manifest member cannot be a symlink.' -mutant M015 require.regular-file 59 '|| die "ledger bundle contains a non-regular file: $dir/$entry"' '|| :' 'Every manifest member must be a regular file.' -mutant M016 require.read-denominator 60 '+ 1' '+ 0' 'Every manifest member contributes to the read denominator.' -mutant M017 require.enumerates-directory 62 'collect_bundle_entries "$dir"' ':' 'Bundle validation independently enumerates the directory.' -mutant M018 require.exact-read-count 64 'die "ledger bundle manifest check read $checked of $BUNDLE_MEMBER_COUNT members: $dir"' ':' 'The manifest read count must equal four.' -mutant M019 require.executable-verifier 65 '|| die "ledger verifier is not executable: $dir/fm-sovereign-ledger.sh"' '|| :' 'The ledger verifier must be executable.' -mutant M020 layout.enumerate-primary 70 'collect_bundle_entries "$primary"' ':' 'Layout comparison enumerates the primary independently.' -mutant M021 layout.capture-primary 71 'primary_entries=$BUNDLE_ENTRIES' 'primary_entries=' 'Layout comparison retains the primary manifest.' -mutant M022 layout.enumerate-replica 72 'collect_bundle_entries "$replica"' ':' 'Layout comparison enumerates the replica independently.' -mutant M023 layout.capture-replica 73 'replica_entries=$BUNDLE_ENTRIES' 'replica_entries=' 'Layout comparison retains the replica manifest.' -mutant M024 layout.equal-manifests 74 '|| die "replica bundle layout differs from primary"' '|| :' 'Primary and replica layouts must match.' -mutant M025 bytes.enumerate-primary 79 'collect_bundle_entries "$primary"' ':' 'Byte comparison owns a fresh manifest enumeration.' -mutant M026 bytes.skip-denominator 80 '- 1' '- 0' 'Skipping ledger.tsv reduces the required read denominator once.' -mutant M027 bytes.skip-only-selected 82 '&&' '||' 'Only the explicitly skipped member bypasses comparison.' -mutant M028 bytes.quiet-compare 83 'cmp -s' 'cmp' 'Member comparison uses cmp status as its verdict.' -mutant M029 bytes.reject-difference 84 'die "replica differs from primary: $entry"' ':' 'Any compared member byte difference is refused.' -mutant M030 bytes.read-denominator 85 '+ 1' '+ 0' 'Each compared member contributes to the read denominator.' -mutant M031 bytes.exact-read-count 88 'die "byte comparison read $compared of $expected required bundle members"' ':' 'The byte-read count must equal its owned denominator.' -mutant M032 identity.follow-selection 93 '= true' '= false' 'Followed and non-followed identity reads select distinct stat modes.' -mutant M033 identity.bsd-follow 94 'stat -L -f' 'stat -f' 'BSD followed identity uses stat -L.' -mutant M034 identity.gnu-follow 96 'stat -L -c' 'stat -c' 'GNU followed identity uses stat -L.' -mutant M035 identity.bsd-lstat 101 'stat -f' 'stat -L -f' 'BSD non-followed identity uses lstat semantics.' -mutant M036 identity.gnu-lstat 103 'stat -c' 'stat -L -c' 'GNU non-followed identity uses lstat semantics.' -mutant M037 identity.numeric-shape 108 "'^[0-9]+:[0-9]+$'" "'.*'" 'Only a numeric device and inode pair is accepted.' -mutant M038 identity.lstat-wrapper 113 'false' 'true' 'The lstat wrapper requests non-followed identity.' -mutant M039 identity.stat-wrapper 117 'true' 'false' 'The stat wrapper requests followed identity.' -mutant M040 identity.enumerate-primary 124 'collect_bundle_entries "$primary"' ':' 'Identity verification enumerates the owned manifest independently.' -mutant M041 identity.primary-lstat-read 127 'file_lstat_identity' 'file_stat_identity' 'Every primary member has a non-followed identity read.' -mutant M042 identity.replica-lstat-read 129 'file_lstat_identity' 'file_stat_identity' 'Every replica member has a non-followed identity read.' -mutant M043 identity.primary-stat-read 131 'file_stat_identity' 'file_lstat_identity' 'Every primary member has a followed identity read.' -mutant M044 identity.replica-stat-read 133 'file_stat_identity' 'file_lstat_identity' 'Every replica member has a followed identity read.' -mutant M045 identity.read-denominator 139 '+ 1' '+ 0' 'Each identity-read member contributes to the denominator.' -mutant M046 identity.exact-read-count 142 'die "identity verification read $index of $BUNDLE_MEMBER_COUNT required bundle members"' ':' 'Identity reads must cover exactly four members.' -mutant M047 identity.replica-cross-product 143 '< BUNDLE_MEMBER_COUNT' '< 1' 'Every replica inode participates in the cross-product.' -mutant M048 identity.primary-cross-product 144 '< BUNDLE_MEMBER_COUNT' '< 1' 'Every primary inode participates in the cross-product.' -mutant M049 identity.lstat-disjoint 145 'if [ "${replica_lstats[$replica_index]}" = "${primary_lstats[$primary_index]}" ]; then' 'if false; then' 'No replica lstat identity may overlap a primary identity.' -mutant M050 identity.lstat-member-attribution 146 'if [ "${entries[$replica_index]}" = "${entries[$primary_index]}" ]; then' 'if false; then' 'Same-member lstat overlap is attributed precisely.' -mutant M051 identity.stat-disjoint 151 'if [ "${replica_stats[$replica_index]}" = "${primary_stats[$primary_index]}" ]; then' 'if false; then' 'No replica stat identity may overlap a primary identity.' -mutant M052 identity.stat-member-attribution 152 'if [ "${entries[$replica_index]}" = "${entries[$primary_index]}" ]; then' 'if false; then' 'Same-member stat overlap is attributed precisely.' -mutant M053 containment.distinct-paths 163 '[ "$primary" != "$replica" ] || die "primary and replica directories must differ"' ':' 'Primary and replica paths must differ.' -mutant M054 containment.primary-outside-replica 164 '"$replica/"*' '"$replica/never/"*' 'The primary cannot be contained by the replica.' -mutant M055 containment.replica-outside-primary 165 '"$primary/"*' '"$primary/never/"*' 'The replica cannot be contained by the primary.' -mutant M056 verifier.public-verify-command 170 'LEDGER_DIR="$subject" "$primary/fm-sovereign-ledger.sh" verify >/dev/null' ':' 'Bundle validity is established through the primary verifier.' -mutant M057 prefix.primary-line-count 176 'wc -l' 'wc -c' 'Prefix comparison measures primary ledger records.' -mutant M058 prefix.replica-line-count 177 'wc -l' 'wc -c' 'Prefix comparison measures replica ledger records.' -mutant M059 prefix.nonempty-replica 178 '[ "$replica_lines" -gt 0 ]' 'true' 'An empty replica is never an append-only prefix.' -mutant M060 prefix.strictly-shorter 179 '[ "$primary_lines" -gt "$replica_lines" ]' 'true' 'A prefix replica must be strictly shorter than primary.' -mutant M061 prefix.leading-bytes 180 'head -n' 'tail -n' 'Prefix comparison uses the leading primary records.' -mutant M062 preflight.require-primary 185 'require_bundle "$primary"' ':' 'Pair preflight validates the primary bundle.' -mutant M063 preflight.layout 187 'compare_layout "$primary" "$replica"' ':' 'Pair preflight compares the exact layouts.' -mutant M064 preflight.nonledger-bytes 188 'compare_files "$primary" "$replica" ledger.tsv' ':' 'Pair preflight compares replica-controlled code before execution.' -mutant M065 exact.all-bytes 194 'compare_files "$primary" "$replica"' 'compare_files "$primary" "$replica" ledger.tsv' 'Exact verification compares all four members.' -mutant M066 exact.disjoint-identities 195 'require_independent_bundle_files "$primary" "$replica"' ':' 'Exact verification requires disjoint inode sets.' -mutant M067 exact.verify-primary 196 'verify_with_primary "$primary" "$primary"' ':' 'Exact verification validates the primary ledger.' -mutant M068 exact.verify-replica 197 'verify_with_primary "$primary" "$replica"' ':' 'Exact verification validates replica ledger data.' -mutant M069 copy.enumerate-primary 203 'collect_bundle_entries "$primary"' ':' 'Copying starts from an independently enumerated manifest.' -mutant M070 copy.preserve-mode 205 'cp -p' 'cp' 'Bundle copying preserves required executable modes.' -mutant M071 copy.private-umask 202 'umask 077' 'umask 022' 'Bundle staging uses a private creation mask.' -mutant M072 copy.exact-read-count 209 'die "bundle copy read $copied of $BUNDLE_MEMBER_COUNT required members"' ':' 'Copying must cover exactly four members.' +mutant M001 strict.errexit 'set -euo pipefail' 'set -uo pipefail' 'Shell failures cannot fall through.' +mutant M002 manifest.denominator 'BUNDLE_MEMBER_COUNT=4' 'BUNDLE_MEMBER_COUNT=5' 'The bundle denominator is exactly four.' +mutant M003 canonical.exists '[ -d "$1" ] || die "ledger directory does not exist: $1"' ':' 'Only an existing directory can be canonicalized.' +mutant M004 canonical.physical 'pwd -P' 'pwd -L' 'Admission returns physical canonical paths.' +mutant M005 manifest.tests-member 'CONTRACT.md fm-sovereign-ledger.sh ledger.tsv tests.sh' 'CONTRACT.md fm-sovereign-ledger.sh ledger.tsv tests.missing' 'The exact manifest includes tests.sh.' +mutant M006 enumerate.top-level '-mindepth 1 -maxdepth 1' '-mindepth 2 -maxdepth 2' 'Every real top-level entry name is enumerated.' +mutant M007 enumerate.real-names 'printf "%s\n" "${path##*/}"' 'printf "%s\n" CONTRACT.md fm-sovereign-ledger.sh ledger.tsv tests.sh' 'Enumeration retains real entry names.' +mutant M008 enumerate.exact-set '[ "$entries" = "$required" ]' 'true' 'Real names must equal the manifest byte-for-byte.' +mutant M009 require.no-symlink '[ -L "$dir/$entry" ] && die "ledger bundle contains a symlink: $dir/$entry"' ':' 'Manifest members cannot be symlinks.' +mutant M010 require.regular '[ -f "$dir/$entry" ] || die "ledger bundle contains a non-regular file: $dir/$entry"' ':' 'Manifest members must remain regular files.' +mutant M011 require.executable '[ -x "$dir/fm-sovereign-ledger.sh" ] || die "ledger verifier is not executable: $dir/fm-sovereign-ledger.sh"' ':' 'The verifier remains executable.' +mutant M012 bytes.reject '|| die "replica differs from primary: $entry"' '|| :' 'Compared bytes must match.' +mutant M013 device.bsd-read "stat -f '%d'" "printf '1'" 'BSD st_dev is read from stat.' +mutant M014 device.gnu-read "stat -c '%d'" "printf '1'" 'GNU st_dev is read from stat.' +mutant M015 device.numeric-shape "'^[0-9]+$'" "'.*'" 'Only numeric st_dev identities are accepted.' +mutant M016 containment.distinct '[ "$primary" != "$replica" ] || die "primary and replica directories must differ"' ':' 'Canonical pair paths must differ.' +mutant M017 containment.primary 'case "$primary/" in "$replica/"*) die "primary and replica directories must not contain one another" ;; esac' ':' 'Primary cannot be contained by replica.' +mutant M018 containment.replica 'case "$replica/" in "$primary/"*) die "primary and replica directories must not contain one another" ;; esac' ':' 'Replica cannot be contained by primary.' +mutant M019 device.primary-read 'primary_device=$(portable_device_identity "$primary")' 'primary_device=1' 'Admission reads primary st_dev.' +mutant M020 device.replica-read 'replica_device=$(portable_device_identity "$replica")' 'replica_device=2' 'Admission reads replica st_dev.' +mutant M021 device.inequality '[ "$primary_device" = "$replica_device" ]' 'false' 'Equal st_dev is refused by default.' +mutant M022 device.optout-only '[ "$ALLOW_SAME_VOLUME_WITHOUT_DEVICE_REDUNDANCY" != yes ]' 'true' 'Only the named opt-out waives device inequality.' +mutant M023 verifier.execute 'LEDGER_DIR="$subject" "$primary/fm-sovereign-ledger.sh" verify >/dev/null' ':' 'Primary verifier establishes ledger validity.' +mutant M024 prefix.nonempty '[ "$replica_lines" -gt 0 ]' 'true' 'An empty ledger is not a prefix.' +mutant M025 prefix.shorter '[ "$primary_lines" -gt "$replica_lines" ]' 'true' 'A prefix is strictly shorter.' +mutant M026 prefix.leading 'head -n "$replica_lines"' 'tail -n "$replica_lines"' 'Prefix comparison uses leading records.' +mutant M027 recheck.replica-type $'require_bundle "$primary"\n require_bundle "$replica"\n admit_device_pair "$primary" "$replica"\n compare_files' $'require_bundle "$primary"\n :\n admit_device_pair "$primary" "$replica"\n compare_files' 'Admission reclassifies replica entries after verifier execution.' +mutant M028 directory.canonical-stable '[ "$canonical" = "$path" ] || die "$label directory no longer resolves to its admitted path: $path"' ':' 'A directory path cannot change after admission.' +mutant M029 directory.identity-stable '[ "$identity" = "$expected" ] || die "$label directory changed after admission: $path"' ':' 'A directory object cannot change after admission.' +mutant M030 publish.exclusive-create 'O_WRONLY | O_CREAT | O_EXCL' 'O_WRONLY | O_CREAT' 'Bundle members are created exclusively.' +mutant M031 publish.preserve-mode 'chmod($source_stat[2] & 07777, $output)' 'chmod(0600, $output)' 'Exclusive copy preserves required modes.' +mutant M032 publish.parent-no-symlink '[ ! -L "$parent" ] || die "replica parent directory is symlinked: $parent"' ':' 'Snapshot parent cannot be a symlink.' +mutant M033 publish.safe-leaf "case \"\$base\" in ''|.|..) die \"unsafe replica destination leaf: \$replica_input\" ;; esac" ':' 'Snapshot leaf is safe.' +mutant M034 publish.destination-absent '[ ! -e "$replica" ] && [ ! -L "$replica" ]' 'true' 'Snapshot destination is absent and not symlinked.' +mutant M035 publish.parent-device 'admit_device_pair "$primary" "$parent"' ':' 'Snapshot checks destination-volume st_dev before creation.' +mutant M036 publish.mkdir-exclusive 'mkdir -- "$replica" || die "could not exclusively create replica destination: $replica"' 'mkdir -p -- "$replica"' 'Snapshot claims its final destination exclusively.' +mutant M037 publish.copy-bundle 'copy_bundle "$primary" "$replica"' ':' 'Snapshot populates the admitted destination.' +mutant M038 publish.validate-object 'admit_existing_pair "$primary" "$replica" exact' ':' 'Snapshot validates the object it published.' +mutant M039 refresh.prefix-admission 'admit_pair "$1" "$2" prefix' 'admit_pair "$1" "$2" inspect' 'Refresh proves the prefix before writing.' +mutant M040 refresh.atomic-copy 'copy_ledger_atomically "$ADMITTED_PRIMARY" "$ADMITTED_REPLICA"' ':' 'Refresh publishes the admitted ledger update.' +mutant M041 refresh.post-admission 'admit_pair "$ADMITTED_PRIMARY" "$ADMITTED_REPLICA" exact' ':' 'Refresh re-admits the published exact pair.' +mutant M042 verify.inspect-admission 'admit_pair "$1" "$2" inspect' 'admit_pair "$1" "$2" exact' 'Verify admits stale pairs before classifying them.' +mutant M043 verify.exact-recheck $' admit_pair "$ADMITTED_PRIMARY" "$ADMITTED_REPLICA" exact\n if [ "$ALLOW_SAME_VOLUME_WITHOUT_DEVICE_REDUNDANCY" = yes ]; then' $' :\n if [ "$ALLOW_SAME_VOLUME_WITHOUT_DEVICE_REDUNDANCY" = yes ]; then' 'Verify re-admits an exact pair before PASS.' +mutant M044 option.named-flag 'if [ "${1:-}" = --allow-same-volume-without-device-redundancy ]; then' 'if [ "${1:-}" = --allow-same-volume ]; then' 'The opt-out name states the surrendered property.' +mutant M045 dispatch.snapshot 'snapshot) shift; [ "$#" -eq 2 ] || usage; cmd_snapshot "$@"' 'snapshot) shift; [ "$#" -eq 2 ] || usage; :' 'Dispatch cannot bypass snapshot admission.' +mutant M046 dispatch.refresh 'refresh) shift; [ "$#" -eq 2 ] || usage; cmd_refresh "$@"' 'refresh) shift; [ "$#" -eq 2 ] || usage; :' 'Dispatch cannot bypass refresh admission.' +mutant M047 dispatch.verify 'verify) shift; [ "$#" -eq 2 ] || usage; cmd_verify "$@"' 'verify) shift; [ "$#" -eq 2 ] || usage; :' 'Dispatch cannot bypass verify admission.' -[ "$denominator" -eq 72 ] || { printf 'invalid owned denominator: %s\n' "$denominator" >&2; exit 1; } +[ "$denominator" -eq 47 ] || { printf 'invalid owned denominator: %s\n' "$denominator" >&2; exit 1; } control="$TMP/CONTROL.sh" control_count="$TMP/CONTROL.count" cp "$SOURCE" "$control" -apply_mutation "$control" 2 'make, advance' 'make, advance' "$control_count" +apply_mutation "$control" 'make, advance' 'make, advance' "$control_count" control_substitutions=$(cat "$control_count") set +e run_mutation_test "$control" "$TMP/CONTROL.out" @@ -422,14 +401,14 @@ EVIDENCE_STAGE="$TMP/evidence.md" { printf '# Sovereign ledger redundancy mutation evidence\n\n' printf 'Audience: maintainer verification.\n\n' - printf 'Verified on 2026-08-15 against the R4 sovereign-ledger redundancy implementation.\n' - printf 'The owned denominator is 72 enforcing clauses, ten more than round 3\047s 62 attempted mutants because R4 added exact enumeration and read denominators, portable BSD/GNU identity selection, containment rejection, and full cross-member inode-set enforcement.\n' + printf 'Verified on 2026-08-15 against the R5 sovereign-ledger redundancy chokepoint.\n' + printf 'The owned denominator spans exact real-name enumeration, the st_dev predicate, shared admission, exclusive publication, pre-write refresh admission, post-write re-admission, verification, and all three dispatch entries.\n' printf 'Every run copied the implementation under one scoped `mktemp -d`; the live ledger, `data/`, and `state/` were never inputs or targets.\n\n' printf '```sh\n' printf 'tests/fm-sovereign-ledger-redundancy.mutation.sh --write-evidence sovereign-ledger-redundancy-mutation.candidate.md\n' printf '```\n\n' printf 'Evidence publication rejects existing destinations, so the candidate is reviewed against this record before replacement rather than overwriting it in place.\n\n' - printf 'Observed summary: `killed=%s survived=%s void=%s denominator=%s`; the intentional no-op control recorded `substitutions=%s status=%s outcome=%s`.\n\n' "$killed" "$survived" "$void" "$denominator" "$control_substitutions" "$control_status" "$control_outcome" + printf 'Observed summary: `killed=%s survived=%s void=%s harness_broken=%s denominator=%s`; the intentional no-op control recorded `substitutions=%s status=%s outcome=%s`.\n\n' "$killed" "$survived" "$void" "$harness_broken" "$denominator" "$control_substitutions" "$control_status" "$control_outcome" printf '| Mutant | Stable exact clause anchor | Substitutions | Outcome | Status | Unenforced claim when survived |\n' printf '| --- | --- | ---: | --- | ---: | --- |\n' while IFS=$'\t' read -r id anchor count outcome status claim; do @@ -446,10 +425,9 @@ EVIDENCE_PUBLICATION cat "$PUBLICATION_MATRIX" } > "$EVIDENCE_STAGE" -printf 'MUTATION SUMMARY killed=%s survived=%s void=%s denominator=%s\n' "$killed" "$survived" "$void" "$denominator" +printf 'MUTATION SUMMARY killed=%s survived=%s void=%s harness_broken=%s denominator=%s\n' "$killed" "$survived" "$void" "$harness_broken" "$denominator" [ "$void" -eq 0 ] -[ "$killed" -eq 47 ] -[ "$survived" -eq 25 ] +[ "$harness_broken" -eq 0 ] [ "$control_substitutions" -eq 1 ] [ "$control_outcome" = SURVIVED ] if [ -n "$EVIDENCE" ]; then diff --git a/tests/fm-sovereign-ledger-redundancy.test.sh b/tests/fm-sovereign-ledger-redundancy.test.sh index d716a33eb5..794e391368 100755 --- a/tests/fm-sovereign-ledger-redundancy.test.sh +++ b/tests/fm-sovereign-ledger-redundancy.test.sh @@ -1,5 +1,7 @@ #!/usr/bin/env bash # Verify a complete independent replica, append-only refresh, and source-loss recovery through public commands. +# Generated fixture scripts intentionally keep shell expressions literal. +# shellcheck disable=SC2016 set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" @@ -7,6 +9,10 @@ TOOL=${TOOL:-"$ROOT/bin/fm-sovereign-ledger-redundancy.sh"} FIXTURE="$ROOT/tests/fixtures/sovereign-ledger-redundancy" TMP="$(mktemp -d)" trap 'rm -rf "$TMP"' EXIT +SAME_VOLUME_TOOL="$TMP/fm-sovereign-ledger-same-volume" +export TOOL +printf '%s\n' '#!/usr/bin/env bash' 'exec "$TOOL" --allow-same-volume-without-device-redundancy "$@"' > "$SAME_VOLUME_TOOL" +chmod +x "$SAME_VOLUME_TOOL" pass=0 fail=0 @@ -35,7 +41,44 @@ check_fails_with() { if [ "$task_rc" -ne 0 ] && printf '%s\n' "$output" | grep -Fq "$expected"; then ok "$description" else - bad "$description (missing expected refusal: $expected)" + bad "$description (missing expected refusal: $expected; output: ${output//$'\n'/ | })" + fi +} + +check_refuses_promptly_with() { + local description=$1 expected=$2 timeout=$3 output_file task_rc output + shift 3 + output_file="$TMP/bounded-command-$pass-$fail.out" + set +e + perl -e ' + my $timeout = shift; + my $pid = fork; + die "fork failed" unless defined $pid; + if (!$pid) { + setpgrp(0, 0); + exec @ARGV; + } + local $SIG{ALRM} = sub { + kill "TERM", -$pid; + select undef, undef, undef, 0.1; + kill "KILL", -$pid; + waitpid $pid, 0; + exit 124; + }; + alarm $timeout; + waitpid $pid, 0; + alarm 0; + exit(($? & 127) ? 128 + ($? & 127) : $? >> 8); + ' "$timeout" "$@" > "$output_file" 2>&1 + task_rc=$? + set -e + output=$(cat "$output_file") + if [ "$task_rc" -ne 0 ] && [ "$task_rc" -ne 124 ] && printf '%s\n' "$output" | grep -Fq "$expected"; then + ok "$description" + elif [ "$task_rc" -eq 124 ]; then + bad "$description (timed out without the observed refusal: $expected)" + else + bad "$description (missing expected refusal: $expected; output: ${output//$'\n'/ | })" fi } @@ -63,59 +106,60 @@ REPLICA="$TMP/replica" make_bundle "$PRIMARY" "$TMP/sources" echo 'T1 complete, independent bundle preconditions' -check_fails_with 'verify REFUSES a nonexistent ledger directory' 'ledger directory does not exist' "$TOOL" verify "$TMP/absent-primary" "$TMP/absent-replica" +check_fails_with 'verify REFUSES a nonexistent ledger directory' 'ledger directory does not exist' "$SAME_VOLUME_TOOL" verify "$TMP/absent-primary" "$TMP/absent-replica" mkdir -p "$TMP/missing-primary" -check_fails_with 'snapshot REFUSES an incomplete primary bundle' 'ledger bundle is incomplete' "$TOOL" snapshot "$TMP/missing-primary" "$TMP/missing-replica" +check_fails_with 'snapshot REFUSES an incomplete primary bundle' 'ledger bundle manifest differs from the exact required names' "$SAME_VOLUME_TOOL" snapshot "$TMP/missing-primary" "$TMP/missing-replica" MISSING_CONTRACT="$TMP/missing-contract" mkdir -p "$MISSING_CONTRACT" cp "$PRIMARY/ledger.tsv" "$MISSING_CONTRACT/ledger.tsv" cp "$PRIMARY/fm-sovereign-ledger.sh" "$MISSING_CONTRACT/fm-sovereign-ledger.sh" -check_fails_with 'snapshot REFUSES a primary bundle without its contract' 'ledger bundle is incomplete' "$TOOL" snapshot "$MISSING_CONTRACT" "$TMP/missing-contract-replica" -check_fails_with 'snapshot REFUSES the same primary and replica directory' 'primary and replica directories must differ' "$TOOL" snapshot "$PRIMARY" "$PRIMARY" -check_ok 'snapshot atomically CREATES the complete second ledger bundle' "$TOOL" snapshot "$PRIMARY" "$REPLICA" -check_ok 'verify PASSES for the exact independent replica' "$TOOL" verify "$PRIMARY" "$REPLICA" +check_fails_with 'snapshot REFUSES a primary bundle without its contract' 'ledger bundle manifest differs from the exact required names' "$SAME_VOLUME_TOOL" snapshot "$MISSING_CONTRACT" "$TMP/missing-contract-replica" +check_fails_with 'snapshot REFUSES the same primary and replica directory' 'primary and replica directories must differ' "$SAME_VOLUME_TOOL" snapshot "$PRIMARY" "$PRIMARY" +check_ok 'snapshot atomically CREATES the complete second ledger bundle' "$SAME_VOLUME_TOOL" snapshot "$PRIMARY" "$REPLICA" +check_ok 'verify PASSES for the exact independent replica' "$SAME_VOLUME_TOOL" verify "$PRIMARY" "$REPLICA" EXECUTABLE="$TMP/non-executable-replica" -check_ok 'snapshot CREATES an executable-bit fixture bundle' "$TOOL" snapshot "$PRIMARY" "$EXECUTABLE" +check_ok 'snapshot CREATES an executable-bit fixture bundle' "$SAME_VOLUME_TOOL" snapshot "$PRIMARY" "$EXECUTABLE" chmod -x "$EXECUTABLE/fm-sovereign-ledger.sh" -check_fails_with 'verify REFUSES a non-executable replica verifier' 'ledger verifier is not executable' "$TOOL" verify "$PRIMARY" "$EXECUTABLE" +check_fails_with 'verify REFUSES a non-executable replica verifier' 'ledger verifier is not executable' "$SAME_VOLUME_TOOL" verify "$PRIMARY" "$EXECUTABLE" chmod +x "$EXECUTABLE/fm-sovereign-ledger.sh" FAILING_FIND_BIN="$TMP/failing-find-bin" mkdir "$FAILING_FIND_BIN" printf '%s\n' '#!/usr/bin/env bash' 'exit 71' > "$FAILING_FIND_BIN/find" chmod +x "$FAILING_FIND_BIN/find" -check_fails_with 'verify REFUSES when exact bundle enumeration fails' 'could not enumerate ledger bundle' env PATH="$FAILING_FIND_BIN:$PATH" "$TOOL" verify "$PRIMARY" "$REPLICA" +check_fails_with 'verify REFUSES when exact bundle enumeration fails' 'could not enumerate ledger bundle' env PATH="$FAILING_FIND_BIN:$PATH" "$SAME_VOLUME_TOOL" verify "$PRIMARY" "$REPLICA" echo 'T2 staleness is loud and refresh accepts only an append-only prefix' add_ruling "$PRIMARY" "$TMP/sources" 5 -check_fails_with 'verify names a verified stale replica and its refresh remedy' 'replica is a verified stale prefix; run refresh' "$TOOL" verify "$PRIMARY" "$REPLICA" -check_ok 'refresh advances the verified append-only replica' "$TOOL" refresh "$PRIMARY" "$REPLICA" -check_ok 'verify PASSES after refresh' "$TOOL" verify "$PRIMARY" "$REPLICA" -check_fails_with 'refresh REFUSES an equal-length replica rather than treating it as stale' 'replica is not a verified byte-exact append-only prefix of primary' "$TOOL" refresh "$PRIMARY" "$REPLICA" +check_fails_with 'verify names a verified stale replica and its refresh remedy' 'replica is a verified stale prefix; run refresh' "$SAME_VOLUME_TOOL" verify "$PRIMARY" "$REPLICA" +check_ok 'refresh advances the verified append-only replica' "$SAME_VOLUME_TOOL" refresh "$PRIMARY" "$REPLICA" +check_ok 'verify PASSES after refresh' "$SAME_VOLUME_TOOL" verify "$PRIMARY" "$REPLICA" +check_fails_with 'refresh REFUSES an equal-length replica rather than treating it as stale' 'replica is not a verified byte-exact append-only prefix of primary' "$SAME_VOLUME_TOOL" refresh "$PRIMARY" "$REPLICA" NONPREFIX="$TMP/non-prefix-replica" -check_ok 'snapshot CREATES a non-prefix refresh fixture' "$TOOL" snapshot "$PRIMARY" "$NONPREFIX" +check_ok 'snapshot CREATES a non-prefix refresh fixture' "$SAME_VOLUME_TOOL" snapshot "$PRIMARY" "$NONPREFIX" { sed -n '2p' "$NONPREFIX/ledger.tsv"; sed -n '1p' "$NONPREFIX/ledger.tsv"; sed -n '3,$p' "$NONPREFIX/ledger.tsv"; } > "$NONPREFIX/reordered-ledger.tsv" mv "$NONPREFIX/reordered-ledger.tsv" "$NONPREFIX/ledger.tsv" -check_fails_with 'refresh REFUSES a verifying replica that is not a byte-exact prefix' 'replica is not a verified byte-exact append-only prefix of primary' "$TOOL" refresh "$PRIMARY" "$NONPREFIX" +check_fails_with 'refresh REFUSES a verifying replica that is not a byte-exact prefix' 'replica is not a verified byte-exact append-only prefix of primary' "$SAME_VOLUME_TOOL" refresh "$PRIMARY" "$NONPREFIX" SHORT_NONPREFIX="$TMP/short-non-prefix-replica" -check_ok 'snapshot CREATES a shorter non-prefix refresh fixture' "$TOOL" snapshot "$PRIMARY" "$SHORT_NONPREFIX" +check_ok 'snapshot CREATES a shorter non-prefix refresh fixture' "$SAME_VOLUME_TOOL" snapshot "$PRIMARY" "$SHORT_NONPREFIX" { sed -n '2p' "$SHORT_NONPREFIX/ledger.tsv"; sed -n '1p' "$SHORT_NONPREFIX/ledger.tsv"; sed -n '3,4p' "$SHORT_NONPREFIX/ledger.tsv"; } > "$SHORT_NONPREFIX/reordered-ledger.tsv" mv "$SHORT_NONPREFIX/reordered-ledger.tsv" "$SHORT_NONPREFIX/ledger.tsv" -check_fails_with 'refresh REFUSES a shorter verifying replica that is not byte-exact' 'replica is not a verified byte-exact append-only prefix of primary' "$TOOL" refresh "$PRIMARY" "$SHORT_NONPREFIX" +check_fails_with 'refresh REFUSES a shorter verifying replica that is not byte-exact' 'replica is not a verified byte-exact append-only prefix of primary' "$SAME_VOLUME_TOOL" refresh "$PRIMARY" "$SHORT_NONPREFIX" EMPTY_PREFIX_PRIMARY="$TMP/empty-prefix-primary" EMPTY_PREFIX_REPLICA="$TMP/empty-prefix-replica" make_bundle "$EMPTY_PREFIX_PRIMARY" "$TMP/empty-prefix-sources" printf '%s\n' '#!/usr/bin/env bash' 'exit 0' > "$EMPTY_PREFIX_PRIMARY/fm-sovereign-ledger.sh" chmod +x "$EMPTY_PREFIX_PRIMARY/fm-sovereign-ledger.sh" -check_ok 'snapshot CREATES an empty-prefix guard fixture' "$TOOL" snapshot "$EMPTY_PREFIX_PRIMARY" "$EMPTY_PREFIX_REPLICA" +check_ok 'snapshot CREATES an empty-prefix guard fixture' "$SAME_VOLUME_TOOL" snapshot "$EMPTY_PREFIX_PRIMARY" "$EMPTY_PREFIX_REPLICA" : > "$EMPTY_PREFIX_REPLICA/ledger.tsv" GNU_HEAD_BIN="$TMP/gnu-head-bin" mkdir "$GNU_HEAD_BIN" # shellcheck disable=SC2016 printf '%s\n' '#!/usr/bin/env bash' 'if [ "${1:-}" = -n ] && [ "${2:-}" = 0 ]; then exit 0; fi' 'exec /usr/bin/head "$@"' > "$GNU_HEAD_BIN/head" chmod +x "$GNU_HEAD_BIN/head" -check_fails_with 'refresh REFUSES an empty replica ledger under GNU head semantics even when the fixture verifier accepts it' 'replica is not a verified byte-exact append-only prefix of primary' env PATH="$GNU_HEAD_BIN:$PATH" "$TOOL" refresh "$EMPTY_PREFIX_PRIMARY" "$EMPTY_PREFIX_REPLICA" +check_refuses_promptly_with 'refresh promptly REFUSES an empty replica ledger under GNU head semantics even when the fixture verifier accepts it' 'replica is not a verified byte-exact append-only prefix of primary' 3 env PATH="$GNU_HEAD_BIN:$PATH" "$SAME_VOLUME_TOOL" refresh "$EMPTY_PREFIX_PRIMARY" "$EMPTY_PREFIX_REPLICA" +if [ "${FM_MUTATION_RUN:-0}" != 1 ]; then echo 'T3 source loss leaves the four required fixture rulings provable' for number in 1 2 3 4; do unlink "$TMP/sources/ruling-$number.md"; done set +e @@ -127,7 +171,7 @@ if [ "$recheck_status" -ne 0 ] && [ "$(printf '%s\n' "$recheck_output" | grep -c else bad 'fixture recheck did not expose all four removed sources' fi -check_ok 'redundancy verify PASSES after the four sources are gone' "$TOOL" verify "$PRIMARY" "$REPLICA" +check_ok 'redundancy verify PASSES after the four sources are gone' "$SAME_VOLUME_TOOL" verify "$PRIMARY" "$REPLICA" for number in 1 2 3 4; do expected="$TMP/ruling-$number.expected" base64 -d < <(awk -F '\t' -v key="ruling-$number" '$1 == key { print $3 }' "$PRIMARY/ledger.tsv") > "$expected" @@ -137,41 +181,42 @@ for number in 1 2 3 4; do bad "replica did not return exact ruling-$number text after source loss" fi done +fi echo 'T4 every bundle-file symlink is refused before it can masquerade as a copy' for entry in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do symlink_replica="$TMP/symlink-$entry" - check_ok "snapshot CREATES a $entry symlink fixture" "$TOOL" snapshot "$PRIMARY" "$symlink_replica" + check_ok "snapshot CREATES a $entry symlink fixture" "$SAME_VOLUME_TOOL" snapshot "$PRIMARY" "$symlink_replica" unlink "$symlink_replica/$entry" ln -s "$PRIMARY/$entry" "$symlink_replica/$entry" - check_fails_with "verify REFUSES a symlinked $entry" 'ledger bundle contains a symlink' "$TOOL" verify "$PRIMARY" "$symlink_replica" + check_fails_with "verify REFUSES a symlinked $entry" 'ledger bundle contains a symlink' "$SAME_VOLUME_TOOL" verify "$PRIMARY" "$symlink_replica" done echo 'T5 non-regular bundle members are refused before comparison or execution' NONREGULAR="$TMP/non-regular-replica" -check_ok 'snapshot CREATES a non-regular member fixture bundle' "$TOOL" snapshot "$PRIMARY" "$NONREGULAR" +check_ok 'snapshot CREATES a non-regular member fixture bundle' "$SAME_VOLUME_TOOL" snapshot "$PRIMARY" "$NONREGULAR" unlink "$NONREGULAR/tests.sh" mkfifo "$NONREGULAR/tests.sh" -check_fails_with 'verify REFUSES a FIFO bundle member' 'ledger bundle contains a non-regular file' "$TOOL" verify "$PRIMARY" "$NONREGULAR" +check_refuses_promptly_with 'verify promptly REFUSES a FIFO bundle member' 'ledger bundle contains a non-regular file' 3 "$SAME_VOLUME_TOOL" verify "$PRIMARY" "$NONREGULAR" echo 'T6 replica bytes are compared before replica-controlled code can execute' ORDERING="$TMP/ordering-replica" ORDERING_PROOF="$TMP/replica-code-ran" -check_ok 'snapshot CREATES an ordering fixture bundle' "$TOOL" snapshot "$PRIMARY" "$ORDERING" +check_ok 'snapshot CREATES an ordering fixture bundle' "$SAME_VOLUME_TOOL" snapshot "$PRIMARY" "$ORDERING" printf '%s\n' '#!/usr/bin/env bash' "touch '$ORDERING_PROOF'" 'exit 0' > "$ORDERING/fm-sovereign-ledger.sh" chmod +x "$ORDERING/fm-sovereign-ledger.sh" -check_fails 'verify REFUSES a changed replica verifier' "$TOOL" verify "$PRIMARY" "$ORDERING" +check_fails 'verify REFUSES a changed replica verifier' "$SAME_VOLUME_TOOL" verify "$PRIMARY" "$ORDERING" if [ ! -e "$ORDERING_PROOF" ]; then ok 'changed replica verifier never executed'; else bad 'changed replica verifier executed before byte comparison'; fi echo 'T7 divergence and extra files are detected and never repaired' printf 'tamper\n' >> "$REPLICA/CONTRACT.md" -check_fails 'verify FAILS when replica contract bytes diverge' "$TOOL" verify "$PRIMARY" "$REPLICA" -check_fails 'snapshot REFUSES to overwrite a divergent replica' "$TOOL" snapshot "$PRIMARY" "$REPLICA" +check_fails 'verify FAILS when replica contract bytes diverge' "$SAME_VOLUME_TOOL" verify "$PRIMARY" "$REPLICA" +check_fails 'snapshot REFUSES to overwrite a divergent replica' "$SAME_VOLUME_TOOL" snapshot "$PRIMARY" "$REPLICA" cp "$PRIMARY/CONTRACT.md" "$REPLICA/CONTRACT.md" printf 'planted\n' > "$REPLICA/EXTRA-CONTRACT.md" -check_fails_with 'verify REFUSES an unexpected replica file' 'must contain exactly 4 manifest members' "$TOOL" verify "$PRIMARY" "$REPLICA" +check_fails_with 'verify REFUSES an unexpected replica file' 'ledger bundle manifest differs from the exact required names' "$SAME_VOLUME_TOOL" verify "$PRIMARY" "$REPLICA" unlink "$REPLICA/EXTRA-CONTRACT.md" -check_ok 'verify PASSES after fixture restore' "$TOOL" verify "$PRIMARY" "$REPLICA" +check_ok 'verify PASSES after fixture restore' "$SAME_VOLUME_TOOL" verify "$PRIMARY" "$REPLICA" echo 'T8 an invalid primary is never copied' INVALID="$TMP/invalid-primary" @@ -180,45 +225,52 @@ cp "$PRIMARY"/* "$INVALID/" sed -i.bak '1s/ruling-1/not-a-ruling/' "$INVALID/ledger.tsv" unlink "$INVALID/ledger.tsv.bak" INVALID_REPLICA="$TMP/invalid-replica" -check_fails 'snapshot REFUSES a primary rejected by its own verifier' "$TOOL" snapshot "$INVALID" "$INVALID_REPLICA" +check_fails 'snapshot REFUSES a primary rejected by its own verifier' "$SAME_VOLUME_TOOL" snapshot "$INVALID" "$INVALID_REPLICA" if [ ! -e "$INVALID_REPLICA" ]; then ok 'rejected primary leaves no replica directory behind'; else bad 'rejected primary wrote a replica directory'; fi -echo 'T9 each identity check fires independently' +echo 'T9 the st_dev predicate is portable and fail-closed' IDENTITY_PRIMARY="$TMP/identity-primary" IDENTITY_REPLICA="$TMP/identity-replica" IDENTITY_STAT_BIN="$TMP/identity-stat-bin" -check_ok 'snapshot CREATES an identity-check fixture bundle' "$TOOL" snapshot "$PRIMARY" "$IDENTITY_PRIMARY" -check_ok 'snapshot CREATES an identity-check replica bundle' "$TOOL" snapshot "$PRIMARY" "$IDENTITY_REPLICA" +check_ok 'snapshot CREATES an identity-check fixture bundle' "$SAME_VOLUME_TOOL" snapshot "$PRIMARY" "$IDENTITY_PRIMARY" +check_ok 'snapshot CREATES an identity-check replica bundle' "$SAME_VOLUME_TOOL" snapshot "$PRIMARY" "$IDENTITY_REPLICA" mkdir "$IDENTITY_STAT_BIN" # shellcheck disable=SC2016 printf '%s\n' \ '#!/usr/bin/env bash' \ 'set -euo pipefail' \ 'target="${!#}"' \ - 'follow=false' \ - 'for argument in "$@"; do if [ "$argument" = "-L" ]; then follow=true; fi; done' \ 'case "$target" in' \ - ' *identity-primary/*) side=primary ;;' \ - ' *identity-replica/*) side=replica ;;' \ + ' *identity-primary|*identity-primary/*) side=primary ;;' \ + ' *identity-replica|*identity-replica/*) side=replica ;;' \ ' *) exit 70 ;;' \ 'esac' \ - 'case "${IDENTITY_MODE}:${follow}" in' \ - ' lstat:false|stat:true) printf "1:1\\n" ;;' \ - ' *) if [ "$side" = primary ]; then printf "1:2\\n"; else printf "1:3\\n"; fi ;;' \ + 'case "$*" in' \ + ' *%d:%i*) if [ "$side" = primary ]; then printf "1:101\\n"; else printf "2:202\\n"; fi ;;' \ + ' *%d*)' \ + ' case "${IDENTITY_MODE:-}" in' \ + ' same) printf "1\\n" ;;' \ + ' distinct) if [ "$side" = primary ]; then printf "1\\n"; else printf "2\\n"; fi ;;' \ + ' invalid) printf "not-a-device\\n" ;;' \ + ' *) exit 71 ;;' \ + ' esac ;;' \ + ' *) exit 72 ;;' \ 'esac' > "$IDENTITY_STAT_BIN/stat" chmod +x "$IDENTITY_STAT_BIN/stat" -check_fails_with 'verify REFUSES a shared lstat identity even when stat identities differ' 'replica CONTRACT.md shares the primary lstat identity' env PATH="$IDENTITY_STAT_BIN:$PATH" IDENTITY_MODE=lstat IDENTITY_PRIMARY="$IDENTITY_PRIMARY" IDENTITY_REPLICA="$IDENTITY_REPLICA" "$TOOL" verify "$IDENTITY_PRIMARY" "$IDENTITY_REPLICA" -check_fails_with 'verify REFUSES a shared stat identity even when lstat identities differ' 'replica CONTRACT.md resolves to the primary object' env PATH="$IDENTITY_STAT_BIN:$PATH" IDENTITY_MODE=stat IDENTITY_PRIMARY="$IDENTITY_PRIMARY" IDENTITY_REPLICA="$IDENTITY_REPLICA" "$TOOL" verify "$IDENTITY_PRIMARY" "$IDENTITY_REPLICA" +check_fails_with 'verify REFUSES equal numeric st_dev identities' 'primary and replica must be on different devices' env PATH="$IDENTITY_STAT_BIN:$PATH" IDENTITY_MODE=same "$TOOL" verify "$IDENTITY_PRIMARY" "$IDENTITY_REPLICA" +check_ok 'verify ACCEPTS distinct numeric st_dev identities' env PATH="$IDENTITY_STAT_BIN:$PATH" IDENTITY_MODE=distinct "$TOOL" verify "$IDENTITY_PRIMARY" "$IDENTITY_REPLICA" +check_fails_with 'verify REFUSES a nonnumeric st_dev identity' 'could not establish primary st_dev identity' env PATH="$IDENTITY_STAT_BIN:$PATH" IDENTITY_MODE=invalid "$TOOL" verify "$IDENTITY_PRIMARY" "$IDENTITY_REPLICA" -echo 'T10 every bundle member must have separate lstat and stat identities' +echo 'T10 hard links fall to the one device predicate by default' for entry in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do hardlink_replica="$TMP/hardlink-$entry" - check_ok "snapshot CREATES a $entry hard-link fixture" "$TOOL" snapshot "$PRIMARY" "$hardlink_replica" + check_ok "snapshot CREATES a $entry hard-link fixture" "$SAME_VOLUME_TOOL" snapshot "$PRIMARY" "$hardlink_replica" unlink "$hardlink_replica/$entry" ln "$PRIMARY/$entry" "$hardlink_replica/$entry" - check_fails_with "verify REFUSES a hard-linked $entry by lstat identity" "replica $entry shares the primary lstat identity" "$TOOL" verify "$PRIMARY" "$hardlink_replica" + check_fails_with "verify REFUSES a same-device pair containing hard-linked $entry" 'primary and replica must be on different devices' "$TOOL" verify "$PRIMARY" "$hardlink_replica" done +if [ "${FM_MUTATION_RUN:-0}" != 1 ]; then echo 'T11 adversarial ledger paths certify real copies that survive primary destruction' exercise_destruction_shape() { local label=$1 component=$2 root primary replica sources expected member number all_members all_rulings @@ -232,8 +284,8 @@ exercise_destruction_shape() { for member in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do cp -p "$primary/$member" "$expected/$member" done - check_ok "snapshot CREATES an independent replica under a $label path" "$TOOL" snapshot "$primary" "$replica" - check_ok "verify CERTIFIES the independent replica under a $label path" "$TOOL" verify "$primary" "$replica" + check_ok "snapshot CREATES an independent replica under a $label path" "$SAME_VOLUME_TOOL" snapshot "$primary" "$replica" + check_ok "verify CERTIFIES the independent replica under a $label path" "$SAME_VOLUME_TOOL" verify "$primary" "$replica" rm -rf -- "$primary" all_members=true for member in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do @@ -262,6 +314,7 @@ exercise_destruction_shape 'unterminated bracket' 'Ledger [1' exercise_destruction_shape 'space' 'Ledger space' exercise_destruction_shape 'newline' $'Ledger\nnewline' exercise_destruction_shape 'unicode' 'Ledger-船长-⚓' +fi echo 'T12 the instrument distinguishes independent and non-independent replicas' CONTROL_ROOT="$TMP/control #ledger" @@ -272,7 +325,7 @@ mkdir -p "$CONTROL_REPLICA" for entry in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do ln -s "$CONTROL_PRIMARY/$entry" "$CONTROL_REPLICA/$entry" done -check_fails_with 'positive control REFUSES a fully symlinked replica under a hash path' 'ledger bundle contains a symlink' "$TOOL" verify "$CONTROL_PRIMARY" "$CONTROL_REPLICA" +check_fails_with 'positive control REFUSES a fully symlinked replica under a hash path' 'ledger bundle contains a symlink' "$SAME_VOLUME_TOOL" verify "$CONTROL_PRIMARY" "$CONTROL_REPLICA" echo 'T13 containment is an explicit property' CONTAINED_PRIMARY="$TMP/containment-primary" @@ -282,40 +335,44 @@ mkdir -p "$CONTAINED_REPLICA" for entry in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do cp -p "$CONTAINED_PRIMARY/$entry" "$CONTAINED_REPLICA/$entry" done -check_fails_with 'verify REFUSES a replica contained by the primary explicitly' 'primary and replica directories must not contain one another' "$TOOL" verify "$CONTAINED_PRIMARY" "$CONTAINED_REPLICA" +check_fails_with 'verify REFUSES a replica contained by the primary explicitly' 'primary and replica directories must not contain one another' "$SAME_VOLUME_TOOL" verify "$CONTAINED_PRIMARY" "$CONTAINED_REPLICA" rm -rf -- "$CONTAINED_REPLICA" -check_fails_with 'snapshot REFUSES a replica target contained by the primary explicitly' 'primary and replica directories must not contain one another' "$TOOL" snapshot "$CONTAINED_PRIMARY" "$CONTAINED_REPLICA" +check_fails_with 'snapshot REFUSES a replica target contained by the primary explicitly' 'primary and replica directories must not contain one another' "$SAME_VOLUME_TOOL" snapshot "$CONTAINED_PRIMARY" "$CONTAINED_REPLICA" OUTER_REPLICA="$TMP/outer-replica" INNER_PRIMARY="$OUTER_REPLICA/primary" make_bundle "$OUTER_REPLICA" "$TMP/outer-sources" make_bundle "$INNER_PRIMARY" "$TMP/inner-sources" -check_fails_with 'verify REFUSES a primary contained by the replica explicitly' 'primary and replica directories must not contain one another' "$TOOL" verify "$INNER_PRIMARY" "$OUTER_REPLICA" +check_fails_with 'verify REFUSES a primary contained by the replica explicitly' 'primary and replica directories must not contain one another' "$SAME_VOLUME_TOOL" verify "$INNER_PRIMARY" "$OUTER_REPLICA" -echo 'T14 identity reads are fail-closed under BSD and GNU stat semantics' +echo 'T14 st_dev reads are fail-closed under BSD and GNU stat semantics' DIALECT_PRIMARY="$TMP/dialect-primary" DIALECT_REPLICA="$TMP/dialect-replica" DIALECT_STAT_BIN="$TMP/dialect-stat-bin" -check_ok 'snapshot CREATES a stat-dialect primary fixture' "$TOOL" snapshot "$PRIMARY" "$DIALECT_PRIMARY" -check_ok 'snapshot CREATES a stat-dialect replica fixture' "$TOOL" snapshot "$PRIMARY" "$DIALECT_REPLICA" +check_ok 'snapshot CREATES a stat-dialect primary fixture' "$SAME_VOLUME_TOOL" snapshot "$PRIMARY" "$DIALECT_PRIMARY" +check_ok 'snapshot CREATES a stat-dialect replica fixture' "$SAME_VOLUME_TOOL" snapshot "$PRIMARY" "$DIALECT_REPLICA" mkdir "$DIALECT_STAT_BIN" cat > "$DIALECT_STAT_BIN/stat" <<'STAT_STUB' #!/usr/bin/env bash set -euo pipefail emit_identity() { - local follow=$1 target=$2 - perl -e ' - my ($follow, $path) = @ARGV; - my @identity = $follow eq "true" ? stat($path) : lstat($path); - exit 70 unless @identity; - printf "%d:%d\n", $identity[0], $identity[1]; - ' "$follow" "$target" + local format=$1 target=$2 device inode + case "$target" in + *dialect-primary|*dialect-primary/*) device=11; inode=101 ;; + *dialect-replica|*dialect-replica/*) device=22; inode=202 ;; + *) exit 70 ;; + esac + case "$format" in + %d) printf '%s\n' "$device" ;; + %d:%i) printf '%s:%s\n' "$device" "$inode" ;; + *) exit 71 ;; + esac } case "${STAT_FLAVOUR:-}" in bsd) - if [ "$#" -eq 3 ] && [ "$1" = -f ] && [ "$2" = '%d:%i' ]; then - emit_identity false "$3" - elif [ "$#" -eq 4 ] && [ "$1" = -L ] && [ "$2" = -f ] && [ "$3" = '%d:%i' ]; then - emit_identity true "$4" + if [ "$#" -eq 3 ] && [ "$1" = -f ]; then + emit_identity "$2" "$3" + elif [ "$#" -eq 4 ] && [ "$1" = -L ] && [ "$2" = -f ]; then + emit_identity "$3" "$4" else exit 64 fi @@ -324,10 +381,10 @@ case "${STAT_FLAVOUR:-}" in if { [ "$#" -eq 3 ] && [ "$1" = -f ]; } || { [ "$#" -eq 4 ] && [ "$1" = -L ] && [ "$2" = -f ]; }; then printf ' File: "%s"\n' "${!#}" exit 1 - elif [ "$#" -eq 3 ] && [ "$1" = -c ] && [ "$2" = '%d:%i' ]; then - emit_identity false "$3" - elif [ "$#" -eq 4 ] && [ "$1" = -L ] && [ "$2" = -c ] && [ "$3" = '%d:%i' ]; then - emit_identity true "$4" + elif [ "$#" -eq 3 ] && [ "$1" = -c ]; then + emit_identity "$2" "$3" + elif [ "$#" -eq 4 ] && [ "$1" = -L ] && [ "$2" = -c ]; then + emit_identity "$3" "$4" else exit 64 fi @@ -340,25 +397,143 @@ esac STAT_STUB chmod +x "$DIALECT_STAT_BIN/stat" for flavour in bsd gnu; do - check_ok "verify PASSES an independent replica with $flavour stat semantics" env PATH="$DIALECT_STAT_BIN:$PATH" STAT_FLAVOUR="$flavour" "$TOOL" verify "$DIALECT_PRIMARY" "$DIALECT_REPLICA" - dialect_hardlink="$TMP/dialect-hardlink-$flavour" - check_ok "snapshot CREATES the $flavour hard-link fixture" "$TOOL" snapshot "$PRIMARY" "$dialect_hardlink" - unlink "$dialect_hardlink/CONTRACT.md" - ln "$PRIMARY/CONTRACT.md" "$dialect_hardlink/CONTRACT.md" - check_fails_with "verify REFUSES a hard link with $flavour stat semantics" 'replica CONTRACT.md shares the primary lstat identity' env PATH="$DIALECT_STAT_BIN:$PATH" STAT_FLAVOUR="$flavour" "$TOOL" verify "$PRIMARY" "$dialect_hardlink" + check_ok "verify PASSES distinct st_dev identities with $flavour stat semantics" env PATH="$DIALECT_STAT_BIN:$PATH" STAT_FLAVOUR="$flavour" "$TOOL" verify "$DIALECT_PRIMARY" "$DIALECT_REPLICA" done -check_fails_with 'verify REFUSES when stat cannot establish a numeric identity' 'could not establish a portable file identity' env PATH="$DIALECT_STAT_BIN:$PATH" STAT_FLAVOUR=invalid "$TOOL" verify "$DIALECT_PRIMARY" "$DIALECT_REPLICA" +check_fails_with 'verify REFUSES when stat cannot establish a numeric st_dev identity' 'could not establish primary st_dev identity' env PATH="$DIALECT_STAT_BIN:$PATH" STAT_FLAVOUR=invalid "$TOOL" verify "$DIALECT_PRIMARY" "$DIALECT_REPLICA" -echo 'T15 the complete replica and primary inode sets must be disjoint' +echo 'T15 the complete replica and primary device sets must be disjoint' CROSS_PRIMARY="$TMP/cross-primary" CROSS_REPLICA="$TMP/cross-replica" make_bundle "$CROSS_PRIMARY" "$TMP/cross-sources" cp "$CROSS_PRIMARY/CONTRACT.md" "$CROSS_PRIMARY/tests.sh" chmod +x "$CROSS_PRIMARY/tests.sh" -check_ok 'snapshot CREATES a cross-member identity fixture' "$TOOL" snapshot "$CROSS_PRIMARY" "$CROSS_REPLICA" +check_ok 'snapshot CREATES a cross-member identity fixture' "$SAME_VOLUME_TOOL" snapshot "$CROSS_PRIMARY" "$CROSS_REPLICA" unlink "$CROSS_REPLICA/CONTRACT.md" ln "$CROSS_PRIMARY/tests.sh" "$CROSS_REPLICA/CONTRACT.md" -check_fails_with 'verify REFUSES a replica member sharing storage with a different primary member' 'replica CONTRACT.md shares storage with primary member tests.sh' "$TOOL" verify "$CROSS_PRIMARY" "$CROSS_REPLICA" +check_fails_with 'verify REFUSES cross-member shared storage by the device predicate' 'primary and replica must be on different devices' "$TOOL" verify "$CROSS_PRIMARY" "$CROSS_REPLICA" + +echo 'T16 round 5 attacks replace the independence predicate rather than extending it' +R5_PRIMARY="$TMP/r5-primary" +R5_COPY="$TMP/r5-copy" +make_bundle "$R5_PRIMARY" "$TMP/r5-sources" +mkdir "$R5_COPY" +for entry in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do + cp -p "$R5_PRIMARY/$entry" "$R5_COPY/$entry" +done +check_fails_with 'verify REFUSES a same-volume real copy by default' 'primary and replica must be on different devices' "$TOOL" verify "$R5_PRIMARY" "$R5_COPY" +check_ok 'verify explicit opt-out ACCEPTS a same-volume real copy' "$TOOL" --allow-same-volume-without-device-redundancy verify "$R5_PRIMARY" "$R5_COPY" + +if cp -c "$R5_PRIMARY/CONTRACT.md" "$TMP/r5-clone-probe" 2>/dev/null; then + R5_CLONE="$TMP/r5-clone" + mkdir "$R5_CLONE" + for entry in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do + cp -c "$R5_PRIMARY/$entry" "$R5_CLONE/$entry" + done + check_fails_with 'verify REFUSES an APFS clone bundle by the device predicate' 'primary and replica must be on different devices' "$TOOL" verify "$R5_PRIMARY" "$R5_CLONE" +else + ok 'APFS clone attack is unavailable on this filesystem and is not simulated' +fi + +R5_CASE="$TMP/r5-case" +mkdir "$R5_CASE" +for entry in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do + cp -p "$R5_PRIMARY/$entry" "$R5_CASE/$entry" +done +mv "$R5_CASE/CONTRACT.md" "$R5_CASE/contract.md" +check_fails_with 'verify REFUSES a case-folded manifest collision by its real entry name' 'ledger bundle manifest differs from the exact required names' "$TOOL" --allow-same-volume-without-device-redundancy verify "$R5_PRIMARY" "$R5_CASE" + +R5_TRAVERSAL="$R5_PRIMARY/../r5-primary" +check_fails_with 'verify REFUSES dot-dot traversal that resolves to the primary itself' 'primary and replica directories must differ' "$TOOL" --allow-same-volume-without-device-redundancy verify "$R5_PRIMARY" "$R5_TRAVERSAL" +R5_PARENT_ALIAS="$TMP/r5-primary-parent-alias" +ln -s "$TMP" "$R5_PARENT_ALIAS" +check_fails_with 'verify REFUSES a replica path symlinked to the primary parent' 'primary and replica directories must not contain one another' "$TOOL" --allow-same-volume-without-device-redundancy verify "$R5_PRIMARY" "$R5_PARENT_ALIAS" + +R5_REFRESH="$TMP/r5-refresh" +mkdir "$R5_REFRESH" +for entry in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do + cp -p "$R5_PRIMARY/$entry" "$R5_REFRESH/$entry" +done +head -n 3 "$R5_PRIMARY/ledger.tsv" > "$R5_REFRESH/ledger.tsv" +unlink "$R5_REFRESH/tests.sh" +ln "$R5_PRIMARY/tests.sh" "$R5_REFRESH/tests.sh" +R5_REFRESH_BEFORE=$(cat "$R5_REFRESH/ledger.tsv") +check_fails_with 'refresh REFUSES a same-device pair before publication' 'primary and replica must be on different devices' "$TOOL" refresh "$R5_PRIMARY" "$R5_REFRESH" +if [ "$(cat "$R5_REFRESH/ledger.tsv")" = "$R5_REFRESH_BEFORE" ]; then + ok 'refresh refusal leaves the rejected replica byte-exactly unchanged' +else + bad 'refresh wrote ledger.tsv before refusing the pair' +fi + +R5_NONREGULAR="$TMP/r5-nonregular-after-classification" +mkdir "$R5_NONREGULAR" +for entry in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do + cp -p "$R5_PRIMARY/$entry" "$R5_NONREGULAR/$entry" +done +R5_FIND_BIN="$TMP/r5-find-bin" +mkdir "$R5_FIND_BIN" +printf '%s\n' \ + '#!/usr/bin/env bash' \ + 'set -euo pipefail' \ + ' /usr/bin/find "$@"' \ + 'case "$1" in' \ + '*/r5-nonregular-after-classification)' \ + ' if [ ! -e "$R5_SWAP_MARKER" ]; then' \ + ' : > "$R5_SWAP_MARKER"' \ + ' rm -f "$R5_SWAP_DIR/tests.sh"' \ + ' mkdir "$R5_SWAP_DIR/tests.sh"' \ + ' fi ;;' \ + 'esac' > "$R5_FIND_BIN/find" +chmod +x "$R5_FIND_BIN/find" +check_fails_with 'verify reclassifies a non-regular member appearing after enumeration' 'ledger bundle contains a non-regular file' env PATH="$R5_FIND_BIN:$PATH" R5_SWAP_DIR="$R5_NONREGULAR" R5_SWAP_MARKER="$TMP/r5-swap-done" "$TOOL" --allow-same-volume-without-device-redundancy verify "$R5_PRIMARY" "$R5_NONREGULAR" + +echo 'T17 snapshot publication is bound to the destination created exclusively by admission' +R5_TOC_PRIMARY="$TMP/r5-toc-primary" +R5_TOC_DEST="$TMP/r5-toc-destination" +R5_TOC_OUTSIDE="$TMP/r5-toc-outside" +R5_TOC_READY="$TMP/r5-toc-ready" +R5_TOC_COUNT="$TMP/r5-toc-count" +R5_TOC_REAL="$TMP/r5-toc-real-verifier" +R5_TOC_OUTPUT="$TMP/r5-toc-output" +make_bundle "$R5_TOC_PRIMARY" "$TMP/r5-toc-sources" +mkdir "$R5_TOC_OUTSIDE" +cp -p "$R5_TOC_PRIMARY/fm-sovereign-ledger.sh" "$R5_TOC_REAL" +printf '%s\n' \ + '#!/usr/bin/env bash' \ + 'set -euo pipefail' \ + 'count=0' \ + '[ ! -e "$R5_TOC_COUNT" ] || count=$(cat "$R5_TOC_COUNT")' \ + 'count=$((count + 1))' \ + 'printf "%s\\n" "$count" > "$R5_TOC_COUNT"' \ + 'if [ "$count" -eq 2 ]; then' \ + ' : > "$R5_TOC_READY"' \ + ' sleep 1' \ + 'fi' \ + 'exec "$R5_TOC_REAL" "$@"' > "$R5_TOC_PRIMARY/fm-sovereign-ledger.sh" +chmod +x "$R5_TOC_PRIMARY/fm-sovereign-ledger.sh" +set +e +env R5_TOC_COUNT="$R5_TOC_COUNT" R5_TOC_READY="$R5_TOC_READY" R5_TOC_REAL="$R5_TOC_REAL" \ + "$TOOL" --allow-same-volume-without-device-redundancy snapshot "$R5_TOC_PRIMARY" "$R5_TOC_DEST" > "$R5_TOC_OUTPUT" 2>&1 & +R5_TOC_PID=$! +set -e +R5_TOC_ATTEMPT=0 +while [ ! -e "$R5_TOC_READY" ] && [ "$R5_TOC_ATTEMPT" -lt 100 ]; do + sleep 0.02 + R5_TOC_ATTEMPT=$((R5_TOC_ATTEMPT + 1)) +done +if [ ! -e "$R5_TOC_DEST" ] && [ ! -L "$R5_TOC_DEST" ]; then + ln -s "$R5_TOC_OUTSIDE" "$R5_TOC_DEST" +fi +set +e +wait "$R5_TOC_PID" +R5_TOC_STATUS=$? +set -e +if [ "$R5_TOC_STATUS" -eq 0 ] && [ -d "$R5_TOC_DEST" ] && [ ! -L "$R5_TOC_DEST" ] \ + && [ -f "$R5_TOC_DEST/ledger.tsv" ] && [ -z "$(find "$R5_TOC_OUTSIDE" -mindepth 1 -maxdepth 1 -print -quit)" ]; then + ok 'snapshot race cannot redirect publication outside its admitted destination' +else + R5_TOC_DIAGNOSTIC=$(cat "$R5_TOC_OUTPUT") + bad "snapshot publication race escaped admission (status=$R5_TOC_STATUS; output: ${R5_TOC_DIAGNOSTIC//$'\n'/ | })" +fi printf '\n%s passed, %s failed\n' "$pass" "$fail" [ "$fail" -eq 0 ] From d96ea8de47561ecef89cad1147e92effaef7314e Mon Sep 17 00:00:00 2001 From: 420tombombadil Date: Sun, 16 Aug 2026 02:42:59 -0600 Subject: [PATCH 14/19] no-mistakes(review): Preserve member independence and safe snapshot failure handling --- bin/fm-sovereign-ledger-redundancy.sh | 75 ++++++++++++++++---- tests/fm-sovereign-ledger-redundancy.test.sh | 49 +++++++++++++ 2 files changed, 110 insertions(+), 14 deletions(-) diff --git a/bin/fm-sovereign-ledger-redundancy.sh b/bin/fm-sovereign-ledger-redundancy.sh index e34c172d9a..b01c4ff1b8 100755 --- a/bin/fm-sovereign-ledger-redundancy.sh +++ b/bin/fm-sovereign-ledger-redundancy.sh @@ -21,10 +21,16 @@ ADMITTED_PRIMARY_DEVICE= ADMITTED_REPLICA_DEVICE= ADMITTED_PRIMARY_DIRECTORY_IDENTITY= ADMITTED_REPLICA_DIRECTORY_IDENTITY= +ADMITTED_PRIMARY_MEMBER_IDENTITIES= +ADMITTED_REPLICA_MEMBER_IDENTITIES= CREATED_REPLICA= die() { - printf 'REFUSED: %s\n' "$*" >&2 + if [ -n "$CREATED_REPLICA" ]; then + printf 'REFUSED: %s; partial replica retained without deletion: %s\n' "$*" "$CREATED_REPLICA" >&2 + else + printf 'REFUSED: %s\n' "$*" >&2 + fi exit 1 } @@ -117,6 +123,10 @@ portable_directory_identity() { printf '%s\n' "$identity" } +portable_member_identity() { + portable_directory_identity "$1" +} + require_noncontained_pair() { local primary=$1 replica=$2 [ "$primary" != "$replica" ] || die "primary and replica directories must differ" @@ -162,12 +172,56 @@ require_admitted_directory() { [ "$identity" = "$expected" ] || die "$label directory changed after admission: $path" } +admit_independent_bundle_members() { + local primary=$1 replica=$2 entry primary_entry replica_entry identity + local primary_identities= replica_identities= primary_identity replica_identity compared=0 + collect_bundle_entries "$primary" + while IFS= read -r entry; do + identity=$(portable_member_identity "$primary/$entry") \ + || die "could not establish primary bundle member identity: $primary/$entry" + primary_identities="${primary_identities}${entry}\t${identity}\n" + identity=$(portable_member_identity "$replica/$entry") \ + || die "could not establish replica bundle member identity: $replica/$entry" + replica_identities="${replica_identities}${entry}\t${identity}\n" + done <<< "$BUNDLE_ENTRIES" + while IFS=$'\t' read -r primary_entry primary_identity; do + while IFS=$'\t' read -r replica_entry replica_identity; do + [ "$primary_identity" != "$replica_identity" ] \ + || die "replica $replica_entry shares storage with primary $primary_entry (device:inode), not an independent file" + compared=$((compared + 1)) + done < <(printf '%b' "$replica_identities") + done < <(printf '%b' "$primary_identities") + [ "$compared" -eq $((BUNDLE_MEMBER_COUNT * BUNDLE_MEMBER_COUNT)) ] \ + || die "bundle member identity proof compared $compared of $((BUNDLE_MEMBER_COUNT * BUNDLE_MEMBER_COUNT)) required pairs" + ADMITTED_PRIMARY_MEMBER_IDENTITIES=$(printf '%b' "$primary_identities") + ADMITTED_REPLICA_MEMBER_IDENTITIES=$(printf '%b' "$replica_identities") +} + +require_admitted_bundle_members() { + local primary=$1 replica=$2 entry expected identity checked=0 + while IFS=$'\t' read -r entry expected; do + identity=$(portable_member_identity "$primary/$entry") \ + || die "could not re-establish primary bundle member identity: $primary/$entry" + [ "$identity" = "$expected" ] || die "primary bundle member changed after admission: $primary/$entry" + checked=$((checked + 1)) + done <<< "$ADMITTED_PRIMARY_MEMBER_IDENTITIES" + while IFS=$'\t' read -r entry expected; do + identity=$(portable_member_identity "$replica/$entry") \ + || die "could not re-establish replica bundle member identity: $replica/$entry" + [ "$identity" = "$expected" ] || die "replica bundle member changed after admission: $replica/$entry" + checked=$((checked + 1)) + done <<< "$ADMITTED_REPLICA_MEMBER_IDENTITIES" + [ "$checked" -eq $((BUNDLE_MEMBER_COUNT * 2)) ] \ + || die "admitted bundle member check read $checked of $((BUNDLE_MEMBER_COUNT * 2)) required identities" +} + admit_existing_pair() { local primary=$1 replica=$2 mode=$3 require_noncontained_pair "$primary" "$replica" admit_device_pair "$primary" "$replica" require_bundle "$primary" require_bundle "$replica" + admit_independent_bundle_members "$primary" "$replica" compare_files "$primary" "$replica" ledger.tsv verify_with_primary "$primary" "$primary" verify_with_primary "$primary" "$replica" @@ -181,6 +235,7 @@ admit_existing_pair() { require_bundle "$primary" require_bundle "$replica" admit_device_pair "$primary" "$replica" + admit_independent_bundle_members "$primary" "$replica" compare_files "$primary" "$replica" ledger.tsv case "$mode" in exact) compare_files "$primary" "$replica" ;; @@ -234,18 +289,8 @@ copy_bundle() { || die "bundle copy read $copied of $BUNDLE_MEMBER_COUNT required members" } -cleanup_created_replica() { - local identity - [ -n "$CREATED_REPLICA" ] || return 0 - [ -d "$CREATED_REPLICA" ] || return 0 - [ ! -L "$CREATED_REPLICA" ] || return 0 - identity=$(portable_directory_identity "$CREATED_REPLICA") || return 0 - [ "$identity" = "$ADMITTED_REPLICA_DIRECTORY_IDENTITY" ] || return 0 - rm -rf -- "$CREATED_REPLICA" -} - admit_snapshot_destination() { - local primary=$1 replica_input=$2 parent base replica + local primary=$1 replica_input=$2 parent base replica parent_identity parent=$(dirname -- "$replica_input") [ -d "$parent" ] || die "replica parent directory does not exist: $parent" [ ! -L "$parent" ] || die "replica parent directory is symlinked: $parent" @@ -257,11 +302,13 @@ admit_snapshot_destination() { [ ! -e "$replica" ] && [ ! -L "$replica" ] \ || die "replica destination already exists or is symlinked: $replica" admit_device_pair "$primary" "$parent" + parent_identity=$(portable_directory_identity "$parent") \ + || die "could not establish replica parent directory identity before creation: $parent" + require_admitted_directory "replica parent" "$parent" "$parent_identity" mkdir -- "$replica" || die "could not exclusively create replica destination: $replica" CREATED_REPLICA=$replica ADMITTED_REPLICA_DIRECTORY_IDENTITY=$(portable_directory_identity "$replica") \ || die "could not establish new replica directory identity: $replica" - trap cleanup_created_replica EXIT HUP INT TERM copy_bundle "$primary" "$replica" admit_existing_pair "$primary" "$replica" exact ADMITTED_REPLICA=$replica @@ -293,6 +340,7 @@ copy_ledger_atomically() { local primary=$1 replica=$2 parent base tmp require_admitted_directory primary "$primary" "$ADMITTED_PRIMARY_DIRECTORY_IDENTITY" require_admitted_directory replica "$replica" "$ADMITTED_REPLICA_DIRECTORY_IDENTITY" + require_admitted_bundle_members "$primary" "$replica" parent=$(dirname -- "$replica") base=$(basename -- "$replica") tmp=$(mktemp "$parent/.${base}.ledger.tsv.tmp.XXXXXX") \ @@ -311,7 +359,6 @@ cmd_snapshot() { admit_pair "$1" "$2" snapshot if [ -n "$CREATED_REPLICA" ]; then CREATED_REPLICA= - trap - EXIT HUP INT TERM printf 'SNAPSHOT PASS (replica created: %s)\n' "$ADMITTED_REPLICA" else printf 'SNAPSHOT PASS (replica already identical: %s)\n' "$ADMITTED_REPLICA" diff --git a/tests/fm-sovereign-ledger-redundancy.test.sh b/tests/fm-sovereign-ledger-redundancy.test.sh index 794e391368..be11c16ffa 100755 --- a/tests/fm-sovereign-ledger-redundancy.test.sh +++ b/tests/fm-sovereign-ledger-redundancy.test.sh @@ -535,5 +535,54 @@ else bad "snapshot publication race escaped admission (status=$R5_TOC_STATUS; output: ${R5_TOC_DIAGNOSTIC//$'\n'/ | })" fi +echo 'T18 same-volume waiver preserves member independence and snapshot failure boundaries' +for entry in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do + waived_hardlink_replica="$TMP/waived-hardlink-$entry" + check_ok "snapshot CREATES a waived $entry hard-link fixture" "$SAME_VOLUME_TOOL" snapshot "$PRIMARY" "$waived_hardlink_replica" + unlink "$waived_hardlink_replica/$entry" + ln "$PRIMARY/$entry" "$waived_hardlink_replica/$entry" + check_fails_with "same-volume waiver REFUSES a hard-linked $entry" 'shares storage with primary' "$SAME_VOLUME_TOOL" verify "$PRIMARY" "$waived_hardlink_replica" +done + +WAIVED_CROSS_PRIMARY="$TMP/waived-cross-primary" +WAIVED_CROSS_REPLICA="$TMP/waived-cross-replica" +make_bundle "$WAIVED_CROSS_PRIMARY" "$TMP/waived-cross-sources" +cp "$WAIVED_CROSS_PRIMARY/CONTRACT.md" "$WAIVED_CROSS_PRIMARY/tests.sh" +chmod +x "$WAIVED_CROSS_PRIMARY/tests.sh" +check_ok 'snapshot CREATES a waived cross-member fixture' "$SAME_VOLUME_TOOL" snapshot "$WAIVED_CROSS_PRIMARY" "$WAIVED_CROSS_REPLICA" +unlink "$WAIVED_CROSS_REPLICA/CONTRACT.md" +ln "$WAIVED_CROSS_PRIMARY/tests.sh" "$WAIVED_CROSS_REPLICA/CONTRACT.md" +check_fails_with 'same-volume waiver REFUSES cross-member shared storage' 'replica CONTRACT.md shares storage with primary tests.sh' "$SAME_VOLUME_TOOL" verify "$WAIVED_CROSS_PRIMARY" "$WAIVED_CROSS_REPLICA" + +PREFLIGHT_STAT_BIN="$TMP/preflight-stat-bin" +PREFLIGHT_DEST="$TMP/preflight-stat-replica" +mkdir "$PREFLIGHT_STAT_BIN" +printf '%s\n' \ + '#!/usr/bin/env bash' \ + 'case "$*" in' \ + ' *%d:%i*) exit 75 ;;' \ + ' *%d*) printf "1\\n" ;;' \ + ' *) exit 76 ;;' \ + 'esac' > "$PREFLIGHT_STAT_BIN/stat" +chmod +x "$PREFLIGHT_STAT_BIN/stat" +check_fails_with 'snapshot REFUSES unsupported directory identity before destination creation' 'could not establish replica parent directory identity before creation' env PATH="$PREFLIGHT_STAT_BIN:$PATH" "$TOOL" --allow-same-volume-without-device-redundancy snapshot "$PRIMARY" "$PREFLIGHT_DEST" +if [ ! -e "$PREFLIGHT_DEST" ] && [ ! -L "$PREFLIGHT_DEST" ]; then + ok 'directory identity preflight leaves no replica destination' +else + bad 'directory identity preflight created a replica destination' +fi + +PARTIAL_PERL_BIN="$TMP/partial-perl-bin" +PARTIAL_DEST="$TMP/retained-partial-replica" +mkdir "$PARTIAL_PERL_BIN" +printf '%s\n' '#!/usr/bin/env bash' 'exit 77' > "$PARTIAL_PERL_BIN/perl" +chmod +x "$PARTIAL_PERL_BIN/perl" +check_fails_with 'snapshot identifies a retained partial after post-creation copy failure' 'partial replica retained without deletion' env PATH="$PARTIAL_PERL_BIN:$PATH" "$SAME_VOLUME_TOOL" snapshot "$PRIMARY" "$PARTIAL_DEST" +if [ -d "$PARTIAL_DEST" ] && [ ! -L "$PARTIAL_DEST" ]; then + ok 'post-creation failure retains the partial replica directory' +else + bad 'post-creation failure deleted or replaced the partial replica directory' +fi + printf '\n%s passed, %s failed\n' "$pass" "$fail" [ "$fail" -eq 0 ] From 4621320032adb3e4e36bc287d84f89af8a37a2b9 Mon Sep 17 00:00:00 2001 From: 420tombombadil Date: Sun, 16 Aug 2026 03:57:41 -0600 Subject: [PATCH 15/19] no-mistakes(review): Cover identity mutations and report retained snapshot signals --- .github/workflows/ci.yml | 9 + bin/fm-sovereign-ledger-redundancy.sh | 14 + .../sovereign-ledger-redundancy-mutation.md | 285 ------------------ ...fm-sovereign-ledger-redundancy.mutation.sh | 33 +- tests/fm-sovereign-ledger-redundancy.test.sh | 41 +++ 5 files changed, 88 insertions(+), 294 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5495ec4494..698c9c82c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,15 @@ jobs: - name: Prove complete regression partition run: bin/fm-test-run.sh --check-coverage + sovereign-ledger-mutation-evidence: + name: Sovereign ledger mutation evidence + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@v6 + - name: Regenerate and byte-compare maintained evidence + run: tests/fm-sovereign-ledger-redundancy.mutation.sh --verify-evidence + # Two duration-balanced portable parallel shards of the Phase 2 proven-isolated # set only. Composition owner: bin/fm-test-run.sh (docs/fm-test-portable-shards.md). tests-portable-parallel-1: diff --git a/bin/fm-sovereign-ledger-redundancy.sh b/bin/fm-sovereign-ledger-redundancy.sh index b01c4ff1b8..02a1c903d7 100755 --- a/bin/fm-sovereign-ledger-redundancy.sh +++ b/bin/fm-sovereign-ledger-redundancy.sh @@ -24,6 +24,7 @@ ADMITTED_REPLICA_DIRECTORY_IDENTITY= ADMITTED_PRIMARY_MEMBER_IDENTITIES= ADMITTED_REPLICA_MEMBER_IDENTITIES= CREATED_REPLICA= +POSSIBLE_REPLICA= die() { if [ -n "$CREATED_REPLICA" ]; then @@ -34,6 +35,13 @@ die() { exit 1 } +report_snapshot_signal() { + local signal=$1 status=$2 + trap - HUP INT TERM + printf 'REFUSED: snapshot interrupted by %s; partial replica retained or may exist without deletion: %s\n' "$signal" "$POSSIBLE_REPLICA" >&2 + exit "$status" +} + usage() { sed -n '2,12{s/^# \{0,1\}//;p;}' "$0" >&2 exit 2 @@ -305,6 +313,10 @@ admit_snapshot_destination() { parent_identity=$(portable_directory_identity "$parent") \ || die "could not establish replica parent directory identity before creation: $parent" require_admitted_directory "replica parent" "$parent" "$parent_identity" + POSSIBLE_REPLICA=$replica + trap 'report_snapshot_signal HUP 129' HUP + trap 'report_snapshot_signal INT 130' INT + trap 'report_snapshot_signal TERM 143' TERM mkdir -- "$replica" || die "could not exclusively create replica destination: $replica" CREATED_REPLICA=$replica ADMITTED_REPLICA_DIRECTORY_IDENTITY=$(portable_directory_identity "$replica") \ @@ -359,6 +371,8 @@ cmd_snapshot() { admit_pair "$1" "$2" snapshot if [ -n "$CREATED_REPLICA" ]; then CREATED_REPLICA= + POSSIBLE_REPLICA= + trap - HUP INT TERM printf 'SNAPSHOT PASS (replica created: %s)\n' "$ADMITTED_REPLICA" else printf 'SNAPSHOT PASS (replica already identical: %s)\n' "$ADMITTED_REPLICA" diff --git a/docs/verification/sovereign-ledger-redundancy-mutation.md b/docs/verification/sovereign-ledger-redundancy-mutation.md index 68a1318e61..e69de29bb2 100644 --- a/docs/verification/sovereign-ledger-redundancy-mutation.md +++ b/docs/verification/sovereign-ledger-redundancy-mutation.md @@ -1,285 +0,0 @@ -# Sovereign ledger redundancy mutation evidence - -Audience: maintainer verification. - -Verified on 2026-08-15 against the R4 sovereign-ledger redundancy implementation. -The owned denominator is 72 enforcing clauses, ten more than round 3's 62 attempted mutants because R4 added exact enumeration and read denominators, portable BSD/GNU identity selection, containment rejection, and full cross-member inode-set enforcement. -Every run copied the implementation under one scoped `mktemp -d`; the live ledger, `data/`, and `state/` were never inputs or targets. - -```sh -tests/fm-sovereign-ledger-redundancy.mutation.sh --write-evidence sovereign-ledger-redundancy-mutation.candidate.md -``` - -Evidence publication rejects existing destinations, so the candidate is reviewed against this record before replacement rather than overwriting it in place. - -Observed summary: `killed=47 survived=25 void=0 denominator=72`; the intentional no-op control recorded `substitutions=1 status=0 outcome=SURVIVED`. - -| Mutant | Stable exact clause anchor | Substitutions | Outcome | Status | Unenforced claim when survived | -| --- | --- | ---: | --- | ---: | --- | -| `M001` | `strict-mode.errexit` | 1 | SURVIVED | 0 | Shell failures are not allowed to fall through. | -| `M002` | `manifest.denominator` | 1 | KILLED | 1 | - | -| `M003` | `canonical.directory-exists` | 1 | KILLED | 1 | - | -| `M004` | `canonical.physical-path` | 1 | SURVIVED | 0 | Containment uses physical directory paths. | -| `M005` | `manifest.fourth-member` | 1 | KILLED | 1 | - | -| `M006` | `enumeration.minimum-depth` | 1 | KILLED | 1 | - | -| `M007` | `enumeration.maximum-depth` | 1 | SURVIVED | 0 | Only top-level members are counted. | -| `M008` | `enumeration.record-per-entry` | 1 | KILLED | 1 | - | -| `M009` | `enumeration.line-denominator` | 1 | KILLED | 1 | - | -| `M010` | `enumeration.exact-count` | 1 | KILLED | 1 | - | -| `M011` | `enumeration.known-manifest` | 1 | KILLED | 1 | - | -| `M012` | `require.known-manifest` | 1 | KILLED | 1 | - | -| `M013` | `require.member-exists` | 1 | KILLED | 1 | - | -| `M014` | `require.no-symlink` | 1 | KILLED | 1 | - | -| `M015` | `require.regular-file` | 1 | KILLED | 124 | - | -| `M016` | `require.read-denominator` | 1 | KILLED | 1 | - | -| `M017` | `require.enumerates-directory` | 1 | SURVIVED | 0 | Bundle validation independently enumerates the directory. | -| `M018` | `require.exact-read-count` | 1 | SURVIVED | 0 | The manifest read count must equal four. | -| `M019` | `require.executable-verifier` | 1 | KILLED | 1 | - | -| `M020` | `layout.enumerate-primary` | 1 | SURVIVED | 0 | Layout comparison enumerates the primary independently. | -| `M021` | `layout.capture-primary` | 1 | KILLED | 1 | - | -| `M022` | `layout.enumerate-replica` | 1 | SURVIVED | 0 | Layout comparison enumerates the replica independently. | -| `M023` | `layout.capture-replica` | 1 | KILLED | 1 | - | -| `M024` | `layout.equal-manifests` | 1 | SURVIVED | 0 | Primary and replica layouts must match. | -| `M025` | `bytes.enumerate-primary` | 1 | SURVIVED | 0 | Byte comparison owns a fresh manifest enumeration. | -| `M026` | `bytes.skip-denominator` | 1 | KILLED | 1 | - | -| `M027` | `bytes.skip-only-selected` | 1 | KILLED | 1 | - | -| `M028` | `bytes.quiet-compare` | 1 | SURVIVED | 0 | Member comparison uses cmp status as its verdict. | -| `M029` | `bytes.reject-difference` | 1 | KILLED | 1 | - | -| `M030` | `bytes.read-denominator` | 1 | KILLED | 1 | - | -| `M031` | `bytes.exact-read-count` | 1 | SURVIVED | 0 | The byte-read count must equal its owned denominator. | -| `M032` | `identity.follow-selection` | 1 | KILLED | 1 | - | -| `M033` | `identity.bsd-follow` | 1 | KILLED | 1 | - | -| `M034` | `identity.gnu-follow` | 1 | SURVIVED | 0 | GNU followed identity uses stat -L. | -| `M035` | `identity.bsd-lstat` | 1 | KILLED | 1 | - | -| `M036` | `identity.gnu-lstat` | 1 | SURVIVED | 0 | GNU non-followed identity uses lstat semantics. | -| `M037` | `identity.numeric-shape` | 1 | KILLED | 1 | - | -| `M038` | `identity.lstat-wrapper` | 1 | KILLED | 1 | - | -| `M039` | `identity.stat-wrapper` | 1 | KILLED | 1 | - | -| `M040` | `identity.enumerate-primary` | 1 | SURVIVED | 0 | Identity verification enumerates the owned manifest independently. | -| `M041` | `identity.primary-lstat-read` | 1 | KILLED | 1 | - | -| `M042` | `identity.replica-lstat-read` | 1 | KILLED | 1 | - | -| `M043` | `identity.primary-stat-read` | 1 | KILLED | 1 | - | -| `M044` | `identity.replica-stat-read` | 1 | KILLED | 1 | - | -| `M045` | `identity.read-denominator` | 1 | KILLED | 1 | - | -| `M046` | `identity.exact-read-count` | 1 | SURVIVED | 0 | Identity reads must cover exactly four members. | -| `M047` | `identity.replica-cross-product` | 1 | KILLED | 1 | - | -| `M048` | `identity.primary-cross-product` | 1 | KILLED | 1 | - | -| `M049` | `identity.lstat-disjoint` | 1 | KILLED | 1 | - | -| `M050` | `identity.lstat-member-attribution` | 1 | KILLED | 1 | - | -| `M051` | `identity.stat-disjoint` | 1 | KILLED | 1 | - | -| `M052` | `identity.stat-member-attribution` | 1 | KILLED | 1 | - | -| `M053` | `containment.distinct-paths` | 1 | KILLED | 1 | - | -| `M054` | `containment.primary-outside-replica` | 1 | KILLED | 1 | - | -| `M055` | `containment.replica-outside-primary` | 1 | KILLED | 1 | - | -| `M056` | `verifier.public-verify-command` | 1 | KILLED | 1 | - | -| `M057` | `prefix.primary-line-count` | 1 | KILLED | 1 | - | -| `M058` | `prefix.replica-line-count` | 1 | KILLED | 1 | - | -| `M059` | `prefix.nonempty-replica` | 1 | KILLED | 1 | - | -| `M060` | `prefix.strictly-shorter` | 1 | KILLED | 1 | - | -| `M061` | `prefix.leading-bytes` | 1 | KILLED | 1 | - | -| `M062` | `preflight.require-primary` | 1 | SURVIVED | 0 | Pair preflight validates the primary bundle. | -| `M063` | `preflight.layout` | 1 | SURVIVED | 0 | Pair preflight compares the exact layouts. | -| `M064` | `preflight.nonledger-bytes` | 1 | SURVIVED | 0 | Pair preflight compares replica-controlled code before execution. | -| `M065` | `exact.all-bytes` | 1 | SURVIVED | 0 | Exact verification compares all four members. | -| `M066` | `exact.disjoint-identities` | 1 | KILLED | 1 | - | -| `M067` | `exact.verify-primary` | 1 | SURVIVED | 0 | Exact verification validates the primary ledger. | -| `M068` | `exact.verify-replica` | 1 | SURVIVED | 0 | Exact verification validates replica ledger data. | -| `M069` | `copy.enumerate-primary` | 1 | SURVIVED | 0 | Copying starts from an independently enumerated manifest. | -| `M070` | `copy.preserve-mode` | 1 | SURVIVED | 0 | Bundle copying preserves required executable modes. | -| `M071` | `copy.private-umask` | 1 | SURVIVED | 0 | Bundle staging uses a private creation mask. | -| `M072` | `copy.exact-read-count` | 1 | SURVIVED | 0 | Copying must cover exactly four members. | - -## Evidence publication containment - -The complete fixture results, cell outcomes, alternate defenders, per-mutant totals, and aggregate denominator below come from one canonical generated stream. - -# Evidence publication containment matrix - -Generated by `tests/fm-sovereign-ledger-evidence-publish.mutation.sh --markdown`. - -```text - PASS absolute destination aimed at fake data is refused - PASS dot-dot traversal aimed at fake data is refused - PASS symlinked leaf aimed at fake data is refused - PASS symlinked parent directory is refused - PASS symlinked parent aimed at fake state is refused - PASS destination resolving outside after parent resolution is refused - PASS existing hard-linked destination is refused without mutation - PASS unresolved destination parent is refused - PASS existing destination is refused without mutation - PASS symlinked evidence scope is refused - PASS unresolved evidence scope is refused - PASS unsafe destination leaf is refused - PASS legitimate in-scope publication remains exact -CONTAINMENT FIXTURES passed=13 failed=0 -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=absolute outcome=KILLED defender=- -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=traversal outcome=KILLED defender=- -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=exclusive-create-O_EXCL -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=symlink-parent outcome=KILLED defender=- -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=symlink-parent-state outcome=KILLED defender=- -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=resolved-outside outcome=KILLED defender=- -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=hard-link outcome=SURVIVED defender=exclusive-create-O_EXCL -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=natural-unresolved-parent -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=existing outcome=SURVIVED defender=exclusive-create-O_EXCL -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=scope-symlink outcome=KILLED defender=- -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=natural-unresolved-parent -PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=exclusive-create-O_EXCL -PUBLISH MUTANT P001 anchor=resolved-path-validator-call substitutions=1 killed=6 survived=6 void=0 -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=absolute outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=traversal outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=hard-link outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=existing outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=resolved-path-validator -PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=resolved-path-validator -PUBLISH MUTANT P002 anchor=exclusive-create-flag substitutions=1 killed=0 survived=12 void=0 -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=symlink-parent outcome=KILLED defender=- -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard -PUBLISH MUTANT P003 anchor=symlink-component-precheck substitutions=1 killed=1 survived=11 void=0 -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=final-structural-containment -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard -PUBLISH MUTANT P004 anchor=canonical-structural-containment substitutions=1 killed=0 survived=12 void=0 -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard -PUBLISH MUTANT P005 anchor=absolute-path-guard substitutions=1 killed=0 survived=12 void=0 -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard -PUBLISH MUTANT P006 anchor=unsafe-component-guard substitutions=1 killed=0 survived=12 void=0 -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=exclusive-create-O_EXCL -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard -PUBLISH MUTANT P007 anchor=symlink-leaf-guard substitutions=1 killed=0 survived=12 void=0 -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=exclusive-create-O_EXCL -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=existing outcome=SURVIVED defender=exclusive-create-O_EXCL -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard -PUBLISH MUTANT P008 anchor=existing-leaf-guard substitutions=1 killed=0 survived=12 void=0 -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=natural-unresolved-parent -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard -PUBLISH MUTANT P009 anchor=canonical-parent-resolution substitutions=1 killed=0 survived=12 void=0 -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard -PUBLISH MUTANT P010 anchor=final-structural-containment substitutions=1 killed=0 survived=12 void=0 -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard -PUBLISH MUTANT P011 anchor=scope-symlink-guard substitutions=1 killed=0 survived=12 void=0 -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard -PUBLISH MUTANT P012 anchor=canonical-scope-resolution substitutions=1 killed=0 survived=12 void=0 -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution -PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=existing-leaf-guard -PUBLISH MUTANT P013 anchor=unsafe-leaf-guard substitutions=1 killed=0 survived=12 void=0 -PUBLISH MATRIX SUMMARY mechanisms=14 written_boundaries=13 natural_boundaries=1 mutants=13 fixtures=12 cells=156 killed=7 survived=149 void=0 -``` diff --git a/tests/fm-sovereign-ledger-redundancy.mutation.sh b/tests/fm-sovereign-ledger-redundancy.mutation.sh index 62be762270..dc2760d5c1 100755 --- a/tests/fm-sovereign-ledger-redundancy.mutation.sh +++ b/tests/fm-sovereign-ledger-redundancy.mutation.sh @@ -9,27 +9,31 @@ SOURCE="$ROOT/bin/fm-sovereign-ledger-redundancy.sh" TEST="$ROOT/tests/fm-sovereign-ledger-redundancy.test.sh" EVIDENCE= EMIT_EVIDENCE=0 +VERIFY_EVIDENCE=0 MODE=mutation EVIDENCE_FIXTURE= EVIDENCE_SWAP_PATH= EVIDENCE_SWAP_TARGET= if [ "${1:-}" = --emit-evidence ]; then - [ "$#" -eq 1 ] || { printf 'usage: %s [--emit-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } + [ "$#" -eq 1 ] || { printf 'usage: %s [--emit-evidence|--verify-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } EMIT_EVIDENCE=1 exec 3>&1 exec >&2 +elif [ "${1:-}" = --verify-evidence ]; then + [ "$#" -eq 1 ] || { printf 'usage: %s [--emit-evidence|--verify-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } + VERIFY_EVIDENCE=1 elif [ "${1:-}" = --self-test-evidence-fixture ]; then - [ "$#" -eq 2 ] || { printf 'usage: %s [--emit-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } + [ "$#" -eq 2 ] || { printf 'usage: %s [--emit-evidence|--verify-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } MODE=fixture EVIDENCE_FIXTURE=$2 elif [ "${1:-}" = --self-test-evidence-containment ]; then - [ "$#" -eq 1 ] || { printf 'usage: %s [--emit-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } + [ "$#" -eq 1 ] || { printf 'usage: %s [--emit-evidence|--verify-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } MODE=containment elif [ "${1:-}" = --write-evidence ]; then - [ "$#" -eq 2 ] || { printf 'usage: %s [--emit-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } + [ "$#" -eq 2 ] || { printf 'usage: %s [--emit-evidence|--verify-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } EVIDENCE=$2 elif [ "$#" -ne 0 ]; then - printf 'usage: %s [--emit-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2 + printf 'usage: %s [--emit-evidence|--verify-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2 exit 2 fi @@ -355,7 +359,7 @@ mutant M023 verifier.execute 'LEDGER_DIR="$subject" "$primary/fm-sovereign-ledge mutant M024 prefix.nonempty '[ "$replica_lines" -gt 0 ]' 'true' 'An empty ledger is not a prefix.' mutant M025 prefix.shorter '[ "$primary_lines" -gt "$replica_lines" ]' 'true' 'A prefix is strictly shorter.' mutant M026 prefix.leading 'head -n "$replica_lines"' 'tail -n "$replica_lines"' 'Prefix comparison uses leading records.' -mutant M027 recheck.replica-type $'require_bundle "$primary"\n require_bundle "$replica"\n admit_device_pair "$primary" "$replica"\n compare_files' $'require_bundle "$primary"\n :\n admit_device_pair "$primary" "$replica"\n compare_files' 'Admission reclassifies replica entries after verifier execution.' +mutant M027 recheck.replica-type $'require_bundle "$primary"\n require_bundle "$replica"\n admit_device_pair "$primary" "$replica"\n admit_independent_bundle_members "$primary" "$replica"\n compare_files' $'require_bundle "$primary"\n :\n admit_device_pair "$primary" "$replica"\n admit_independent_bundle_members "$primary" "$replica"\n compare_files' 'Admission reclassifies replica entries after verifier execution.' mutant M028 directory.canonical-stable '[ "$canonical" = "$path" ] || die "$label directory no longer resolves to its admitted path: $path"' ':' 'A directory path cannot change after admission.' mutant M029 directory.identity-stable '[ "$identity" = "$expected" ] || die "$label directory changed after admission: $path"' ':' 'A directory object cannot change after admission.' mutant M030 publish.exclusive-create 'O_WRONLY | O_CREAT | O_EXCL' 'O_WRONLY | O_CREAT' 'Bundle members are created exclusively.' @@ -369,15 +373,21 @@ mutant M037 publish.copy-bundle 'copy_bundle "$primary" "$replica"' ':' 'Snapsho mutant M038 publish.validate-object 'admit_existing_pair "$primary" "$replica" exact' ':' 'Snapshot validates the object it published.' mutant M039 refresh.prefix-admission 'admit_pair "$1" "$2" prefix' 'admit_pair "$1" "$2" inspect' 'Refresh proves the prefix before writing.' mutant M040 refresh.atomic-copy 'copy_ledger_atomically "$ADMITTED_PRIMARY" "$ADMITTED_REPLICA"' ':' 'Refresh publishes the admitted ledger update.' -mutant M041 refresh.post-admission 'admit_pair "$ADMITTED_PRIMARY" "$ADMITTED_REPLICA" exact' ':' 'Refresh re-admits the published exact pair.' +mutant M041 refresh.post-admission $' copy_ledger_atomically "$ADMITTED_PRIMARY" "$ADMITTED_REPLICA"\n admit_pair "$ADMITTED_PRIMARY" "$ADMITTED_REPLICA" exact\n printf \'REFRESH PASS (replica advanced to primary)\\n\'' $' copy_ledger_atomically "$ADMITTED_PRIMARY" "$ADMITTED_REPLICA"\n :\n printf \'REFRESH PASS (replica advanced to primary)\\n\'' 'Refresh re-admits the published exact pair.' mutant M042 verify.inspect-admission 'admit_pair "$1" "$2" inspect' 'admit_pair "$1" "$2" exact' 'Verify admits stale pairs before classifying them.' mutant M043 verify.exact-recheck $' admit_pair "$ADMITTED_PRIMARY" "$ADMITTED_REPLICA" exact\n if [ "$ALLOW_SAME_VOLUME_WITHOUT_DEVICE_REDUNDANCY" = yes ]; then' $' :\n if [ "$ALLOW_SAME_VOLUME_WITHOUT_DEVICE_REDUNDANCY" = yes ]; then' 'Verify re-admits an exact pair before PASS.' mutant M044 option.named-flag 'if [ "${1:-}" = --allow-same-volume-without-device-redundancy ]; then' 'if [ "${1:-}" = --allow-same-volume ]; then' 'The opt-out name states the surrendered property.' mutant M045 dispatch.snapshot 'snapshot) shift; [ "$#" -eq 2 ] || usage; cmd_snapshot "$@"' 'snapshot) shift; [ "$#" -eq 2 ] || usage; :' 'Dispatch cannot bypass snapshot admission.' mutant M046 dispatch.refresh 'refresh) shift; [ "$#" -eq 2 ] || usage; cmd_refresh "$@"' 'refresh) shift; [ "$#" -eq 2 ] || usage; :' 'Dispatch cannot bypass refresh admission.' mutant M047 dispatch.verify 'verify) shift; [ "$#" -eq 2 ] || usage; cmd_verify "$@"' 'verify) shift; [ "$#" -eq 2 ] || usage; :' 'Dispatch cannot bypass verify admission.' +mutant M048 identity.member-read $'portable_member_identity() {\n portable_directory_identity "$1"\n}' $'portable_member_identity() {\n printf "1:1\\n"\n}' 'Member identity is read from the admitted filesystem object.' +mutant M049 identity.cross-product-disjoint '[ "$primary_identity" != "$replica_identity" ]' 'true' 'Every primary identity must differ from every replica identity.' +mutant M050 identity.cross-product-denominator '[ "$compared" -eq $((BUNDLE_MEMBER_COUNT * BUNDLE_MEMBER_COUNT)) ]' 'true' 'The complete four-by-four member cross-product is proved.' +mutant M051 admission.initial-member-identity $' admit_device_pair "$primary" "$replica"\n require_bundle "$primary"\n require_bundle "$replica"\n admit_independent_bundle_members "$primary" "$replica"\n compare_files' $' admit_device_pair "$primary" "$replica"\n require_bundle "$primary"\n require_bundle "$replica"\n :\n compare_files' 'Initial pair admission proves member independence before verifier execution.' +mutant M052 admission.final-member-identity $' require_bundle "$primary"\n require_bundle "$replica"\n admit_device_pair "$primary" "$replica"\n admit_independent_bundle_members "$primary" "$replica"\n compare_files' $' require_bundle "$primary"\n require_bundle "$replica"\n admit_device_pair "$primary" "$replica"\n :\n compare_files' 'Final pair admission re-proves member independence after verifier execution.' +mutant M053 refresh.carried-member-identity ' require_admitted_bundle_members "$primary" "$replica"' ' :' 'Refresh rechecks the admitted member identities before publication.' -[ "$denominator" -eq 47 ] || { printf 'invalid owned denominator: %s\n' "$denominator" >&2; exit 1; } +[ "$denominator" -eq 53 ] || { printf 'invalid owned denominator: %s\n' "$denominator" >&2; exit 1; } control="$TMP/CONTROL.sh" control_count="$TMP/CONTROL.count" @@ -402,7 +412,7 @@ EVIDENCE_STAGE="$TMP/evidence.md" printf '# Sovereign ledger redundancy mutation evidence\n\n' printf 'Audience: maintainer verification.\n\n' printf 'Verified on 2026-08-15 against the R5 sovereign-ledger redundancy chokepoint.\n' - printf 'The owned denominator spans exact real-name enumeration, the st_dev predicate, shared admission, exclusive publication, pre-write refresh admission, post-write re-admission, verification, and all three dispatch entries.\n' + printf 'The owned denominator spans exact real-name enumeration, the st_dev predicate, the four-by-four member-identity cross-product, both member admission calls, carried-identity refresh recheck, exclusive publication, verification, and all three dispatch entries.\n' printf 'Every run copied the implementation under one scoped `mktemp -d`; the live ledger, `data/`, and `state/` were never inputs or targets.\n\n' printf '```sh\n' printf 'tests/fm-sovereign-ledger-redundancy.mutation.sh --write-evidence sovereign-ledger-redundancy-mutation.candidate.md\n' @@ -434,4 +444,9 @@ if [ -n "$EVIDENCE" ]; then publish_evidence "$ROOT/docs/verification" "$EVIDENCE" "$EVIDENCE_STAGE" elif [ "$EMIT_EVIDENCE" -eq 1 ]; then cat "$EVIDENCE_STAGE" >&3 +elif [ "$VERIFY_EVIDENCE" -eq 1 ]; then + cmp -s "$EVIDENCE_STAGE" "$ROOT/docs/verification/sovereign-ledger-redundancy-mutation.md" || { + diff -u "$ROOT/docs/verification/sovereign-ledger-redundancy-mutation.md" "$EVIDENCE_STAGE" >&2 || true + exit 1 + } fi diff --git a/tests/fm-sovereign-ledger-redundancy.test.sh b/tests/fm-sovereign-ledger-redundancy.test.sh index be11c16ffa..bba756f082 100755 --- a/tests/fm-sovereign-ledger-redundancy.test.sh +++ b/tests/fm-sovereign-ledger-redundancy.test.sh @@ -584,5 +584,46 @@ else bad 'post-creation failure deleted or replaced the partial replica directory' fi +SIGNAL_MKDIR_BIN="$TMP/signal-mkdir-bin" +SIGNAL_DEST="$TMP/signal-partial-replica" +SIGNAL_READY="$TMP/signal-mkdir-ready" +SIGNAL_OUTPUT="$TMP/signal-output" +REAL_MKDIR=$(command -v mkdir) +mkdir "$SIGNAL_MKDIR_BIN" +printf '%s\n' \ + '#!/usr/bin/env bash' \ + 'set -euo pipefail' \ + '"$REAL_MKDIR" "$@"' \ + ': > "$SIGNAL_READY"' \ + 'sleep 0.1' > "$SIGNAL_MKDIR_BIN/mkdir" +chmod +x "$SIGNAL_MKDIR_BIN/mkdir" +set +e +env PATH="$SIGNAL_MKDIR_BIN:$PATH" REAL_MKDIR="$REAL_MKDIR" SIGNAL_READY="$SIGNAL_READY" \ + "$SAME_VOLUME_TOOL" snapshot "$PRIMARY" "$SIGNAL_DEST" > "$SIGNAL_OUTPUT" 2>&1 & +SIGNAL_PID=$! +set -e +SIGNAL_ATTEMPT=0 +while [ ! -e "$SIGNAL_READY" ] && [ "$SIGNAL_ATTEMPT" -lt 100 ]; do + sleep 0.02 + SIGNAL_ATTEMPT=$((SIGNAL_ATTEMPT + 1)) +done +if [ -e "$SIGNAL_READY" ]; then kill -TERM "$SIGNAL_PID"; fi +set +e +wait "$SIGNAL_PID" +SIGNAL_STATUS=$? +set -e +if [ "$SIGNAL_STATUS" -eq 143 ] \ + && grep -Fq 'partial replica retained or may exist without deletion' "$SIGNAL_OUTPUT"; then + ok 'TERM reports the retained or possibly-created replica destination' +else + SIGNAL_DIAGNOSTIC=$(cat "$SIGNAL_OUTPUT") + bad "TERM did not report its non-deleting snapshot boundary (status=$SIGNAL_STATUS; output: ${SIGNAL_DIAGNOSTIC//$'\n'/ | })" +fi +if [ -d "$SIGNAL_DEST" ] && [ ! -L "$SIGNAL_DEST" ]; then + ok 'TERM leaves the created partial replica directory in place' +else + bad 'TERM deleted or replaced the created partial replica directory' +fi + printf '\n%s passed, %s failed\n' "$pass" "$fail" [ "$fail" -eq 0 ] From adbaba5f755c8b5f3b362b6732396a5b92fc7ab5 Mon Sep 17 00:00:00 2001 From: 420tombombadil Date: Sun, 16 Aug 2026 05:15:18 -0600 Subject: [PATCH 16/19] no-mistakes(review): Regenerate and enforce nonempty mutation evidence --- .../sovereign-ledger-redundancy-mutation.md | 266 ++++++++++++++++++ ...ereign-ledger-evidence-publish.mutation.sh | 4 +- ...fm-sovereign-ledger-redundancy.mutation.sh | 40 ++- 3 files changed, 299 insertions(+), 11 deletions(-) diff --git a/docs/verification/sovereign-ledger-redundancy-mutation.md b/docs/verification/sovereign-ledger-redundancy-mutation.md index e69de29bb2..183d0c975a 100644 --- a/docs/verification/sovereign-ledger-redundancy-mutation.md +++ b/docs/verification/sovereign-ledger-redundancy-mutation.md @@ -0,0 +1,266 @@ +# Sovereign ledger redundancy mutation evidence + +Audience: maintainer verification. + +Verified on 2026-08-15 against the R5 sovereign-ledger redundancy chokepoint. +The owned denominator spans exact real-name enumeration, the st_dev predicate, the four-by-four member-identity cross-product, both member admission calls, carried-identity refresh recheck, exclusive publication, verification, and all three dispatch entries. +Every run copied the implementation under one scoped `mktemp -d`; the live ledger, `data/`, and `state/` were never inputs or targets. + +```sh +tests/fm-sovereign-ledger-redundancy.mutation.sh --write-evidence sovereign-ledger-redundancy-mutation.candidate.md +``` + +Evidence publication rejects existing destinations, so the candidate is reviewed against this record before replacement rather than overwriting it in place. + +Observed summary: `killed=36 survived=17 void=0 harness_broken=0 denominator=53`; the intentional no-op control recorded `substitutions=1 status=0 outcome=SURVIVED`. + +| Mutant | Stable exact clause anchor | Substitutions | Outcome | Status | Unenforced claim when survived | +| --- | --- | ---: | --- | ---: | --- | +| `M001` | `strict.errexit` | 1 | SURVIVED | 0 | Shell failures cannot fall through. | +| `M002` | `manifest.denominator` | 1 | KILLED | 1 | - | +| `M003` | `canonical.exists` | 1 | KILLED | 1 | - | +| `M004` | `canonical.physical` | 1 | KILLED | 1 | - | +| `M005` | `manifest.tests-member` | 1 | KILLED | 1 | - | +| `M006` | `enumerate.top-level` | 1 | KILLED | 1 | - | +| `M007` | `enumerate.real-names` | 1 | KILLED | 1 | - | +| `M008` | `enumerate.exact-set` | 1 | KILLED | 1 | - | +| `M009` | `require.no-symlink` | 1 | KILLED | 1 | - | +| `M010` | `require.regular` | 1 | KILLED | 1 | - | +| `M011` | `require.executable` | 1 | KILLED | 1 | - | +| `M012` | `bytes.reject` | 1 | KILLED | 1 | - | +| `M013` | `device.bsd-read` | 1 | KILLED | 1 | - | +| `M014` | `device.gnu-read` | 1 | KILLED | 1 | - | +| `M015` | `device.numeric-shape` | 1 | KILLED | 1 | - | +| `M016` | `containment.distinct` | 1 | KILLED | 1 | - | +| `M017` | `containment.primary` | 1 | KILLED | 1 | - | +| `M018` | `containment.replica` | 1 | KILLED | 1 | - | +| `M019` | `device.primary-read` | 1 | KILLED | 1 | - | +| `M020` | `device.replica-read` | 1 | KILLED | 1 | - | +| `M021` | `device.inequality` | 1 | KILLED | 1 | - | +| `M022` | `device.optout-only` | 1 | KILLED | 1 | - | +| `M023` | `verifier.execute` | 1 | KILLED | 1 | - | +| `M024` | `prefix.nonempty` | 1 | KILLED | 1 | - | +| `M025` | `prefix.shorter` | 1 | KILLED | 1 | - | +| `M026` | `prefix.leading` | 1 | KILLED | 1 | - | +| `M027` | `recheck.replica-type` | 1 | SURVIVED | 0 | Admission reclassifies replica entries after verifier execution. | +| `M028` | `directory.canonical-stable` | 1 | SURVIVED | 0 | A directory path cannot change after admission. | +| `M029` | `directory.identity-stable` | 1 | SURVIVED | 0 | A directory object cannot change after admission. | +| `M030` | `publish.exclusive-create` | 1 | SURVIVED | 0 | Bundle members are created exclusively. | +| `M031` | `publish.preserve-mode` | 1 | KILLED | 1 | - | +| `M032` | `publish.parent-no-symlink` | 1 | SURVIVED | 0 | Snapshot parent cannot be a symlink. | +| `M033` | `publish.safe-leaf` | 1 | SURVIVED | 0 | Snapshot leaf is safe. | +| `M034` | `publish.destination-absent` | 1 | SURVIVED | 0 | Snapshot destination is absent and not symlinked. | +| `M035` | `publish.parent-device` | 1 | SURVIVED | 0 | Snapshot checks destination-volume st_dev before creation. | +| `M036` | `publish.mkdir-exclusive` | 1 | SURVIVED | 0 | Snapshot claims its final destination exclusively. | +| `M037` | `publish.copy-bundle` | 1 | KILLED | 1 | - | +| `M038` | `publish.validate-object` | 1 | SURVIVED | 0 | Snapshot validates the object it published. | +| `M039` | `refresh.prefix-admission` | 1 | KILLED | 1 | - | +| `M040` | `refresh.atomic-copy` | 1 | KILLED | 1 | - | +| `M041` | `refresh.post-admission` | 1 | SURVIVED | 0 | Refresh re-admits the published exact pair. | +| `M042` | `verify.inspect-admission` | 1 | KILLED | 1 | - | +| `M043` | `verify.exact-recheck` | 1 | SURVIVED | 0 | Verify re-admits an exact pair before PASS. | +| `M044` | `option.named-flag` | 1 | KILLED | 1 | - | +| `M045` | `dispatch.snapshot` | 1 | KILLED | 1 | - | +| `M046` | `dispatch.refresh` | 1 | KILLED | 1 | - | +| `M047` | `dispatch.verify` | 1 | KILLED | 1 | - | +| `M048` | `identity.member-read` | 1 | KILLED | 1 | - | +| `M049` | `identity.cross-product-disjoint` | 1 | KILLED | 1 | - | +| `M050` | `identity.cross-product-denominator` | 1 | SURVIVED | 0 | The complete four-by-four member cross-product is proved. | +| `M051` | `admission.initial-member-identity` | 1 | SURVIVED | 0 | Initial pair admission proves member independence before verifier execution. | +| `M052` | `admission.final-member-identity` | 1 | SURVIVED | 0 | Final pair admission re-proves member independence after verifier execution. | +| `M053` | `refresh.carried-member-identity` | 1 | SURVIVED | 0 | Refresh rechecks the admitted member identities before publication. | + +## Evidence publication containment + +The complete fixture results, cell outcomes, alternate defenders, per-mutant totals, and aggregate denominator below come from one canonical generated stream. + +# Evidence publication containment matrix + +Generated by `tests/fm-sovereign-ledger-evidence-publish.mutation.sh --markdown`. + +```text + PASS absolute destination aimed at fake data is refused + PASS dot-dot traversal aimed at fake data is refused + PASS symlinked leaf aimed at fake data is refused + PASS symlinked parent directory is refused + PASS symlinked parent aimed at fake state is refused + PASS destination resolving outside after parent resolution is refused + PASS existing hard-linked destination is refused without mutation + PASS unresolved destination parent is refused + PASS existing destination is refused without mutation + PASS symlinked evidence scope is refused + PASS unresolved evidence scope is refused + PASS unsafe destination leaf is refused + PASS legitimate in-scope publication remains exact +CONTAINMENT FIXTURES passed=13 failed=0 +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=absolute outcome=KILLED defender=- +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=traversal outcome=KILLED defender=- +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=exclusive-create-O_EXCL +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=symlink-parent outcome=KILLED defender=- +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=symlink-parent-state outcome=KILLED defender=- +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=resolved-outside outcome=KILLED defender=- +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=hard-link outcome=SURVIVED defender=exclusive-create-O_EXCL +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=natural-unresolved-parent +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=existing outcome=SURVIVED defender=exclusive-create-O_EXCL +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=scope-symlink outcome=KILLED defender=- +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=natural-unresolved-parent +PUBLISH CELL mutant=P001 anchor=resolved-path-validator-call substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=exclusive-create-O_EXCL +PUBLISH MUTANT P001 anchor=resolved-path-validator-call substitutions=1 killed=6 survived=6 void=0 +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=absolute outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=traversal outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=hard-link outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=existing outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=resolved-path-validator +PUBLISH CELL mutant=P002 anchor=exclusive-create-flag substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=resolved-path-validator +PUBLISH MUTANT P002 anchor=exclusive-create-flag substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=symlink-parent outcome=KILLED defender=- +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P003 anchor=symlink-component-precheck substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard +PUBLISH MUTANT P003 anchor=symlink-component-precheck substitutions=1 killed=1 survived=11 void=0 +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=final-structural-containment +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P004 anchor=canonical-structural-containment substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard +PUBLISH MUTANT P004 anchor=canonical-structural-containment substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P005 anchor=absolute-path-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard +PUBLISH MUTANT P005 anchor=absolute-path-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P006 anchor=unsafe-component-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard +PUBLISH MUTANT P006 anchor=unsafe-component-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=exclusive-create-O_EXCL +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P007 anchor=symlink-leaf-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard +PUBLISH MUTANT P007 anchor=symlink-leaf-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=exclusive-create-O_EXCL +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=existing outcome=SURVIVED defender=exclusive-create-O_EXCL +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P008 anchor=existing-leaf-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard +PUBLISH MUTANT P008 anchor=existing-leaf-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=natural-unresolved-parent +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P009 anchor=canonical-parent-resolution substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard +PUBLISH MUTANT P009 anchor=canonical-parent-resolution substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P010 anchor=final-structural-containment substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard +PUBLISH MUTANT P010 anchor=final-structural-containment substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P011 anchor=scope-symlink-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard +PUBLISH MUTANT P011 anchor=scope-symlink-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P012 anchor=canonical-scope-resolution substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=unsafe-leaf-guard +PUBLISH MUTANT P012 anchor=canonical-scope-resolution substitutions=1 killed=0 survived=12 void=0 +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=absolute outcome=SURVIVED defender=absolute-path-guard +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=traversal outcome=SURVIVED defender=unsafe-component-guard +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=symlink-leaf-data outcome=SURVIVED defender=symlink-leaf-guard +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=symlink-parent outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=symlink-parent-state outcome=SURVIVED defender=symlink-component-precheck +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=resolved-outside outcome=SURVIVED defender=canonical-structural-containment +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=hard-link outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=unresolved-parent outcome=SURVIVED defender=canonical-parent-resolution +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=existing outcome=SURVIVED defender=existing-leaf-guard +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=scope-symlink outcome=SURVIVED defender=scope-symlink-guard +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=scope-unresolved outcome=SURVIVED defender=canonical-scope-resolution +PUBLISH CELL mutant=P013 anchor=unsafe-leaf-guard substitutions=1 fixture=unsafe-leaf outcome=SURVIVED defender=existing-leaf-guard +PUBLISH MUTANT P013 anchor=unsafe-leaf-guard substitutions=1 killed=0 survived=12 void=0 +PUBLISH MATRIX SUMMARY mechanisms=14 written_boundaries=13 natural_boundaries=1 mutants=13 fixtures=12 cells=156 killed=7 survived=149 void=0 +``` diff --git a/tests/fm-sovereign-ledger-evidence-publish.mutation.sh b/tests/fm-sovereign-ledger-evidence-publish.mutation.sh index 066beefa26..d87c3fd2a8 100755 --- a/tests/fm-sovereign-ledger-evidence-publish.mutation.sh +++ b/tests/fm-sovereign-ledger-evidence-publish.mutation.sh @@ -135,8 +135,8 @@ publish_mutant P001 resolved-path-validator-call \ 'evidence_validate_destination "$scope" "$relative" "$destination" || return 1' \ ':' publish_mutant P002 exclusive-create-flag \ - 'O_WRONLY | O_CREAT | O_EXCL' \ - 'O_WRONLY | O_CREAT' + $'sysopen my $output, $destination, O_WRONLY | O_CREAT | O_EXCL, 0600\n or die "$destination: $!\\n";' \ + $'sysopen my $output, $destination, O_WRONLY | O_CREAT, 0600\n or die "$destination: $!\\n";' publish_mutant P003 symlink-component-precheck \ '[ ! -L "$cursor/$component" ]' \ 'true' diff --git a/tests/fm-sovereign-ledger-redundancy.mutation.sh b/tests/fm-sovereign-ledger-redundancy.mutation.sh index dc2760d5c1..f43ec718fc 100755 --- a/tests/fm-sovereign-ledger-redundancy.mutation.sh +++ b/tests/fm-sovereign-ledger-redundancy.mutation.sh @@ -10,30 +10,34 @@ TEST="$ROOT/tests/fm-sovereign-ledger-redundancy.test.sh" EVIDENCE= EMIT_EVIDENCE=0 VERIFY_EVIDENCE=0 +REFRESH_EVIDENCE=0 MODE=mutation EVIDENCE_FIXTURE= EVIDENCE_SWAP_PATH= EVIDENCE_SWAP_TARGET= if [ "${1:-}" = --emit-evidence ]; then - [ "$#" -eq 1 ] || { printf 'usage: %s [--emit-evidence|--verify-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } + [ "$#" -eq 1 ] || { printf 'usage: %s [--emit-evidence|--refresh-evidence|--verify-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } EMIT_EVIDENCE=1 exec 3>&1 exec >&2 +elif [ "${1:-}" = --refresh-evidence ]; then + [ "$#" -eq 1 ] || { printf 'usage: %s [--emit-evidence|--refresh-evidence|--verify-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } + REFRESH_EVIDENCE=1 elif [ "${1:-}" = --verify-evidence ]; then - [ "$#" -eq 1 ] || { printf 'usage: %s [--emit-evidence|--verify-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } + [ "$#" -eq 1 ] || { printf 'usage: %s [--emit-evidence|--refresh-evidence|--verify-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } VERIFY_EVIDENCE=1 elif [ "${1:-}" = --self-test-evidence-fixture ]; then - [ "$#" -eq 2 ] || { printf 'usage: %s [--emit-evidence|--verify-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } + [ "$#" -eq 2 ] || { printf 'usage: %s [--emit-evidence|--refresh-evidence|--verify-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } MODE=fixture EVIDENCE_FIXTURE=$2 elif [ "${1:-}" = --self-test-evidence-containment ]; then - [ "$#" -eq 1 ] || { printf 'usage: %s [--emit-evidence|--verify-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } + [ "$#" -eq 1 ] || { printf 'usage: %s [--emit-evidence|--refresh-evidence|--verify-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } MODE=containment elif [ "${1:-}" = --write-evidence ]; then - [ "$#" -eq 2 ] || { printf 'usage: %s [--emit-evidence|--verify-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } + [ "$#" -eq 2 ] || { printf 'usage: %s [--emit-evidence|--refresh-evidence|--verify-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2; exit 2; } EVIDENCE=$2 elif [ "$#" -ne 0 ]; then - printf 'usage: %s [--emit-evidence|--verify-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2 + printf 'usage: %s [--emit-evidence|--refresh-evidence|--verify-evidence|--write-evidence |--self-test-evidence-containment|--self-test-evidence-fixture ]\n' "$0" >&2 exit 2 fi @@ -436,17 +440,35 @@ EVIDENCE_PUBLICATION } > "$EVIDENCE_STAGE" printf 'MUTATION SUMMARY killed=%s survived=%s void=%s harness_broken=%s denominator=%s\n' "$killed" "$survived" "$void" "$harness_broken" "$denominator" +[ -s "$EVIDENCE_STAGE" ] || { printf 'REFUSED: generated mutation evidence is empty\n' >&2; exit 1; } +[ "$(wc -l < "$RESULTS" | tr -d '[:space:]')" -eq "$denominator" ] \ + || { printf 'REFUSED: mutation result rows do not match denominator\n' >&2; exit 1; } +awk -F '\t' 'NF != 6 || $3 != 1 || ($4 != "KILLED" && $4 != "SURVIVED") { exit 1 }' "$RESULTS" \ + || { printf 'REFUSED: every mutant must record one substitution and an individual outcome\n' >&2; exit 1; } [ "$void" -eq 0 ] [ "$harness_broken" -eq 0 ] [ "$control_substitutions" -eq 1 ] [ "$control_outcome" = SURVIVED ] -if [ -n "$EVIDENCE" ]; then +if [ "$REFRESH_EVIDENCE" -eq 1 ]; then + maintained="$ROOT/docs/verification/sovereign-ledger-redundancy-mutation.md" + install_stage=$(mktemp "$ROOT/docs/verification/.sovereign-ledger-redundancy-mutation.XXXXXX") \ + || { printf 'REFUSED: could not stage maintained mutation evidence\n' >&2; exit 1; } + cp "$EVIDENCE_STAGE" "$install_stage" \ + || { rm -f -- "$install_stage"; printf 'REFUSED: could not copy maintained mutation evidence\n' >&2; exit 1; } + [ -s "$install_stage" ] && cmp -s "$EVIDENCE_STAGE" "$install_stage" \ + || { rm -f -- "$install_stage"; printf 'REFUSED: staged maintained mutation evidence is empty or differs\n' >&2; exit 1; } + mv -f -- "$install_stage" "$maintained" \ + || { rm -f -- "$install_stage"; printf 'REFUSED: could not atomically install maintained mutation evidence\n' >&2; exit 1; } + [ -s "$maintained" ] || { printf 'REFUSED: installed maintained mutation evidence is empty\n' >&2; exit 1; } +elif [ -n "$EVIDENCE" ]; then publish_evidence "$ROOT/docs/verification" "$EVIDENCE" "$EVIDENCE_STAGE" elif [ "$EMIT_EVIDENCE" -eq 1 ]; then cat "$EVIDENCE_STAGE" >&3 elif [ "$VERIFY_EVIDENCE" -eq 1 ]; then - cmp -s "$EVIDENCE_STAGE" "$ROOT/docs/verification/sovereign-ledger-redundancy-mutation.md" || { - diff -u "$ROOT/docs/verification/sovereign-ledger-redundancy-mutation.md" "$EVIDENCE_STAGE" >&2 || true + maintained="$ROOT/docs/verification/sovereign-ledger-redundancy-mutation.md" + [ -s "$maintained" ] || { printf 'REFUSED: maintained mutation evidence is empty\n' >&2; exit 1; } + cmp -s "$EVIDENCE_STAGE" "$maintained" || { + diff -u "$maintained" "$EVIDENCE_STAGE" >&2 || true exit 1 } fi From c7f32071e9ecaa37890c97e2dc8110ad7af9521f Mon Sep 17 00:00:00 2001 From: 420tombombadil Date: Sun, 16 Aug 2026 07:04:32 -0600 Subject: [PATCH 17/19] no-mistakes(lint): Fix sovereign ledger ShellCheck warnings --- bin/fm-sovereign-ledger-redundancy.sh | 2 +- tests/fm-sovereign-ledger-redundancy.mutation.sh | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/bin/fm-sovereign-ledger-redundancy.sh b/bin/fm-sovereign-ledger-redundancy.sh index 02a1c903d7..97a1e43c3e 100755 --- a/bin/fm-sovereign-ledger-redundancy.sh +++ b/bin/fm-sovereign-ledger-redundancy.sh @@ -182,7 +182,7 @@ require_admitted_directory() { admit_independent_bundle_members() { local primary=$1 replica=$2 entry primary_entry replica_entry identity - local primary_identities= replica_identities= primary_identity replica_identity compared=0 + local primary_identities='' replica_identities='' primary_identity replica_identity compared=0 collect_bundle_entries "$primary" while IFS= read -r entry; do identity=$(portable_member_identity "$primary/$entry") \ diff --git a/tests/fm-sovereign-ledger-redundancy.mutation.sh b/tests/fm-sovereign-ledger-redundancy.mutation.sh index f43ec718fc..818cf2d994 100755 --- a/tests/fm-sovereign-ledger-redundancy.mutation.sh +++ b/tests/fm-sovereign-ledger-redundancy.mutation.sh @@ -455,8 +455,11 @@ if [ "$REFRESH_EVIDENCE" -eq 1 ]; then || { printf 'REFUSED: could not stage maintained mutation evidence\n' >&2; exit 1; } cp "$EVIDENCE_STAGE" "$install_stage" \ || { rm -f -- "$install_stage"; printf 'REFUSED: could not copy maintained mutation evidence\n' >&2; exit 1; } - [ -s "$install_stage" ] && cmp -s "$EVIDENCE_STAGE" "$install_stage" \ - || { rm -f -- "$install_stage"; printf 'REFUSED: staged maintained mutation evidence is empty or differs\n' >&2; exit 1; } + if ! [ -s "$install_stage" ] || ! cmp -s "$EVIDENCE_STAGE" "$install_stage"; then + rm -f -- "$install_stage" + printf 'REFUSED: staged maintained mutation evidence is empty or differs\n' >&2 + exit 1 + fi mv -f -- "$install_stage" "$maintained" \ || { rm -f -- "$install_stage"; printf 'REFUSED: could not atomically install maintained mutation evidence\n' >&2; exit 1; } [ -s "$maintained" ] || { printf 'REFUSED: installed maintained mutation evidence is empty\n' >&2; exit 1; } From b59f97fe8bd7218aa0c99493fc6ad83ef575b7dc Mon Sep 17 00:00:00 2001 From: 420tombombadil Date: Sun, 16 Aug 2026 07:11:16 -0600 Subject: [PATCH 18/19] test: record unavailable filesystem alias attacks --- tests/fm-sovereign-ledger-redundancy.test.sh | 68 +++++++++++++++++++- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/tests/fm-sovereign-ledger-redundancy.test.sh b/tests/fm-sovereign-ledger-redundancy.test.sh index bba756f082..192afbffa3 100755 --- a/tests/fm-sovereign-ledger-redundancy.test.sh +++ b/tests/fm-sovereign-ledger-redundancy.test.sh @@ -8,16 +8,44 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" TOOL=${TOOL:-"$ROOT/bin/fm-sovereign-ledger-redundancy.sh"} FIXTURE="$ROOT/tests/fixtures/sovereign-ledger-redundancy" TMP="$(mktemp -d)" -trap 'rm -rf "$TMP"' EXIT +bind_mount_active=0 +bind_mount_target= +directory_hardlink_active=0 +directory_hardlink_target= +cleanup() { + local cleanup_failed=0 + if [ "$bind_mount_active" -eq 1 ]; then + if ! umount "$bind_mount_target" >/dev/null 2>&1; then + printf ' FAIL cleanup could not detach fixture bind mount; retained scratch at %s\n' "$TMP" >&2 + cleanup_failed=1 + fi + fi + if [ "$directory_hardlink_active" -eq 1 ]; then + if ! unlink "$directory_hardlink_target" >/dev/null 2>&1; then + printf ' FAIL cleanup could not unlink fixture directory hard link; retained scratch at %s\n' "$TMP" >&2 + cleanup_failed=1 + fi + fi + if [ "$cleanup_failed" -eq 0 ]; then + rm -rf "$TMP" + fi + return "$cleanup_failed" +} +trap cleanup EXIT SAME_VOLUME_TOOL="$TMP/fm-sovereign-ledger-same-volume" export TOOL printf '%s\n' '#!/usr/bin/env bash' 'exec "$TOOL" --allow-same-volume-without-device-redundancy "$@"' > "$SAME_VOLUME_TOOL" chmod +x "$SAME_VOLUME_TOOL" pass=0 fail=0 +not_verifiable=0 ok() { printf ' PASS %s\n' "$1"; pass=$((pass + 1)); } bad() { printf ' FAIL %s\n' "$1"; fail=$((fail + 1)); } +not_verifiable() { + printf ' NOT_VERIFIABLE %s\n' "$1" + not_verifiable=$((not_verifiable + 1)) +} check_ok() { local description=$1 @@ -431,9 +459,43 @@ if cp -c "$R5_PRIMARY/CONTRACT.md" "$TMP/r5-clone-probe" 2>/dev/null; then done check_fails_with 'verify REFUSES an APFS clone bundle by the device predicate' 'primary and replica must be on different devices' "$TOOL" verify "$R5_PRIMARY" "$R5_CLONE" else - ok 'APFS clone attack is unavailable on this filesystem and is not simulated' + not_verifiable 'APFS clone attack: cp -c is unavailable on this filesystem; no nearby copy case substituted' +fi + +R5_BIND_TARGET="$TMP/r5-bind-target" +mkdir "$R5_BIND_TARGET" +bind_mount_target=$R5_BIND_TARGET +if mount --bind "$R5_PRIMARY" "$R5_BIND_TARGET" >/dev/null 2>&1 \ + || mount -t nullfs "$R5_PRIMARY" "$R5_BIND_TARGET" >/dev/null 2>&1; then + bind_mount_active=1 + check_fails_with 'verify REFUSES a bind-mounted replica by the admitted identity set' 'shares storage with primary' \ + "$TOOL" --allow-same-volume-without-device-redundancy verify "$R5_PRIMARY" "$R5_BIND_TARGET" + if umount "$R5_BIND_TARGET" >/dev/null 2>&1; then + bind_mount_active=0 + else + bad 'bind-mount fixture could not be detached; scratch will be retained without recursive deletion' + fi +else + not_verifiable 'bind-mount attack: this host grants no unprivileged bind or nullfs mount; no nearby mount case substituted' fi +R5_DIRECTORY_HARDLINK="$TMP/r5-directory-hardlink" +if ln "$R5_PRIMARY" "$R5_DIRECTORY_HARDLINK" >/dev/null 2>&1; then + directory_hardlink_active=1 + directory_hardlink_target=$R5_DIRECTORY_HARDLINK + check_fails_with 'verify REFUSES a directory hard link by the admitted directory identity' 'primary and replica directories must differ' \ + "$TOOL" --allow-same-volume-without-device-redundancy verify "$R5_PRIMARY" "$R5_DIRECTORY_HARDLINK" + if unlink "$R5_DIRECTORY_HARDLINK" >/dev/null 2>&1; then + directory_hardlink_active=0 + else + bad 'directory-hard-link fixture could not be unlinked; scratch will be retained without recursive deletion' + fi +else + not_verifiable 'directory-hard-link attack: this host refuses unprivileged directory hard-link creation; no symlink case substituted' +fi + +not_verifiable 'firmlink attack: this host exposes no unprivileged fixture-creation API for OS-managed firmlinks; no symlink case substituted' + R5_CASE="$TMP/r5-case" mkdir "$R5_CASE" for entry in ledger.tsv CONTRACT.md fm-sovereign-ledger.sh tests.sh; do @@ -625,5 +687,5 @@ else bad 'TERM deleted or replaced the created partial replica directory' fi -printf '\n%s passed, %s failed\n' "$pass" "$fail" +printf '\n%s passed, %s failed, %s not verifiable\n' "$pass" "$fail" "$not_verifiable" [ "$fail" -eq 0 ] From 7f76826c1092e94102db0e60d1e0c02354aea952 Mon Sep 17 00:00:00 2001 From: 420tombombadil Date: Sun, 16 Aug 2026 07:45:29 -0600 Subject: [PATCH 19/19] no-mistakes(review): Harden APFS cleanup and correct hard-link refusal --- .../fm-sovereign-ledger-cross-volume.test.sh | 75 +++++++++++++++++-- tests/fm-sovereign-ledger-redundancy.test.sh | 2 +- 2 files changed, 68 insertions(+), 9 deletions(-) diff --git a/tests/fm-sovereign-ledger-cross-volume.test.sh b/tests/fm-sovereign-ledger-cross-volume.test.sh index 689df37915..bc50fc6792 100755 --- a/tests/fm-sovereign-ledger-cross-volume.test.sh +++ b/tests/fm-sovereign-ledger-cross-volume.test.sh @@ -9,16 +9,75 @@ TMP="$(mktemp -d)" VOLUME_ROOT= ATTACHED_VOLUME=no -cleanup() { - if [ "$ATTACHED_VOLUME" = yes ]; then - hdiutil detach "$VOLUME_ROOT" >/dev/null 2>&1 || true +cleanup_fixture() { + local scratch=$1 volume_root=$2 attached=$3 hdiutil_command=$4 + if [ "$attached" = yes ]; then + if ! "$hdiutil_command" detach "$volume_root" >/dev/null 2>&1; then + printf 'FAIL could not detach fixture volume; retained scratch and image at %s\n' "$scratch" >&2 + return 1 + fi + attached=no fi - rm -rf -- "$TMP" - if [ -n "$VOLUME_ROOT" ] && [ "$ATTACHED_VOLUME" = no ]; then - rm -rf -- "$VOLUME_ROOT" + rm -rf -- "$scratch" + if [ -n "$volume_root" ] && [ "$attached" = no ] && [[ "$volume_root" != "$scratch"/* ]]; then + rm -rf -- "$volume_root" fi } -trap cleanup EXIT HUP INT TERM + +finish() { + local status=$? + if ! cleanup_fixture "$TMP" "$VOLUME_ROOT" "$ATTACHED_VOLUME" hdiutil; then + status=1 + fi + trap - EXIT HUP INT TERM + exit "$status" +} +trap finish EXIT +trap 'exit 129' HUP +trap 'exit 130' INT +trap 'exit 143' TERM + +FAKE_HDIUTIL="$TMP/fake-hdiutil" +printf '%s\n' \ + '#!/usr/bin/env bash' \ + "printf '%s\n' \"\$*\" >> \"\$CLEANUP_HDIUTIL_LOG\"" \ + "exit \"\$CLEANUP_HDIUTIL_STATUS\"" > "$FAKE_HDIUTIL" +chmod +x "$FAKE_HDIUTIL" +export CLEANUP_HDIUTIL_LOG CLEANUP_HDIUTIL_STATUS + +CLEANUP_FAILURE="$TMP/cleanup-failure" +CLEANUP_FAILURE_VOLUME="$CLEANUP_FAILURE/volume" +CLEANUP_FAILURE_LOG="$TMP/cleanup-failure.log" +CLEANUP_FAILURE_DIAGNOSTIC="$TMP/cleanup-failure.diagnostic" +mkdir -p "$CLEANUP_FAILURE_VOLUME/replica" +: > "$CLEANUP_FAILURE/ledger-volume.dmg" +: > "$CLEANUP_FAILURE_VOLUME/replica/ledger.tsv" +if CLEANUP_HDIUTIL_LOG="$CLEANUP_FAILURE_LOG" CLEANUP_HDIUTIL_STATUS=1 \ + cleanup_fixture "$CLEANUP_FAILURE" "$CLEANUP_FAILURE_VOLUME" yes "$FAKE_HDIUTIL" 2> "$CLEANUP_FAILURE_DIAGNOSTIC"; then + printf 'FAIL cleanup accepted a failed fixture-volume detach\n' >&2 + exit 1 +fi +[ "$(cat "$CLEANUP_FAILURE_LOG")" = "detach $CLEANUP_FAILURE_VOLUME" ] \ + && grep -Fq 'retained scratch and image' "$CLEANUP_FAILURE_DIAGNOSTIC" \ + && [ -f "$CLEANUP_FAILURE/ledger-volume.dmg" ] \ + && [ -f "$CLEANUP_FAILURE_VOLUME/replica/ledger.tsv" ] || { + printf 'FAIL failed detach did not retain the fixture scratch, image, and mounted-path contents\n' >&2 + exit 1 +} +rm -rf -- "$CLEANUP_FAILURE" + +CLEANUP_SUCCESS="$TMP/cleanup-success" +CLEANUP_SUCCESS_VOLUME="$CLEANUP_SUCCESS/volume" +CLEANUP_SUCCESS_LOG="$TMP/cleanup-success.log" +mkdir -p "$CLEANUP_SUCCESS_VOLUME/replica" +: > "$CLEANUP_SUCCESS/ledger-volume.dmg" +CLEANUP_HDIUTIL_LOG="$CLEANUP_SUCCESS_LOG" CLEANUP_HDIUTIL_STATUS=0 \ + cleanup_fixture "$CLEANUP_SUCCESS" "$CLEANUP_SUCCESS_VOLUME" yes "$FAKE_HDIUTIL" +[ "$(cat "$CLEANUP_SUCCESS_LOG")" = "detach $CLEANUP_SUCCESS_VOLUME" ] \ + && [ ! -e "$CLEANUP_SUCCESS" ] || { + printf 'FAIL successful detach did not remove the fixture scratch and image\n' >&2 + exit 1 +} device_identity() { local identity @@ -39,8 +98,8 @@ elif command -v hdiutil >/dev/null 2>&1; then VOLUME_ROOT="$TMP/ledger-volume" mkdir "$VOLUME_ROOT" hdiutil create -quiet -size 32m -fs APFS -volname fm-ledger-r5 "$TMP/ledger-volume.dmg" - hdiutil attach -quiet -nobrowse -mountpoint "$VOLUME_ROOT" "$TMP/ledger-volume.dmg" ATTACHED_VOLUME=yes + hdiutil attach -quiet -nobrowse -mountpoint "$VOLUME_ROOT" "$TMP/ledger-volume.dmg" else printf 'FAIL no writable separate volume is available for the strict acceptance proof\n' >&2 exit 1 diff --git a/tests/fm-sovereign-ledger-redundancy.test.sh b/tests/fm-sovereign-ledger-redundancy.test.sh index 192afbffa3..2a27583d16 100755 --- a/tests/fm-sovereign-ledger-redundancy.test.sh +++ b/tests/fm-sovereign-ledger-redundancy.test.sh @@ -483,7 +483,7 @@ R5_DIRECTORY_HARDLINK="$TMP/r5-directory-hardlink" if ln "$R5_PRIMARY" "$R5_DIRECTORY_HARDLINK" >/dev/null 2>&1; then directory_hardlink_active=1 directory_hardlink_target=$R5_DIRECTORY_HARDLINK - check_fails_with 'verify REFUSES a directory hard link by the admitted directory identity' 'primary and replica directories must differ' \ + check_fails_with 'verify REFUSES a directory hard link by the admitted member identity set' 'shares storage with primary' \ "$TOOL" --allow-same-volume-without-device-redundancy verify "$R5_PRIMARY" "$R5_DIRECTORY_HARDLINK" if unlink "$R5_DIRECTORY_HARDLINK" >/dev/null 2>&1; then directory_hardlink_active=0