review: a key split by an invisible character is still a secret

has_secret masked each character a character rule matches to a space, so one
zero-width space inside a key cut it into two runs too short to match. The
secret rules now also run on the text with those characters deleted, and the
text is a secret if either matches.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-28 14:12:12 -07:00
co-authored by Claude Opus 5.5
parent 3d266df9de
commit bd4cf9fb7f
2 changed files with 60 additions and 13 deletions
@@ -0,0 +1,44 @@
//! A key split by an invisible character is still a secret (review of M5c-1, finding 5): masking
//! the character to a space would cut the key into two short runs, so the text with it deleted is
//! checked too. Every key here is built from pieces so that no file holds one whole.
use memoryd::honcho::decide::has_secret;
fn mixed(n: usize) -> String {
"A1b2".chars().cycle().take(n).collect()
}
#[test]
fn an_invisible_character_inside_a_key_does_not_hide_it() {
let github = ["gh", "p_"].concat();
let sk = ["sk", "-proj-"].concat();
let aws = ["AK", "IA"].concat();
for mark in ["\u{200b}", "\u{7}", "\r", "\u{e000}", "\u{fdd0}", "\u{ad}"] {
let texts = [
format!("{github}{}{mark}{}", mixed(18), mixed(18)),
format!("{github}{mark}{}", mixed(36)),
format!("{}{mark}{}{}", &github[..2], &github[2..], mixed(36)),
format!("{sk}{}{mark}{}", mixed(20), mixed(20)),
format!("{aws}IOSF{mark}ODNN7EXAMPLE"),
format!(
"my token is {github}{}{mark}{}, keep it",
mixed(30),
mixed(6)
),
];
for text in &texts {
assert!(has_secret(text), "{text:?}");
}
}
}
#[test]
fn deleting_invisible_characters_finds_no_secret_in_plain_text() {
for text in [
"a zero width\u{200b}space",
"gh\u{200b}p_ is how GitHub tokens start",
"soft\u{ad}hyphen and sk-learn",
] {
assert!(!has_secret(text), "{text:?}");
}
}