server/lib/tests/every_refusal_leaves_an_audit_row.rs
//! ★ **Every socket asks [`egress::check`] first, and every refusal leaves an audit row**
//! (lane HOL-NCSC, NCSC P1 item 5).
//!
//! * a dial `network: Offline` refuses is written to the audit log (`refusals`), with its code;
//! * an offline Holger exports its audit log to local sinks only (`local_only` is implied), and a
//! SIEM socket to a local collector is the one dial an offline box still makes
//! (`egress::check_site_local`);
//! * the off-site copy's S3 store is a dial like any other;
//! * a source guard: every raw socket in server-lib and server-cli (TCP, UDP, a WireGuard
//! dataplane) is preceded in its function by an `egress::check`.
//!
//! Its own test binary: `egress::set_offline` is one-way and process-wide.
use std::sync::Arc;
use holger_server_lib::audit::{siem::SiemSettings, MemoryAuditLog};
use holger_server_lib::{egress, offsite::Remote as _, refusals};
fn siem(target: &str) -> SiemSettings {
ron::from_str(&format!(
r#"(sinks: [(name: "soc", enabled: true, format: Syslog, target: {target})])"#
))
.unwrap()
}
#[test]
fn an_offline_holgers_refusals_are_audited_and_its_siem_stays_on_site() {
let log = Arc::new(MemoryAuditLog::new());
refusals::set_sink(log.clone());
egress::set_offline();
// A refused dial leaves its row, with the code.
let e = egress::check("pull-through", "https://pypi.org/simple/").unwrap_err();
assert!(e.to_string().contains("[holger-offline-1]"), "{e}");
let rows = log.events();
let row = rows
.iter()
.find(|r| r.detail.contains("pull-through"))
.unwrap_or_else(|| panic!("no audit row for the refused dial: {rows:?}"));
assert!(
row.detail.contains("[holger-offline-1]") && row.detail.contains("pypi.org"),
"{row:?}"
);
// Offline implies local_only: a public collector is refused, a local one kept.
let e = siem(r#"Tcp("203.0.113.5:514")"#)
.enabled_sinks()
.unwrap_err();
assert!(e.contains("local sinks only"), "{e}");
siem(r#"Tcp("10.0.0.5:514")"#)
.enabled_sinks()
.expect("a local collector");
// The SIEM's own socket: on site yes, off site no.
egress::check_site_local("siem", "10.0.0.5:514").expect("a local collector is dialled");
egress::check_site_local("siem", "127.0.0.1:514").expect("loopback");
assert!(
egress::check_site_local("siem", "203.0.113.5:514").is_err(),
"an off-site collector is refused"
);
assert!(
egress::check_site_local("siem", "siem.example:514").is_err(),
"a name is not on site"
);
// The off-site copy dials its S3 store through the same check.
let s3 = holger_server_lib::offsite::S3Remote::new(
"https://r2.example",
"bucket",
"auto",
"ak",
"sk",
);
let e = s3.put("k", b"v").unwrap_err();
assert!(e.contains("[holger-offline-1]"), "{e}");
}
/// Pure: the function around line `at` (from its `fn` line).
fn enclosing_fn(lines: &[&str], at: usize) -> String {
let start = (0..=at).rev().find(|i| {
let t = lines[*i].trim_start();
t.starts_with("fn ")
|| t.starts_with("pub fn ")
|| t.starts_with("async fn ")
|| t.starts_with("pub async fn ")
|| t.starts_with("pub(crate) fn ")
|| t.contains(" fn ")
});
lines[start.unwrap_or(0)..=at].join("\n")
}
/// The sockets that are not egress, each with why.
const NOT_EGRESS: &[(&str, &str, &str)] = &[
(
"server/cli/src/paired.rs",
"fn doors_answer",
"the box's own doors, on this machine",
),
(
"server/cli/src/passkey_cli.rs",
"fn post",
"a CLI verb dialling the door the operator named, not the server's egress",
),
];
#[test]
fn every_socket_asks_egress_first() {
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
let mut files = Vec::new();
for dir in ["server/lib/src", "server/cli/src"] {
let mut stack = vec![root.join(dir)];
while let Some(d) = stack.pop() {
for e in std::fs::read_dir(&d).unwrap().flatten() {
let p = e.path();
if p.is_dir() {
stack.push(p);
} else if p.extension().is_some_and(|x| x == "rs")
&& !p.ends_with("tests.rs")
&& !p.to_string_lossy().contains("/generated/")
{
files.push(p);
}
}
}
}
let mut bare = Vec::new();
for f in &files {
let text = std::fs::read_to_string(f).unwrap();
let text = match text.find("#[cfg(test)]\nmod tests") {
Some(i) => &text[..i],
None => &text[..],
};
let lines: Vec<&str> = text.lines().collect();
let rel = f
.strip_prefix(&root)
.unwrap_or(f)
.to_string_lossy()
.replace("../", "");
for (i, l) in lines.iter().enumerate() {
let code = l.split("//").next().unwrap_or_default();
if !["TcpStream::connect", "UdpSocket::bind", "dataplane::spawn"]
.iter()
.any(|p| code.contains(p))
{
continue;
}
let body = enclosing_fn(&lines, i);
if body.contains("egress::check") {
continue;
}
if NOT_EGRESS
.iter()
.any(|(file, func, _)| rel.ends_with(file) && body.contains(func))
{
continue;
}
bare.push(format!("{rel}:{}: {}", i + 1, l.trim()));
}
}
assert!(
bare.is_empty(),
"a socket that does not ask egress::check first:\n{}",
bare.join("\n")
);
}