field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

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 }