pdbethke/warp, scoped to crates/secret_redaction, at 325d4d4701: the project's own tests caught 0 of 26 planted faults, 26 got through, and corral proved 26 of those missing tests by writing a test and running it.
#[cfg(test)]
mod corral_tests {
use super::super::*;
use regex::Regex;
#[test]
fn corral_first_user_pattern_is_user_at_enterprise_boundary() {
let enterprise = Regex::new("enterprise-secret").unwrap();
let user = Regex::new("user-secret").unwrap();
for enterprise_count in 0..=1 {
let patterns = if enterprise_count == 0 {
vec![user.as_str()]
} else {
vec![enterprise.as_str(), user.as_str()]
};
let secrets_regex = SecretsRegex {
regex: regex_automata::meta::Regex::new_many(&patterns).unwrap(),
dfas: regex_dfas::RegexDFAs::new_many(&patterns, false, true).unwrap(),
level_metadata: RegexLevelMetadata {
enterprise_count,
user_count: 1,
},
};
let matches =
find_secrets_in_text_with_levels_using_regex("!user-secret!", &secrets_regex);
assert_eq!(matches.len(), 1);
assert_eq!(matches[0].0.byte_range, 1..12);
assert_eq!(matches[0].0.char_range, 1..12);
assert_eq!(matches[0].1, SecretLevel::User);
if enterprise_count == 1 {
let matches = find_secrets_in_text_with_levels_using_regex(
"!enterprise-secret!",
&secrets_regex,
);
assert_eq!(matches.len(), 1);
assert_eq!(matches[0].1, SecretLevel::Enterprise);
}
}
}
#[test]
fn corral_secret_pattern_sources_preserve_enterprise_and_user_levels() {
let enterprise = Regex::new("enterprise-secret").unwrap();
let user = Regex::new("user-secret").unwrap();
let patterns = [enterprise.as_str(), user.as_str()];
let secrets_regex = SecretsRegex {
regex: regex_automata::meta::Regex::new_many(&patterns).unwrap(),
dfas: RegexDFAs::new_many(&patterns, false, true).unwrap(),
level_metadata: RegexLevelMetadata {
enterprise_count: 1,
user_count: 1,
},
};
let matches = find_secrets_in_text_with_levels_using_regex(
"enterprise-secret / user-secret",
&secrets_regex,
);
assert_eq!(matches.len(), 2);
assert_eq!(matches[0].0.byte_range, 0..17);
assert_eq!(matches[0].0.char_range, 0..17);
assert_eq!(matches[0].1, SecretLevel::Enterprise);
assert_eq!(matches[1].0.byte_range, 20..31);
assert_eq!(matches[1].0.char_range, 20..31);
assert_eq!(matches[1].1, SecretLevel::User);
}
#[test]
fn corral_secret_matches_preserve_exclusive_end_and_source_level() {
let enterprise = Regex::new("E").expect("Test regex did not compile");
let user = Regex::new("user-secret").expect("Test regex did not compile");
let patterns = [enterprise.as_str(), user.as_str()];
let secrets = SecretsRegex {
regex: regex_automata::meta::Regex::new_many(&patterns)
.expect("Test regex did not compile"),
dfas: RegexDFAs::new_many(&patterns, false, true)
.expect("Test regex DFA did not compile"),
level_metadata: RegexLevelMetadata {
enterprise_count: 1,
user_count: 1,
},
};
let text = "é[E] / user-secret!";
let matches = find_secrets_in_text_with_levels_using_regex(text, &secrets);
assert_eq!(matches.len(), 2);
for ((range, level), (secret, expected_level)) in matches.iter().zip([
("E", SecretLevel::Enterprise),
("user-secret", SecretLevel::User),
]) {
let start = text.find(secret).expect("Secret missing from fixture");
let end = start + secret.len();
assert_eq!(range.byte_range, start..end);
assert_eq!(
range.char_range,
text[..start].chars().count()..text[..end].chars().count()
);
assert_eq!(&text[range.byte_range.clone()], secret);
assert_eq!(*level, expected_level);
}
}
#[test]
fn corral_user_matches_survive_enterprise_metadata_boundary() {
let enterprise_pattern = Regex::new("ORG").expect("Test regex did not compile");
let user_pattern = Regex::new("USER").expect("Test regex did not compile");
let patterns = [enterprise_pattern.as_str(), user_pattern.as_str()];
let secrets_regex = SecretsRegex {
regex: regex_automata::meta::Regex::new_many(&patterns)
.expect("Test regex did not compile"),
dfas: RegexDFAs::new_many(&patterns, false, true)
.expect("Test regex DFA did not compile"),
level_metadata: RegexLevelMetadata {
enterprise_count: 1,
user_count: 1,
},
};
let matches =
find_secrets_in_text_with_levels_using_regex("é|ORG|猫|USER|終", &secrets_regex);
assert_eq!(matches.len(), 2);
assert_eq!(matches[0].0.byte_range, 3..6);
assert_eq!(matches[0].0.char_range, 2..5);
assert_eq!(matches[0].1, SecretLevel::Enterprise);
assert_eq!(matches[1].0.byte_range, 11..15);
assert_eq!(matches[1].0.char_range, 8..12);
assert_eq!(matches[1].1, SecretLevel::User);
}
#[test]
fn test_corral_secret_ranges_include_first_byte_after_unicode_prefix() {
let enterprise = Regex::new("TOKEN").expect("Test regex did not compile");
let user = Regex::new("x").expect("Test regex did not compile");
let patterns = [enterprise.as_str(), user.as_str()];
let secrets_regex = SecretsRegex {
regex: regex_automata::meta::Regex::new_many(&patterns)
.expect("Test regex did not compile"),
dfas: RegexDFAs::new_many(&patterns, false, true)
.expect("Test regex DFA did not compile"),
level_metadata: RegexLevelMetadata {
enterprise_count: 1,
user_count: 1,
},
};
let matches =
find_secrets_in_text_with_levels_using_regex("éTOKEN x終", &secrets_regex);
assert_eq!(matches.len(), 2);
assert_eq!(matches[0].0.byte_range, 2..7);
assert_eq!(matches[0].0.char_range, 1..6);
assert_eq!(matches[0].1, SecretLevel::Enterprise);
assert_eq!(matches[1].0.byte_range, 8..9);
assert_eq!(matches[1].0.char_range, 7..8);
assert_eq!(matches[1].1, SecretLevel::User);
}
#[test]
fn corral_adjacent_secret_ranges_merge_with_enterprise_priority() {
for (first_level, second_level) in [
(SecretLevel::User, SecretLevel::Enterprise),
(SecretLevel::Enterprise, SecretLevel::User),
] {
// Byte and character offsets correspond to "é猫 x".
let merged = merge_sorted_ranges_with_levels(vec![
(
StringRange {
char_range: 0..1,
byte_range: 0..2,
},
first_level,
),
(
StringRange {
char_range: 1..2,
byte_range: 2..5,
},
second_level,
),
(
StringRange {
char_range: 3..4,
byte_range: 6..7,
},
SecretLevel::User,
),
]);
assert_eq!(merged.len(), 2);
assert_eq!(merged[0].0.char_range, 0..2);
assert_eq!(merged[0].0.byte_range, 0..5);
assert_eq!(merged[0].1, SecretLevel::Enterprise);
assert_eq!(merged[1].0.char_range, 3..4);
assert_eq!(merged[1].0.byte_range, 6..7);
assert_eq!(merged[1].1, SecretLevel::User);
}
}
#[test]
fn corral_merge_preserves_enterprise_priority_in_both_orders() {
for (first_level, second_level) in [
(SecretLevel::User, SecretLevel::Enterprise),
(SecretLevel::Enterprise, SecretLevel::User),
] {
for second_start in [2, 4] {
let merged = merge_sorted_ranges_with_levels(vec![
(
StringRange {
char_range: 0..4,
byte_range: 0..8,
},
first_level,
),
(
StringRange {
char_range: second_start..6,
byte_range: second_start * 2..12,
},
second_level,
),
(
StringRange {
char_range: 7..8,
byte_range: 14..16,
},
SecretLevel::User,
),
]);
assert_eq!(merged.len(), 2);
assert_eq!(merged[0].0.char_range, 0..6);
assert_eq!(merged[0].0.byte_range, 0..12);
assert_eq!(merged[0].1, SecretLevel::Enterprise);
assert_eq!(merged[1].0.char_range, 7..8);
assert_eq!(merged[1].0.byte_range, 14..16);
assert_eq!(merged[1].1, SecretLevel::User);
}
}
}
#[test]
fn test_corral_merge_preserves_single_final_range() {
let merged = merge_sorted_ranges_with_levels(vec![(
StringRange {
char_range: 2..5,
byte_range: 3..9,
},
SecretLevel::Enterprise,
)]);
assert_eq!(merged.len(), 1);
assert_eq!(merged[0].0.char_range, 2..5);
assert_eq!(merged[0].0.byte_range, 3..9);
assert_eq!(merged[0].1, SecretLevel::Enterprise);
}
#[test]
fn test_corral_merge_preserves_final_adjacent_group_and_priority() {
let merged = merge_sorted_ranges_with_levels(vec![
(
StringRange {
char_range: 0..2,
byte_range: 0..2,
},
SecretLevel::User,
),
(
StringRange {
char_range: 4..7,
byte_range: 4..7,
},
SecretLevel::User,
),
(
StringRange {
char_range: 6..8,
byte_range: 6..8,
},
SecretLevel::Enterprise,
),
(
StringRange {
char_range: 8..10,
byte_range: 8..10,
},
SecretLevel::User,
),
]);
assert_eq!(merged.len(), 2);
assert_eq!(merged[0].0.char_range, 0..2);
assert_eq!(merged[0].0.byte_range, 0..2);
assert_eq!(merged[0].1, SecretLevel::User);
assert_eq!(merged[1].0.char_range, 4..10);
assert_eq!(merged[1].0.byte_range, 4..10);
assert_eq!(merged[1].1, SecretLevel::Enterprise);
}
#[test]
fn corral_disjoint_ranges_preserve_each_source_level() {
let ranges = vec![
(
StringRange {
char_range: 0..2,
byte_range: 0..4,
},
SecretLevel::Enterprise,
),
(
StringRange {
char_range: 3..5,
byte_range: 5..7,
},
SecretLevel::User,
),
(
StringRange {
char_range: 6..8,
byte_range: 8..10,
},
SecretLevel::Enterprise,
),
];
let merged = merge_sorted_ranges_with_levels(ranges);
assert_eq!(merged.len(), 3);
assert_eq!(merged[0].0.char_range, 0..2);
assert_eq!(merged[0].0.byte_range, 0..4);
assert_eq!(merged[0].1, SecretLevel::Enterprise);
assert_eq!(merged[1].0.char_range, 3..5);
assert_eq!(merged[1].0.byte_range, 5..7);
assert_eq!(merged[1].1, SecretLevel::User);
assert_eq!(merged[2].0.char_range, 6..8);
assert_eq!(merged[2].0.byte_range, 8..10);
assert_eq!(merged[2].1, SecretLevel::Enterprise);
}
#[test]
fn test_corral_merged_ranges_extend_both_offsets_and_preserve_enterprise_priority() {
let merged = merge_sorted_ranges_with_levels(vec![
(
StringRange {
char_range: 1..3,
byte_range: 1..6,
},
SecretLevel::User,
),
(
StringRange {
char_range: 2..4,
byte_range: 3..10,
},
SecretLevel::Enterprise,
),
(
StringRange {
char_range: 4..6,
byte_range: 10..13,
},
SecretLevel::User,
),
(
StringRange {
char_range: 7..8,
byte_range: 14..15,
},
SecretLevel::User,
),
]);
assert_eq!(merged.len(), 2);
assert_eq!(merged[0].0.char_range, 1..6);
assert_eq!(merged[0].0.byte_range, 1..13);
assert_eq!(merged[0].1, SecretLevel::Enterprise);
assert_eq!(merged[1].0.char_range, 7..8);
assert_eq!(merged[1].0.byte_range, 14..15);
assert_eq!(merged[1].1, SecretLevel::User);
}
#[test]
fn corral_pattern_order_preserves_enterprise_and_user_levels() {
let enterprise = Regex::new("enterprise-secret").expect("Test regex did not compile");
let user = Regex::new("user-secret").expect("Test regex did not compile");
let previous = SECRETS_REGEX.lock().clone();
set_user_and_enterprise_secret_regexes([&user], [&enterprise]);
let configured = {
let mut current = SECRETS_REGEX.lock();
let configured = current.clone();
*current = previous;
configured
};
let matches = find_secrets_in_text_with_levels_using_regex(
"enterprise-secret user-secret",
&configured,
);
assert_eq!(matches.len(), 2);
assert_eq!(matches[0].0.byte_range, 0..17);
assert_eq!(matches[0].0.char_range, 0..17);
assert_eq!(matches[0].1, SecretLevel::Enterprise);
assert_eq!(matches[1].0.byte_range, 18..29);
assert_eq!(matches[1].0.char_range, 18..29);
assert_eq!(matches[1].1, SecretLevel::User);
}
#[test]
fn test_corral_unequal_pattern_counts_preserve_secret_sources() {
let enterprise_patterns = [
Regex::new("corral_enterprise_alpha").unwrap(),
Regex::new("corral_enterprise_beta").unwrap(),
];
let user_patterns = [Regex::new("corral_user_gamma").unwrap()];
let previous = SECRETS_REGEX.lock().clone();
set_user_and_enterprise_secret_regexes(user_patterns.iter(), enterprise_patterns.iter());
let configured = {
let mut shared = SECRETS_REGEX.lock();
let configured = shared.clone();
*shared = previous;
configured
};
let text = "corral_enterprise_alpha corral_enterprise_beta corral_user_gamma";
let found = find_secrets_in_text_with_levels_using_regex(text, &configured);
assert_eq!(found.len(), 3);
for ((range, level), (expected_text, expected_level)) in found.iter().zip([
("corral_enterprise_alpha", SecretLevel::Enterprise),
("corral_enterprise_beta", SecretLevel::Enterprise),
("corral_user_gamma", SecretLevel::User),
]) {
assert_eq!(&text[range.byte_range.clone()], expected_text);
assert_eq!(*level, expected_level);
}
}
#[test]
fn test_corral_unique_user_patterns_survive_enterprise_deduplication() {
const CHILD_ENV: &str = "CORRAL_SECRET_REDACTION_DEDUP_CHILD";
if std::env::var_os(CHILD_ENV).is_none() {
let module = module_path!();
let test_module = module.split_once("::").expect("test module path").1;
let test_name = format!(
"{}::test_corral_unique_user_patterns_survive_enterprise_deduplication",
test_module
);
let status = std::process::Command::new(
std::env::current_exe().expect("test executable"),
)
.args(["--exact", &test_name, "--nocapture"])
.env(CHILD_ENV, "1")
.status()
.expect("run isolated secret configuration test");
assert!(status.success());
return;
}
let user = Regex::new("corral_user_secret").expect("user regex");
let enterprise = Regex::new("corral_enterprise_secret").expect("enterprise regex");
set_user_and_enterprise_secret_regexes([&user, &enterprise], [&enterprise]);
let compiled = SECRETS_REGEX.lock().clone();
assert_eq!(compiled.level_metadata.enterprise_count, 1);
assert_eq!(compiled.level_metadata.user_count, 1);
let text = "é corral_user_secret | corral_enterprise_secret 終";
let matches = find_secrets_in_text_with_levels_using_regex(text, &compiled);
assert_eq!(matches.len(), 2);
assert_eq!(matches[0].1, SecretLevel::User);
assert_eq!(matches[1].1, SecretLevel::Enterprise);
assert_eq!(&text[matches[0].0.byte_range.clone()], user.as_str());
assert_eq!(
&text[matches[1].0.byte_range.clone()],
enterprise.as_str()
);
let mut redacted = text.to_owned();
redact_secrets(&mut redacted);
assert_eq!(
redacted,
format!(
"é {} | {} 終",
"*".repeat(user.as_str().len()),
"*".repeat(enterprise.as_str().len())
)
);
assert_eq!(redacted.len(), text.len());
}
#[test]
fn corral_enterprise_count_preserves_single_enterprise_and_user_levels() {
let enterprise = Regex::new(r"CORRAL_ENTERPRISE_SECRET").unwrap();
let user = Regex::new(r"CORRAL_USER_SECRET").unwrap();
let previous = SECRETS_REGEX.lock().clone();
set_user_and_enterprise_secret_regexes([&user], [&enterprise]);
let configured = {
let mut current = SECRETS_REGEX.lock();
std::mem::replace(&mut *current, previous)
};
assert_eq!(configured.level_metadata.enterprise_count, 1);
assert_eq!(configured.level_metadata.user_count, 1);
let text = "CORRAL_ENTERPRISE_SECRET | CORRAL_USER_SECRET";
let matches = find_secrets_in_text_with_levels_using_regex(text, &configured);
assert_eq!(matches.len(), 2);
assert_eq!(matches[0].1, SecretLevel::Enterprise);
assert_eq!(matches[0].0.byte_range, 0..enterprise.as_str().len());
assert_eq!(matches[0].0.char_range, 0..enterprise.as_str().len());
let user_start = enterprise.as_str().len() + 3;
assert_eq!(matches[1].1, SecretLevel::User);
assert_eq!(matches[1].0.byte_range, user_start..text.len());
assert_eq!(matches[1].0.char_range, user_start..text.len());
}
#[test]
fn test_corral_first_enterprise_pattern_is_retained_and_levels_stay_aligned() {
let enterprise = Regex::new("CORRAL_ENTERPRISE_SECRET").unwrap();
let user = Regex::new("CORRAL_USER_SECRET").unwrap();
let previous = SECRETS_REGEX.lock().clone();
set_user_and_enterprise_secret_regexes([&user], [&enterprise]);
let configured = {
let mut current = SECRETS_REGEX.lock();
let configured = current.clone();
*current = previous;
configured
};
let text = "CORRAL_ENTERPRISE_SECRET|CORRAL_USER_SECRET";
let matches = find_secrets_in_text_with_levels_using_regex(text, &configured);
assert_eq!(matches.len(), 2);
assert_eq!(matches[0].0.byte_range, 0..enterprise.as_str().len());
assert_eq!(matches[0].0.char_range, 0..enterprise.as_str().len());
assert_eq!(matches[0].1, SecretLevel::Enterprise);
let user_start = enterprise.as_str().len() + 1;
assert_eq!(matches[1].0.byte_range, user_start..text.len());
assert_eq!(matches[1].0.char_range, user_start..text.len());
assert_eq!(matches[1].1, SecretLevel::User);
}
#[test]
fn corral_enterprise_priority_survives_overlapping_and_adjacent_user_ranges() {
for (first_level, second_level) in [
(SecretLevel::User, SecretLevel::Enterprise),
(SecretLevel::Enterprise, SecretLevel::User),
] {
for second_start in [2, 4] {
let merged = merge_sorted_ranges_with_levels(vec![
(
StringRange {
char_range: 1..4,
byte_range: 1..4,
},
first_level,
),
(
StringRange {
char_range: second_start..7,
byte_range: second_start..7,
},
second_level,
),
]);
assert_eq!(merged.len(), 1);
assert_eq!(merged[0].0.char_range, 1..7);
assert_eq!(merged[0].0.byte_range, 1..7);
assert_eq!(merged[0].1, SecretLevel::Enterprise);
}
}
}
#[test]
fn corral_enterprise_priority_wins_when_merging_into_user_range() {
for enterprise_start in [2, 4] {
let merged = merge_sorted_ranges_with_levels(vec![
(
StringRange {
char_range: 0..4,
byte_range: 0..4,
},
SecretLevel::User,
),
(
StringRange {
char_range: enterprise_start..8,
byte_range: enterprise_start..8,
},
SecretLevel::Enterprise,
),
]);
assert_eq!(merged.len(), 1);
assert_eq!(merged[0].0.char_range, 0..8);
assert_eq!(merged[0].0.byte_range, 0..8);
assert_eq!(merged[0].1, SecretLevel::Enterprise);
}
}
#[test]
fn test_corral_enterprise_priority_survives_user_overlap_and_adjacency() {
for (first_level, second_level) in [
(SecretLevel::User, SecretLevel::Enterprise),
(SecretLevel::Enterprise, SecretLevel::User),
] {
for second_start in [2, 4] {
let merged = merge_sorted_ranges_with_levels(vec![
(
StringRange {
char_range: 0..4,
byte_range: 0..8,
},
first_level,
),
(
StringRange {
char_range: second_start..6,
byte_range: (second_start * 2)..12,
},
second_level,
),
]);
assert_eq!(merged.len(), 1);
assert_eq!(merged[0].0.char_range, 0..6);
assert_eq!(merged[0].0.byte_range, 0..12);
assert_eq!(merged[0].1, SecretLevel::Enterprise);
}
}
}
#[test]
fn corral_redaction_replaces_every_secret_byte_without_shortening() {
struct CorralRestoreSecrets(std::sync::Arc<SecretsRegex>);
impl Drop for CorralRestoreSecrets {
fn drop(&mut self) {
*SECRETS_REGEX.lock() = self.0.clone();
}
}
let _restore = CorralRestoreSecrets(SECRETS_REGEX.lock().clone());
let user_patterns = [Regex::new("é密|X").expect("Test regex did not compile")];
let enterprise_patterns = [Regex::new("TOKEN").expect("Test regex did not compile")];
set_user_and_enterprise_secret_regexes(
user_patterns.iter(),
enterprise_patterns.iter(),
);
let original = "🙂prefix[X] middle[é密] enterprise[TOKEN]suffix終";
let mut redacted = original.to_owned();
redact_secrets(&mut redacted);
assert_eq!(
redacted,
"🙂prefix[*] middle[*****] enterprise[*****]suffix終"
);
assert_eq!(redacted.len(), original.len());
}
#[test]
fn corral_redaction_covers_last_secret_and_preserves_surrounding_bytes() {
let pattern = Regex::new("密钥|終").expect("Test regex did not compile");
let previous_regex = SECRETS_REGEX.lock().clone();
set_user_and_enterprise_secret_regexes(
std::iter::once(&pattern),
std::iter::empty::<&Regex>(),
);
let original = "π:密钥 / visible / token:終!";
let mut redacted = original.to_string();
redact_secrets(&mut redacted);
*SECRETS_REGEX.lock() = previous_regex;
assert_eq!(redacted, "π:****** / visible / token:***!");
assert_eq!(redacted.len(), original.len());
}
struct CorralSecretRegexRestore(std::sync::Arc<SecretsRegex>);
impl Drop for CorralSecretRegexRestore {
fn drop(&mut self) {
*SECRETS_REGEX.lock() = self.0.clone();
}
}
#[test]
fn test_corral_first_secret_range_is_preserved_and_redacted() {
let _restore = CorralSecretRegexRestore(SECRETS_REGEX.lock().clone());
let pattern = Regex::new("CORRAL_SECRET").expect("Test regex did not compile");
set_user_and_enterprise_secret_regexes(
std::iter::once(&pattern),
std::iter::empty::<&Regex>(),
);
let original = "é[CORRAL_SECRET]安全[CORRAL_SECRET]!";
let ranges = find_secrets_in_text(original);
assert_eq!(ranges.len(), 2);
assert_eq!(ranges[0].byte_range, 3..16);
assert_eq!(ranges[0].char_range, 2..15);
assert_eq!(ranges[1].byte_range, 24..37);
assert_eq!(ranges[1].char_range, 19..32);
let mut redacted = original.to_owned();
redact_secrets(&mut redacted);
assert_eq!(redacted, "é[*************]安全[*************]!");
assert_eq!(redacted.len(), original.len());
}
#[test]
fn corral_enterprise_levels_survive_public_text_lookup() {
let enterprise_text = "CORRAL_ENTERPRISE_SECRET";
let user_text = "CORRAL_USER_SECRET";
let enterprise = Regex::new(enterprise_text).unwrap();
let user = Regex::new(user_text).unwrap();
let previous = SECRETS_REGEX.lock().clone();
set_user_and_enterprise_secret_regexes([&user], [&enterprise]);
let text = format!("{}|{}", enterprise_text, user_text);
let matches = find_secrets_in_text_with_levels(&text);
*SECRETS_REGEX.lock() = previous;
let user_start = enterprise_text.len() + 1;
assert_eq!(matches.len(), 2);
assert_eq!(matches[0].0.byte_range, 0..enterprise_text.len());
assert_eq!(matches[0].0.char_range, 0..enterprise_text.len());
assert_eq!(matches[0].1, SecretLevel::Enterprise);
assert_eq!(matches[1].0.byte_range, user_start..text.len());
assert_eq!(matches[1].0.char_range, user_start..text.len());
assert_eq!(matches[1].1, SecretLevel::User);
}
#[test]
fn corral_public_secret_scan_preserves_last_match_and_redacts_every_byte() {
struct CorralRestoreSecrets(std::sync::Arc<SecretsRegex>);
impl Drop for CorralRestoreSecrets {
fn drop(&mut self) {
*SECRETS_REGEX.lock() = self.0.clone();
}
}
let _restore = CorralRestoreSecrets(SECRETS_REGEX.lock().clone());
let enterprise = Regex::new("éCORRAL_ENTERPRISE").expect("Test regex did not compile");
let user = Regex::new("CORRAL_USER🔑").expect("Test regex did not compile");
set_user_and_enterprise_secret_regexes([&user], [&enterprise]);
let input = "前|éCORRAL_ENTERPRISE|中|CORRAL_USER🔑|後";
let found = find_secrets_in_text_with_levels(input);
assert_eq!(found.len(), 2);
for ((range, level), (secret, expected_level)) in found.iter().zip([
("éCORRAL_ENTERPRISE", SecretLevel::Enterprise),
("CORRAL_USER🔑", SecretLevel::User),
]) {
let start = input.find(secret).expect("Secret missing from fixture");
assert_eq!(range.byte_range, start..start + secret.len());
let char_start = input[..start].chars().count();
assert_eq!(
range.char_range,
char_start..char_start + secret.chars().count()
);
assert_eq!(*level, expected_level);
}
let mut redacted = input.to_owned();
redact_secrets(&mut redacted);
let expected = format!(
"前|{}|中|{}|後",
"*".repeat("éCORRAL_ENTERPRISE".len()),
"*".repeat("CORRAL_USER🔑".len())
);
assert_eq!(redacted.len(), input.len());
assert_eq!(redacted, expected);
}
#[test]
fn corral_user_secrets_survive_public_lookup_and_are_redacted() {
struct CorralRestoreSecrets(std::sync::Arc<SecretsRegex>);
impl Drop for CorralRestoreSecrets {
fn drop(&mut self) {
*SECRETS_REGEX.lock() = self.0.clone();
}
}
let _restore = CorralRestoreSecrets(SECRETS_REGEX.lock().clone());
let user = Regex::new("user-key").expect("Test regex did not compile");
let enterprise = Regex::new("org-key").expect("Test regex did not compile");
set_user_and_enterprise_secret_regexes([&user], [&enterprise]);
let original = "é user-key | org-key 🦀";
let matches = find_secrets_in_text_with_levels(original);
assert_eq!(matches.len(), 2);
assert_eq!(matches[0].0.char_range, 2..10);
assert_eq!(matches[0].0.byte_range, 3..11);
assert_eq!(matches[0].1, SecretLevel::User);
assert_eq!(matches[1].0.char_range, 13..20);
assert_eq!(matches[1].0.byte_range, 14..21);
assert_eq!(matches[1].1, SecretLevel::Enterprise);
let mut redacted = original.to_owned();
redact_secrets(&mut redacted);
assert_eq!(redacted, "é ******** | ******* 🦀");
assert_eq!(redacted.len(), original.len());
}
#[test]
fn corral_secret_level_enterprise_classification_matches_source() {
assert!(SecretLevel::Enterprise.is_enterprise());
assert!(!SecretLevel::User.is_enterprise());
}
#[test]
fn corral_secret_level_user_classification_matches_source() {
assert!(SecretLevel::User.is_user());
assert!(!SecretLevel::Enterprise.is_user());
assert!(SecretLevel::Enterprise.is_enterprise());
assert!(!SecretLevel::User.is_enterprise());
assert!(SecretLevel::Enterprise.priority() > SecretLevel::User.priority());
}
}
A proven fault was caught by a test corral wrote and ran; an unproven one was not, so it may be a missing test or a fault no test can see.
s6/m1 · line 75 proven
- matches!(self, SecretLevel::Enterprise) + !matches!(self, SecretLevel::Enterprise)
s7/m1 · line 80 proven
- matches!(self, SecretLevel::User) + matches!(self, SecretLevel::Enterprise)
s3/m1 · line 87 proven
- SecretLevel::User => 0, - SecretLevel::Enterprise => 1, + SecretLevel::User => 1, + SecretLevel::Enterprise => 0,
s3/m2 · line 87 proven
- SecretLevel::Enterprise => 1, + SecretLevel::Enterprise => 0,
s3/m3 · line 87 proven
- SecretLevel::User => 0, + SecretLevel::User => 2,
s2/m3 · line 114 proven
- .filter(|r| seen_patterns.insert(r.as_str())) + .filter(|r| !seen_patterns.insert(r.as_str()))
s2/m1 · line 118 proven
- let all_secrets = enterprise_secrets_vec - .iter() - .map(|regex| regex.as_str()) - .chain(filtered_user_secrets_vec.iter().map(|regex| regex.as_str())) + let all_secrets = filtered_user_secrets_vec + .iter() + .map(|regex| regex.as_str()) + .chain(enterprise_secrets_vec.iter().map(|regex| regex.as_str()))
s2/m5 · line 118 proven
+ .skip(1)
s2/m2 · line 141 proven
- enterprise_count: enterprise_secrets_vec.len(), - user_count: filtered_user_secrets_vec.len(), + enterprise_count: filtered_user_secrets_vec.len(), + user_count: enterprise_secrets_vec.len(),
s2/m4 · line 141 proven
- enterprise_count: enterprise_secrets_vec.len(), + enterprise_count: enterprise_secrets_vec.len().saturating_sub(1),
s4/m3 · line 160 proven
+ .skip(1)
s5/m1 · line 170 proven
+ .into_iter() + .map(|(range, _)| (range, SecretLevel::User)) + .collect()
s5/m2 · line 170 proven
- find_secrets_in_text_with_levels_using_regex(text, &secrets_regex) + let mut found = find_secrets_in_text_with_levels_using_regex(text, &secrets_regex); + found.pop(); + found
s5/m3 · line 170 proven
+ .into_iter() + .filter(|(_, level)| matches!(level, SecretLevel::Enterprise)) + .collect()
s0/m5 · line 197 proven
- let start_byte = mat.start(); + let start_byte = (mat.start() + 1).min(mat.end());
s0/m3 · line 198 proven
- let end_byte = mat.end(); + let end_byte = mat.end().saturating_sub(1).max(mat.start());
s0/m4 · line 204 proven
- if pattern_id >= total_patterns {
+ if pattern_id >= total_patterns.min(level_metadata.enterprise_count) {s0/m1 · line 212 proven
- let secret_level = if pattern_id < level_metadata.enterprise_count {
+ let secret_level = if pattern_id <= level_metadata.enterprise_count {s0/m2 · line 212 proven
- SecretLevel::Enterprise
- } else {
- SecretLevel::User
+ SecretLevel::User
+ } else {
+ SecretLevel::Enterprises1/m1 · line 245 proven
- if range.char_range.start <= current_range.char_range.end {
+ if range.char_range.start < current_range.char_range.end {s1/m5 · line 247 proven
- current_range.extend_range_end(&range); + let _ = ⦥
s1/m2 · line 249 proven
- if level.priority() > current_level.priority() {
+ if level.priority() < current_level.priority() {s1/m4 · line 255 proven
- current_level = level;
s1/m3 · line 260 proven
- merged_ranges.push((current_range, current_level));
s4/m2 · line 274 proven
- for range in secrets.into_iter().rev() {
+ for range in secrets.into_iter().rev().skip(1) {s4/m1 · line 275 proven
- SECRET_REDACTION_REPLACEMENT_CHARACTER.repeat(range.end.saturating_sub(range.start)); + SECRET_REDACTION_REPLACEMENT_CHARACTER.repeat(range.end.saturating_sub(range.start + 1));