logging_privacy.rs (3917B)
1 use std::process::{Command, Stdio}; 2 use std::time::{Duration, Instant}; 3 4 const CHILD_ROOT: &str = "TERA_LOG_PRIVACY_CHILD_ROOT"; 5 const TEST_NAME: &str = "actual_file_and_stdout_sinks_exclude_private_material"; 6 const CANARIES: [&str; 7] = [ 7 "plain private post text without a redaction keyword", 8 "Bearer CANARY_AUTHORIZATION_VALUE", 9 "49.123456,-123.987654 precise location", 10 "/Users/private/canary.json", 11 "nsec1CANARY_SECRET", 12 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 13 "PRIVATE_TARGET_CANARY", 14 ]; 15 16 #[test] 17 fn actual_file_and_stdout_sinks_exclude_private_material() { 18 if let Some(root) = std::env::var_os(CHILD_ROOT) { 19 emit_and_drain(std::path::Path::new(&root)); 20 return; 21 } 22 let root = tempfile::tempdir().unwrap(); 23 let mut child = Command::new(std::env::current_exe().unwrap()) 24 .args(["--exact", TEST_NAME, "--nocapture"]) 25 .env(CHILD_ROOT, root.path()) 26 .stdin(Stdio::null()) 27 .stdout(Stdio::piped()) 28 .stderr(Stdio::piped()) 29 .spawn() 30 .unwrap(); 31 let deadline = Instant::now() + Duration::from_secs(10); 32 loop { 33 if child.try_wait().unwrap().is_some() { 34 break; 35 } 36 if Instant::now() >= deadline { 37 child.kill().unwrap(); 38 child.wait().unwrap(); 39 panic!("bounded diagnostics sink process did not finish"); 40 } 41 std::thread::sleep(Duration::from_millis(10)); 42 } 43 let output = child.wait_with_output().unwrap(); 44 assert!(output.status.success()); 45 let stdout = String::from_utf8(output.stdout).unwrap(); 46 let stderr = String::from_utf8(output.stderr).unwrap(); 47 let file = std::fs::read_to_string(root.path().join("privacy.log")).unwrap(); 48 assert_safe(&stdout); 49 assert_safe(&file); 50 for canary in CANARIES { 51 assert!(!stderr.contains(canary)); 52 } 53 let lines = stdout 54 .lines() 55 .filter(|line| line.starts_with("level=")) 56 .collect::<Vec<_>>(); 57 assert_eq!(lines.len(), 6); 58 assert_eq!(lines, file.lines().collect::<Vec<_>>()); 59 } 60 61 fn emit_and_drain(root: &std::path::Path) { 62 tera_ffi::logging::init_logging( 63 Some(root.to_str().unwrap().to_owned()), 64 Some("privacy.log".to_owned()), 65 Some(true), 66 ) 67 .unwrap(); 68 tera_ffi::logging::log_info(CANARIES[0].to_owned()).unwrap(); 69 tera_ffi::logging::log_error(CANARIES[1].to_owned()).unwrap(); 70 tera_ffi::logging::log_debug(CANARIES[2].to_owned()).unwrap(); 71 let span = tracing::info_span!("private_span", path = CANARIES[3], secret = CANARIES[4]); 72 let _entered = span.enter(); 73 tracing::error!(target: "PRIVATE_TARGET_CANARY", authorization=CANARIES[1], content=CANARIES[0], location=CANARIES[2], id=CANARIES[5], message=CANARIES[3]); 74 tracing::info!( 75 code = "runtime_started", 76 count = 7_u64, 77 attempts = 2_i64, 78 schema = 1_u64, 79 latency_ms = 9_u64 80 ); 81 tracing::warn!(code = CANARIES[0], count = u64::MAX, unknown = CANARIES[4]); 82 let path = root.join("privacy.log"); 83 let deadline = Instant::now() + Duration::from_secs(5); 84 loop { 85 let text = std::fs::read_to_string(&path).unwrap_or_default(); 86 if text.lines().count() >= 6 { 87 assert_safe(&text); 88 return; 89 } 90 assert!( 91 Instant::now() < deadline, 92 "bounded owned writer did not drain" 93 ); 94 std::thread::sleep(Duration::from_millis(10)); 95 } 96 } 97 98 fn assert_safe(text: &str) { 99 for canary in CANARIES { 100 assert!(!text.contains(canary)); 101 } 102 assert!(!text.contains("private_span")); 103 assert!(!text.contains(&u64::MAX.to_string())); 104 assert!(text.contains("code=runtime_started count=7 attempts=2 schema=1 latency_ms=9")); 105 assert!(text.contains("level=ERROR code=message_redacted")); 106 assert!(text.len() < 4096); 107 }