use std::fs;
use std::path::Path;
struct ZeroizeRow {
label: &'static str,
source_file: &'static str,
evidence: &'static [&'static str],
}
const ZEROIZE_ROWS: &[ZeroizeRow] = &[
ZeroizeRow {
label: "EncodeArgs::phrase consume + Zeroizing wrap at run() entry",
source_file: "src/cmd/encode.rs",
evidence: &["std::mem::take(&mut args.phrase).map(Zeroizing::new)"],
},
ZeroizeRow {
label: "EncodeArgs::hex consume + Zeroizing wrap at run() entry",
source_file: "src/cmd/encode.rs",
evidence: &["std::mem::take(&mut args.hex).map(Zeroizing::new)"],
},
ZeroizeRow {
label: "VerifyArgs::phrase consume + Zeroizing wrap at run() entry",
source_file: "src/cmd/verify.rs",
evidence: &["std::mem::take(&mut args.phrase).map(Zeroizing::new)"],
},
ZeroizeRow {
label: "parse::read_phrase_input returns Zeroizing<String>",
source_file: "src/parse.rs",
evidence: &["pub fn read_phrase_input(arg: Option<&str>) -> Result<Zeroizing<String>>"],
},
ZeroizeRow {
label: "parse::read_stdin raw buffer wrapped",
source_file: "src/parse.rs",
evidence: &["let mut buf: Zeroizing<String> = Zeroizing::new(String::new())"],
},
ZeroizeRow {
label: "cmd/encode::run locals (phrase / entropy) wrapped",
source_file: "src/cmd/encode.rs",
evidence: &["let (entropy, language_for_card): (Zeroizing<Vec<u8>>"],
},
ZeroizeRow {
label: "cmd/encode entropy buffer fed to Payload via wrapped clone",
source_file: "src/cmd/encode.rs",
evidence: &[
"Payload::Entr((*entropy_for_codec).clone())",
"Payload::Entr((*entropy).clone())",
],
},
ZeroizeRow {
label: "cmd/decode::run locals (entropy / phrase) wrapped",
source_file: "src/cmd/decode.rs",
evidence: &["let entropy: Zeroizing<Vec<u8>>"],
},
ZeroizeRow {
label: "cmd/verify::run locals (entropy / supplied / derived) wrapped",
source_file: "src/cmd/verify.rs",
evidence: &["let entropy: Zeroizing<Vec<u8>>"],
},
ZeroizeRow {
label: "cmd/verify emit_round_trip_ok word-count temp wrapped (slug #9)",
source_file: "src/cmd/verify.rs",
evidence: &["let wc_src: Zeroizing<String>"],
},
ZeroizeRow {
label: "cmd/inspect ms1 intake wrapped (slug #5)",
source_file: "src/cmd/inspect.rs",
evidence: &["Zeroizing::new(read_input("],
},
ZeroizeRow {
label: "cmd/repair ms1 intake wrapped (slug #6)",
source_file: "src/cmd/repair.rs",
evidence: &["Zeroizing::new(read_input("],
},
ZeroizeRow {
label: "cmd/repair RepairDetail chunk fields are Zeroizing<String> (slug #6)",
source_file: "src/cmd/repair.rs",
evidence: &["original_chunk: Zeroizing<String>"],
},
ZeroizeRow {
label: "cmd/derive ScrubbedXpriv scrubs master/account Xpriv on drop (wave2)",
source_file: "src/cmd/derive.rs",
evidence: &["struct ScrubbedXpriv", "non_secure_erase()"],
},
];
fn crate_root() -> &'static Path {
Path::new(".")
}
#[test]
fn canonical_list_has_expected_row_count() {
let n = ZEROIZE_ROWS.len();
assert_eq!(
n, 14,
"ZEROIZE_ROWS row count = {n}; expected 14 (survey §1 ms-cli table, post-R1 C-2 fold + cycle-15 Lane M: inspect-intake/repair-intake/repair-chunk-field rows, the verify row was re-pointed not added; + Wave-2 ms lane: the derive.rs ScrubbedXpriv derived-Xpriv scrub row)."
);
}
#[test]
fn repair_detail_does_not_derive_debug() {
let src =
fs::read_to_string(crate_root().join("src/cmd/repair.rs")).expect("read src/cmd/repair.rs");
assert!(
!src.contains("#[derive(Debug, Clone)]\nstruct RepairDetail")
&& !src.contains("#[derive(Debug, Clone)]\npub struct RepairDetail")
&& !src.contains("#[derive(Clone, Debug)]\nstruct RepairDetail"),
"RepairDetail still derives Debug — its Zeroizing<String> chunk fields would \
leak via the derived Debug (RULE Z-DEBUG). Drop the Debug derive (keep Clone)."
);
}
#[test]
fn every_canonical_zeroize_row_has_evidence_anchor() {
let mut missing: Vec<String> = Vec::new();
for row in ZEROIZE_ROWS {
let path = crate_root().join(row.source_file);
let source = fs::read_to_string(&path).unwrap_or_else(|e| {
panic!(
"failed to read evidence source {} for row {:?}: {e}",
path.display(),
row.label
)
});
let hit = row.evidence.iter().any(|needle| source.contains(needle));
if !hit {
missing.push(format!(
" - {} ({}): no evidence anchor; expected one of {:?}",
row.label, row.source_file, row.evidence,
));
}
}
assert!(
missing.is_empty(),
"ms-cli zeroize-discipline lint: {} row(s) missing Zeroizing evidence:\n{}",
missing.len(),
missing.join("\n"),
);
}