use super::format::parse_patterns;
use super::{compile_from_text, load_precompiled, LoadError};
use forbidden_regex::{CompileError, RegexSet};
use rayon::prelude::*;
const BUILTIN_PORTED: &str = include_str!("../../../data/builtin-rules.ported.txt");
const APPEND_PORTED: &str = include_str!("../../../../../../forbidden-strings.append.ported.txt");
const SENTINEL: &str = "frx_redaction_canary_do_not_leak_Zq9";
fn error_rules() -> [String; 4] {
return [
format!("/{SENTINEL}/i"), format!("/{SENTINEL}*/"), format!("/(?:{SENTINEL})?/"), format!("/{SENTINEL}{{2000}}/"), ]
}
#[test]
fn builtin_ported_all_compile() {
let patterns = parse_patterns(BUILTIN_PORTED).expect("parse builtin ported");
assert!(!patterns.is_empty());
let failures: Vec<usize> = patterns
.par_iter()
.enumerate()
.filter_map(|(index, pattern)| {
return RegexSet::new(std::slice::from_ref(pattern)).err().map(|_| return index)
})
.collect();
assert!(failures.is_empty(), "builtin ported rules failed at indices {failures:?}");
}
#[test]
fn append_ported_compiles_end_to_end() {
let set = compile_from_text(APPEND_PORTED).expect("append ported compiles");
assert!(!set.is_empty());
}
#[test]
fn each_error_path_is_exercised() {
let bad_flag = compile_from_text(&format!("/{SENTINEL}/i")).expect_err("flag");
assert!(matches!(bad_flag, LoadError::UnsupportedFlag { flag: 'i', .. }));
let dialect = compile_from_text(&format!("/{SENTINEL}*/")).expect_err("dialect");
assert!(matches!(dialect, LoadError::Compile { reason: CompileError::Syntax { .. }, .. }));
let empty = compile_from_text(&format!("/(?:{SENTINEL})?/")).expect_err("empty");
assert!(matches!(empty, LoadError::Compile { reason: CompileError::EmptyMatchable, .. }));
let oversized = compile_from_text(&format!("/{SENTINEL}{{2000}}/")).expect_err("oversized");
assert!(matches!(oversized, LoadError::Compile { reason: CompileError::Syntax { .. }, .. }));
}
#[test]
fn sentinel_never_leaks_through_a_diagnostic() {
for rule in error_rules() {
let err = compile_from_text(&rule).expect_err("must fail closed");
assert!(!format!("{err}").contains(SENTINEL), "sentinel in Display of {err:?}");
assert!(!format!("{err:?}").contains(SENTINEL), "sentinel in Debug");
}
}
#[derive(Clone)]
struct BufSink(std::sync::Arc<std::sync::Mutex<Vec<u8>>>);
impl std::io::Write for BufSink {
fn write(&mut self, data: &[u8]) -> std::io::Result<usize> {
self.0.lock().expect("sink lock").extend_from_slice(data);
return Ok(data.len())
}
fn flush(&mut self) -> std::io::Result<()> {
return Ok(())
}
}
impl<'a> tracing_subscriber::fmt::MakeWriter<'a> for BufSink {
type Writer = BufSink;
fn make_writer(&'a self) -> BufSink {
return self.clone()
}
}
#[test]
fn sentinel_never_reaches_a_tracing_subscriber() {
let buf = std::sync::Arc::new(std::sync::Mutex::new(Vec::<u8>::new()));
let subscriber = tracing_subscriber::fmt()
.with_max_level(tracing::Level::TRACE)
.with_writer(BufSink(buf.clone()))
.finish();
tracing::subscriber::with_default(subscriber, || {
for rule in error_rules() {
let _ = compile_from_text(&rule);
}
});
let captured = String::from_utf8_lossy(&buf.lock().expect("sink lock")).into_owned();
assert!(!captured.contains(SENTINEL), "sentinel reached the tracing/stderr path");
}
#[test]
fn precompiled_round_trip_matches() {
let set = compile_from_text("/foo[0-9]/\nbar.baz").expect("compile");
let bytes = set.to_bytes().expect("serialize");
let reloaded = load_precompiled(&bytes).expect("load precompiled");
assert_eq!(reloaded.len(), set.len());
assert!(reloaded.is_match(b"foo7"));
assert!(reloaded.is_match(b"bar.baz"));
assert!(!reloaded.is_match(b"barxbaz"));
}
#[test]
fn precompiled_rejects_garbage_redacted() {
let err = load_precompiled(b"not a valid serialized regexset").expect_err("garbage");
assert!(matches!(err, LoadError::Precompiled { .. }));
assert!(!format!("{err}").is_empty());
}