bxctl audit verify: name the log-like files it did not check
A .jsonl file whose name is not a date is ignored by brokerd and by verify; it is now listed, so "ok" does not seem to cover it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,14 +11,22 @@ use proto::ChainVerifier;
|
|||||||
pub fn run(home: &Path, out: &mut dyn Write) -> std::io::Result<bool> {
|
pub fn run(home: &Path, out: &mut dyn Write) -> std::io::Result<bool> {
|
||||||
let audit_dir = home.join("audit");
|
let audit_dir = home.join("audit");
|
||||||
let mut names: Vec<String> = Vec::new();
|
let mut names: Vec<String> = Vec::new();
|
||||||
|
let mut skipped: Vec<String> = Vec::new();
|
||||||
for entry in std::fs::read_dir(&audit_dir)? {
|
for entry in std::fs::read_dir(&audit_dir)? {
|
||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
let name = match entry.file_name().into_string() {
|
let name = match entry.file_name().into_string() {
|
||||||
Ok(name) => name,
|
Ok(name) => name,
|
||||||
Err(_) => continue,
|
Err(raw) => {
|
||||||
|
skipped.push(raw.to_string_lossy().into_owned());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if proto::is_audit_log_name(&name) {
|
if proto::is_audit_log_name(&name) {
|
||||||
names.push(name);
|
names.push(name);
|
||||||
|
} else if name.ends_with(".jsonl") {
|
||||||
|
// Looks like a log file but is not named as one: brokerd ignores it too. Say so, so
|
||||||
|
// that nobody takes "ok" to cover it.
|
||||||
|
skipped.push(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
names.sort();
|
names.sort();
|
||||||
@@ -33,6 +41,7 @@ pub fn run(home: &Path, out: &mut dyn Write) -> std::io::Result<bool> {
|
|||||||
if let Some(failure) = &report.failure {
|
if let Some(failure) = &report.failure {
|
||||||
writeln!(out, "{}:{}: {}", failure.file, failure.line, failure.what)?;
|
writeln!(out, "{}:{}: {}", failure.file, failure.line, failure.what)?;
|
||||||
writeln!(out, "see docs/runbook.md#audit-chain-broken")?;
|
writeln!(out, "see docs/runbook.md#audit-chain-broken")?;
|
||||||
|
write_skipped(out, &skipped)?;
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,5 +72,19 @@ pub fn run(home: &Path, out: &mut dyn Write) -> std::io::Result<bool> {
|
|||||||
torn.at.file, torn.at.line
|
torn.at.file, torn.at.line
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
write_skipped(out, &skipped)?;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// One line per file in `audit/` that looks like a log but is not named as one; neither `brokerd`
|
||||||
|
/// nor this check reads it. Names are escaped: a file name can hold anything.
|
||||||
|
fn write_skipped(out: &mut dyn Write, skipped: &[String]) -> std::io::Result<()> {
|
||||||
|
for name in skipped {
|
||||||
|
writeln!(
|
||||||
|
out,
|
||||||
|
"not an audit log file, not checked: {}",
|
||||||
|
crate::escape::escape_json_text(name)
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
//! `bxctl audit verify` says which files in `audit/` it did not read: a file that looks like a
|
||||||
|
//! log but is not named as one is ignored by `brokerd` too, and "ok" must not seem to cover it.
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
fn home(tag: &str) -> PathBuf {
|
||||||
|
let dir = std::env::temp_dir().join(format!("bx-verify-skip-{tag}-{}", std::process::id()));
|
||||||
|
let _ = std::fs::remove_dir_all(&dir);
|
||||||
|
std::fs::create_dir_all(dir.join("audit")).unwrap();
|
||||||
|
let fixture =
|
||||||
|
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../proto/tests/fixtures/audit/good");
|
||||||
|
for entry in std::fs::read_dir(fixture).unwrap() {
|
||||||
|
let entry = entry.unwrap();
|
||||||
|
std::fs::copy(entry.path(), dir.join("audit").join(entry.file_name())).unwrap();
|
||||||
|
}
|
||||||
|
dir
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_stray_jsonl_file_is_named_and_the_log_still_verifies() {
|
||||||
|
let dir = home("stray");
|
||||||
|
std::fs::write(dir.join("audit/2026-0x-19.jsonl"), "x\n").unwrap();
|
||||||
|
std::fs::write(dir.join("audit/notes.txt"), "not a log\n").unwrap();
|
||||||
|
let mut out = Vec::new();
|
||||||
|
let ok = bxctl::verify::run(&dir, &mut out).unwrap();
|
||||||
|
let text = String::from_utf8(out).unwrap();
|
||||||
|
assert!(ok, "{text}");
|
||||||
|
assert!(text.starts_with("audit: ok, 10 records"), "{text}");
|
||||||
|
assert!(
|
||||||
|
text.contains("not an audit log file, not checked: 2026-0x-19.jsonl"),
|
||||||
|
"{text}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!text.contains("notes.txt"),
|
||||||
|
"only files that look like logs: {text}"
|
||||||
|
);
|
||||||
|
let _ = std::fs::remove_dir_all(&dir);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user