const PIECE: usize = 8;
pub(crate) const WITHHELD: &str = "[withheld: it contained part of the credential sent]";
pub(crate) fn server_text(text: &str, secrets: &[&str], max_chars: usize) -> String {
let text: String = text.chars().filter(|c| !c.is_control()).collect();
if secrets.iter().any(|secret| holds_piece(&text, secret)) {
return WITHHELD.to_owned();
}
text.chars().take(max_chars).collect()
}
fn holds_piece(text: &str, secret: &str) -> bool {
let secret: Vec<char> = secret.chars().filter(|c| !c.is_control()).collect();
if secret.is_empty() {
return false;
}
let size = PIECE.min(secret.len());
secret
.windows(size)
.any(|piece| text.contains(&piece.iter().collect::<String>()))
}
#[cfg(test)]
mod tests {
use super::*;
const TOKEN: &str = "azt_0123456789abcdef0123456789abcdef";
#[test]
fn text_holding_any_piece_of_a_credential_is_withheld() {
let (head, tail) = TOKEN.split_at(12);
for text in [
format!("Bad request; Authorization: Bearer {TOKEN}"),
format!("Authorization: Bearer {head}\n{tail}"),
format!("{head}\u{1b}[0m{tail}"),
format!("{}{TOKEN}", "x".repeat(295)),
format!("{}{}", "x".repeat(299), &TOKEN[..8]),
format!("unknown field `{}`", &TOKEN[20..30]),
] {
assert_eq!(
server_text(&text, &[TOKEN], 300),
"[withheld: it contained part of the credential sent]",
"{text:?}"
);
}
}
#[test]
fn other_text_loses_control_characters_and_is_clipped() {
assert_eq!(
server_text("Unknown\u{7} schema.\n", &[TOKEN], 300),
"Unknown schema."
);
assert_eq!(server_text(&"y".repeat(500), &[TOKEN], 300).len(), 300);
assert_eq!(server_text(&TOKEN[..7], &[TOKEN], 300), &TOKEN[..7]);
assert_eq!(server_text("plain", &[], 300), "plain");
assert_eq!(
server_text("ab", &["ab"], 300),
"[withheld: it contained part of the credential sent]"
);
}
}