Harden Signal release evidence privacy

ober

534120b6b5de87890628d8e4d431d82578b8a502

diff --git a/.jerboa/security.json b/.jerboa/security.json
index 4aa9ee0..eee565b 100644
--- a/.jerboa/security.json
+++ b/.jerboa/security.json
@@ -17,7 +17,32 @@
     "network": { "requireTimeouts": true },
     "releaseEvidence": {
       "targetLiveProof": "marker-validated-fail-closed",
-      "signalCliAdvisoryReview": "version-mismatch-fail-closed"
+      "signalCliAdvisoryReview": "version-mismatch-fail-closed",
+      "privacy": "sanitized-host-neutral",
+      "sanitizer": "scripts/sanitize-evidence.sh",
+      "requiredResultMarkers": [
+        "release_evidence_status=pass",
+        "evidence_privacy_status=sanitized",
+        "private_path_scan_status=pass"
+      ],
+      "proofMaterialPolicy": {
+        "maxBytes": 65536,
+        "rejectBeforeCopy": true,
+        "forbiddenMaterial": [
+          "local-user-paths",
+          "ssh-urls",
+          "authorization-headers",
+          "signal-account-identifiers",
+          "phone-numbers",
+          "raw-listAccounts-output",
+          "raw-traces",
+          "raw-envelopes",
+          "message-bodies",
+          "attachments",
+          "tokens-or-passwords",
+          "private-keys"
+        ]
+      }
     },
     "eval": { "stringEval": "deny", "bareRead": "deny", "allowReadEval": false },
     "secrets": { "forbidLogging": true, "strictStateFilePermissions": true }
diff --git a/.jerbuild b/.jerbuild
index 27f0f4b..cbdb8cd 100644
--- a/.jerbuild
+++ b/.jerbuild
@@ -9,3 +9,6 @@
 (output "jerboa-signal")
 (libdirs "." "vendor/jsqlite/src")
 (static-native #t)
+(os-libs
+  (Darwin "-lm -lpthread -lncurses -liconv -lc++")
+  (Linux "-lm -ldl -lpthread -static -lncurses -lstdc++"))
diff --git a/Makefile b/Makefile
index bf0d5fe..128a58c 100644
--- a/Makefile
+++ b/Makefile
@@ -56,7 +56,7 @@ else \
 	printf '%s\n' signal-cli; \
 fi)
 
-.PHONY: all build binary run run-tui test security audit sbom reproducibility-report soak-evidence binary-smoke signal-cli-advisory-check verify release-evidence install install-log-shim clean help vendor-deps tui-shim log-shim ensure-jerboa-tools ensure-jerboa-native ensure-jsqlite
+.PHONY: all build binary run run-tui test security sanitize-evidence audit sbom reproducibility-report soak-evidence binary-smoke signal-cli-advisory-check verify release-evidence install install-log-shim clean help vendor-deps tui-shim log-shim ensure-jerboa-tools ensure-jerboa-native ensure-jsqlite
 .DEFAULT_GOAL := help
 
 all: binary
@@ -84,6 +84,9 @@ test: binary
 security:
 	sh scripts/security-check.sh
 
+sanitize-evidence:
+	sh scripts/sanitize-evidence.sh "$(DIST_DIR)"
+
 audit: ensure-jerboa-native
 	@mkdir -p "$(DIST_DIR)"
 	@if "$(SIGNAL_CLI)" --version > "$(DIST_DIR)/signal-cli-version.txt" 2>&1; then \
@@ -144,20 +147,22 @@ release-evidence: verify reproducibility-report sbom soak-evidence binary-smoke
 	@git status --short > "$(DIST_DIR)/git-status.txt"
 	@git rev-parse HEAD > "$(DIST_DIR)/git-commit.txt" 2>/dev/null || true
 	@{ \
-	  echo "date=$$(date -u +%Y-%m-%dT%H:%M:%SZ)"; \
-	  echo "uname=$$(uname -a)"; \
-	  echo "jerbuild=$(JERBUILD)"; \
-	  echo "jerboa_home=$(JH)"; \
+	  echo "repo=jerboa-signal"; \
+	  echo "generated_at_utc=$$(date -u +%Y-%m-%dT%H:%M:%SZ)"; \
+	  echo "platform=$$(uname -srm)"; \
+	  echo "jerbuild_status=present"; \
+	  echo "jerboa_home_status=present"; \
 	  "$(JERBUILD)" --version 2>/dev/null || true; \
 	} > "$(DIST_DIR)/build-environment.txt"
 	@if "$(SIGNAL_CLI)" --version > "$(DIST_DIR)/signal-cli-version.txt" 2>&1; then \
-	  echo "signal_cli=$(SIGNAL_CLI)" >> "$(DIST_DIR)/signal-cli-version.txt"; \
+	  echo "signal_cli_status=version-recorded" >> "$(DIST_DIR)/signal-cli-version.txt"; \
 	else \
-	  echo "signal-cli unavailable via SIGNAL_CLI=$(SIGNAL_CLI); live Signal smoke is a release blocker." > "$(DIST_DIR)/signal-cli-version.txt"; \
+	  echo "signal_cli_status=blocked-unavailable" > "$(DIST_DIR)/signal-cli-version.txt"; \
+	  echo "live_signal_smoke_status=release-blocker" >> "$(DIST_DIR)/signal-cli-version.txt"; \
 	fi
 	@find signal tests -type f \( -name '*.ss' -o -name '*.c' -o -name '*.h' \) -print | sort | xargs shasum -a 256 > "$(DIST_DIR)/source-sha256.txt"
 	@find vendor/jsqlite/src -type f -name '*.ss' -print | sort | xargs shasum -a 256 > "$(DIST_DIR)/jsqlite-sha256.txt"
-	@shasum -a 256 Makefile .jerbuild .jerboa/security.json SECURITY.md docs/threat-model.md docs/credential-handling.md docs/ffi-boundary.md docs/release-evidence.md docs/signal-cli-advisory-review.md scripts/security-check.sh scripts/sbom.sh scripts/reproducibility-report.sh scripts/soak-evidence.sh scripts/binary-smoke.sh scripts/signal-cli-advisory-check.sh > "$(DIST_DIR)/release-inputs-sha256.txt"
+	@shasum -a 256 Makefile .jerbuild .jerboa/security.json SECURITY.md docs/threat-model.md docs/credential-handling.md docs/ffi-boundary.md docs/release-evidence.md docs/signal-cli-advisory-review.md scripts/security-check.sh scripts/sanitize-evidence.sh scripts/sbom.sh scripts/reproducibility-report.sh scripts/soak-evidence.sh scripts/binary-smoke.sh scripts/signal-cli-advisory-check.sh > "$(DIST_DIR)/release-inputs-sha256.txt"
 	@shasum -a 256 "$(BIN)" > "$(DIST_DIR)/binary-sha256.txt"
 	@if command -v otool >/dev/null 2>&1; then otool -L "$(BIN)" > "$(DIST_DIR)/native-linkage.txt"; elif command -v ldd >/dev/null 2>&1; then ldd "$(BIN)" > "$(DIST_DIR)/native-linkage.txt"; else echo "no otool/ldd available" > "$(DIST_DIR)/native-linkage.txt"; fi
 	@if command -v cargo >/dev/null 2>&1 && [ -f "$(JERBOA_NATIVE_MANIFEST)" ]; then \
@@ -181,6 +186,22 @@ release-evidence: verify reproducibility-report sbom soak-evidence binary-smoke
 	  echo "current Signal live-smoke evidence failed; see $(DIST_DIR)/soak/status.txt"; \
 	  exit 1; \
 	fi
+	@set -eu; \
+	  code=0; \
+	  sh scripts/sanitize-evidence.sh "$(DIST_DIR)" >/dev/null 2>&1 || code=$$?; \
+	  { \
+	    echo "sanitize_evidence_exit_status=$$code"; \
+	    echo "sanitize_evidence_log_status=not-retained"; \
+	  } > "$(DIST_DIR)/sanitize-evidence.status"; \
+	  if [ "$$code" -ne 0 ]; then \
+	    { echo "release_evidence_status=blocked"; echo "blocked_reason=evidence-privacy-scan-failed"; echo "evidence_privacy_status=failed"; echo "private_path_scan_status=fail"; } > "$(DIST_DIR)/result.txt"; \
+	    exit "$$code"; \
+	  fi
+	@{ \
+	  echo "release_evidence_status=pass"; \
+	  echo "evidence_privacy_status=sanitized"; \
+	  echo "private_path_scan_status=pass"; \
+	} > "$(DIST_DIR)/result.txt"
 	@echo "release evidence written to $(DIST_DIR)"
 
 install: binary
@@ -288,6 +309,7 @@ help:
 	@echo "  run-tui             Build shim and start the TUI"
 	@echo "  test                Run tests"
 	@echo "  security            Run local security checks"
+	@echo "  sanitize-evidence   Sanitize ignored release evidence"
 	@echo "  audit               Record signal-cli/native dependency audit status"
 	@echo "  sbom                Write SBOM/provenance evidence under dist/sbom"
 	@echo "  reproducibility-report  Rebuild twice and compare release artifacts"
diff --git a/docs/release-evidence.md b/docs/release-evidence.md
index fe4a17e..25246dc 100644
--- a/docs/release-evidence.md
+++ b/docs/release-evidence.md
@@ -26,6 +26,15 @@ The target runs `make verify`, then writes ignored artifacts under
   storing Signal account identifiers, message contents, traces, or attachment
   paths.
 
+Before the final result is written, release evidence is sanitized for
+host-neutral review. Passing evidence must include:
+
+```text
+release_evidence_status=pass
+evidence_privacy_status=sanitized
+private_path_scan_status=pass
+```
+
 ## Evidence Files
 
 - `git-status.txt` and `git-commit.txt`
@@ -79,8 +88,9 @@ The proof file is marker-validated and must contain:
 - `signal_cli_update_window_status=within-reviewed-window`
 - `sensitive_artifact_policy=no-signal-account-state-or-message-data`
 
-Missing, empty, marker-incomplete, or sensitive-looking required proof fails
-closed with `status=blocked-review-proof`. Accepted proof is copied to
+Missing, empty, oversized, binary, marker-incomplete, or sensitive-looking
+required proof fails closed with `status=blocked-review-proof`. Sensitive
+material is rejected before copy. Accepted proof is copied to
 `signal-cli-advisory/signal-cli-review-proof.txt` with a SHA-256 sidecar.
 
 Release-host live-smoke proof can also be attached as a reviewed, redacted proof
@@ -102,9 +112,11 @@ The proof file is marker-validated and must contain:
 - `production_min_seconds_met=1`
 - `sensitive_artifact_policy=no-raw-account-traces-attachments-or-message-bodies`
 
-Missing, empty, or marker-incomplete required proofs fail closed with
-`status=blocked-target-proof`. Accepted proofs are copied to
-`soak/target-live-proof.txt` with a SHA-256 sidecar.
+Missing, empty, oversized, binary, sensitive-material-containing, or
+marker-incomplete required proofs fail closed with
+`status=blocked-target-proof`. Sensitive material is rejected before copy.
+Accepted proofs are copied to `soak/target-live-proof.txt` with a SHA-256
+sidecar.
 
 Do not publish `dist/release-evidence/` if it was produced from a working tree
 containing real Signal traces, message logs, attachments, or production account
diff --git a/scripts/reproducibility-report.sh b/scripts/reproducibility-report.sh
index 02fb968..b7bc898 100755
--- a/scripts/reproducibility-report.sh
+++ b/scripts/reproducibility-report.sh
@@ -3,6 +3,7 @@ set -eu
 
 ROOT=$(cd "$(dirname "$0")/.." && pwd)
 REPRO_DIR=${JSIGNAL_REPRO_DIR:-${REPRO_DIR:-dist/reproducibility}}
+SANITIZER="$ROOT/scripts/sanitize-evidence.sh"
 MAKE_CMD=${MAKE:-make}
 JERBUILD=${JERBUILD:-jerbuild}
 BIN=${BIN:-jerboa-signal}
@@ -108,14 +109,26 @@ build_once() {
 
 {
     echo "repo=jerboa-signal"
-    echo "jerbuild=$JERBUILD"
+    echo "platform=$(uname -srm)"
+    echo "jerbuild_status=present"
     "$JERBUILD" --version 2>/dev/null || true
-    printf 'jerboa_home='
-    "$JERBUILD" --jerboa-home 2>/dev/null || true
+    echo "jerboa_home_status=present"
     echo "bin=$BIN"
-    echo "jerboa_native_manifest=$JERBOA_NATIVE_MANIFEST"
-    echo "jerboa_native_staticlib=$JERBOA_NATIVE_A"
-    echo "jsqlite_src=$JSQLITE_SRC"
+    if [ -n "$JERBOA_NATIVE_MANIFEST" ] && [ -f "$JERBOA_NATIVE_MANIFEST" ]; then
+        echo "jerboa_native_manifest_status=present"
+    else
+        echo "jerboa_native_manifest_status=missing"
+    fi
+    if [ -n "$JERBOA_NATIVE_A" ] && [ -f "$JERBOA_NATIVE_A" ]; then
+        echo "jerboa_native_staticlib_status=present"
+    else
+        echo "jerboa_native_staticlib_status=missing"
+    fi
+    if [ -d "$JSQLITE_SRC" ]; then
+        echo "jsqlite_status=present"
+    else
+        echo "jsqlite_status=missing"
+    fi
 } > "$TMP_DIR/build-env.txt"
 
 source_manifest > "$TMP_DIR/source-before.sha256"
@@ -170,7 +183,7 @@ done
     echo "prewarm_status=present"
     echo "stabilization_build_used=1"
     echo "measurement_mode=post-warm"
-    echo "jerbuild_binary_obj_dir=$OBJ_DIR"
+    echo "jerbuild_binary_obj_dir_status=ephemeral"
     echo "jerbuild_binary_keep_obj_dir=1"
     echo "status=$status"
 } > "$TMP_DIR/result.txt"
@@ -189,5 +202,9 @@ rm -rf "$REPRO_DIR"
 mkdir -p "$REPRO_DIR"
 cp -R "$TMP_DIR"/. "$REPRO_DIR"/
 
+if [ -x "$SANITIZER" ]; then
+    sh "$SANITIZER" "$REPRO_DIR"
+fi
+
 cat "$REPRO_DIR/result.txt"
 [ "$status" = "match" ]
diff --git a/scripts/sanitize-evidence.sh b/scripts/sanitize-evidence.sh
new file mode 100755
index 0000000..7c56b8e
--- /dev/null
+++ b/scripts/sanitize-evidence.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+set -eu
+
+if [ "$#" -eq 0 ]; then
+  set -- dist/release-evidence
+fi
+
+sanitize_file() {
+  file=$1
+
+  if ! LC_ALL=C grep -Iq . "$file" 2>/dev/null; then
+    return 0
+  fi
+
+  perl -0pi -e '
+    s#/Users/[^[:space:]\x22\x27()]+#<local-path>#g;
+    s#/private/var/folders/[^[:space:]\x22\x27()]+#<local-temp-path>#g;
+    s#/var/folders/[^[:space:]\x22\x27()]+#<local-temp-path>#g;
+    if (defined $ENV{HOME} && length $ENV{HOME}) {
+      my $home = quotemeta($ENV{HOME});
+      s#$home#<home>#g;
+    }
+    s#~/mine#<local-path>#g;
+    s#git\@#git-at-#g;
+    s#ssh://[^[:space:]\x22\x27()]+#ssh://<redacted>#g;
+    s#Authorization:[^\x0a]*#Authorization: <redacted>#gi;
+    s#(^|[[:space:]])(\+[0-9][0-9 .()/-]{6,}[0-9])#${1}<redacted-phone>#g;
+    s#((signal[_-]?account|phone[_-]?number|recipient|sender|token|password|secret|passphrase|api[_-]?key|private[_-]?key|raw[_-]?(trace|envelope|message|attachment)|message[_-]?body|attachment[_-]?path|list[_-]?accounts[_-]?output)[[:space:]]*[:=][[:space:]]*)[^[:space:]\x0a]+#${1}<redacted>#gi;
+    s#(signal-cli[[:space:]][^\x0a]*listAccounts)[^\x0a]*#${1} <redacted>#gi;
+  ' "$file"
+}
+
+scan_for_private_material() {
+  evidence_dir=$1
+
+  if command -v rg >/dev/null 2>&1; then
+    if rg -n -S --hidden \
+      -e '/Users/' \
+      -e '/private/var/folders/' \
+      -e '/var/folders/' \
+      -e 'git@' \
+      -e 'ssh://[^[:space:]]+' \
+      -e '-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----' \
+      -e 'Authorization:[[:space:]]*[^<[:space:]]' \
+      -e '(^|[[:space:]])\+[0-9][0-9 .()/-]{6,}[0-9]' \
+      -e '(?i)(signal[_-]?account|phone[_-]?number|recipient|sender|token|password|secret|passphrase|api[_-]?key|private[_-]?key|raw[_-]?(trace|envelope|message|attachment)|message[_-]?body|attachment[_-]?path|list[_-]?accounts[_-]?output)[[:space:]]*[:=][[:space:]]*[^<[:space:]]' \
+      -e '(?i)signal-cli[[:space:]][^[:space:]]*listAccounts[[:space:]][^<[:space:]]' \
+      "$evidence_dir"; then
+      printf '[signal-sanitize] private or sensitive evidence material remains in %s\n' "$evidence_dir" >&2
+      return 1
+    fi
+  fi
+}
+
+for evidence_dir in "$@"; do
+  if [ ! -d "$evidence_dir" ]; then
+    continue
+  fi
+
+  find "$evidence_dir" -type f -print | while IFS= read -r file; do
+    sanitize_file "$file"
+  done
+  scan_for_private_material "$evidence_dir"
+done
diff --git a/scripts/sbom.sh b/scripts/sbom.sh
index fe3bf38..8b7e6f4 100755
--- a/scripts/sbom.sh
+++ b/scripts/sbom.sh
@@ -5,6 +5,7 @@ ROOT=$(cd "$(dirname "$0")/.." && pwd)
 cd "$ROOT"
 
 OUT=${JSIGNAL_SBOM_DIR:-${SBOM_DIR:-dist/sbom}}
+SANITIZER="$ROOT/scripts/sanitize-evidence.sh"
 JERBUILD=${JERBUILD:-jerbuild}
 BIN=${BIN:-jerboa-signal}
 JERBOA_NATIVE_MANIFEST=${JERBOA_NATIVE_MANIFEST:-}
@@ -63,6 +64,7 @@ write_release_inputs() {
         docs/release-evidence.md \
         docs/signal-cli-advisory-review.md \
         scripts/security-check.sh \
+        scripts/sanitize-evidence.sh \
         scripts/sbom.sh \
         scripts/reproducibility-report.sh \
         scripts/binary-smoke.sh \
@@ -80,9 +82,10 @@ signal_cli_status=blocked-unavailable
 if "$SIGNAL_CLI" --version > "$OUT/signal-cli-version.txt" 2>&1; then
     signal_cli_status=version-recorded
 else
-    cat > "$OUT/signal-cli-version.txt" <<LOG
-signal-cli unavailable via SIGNAL_CLI=$SIGNAL_CLI
-LOG
+    {
+        echo "signal_cli_status=blocked-unavailable"
+        echo "signal_cli_path_status=unavailable"
+    } > "$OUT/signal-cli-version.txt"
 fi
 
 jsqlite_status=missing
@@ -108,10 +111,10 @@ fi
     if [ -x "$JERBUILD" ] || command -v "$JERBUILD" >/dev/null 2>&1; then
         printf 'jerbuild_version='
         "$JERBUILD" --version 2>/dev/null || true
-        printf 'jerboa_home='
-        "$JERBUILD" --jerboa-home 2>/dev/null || true
+        echo "jerboa_home_status=present"
     else
         echo "jerbuild_status=missing"
+        echo "jerboa_home_status=missing"
     fi
     echo "external_runtime_helper=signal-cli"
     echo "signal_cli_status=$signal_cli_status"
@@ -119,13 +122,11 @@ fi
     echo "signal_cli_advisory_status=see-signal-cli-advisory"
     echo "signal_cli_security_advisory_status=no-published-github-advisories-as-of-2026-06-23"
     echo "upstream_security_policy_status=no-upstream-security-policy-as-of-2026-06-23"
-    echo "jsqlite_src=$JSQLITE_SRC"
     echo "jsqlite_status=$jsqlite_status"
     echo "native_dependency=jerboa-native-rs"
-    echo "jerboa_native_manifest=$JERBOA_NATIVE_MANIFEST"
     echo "jerboa_native_manifest_status=$native_manifest_status"
-    echo "jerboa_native_staticlib=$JERBOA_NATIVE_A"
     echo "jerboa_native_staticlib_status=$native_staticlib_status"
+    echo "evidence_privacy=sanitized-host-neutral"
     echo "sensitive_artifact_policy=no Signal account state, traces, attachments, message bodies, phone numbers, or raw listAccounts output"
     echo "target_live_proof_status=see-soak-evidence"
 } > "$OUT/manifest.txt"
@@ -158,3 +159,7 @@ fi
 if [ "$native_staticlib_status" = "present" ]; then
     sha256_file "$JERBOA_NATIVE_A" > "$OUT/jerboa-native-staticlib-sha256.txt"
 fi
+
+if [ -x "$SANITIZER" ]; then
+    sh "$SANITIZER" "$OUT"
+fi
diff --git a/scripts/security-check.sh b/scripts/security-check.sh
index 5fc5bb7..0749fa1 100755
--- a/scripts/security-check.sh
+++ b/scripts/security-check.sh
@@ -23,6 +23,7 @@ require_file docs/ffi-boundary.md
 require_file docs/release-evidence.md
 require_file docs/signal-cli-advisory-review.md
 require_file scripts/security-check.sh
+require_file scripts/sanitize-evidence.sh
 require_file scripts/sbom.sh
 require_file scripts/reproducibility-report.sh
 require_file scripts/binary-smoke.sh
@@ -43,6 +44,16 @@ grep -q 'blocked-version-review-required' scripts/signal-cli-advisory-check.sh 2
     note_fail "signal-cli advisory check must fail closed on unreviewed versions"
 grep -q 'no-published-github-advisories' docs/signal-cli-advisory-review.md 2>/dev/null || \
     note_fail "signal-cli advisory review must record upstream advisory posture"
+grep -q 'evidence_privacy_status=sanitized' Makefile 2>/dev/null || \
+    note_fail "release evidence must record sanitized privacy status"
+grep -q 'private_path_scan_status=pass' Makefile 2>/dev/null || \
+    note_fail "release evidence must record private path scan status"
+grep -q 'reject_sensitive_proof' scripts/soak-evidence.sh 2>/dev/null || \
+    note_fail "soak evidence must reject sensitive target proof material before copy"
+grep -q 'reject_sensitive_proof' scripts/signal-cli-advisory-check.sh 2>/dev/null || \
+    note_fail "signal-cli advisory proof must reject sensitive proof material before copy"
+grep -q 'proofMaterialPolicy' .jerboa/security.json 2>/dev/null || \
+    note_fail "security policy must record proof material policy"
 
 if grep -R -n -I '(chezscheme)' signal tests 2>/dev/null; then
     note_fail "direct (chezscheme) import remains in runtime/test source"
diff --git a/scripts/signal-cli-advisory-check.sh b/scripts/signal-cli-advisory-check.sh
index c2393d6..bb0f27a 100755
--- a/scripts/signal-cli-advisory-check.sh
+++ b/scripts/signal-cli-advisory-check.sh
@@ -13,6 +13,7 @@ REVIEW_DATE=${JSIGNAL_SIGNAL_CLI_REVIEW_DATE:-2026-06-23}
 REVIEWED_RELEASE_COMMIT=${JSIGNAL_SIGNAL_CLI_REVIEWED_RELEASE_COMMIT:-6bef205}
 REVIEW_PROOF_FILE=${JSIGNAL_SIGNAL_CLI_REVIEW_PROOF_FILE:-}
 REQUIRE_REVIEW_PROOF=${JSIGNAL_REQUIRE_SIGNAL_CLI_REVIEW_PROOF:-0}
+MAX_PROOF_BYTES=${JSIGNAL_MAX_PROOF_BYTES:-65536}
 
 rm -rf "$OUT"
 mkdir -p "$OUT"
@@ -43,9 +44,20 @@ require_marker() {
 
 reject_sensitive_proof() {
     file=$1
-    if grep -E -i \
-        '(BEGIN (RSA|OPENSSH|EC|DSA|PRIVATE) KEY|github_pat_|ghp_|sk-(ant-api03|proj|svcacct)-|AKIA[0-9A-Z]{16}|signal-cli[[:space:]].*listAccounts|raw[_ -]?(trace|envelope|message|attachment)|message[_ -]?body|phone[_ -]?number|[+][0-9][0-9][0-9][0-9][0-9])' \
-        "$file" >/dev/null 2>&1; then
+    proof_size=$(wc -c < "$file" | tr -d '[:space:]')
+
+    if [ "$proof_size" -gt "$MAX_PROOF_BYTES" ]; then
+        echo "[jerboa-signal-advisory] review proof is too large" >&2
+        return 1
+    fi
+
+    if ! LC_ALL=C grep -Iq . "$file" 2>/dev/null; then
+        echo "[jerboa-signal-advisory] review proof is not text" >&2
+        return 1
+    fi
+
+    sensitive_pattern='(/Users/|/private/var/folders/|/var/folders/|git@|ssh://|-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----|Authorization:[[:space:]]*[^[:space:]]|github_pat_|ghp_|sk-(ant-api03|proj|svcacct)-|AKIA[0-9A-Z]{16}|(^|[[:space:]])\+[0-9][0-9 .()/-]{6,}[0-9]|signal-cli[[:space:]].*listAccounts|raw[_ -]?(trace|envelope|message|attachment)|message[_ -]?body|phone[_ -]?number|(signal[_-]?account|phone[_-]?number|recipient|sender|token|password|secret|passphrase|api[_-]?key|private[_-]?key|raw[_-]?(trace|envelope|message|attachment)|message[_-]?body|attachment[_-]?path|list[_-]?accounts[_-]?output)[[:space:]]*[:=])'
+    if grep -I -E -i -q "$sensitive_pattern" "$file"; then
         echo "[jerboa-signal-advisory] review proof contains sensitive-looking material" >&2
         return 1
     fi
diff --git a/scripts/soak-evidence.sh b/scripts/soak-evidence.sh
index ec65bcc..9f74f17 100755
--- a/scripts/soak-evidence.sh
+++ b/scripts/soak-evidence.sh
@@ -10,6 +10,7 @@ RUN_LINKED=${JSIGNAL_RUN_LINKED_SMOKE:-0}
 PRODUCTION_MIN_SECONDS=${JSIGNAL_PRODUCTION_SMOKE_MIN_SECONDS:-3600}
 TARGET_LIVE_PROOF_FILE=${JSIGNAL_TARGET_LIVE_PROOF_FILE:-}
 REQUIRE_TARGET_LIVE_PROOF=${JSIGNAL_REQUIRE_TARGET_LIVE_PROOF:-0}
+MAX_PROOF_BYTES=${JSIGNAL_MAX_PROOF_BYTES:-65536}
 PLATFORM=$(uname -s)
 
 rm -rf "$OUT"
@@ -39,6 +40,24 @@ require_marker() {
     fi
 }
 
+reject_sensitive_proof() {
+    file=$1
+    proof_size=$(wc -c < "$file" | tr -d '[:space:]')
+
+    if [ "$proof_size" -gt "$MAX_PROOF_BYTES" ]; then
+        return 1
+    fi
+
+    if ! LC_ALL=C grep -Iq . "$file" 2>/dev/null; then
+        return 1
+    fi
+
+    sensitive_pattern='(/Users/|/private/var/folders/|/var/folders/|git@|ssh://|-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----|Authorization:[[:space:]]*[^[:space:]]|(^|[[:space:]])\+[0-9][0-9 .()/-]{6,}[0-9]|signal-cli[[:space:]].*listAccounts|(signal[_-]?account|phone[_-]?number|recipient|sender|token|password|secret|passphrase|api[_-]?key|private[_-]?key|raw[_-]?(trace|envelope|message|attachment)|message[_-]?body|attachment[_-]?path|list[_-]?accounts[_-]?output)[[:space:]]*[:=])'
+    if grep -I -E -i -q "$sensitive_pattern" "$file"; then
+        return 1
+    fi
+}
+
 record_proof() {
     proof=$1
     destination=$2
@@ -124,6 +143,11 @@ if [ "$target_live_proof_requested" = "1" ]; then
         exit 1
     fi
 
+    if ! reject_sensitive_proof "$TARGET_LIVE_PROOF_FILE"; then
+        write_blocked_target_proof sensitive-material "target live-smoke proof contains private or sensitive material"
+        exit 1
+    fi
+
     if ! require_marker "$TARGET_LIVE_PROOF_FILE" "linked_account_status=target-evidence-recorded" ||
        ! require_marker "$TARGET_LIVE_PROOF_FILE" "send_receive_status=target-evidence-recorded" ||
        ! require_marker "$TARGET_LIVE_PROOF_FILE" "trace_redaction_live_status=target-evidence-recorded" ||