99 lines
3.2 KiB
Rust
99 lines
3.2 KiB
Rust
//! Temporary audit directories for the audit tests. Do not edit.
|
|
|
|
#![allow(dead_code)] // each test file uses its own part of this
|
|
|
|
use std::collections::BTreeMap;
|
|
use std::path::{Path, PathBuf};
|
|
use std::sync::atomic::{AtomicU32, Ordering};
|
|
|
|
use proto::{AuditEvent, CallId, DataClass, DecisionRecord, SessionId, Timestamp};
|
|
|
|
pub const D1: &str = "2026-09-17.jsonl";
|
|
pub const D2: &str = "2026-09-18.jsonl";
|
|
|
|
static NEXT: AtomicU32 = AtomicU32::new(0);
|
|
|
|
/// A directory under the system's temporary directory, removed when dropped.
|
|
pub struct TempDir {
|
|
pub path: PathBuf,
|
|
}
|
|
|
|
impl TempDir {
|
|
/// A path that does not exist yet.
|
|
pub fn unmade(tag: &str) -> TempDir {
|
|
let n = NEXT.fetch_add(1, Ordering::SeqCst);
|
|
let name = format!("brokerd-{tag}-{}-{n}", std::process::id());
|
|
let path = std::env::temp_dir().join(name);
|
|
let _ = std::fs::remove_dir_all(&path);
|
|
TempDir { path }
|
|
}
|
|
|
|
/// A copy of the fixture log `case` from `crates/proto/tests/fixtures/audit/`. With `only`,
|
|
/// just those files: damage in an older file is not seen by an ordinary start, so tests of
|
|
/// the startup check copy the damaged file alone.
|
|
pub fn case(case: &str, only: Option<&[&str]>) -> TempDir {
|
|
let dir = TempDir::unmade(case);
|
|
std::fs::create_dir_all(&dir.path).unwrap();
|
|
let from = format!(
|
|
"{}/../proto/tests/fixtures/audit/{case}",
|
|
env!("CARGO_MANIFEST_DIR")
|
|
);
|
|
let mut copied = 0;
|
|
for entry in std::fs::read_dir(&from).unwrap_or_else(|e| panic!("{from}: {e}")) {
|
|
let entry = entry.unwrap();
|
|
let name = entry.file_name().into_string().unwrap();
|
|
if only.is_none_or(|names| names.contains(&name.as_str())) {
|
|
std::fs::copy(entry.path(), dir.path.join(&name)).unwrap();
|
|
copied += 1;
|
|
}
|
|
}
|
|
assert!(copied > 0, "{from}: nothing copied");
|
|
dir
|
|
}
|
|
}
|
|
|
|
impl Drop for TempDir {
|
|
fn drop(&mut self) {
|
|
let _ = std::fs::remove_dir_all(&self.path);
|
|
}
|
|
}
|
|
|
|
/// Every log file in `dir` with its bytes.
|
|
pub fn snapshot(dir: &Path) -> BTreeMap<String, Vec<u8>> {
|
|
std::fs::read_dir(dir)
|
|
.unwrap()
|
|
.map(|entry| entry.unwrap())
|
|
.filter(|entry| entry.file_name().to_string_lossy().ends_with(".jsonl"))
|
|
.map(|entry| {
|
|
let name = entry.file_name().into_string().unwrap();
|
|
(name, std::fs::read(entry.path()).unwrap())
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
pub fn lines(dir: &Path, file: &str) -> Vec<String> {
|
|
let text = std::fs::read_to_string(dir.join(file)).unwrap();
|
|
text.lines().map(str::to_string).collect()
|
|
}
|
|
|
|
pub fn ts(s: &str) -> Timestamp {
|
|
Timestamp::parse(s).unwrap()
|
|
}
|
|
|
|
/// A denied decision for call `call`: an event that leaves nothing open in the report.
|
|
pub fn denied(call: u64) -> AuditEvent {
|
|
AuditEvent::Decision {
|
|
session: SessionId::new("chat-1").unwrap(),
|
|
call: CallId(call),
|
|
tool: "read_file".to_string(),
|
|
arguments: r#"{"path":"/etc/hosts"}"#.to_string(),
|
|
outcome: DecisionRecord::Denied {
|
|
reason: proto::DenyReason::NoGrant,
|
|
},
|
|
grant: None,
|
|
grant_sha256: None,
|
|
taint: DataClass::Private,
|
|
untrusted: false,
|
|
}
|
|
}
|