Some checks failed
Native Rust workspace compile / compile (push) Has been cancelled
216 lines
6.5 KiB
Rust
216 lines
6.5 KiB
Rust
use std::fs;
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::{Command, Output, Stdio};
|
|
use std::sync::atomic::{AtomicU64, Ordering};
|
|
|
|
const USE_CIRCUIT_CODE: &str = concat!(
|
|
"400000000000ffff000344332211",
|
|
"000102030405060708090a0b0c0d0e0f",
|
|
"101112131415161718191a1b1c1d1e1f"
|
|
);
|
|
const UNKNOWN_PACKET: &str = "400000000000fe";
|
|
const EXIT_USAGE: i32 = 2;
|
|
const EXIT_INPUT: i32 = 3;
|
|
const EXIT_OUTPUT: i32 = 5;
|
|
|
|
static TEMP_ID: AtomicU64 = AtomicU64::new(0);
|
|
|
|
struct TestDir(PathBuf);
|
|
|
|
impl TestDir {
|
|
fn new(test_name: &str) -> Self {
|
|
let id = TEMP_ID.fetch_add(1, Ordering::Relaxed);
|
|
let path = std::env::temp_dir().join(format!(
|
|
"metacrate-packet-dump-{test_name}-{}-{id}",
|
|
std::process::id()
|
|
));
|
|
fs::create_dir(&path).expect("create isolated test directory");
|
|
Self(path)
|
|
}
|
|
|
|
fn path(&self, name: &str) -> PathBuf {
|
|
self.0.join(name)
|
|
}
|
|
}
|
|
|
|
impl Drop for TestDir {
|
|
fn drop(&mut self) {
|
|
let _ = fs::remove_dir_all(&self.0);
|
|
}
|
|
}
|
|
|
|
fn run(args: &[&str]) -> Output {
|
|
Command::new(env!("CARGO_BIN_EXE_packet-dump"))
|
|
.args(args)
|
|
.stdin(Stdio::null())
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.output()
|
|
.expect("run packet-dump")
|
|
}
|
|
|
|
fn text(bytes: &[u8]) -> &str {
|
|
std::str::from_utf8(bytes).expect("command output is UTF-8")
|
|
}
|
|
|
|
fn path_text(path: &Path) -> &str {
|
|
path.to_str().expect("test path is UTF-8")
|
|
}
|
|
|
|
fn assert_exit(output: &Output, expected: i32) {
|
|
assert_eq!(
|
|
output.status.code(),
|
|
Some(expected),
|
|
"stdout:\n{}\nstderr:\n{}",
|
|
String::from_utf8_lossy(&output.stdout),
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
}
|
|
|
|
fn script(directory: &TestDir) -> PathBuf {
|
|
let path = directory.path("packets.tsv");
|
|
fs::write(
|
|
&path,
|
|
format!(
|
|
"# direction, simulator, and wire bytes\n\
|
|
incoming\tAlpha Region\t{USE_CIRCUIT_CODE}\n\
|
|
outgoing\tBeta Region\t{USE_CIRCUIT_CODE}\n\
|
|
incoming\tAlpha Region\t{UNKNOWN_PACKET}\n"
|
|
),
|
|
)
|
|
.expect("write fake packet script");
|
|
path
|
|
}
|
|
|
|
#[test]
|
|
fn help_preserves_upstream_arguments_and_documents_safe_capture_controls() {
|
|
let output = run(&["--help"]);
|
|
assert!(output.status.success());
|
|
let help = text(&output.stdout);
|
|
for marker in [
|
|
"[FIRSTNAME]",
|
|
"[LASTNAME]",
|
|
"[PASSWORD]",
|
|
"[SECONDS]",
|
|
"--fake-script",
|
|
"--direction",
|
|
"--packet-type",
|
|
"--raw",
|
|
"--output",
|
|
"--max-output-bytes",
|
|
"--max-packets",
|
|
"incoming<TAB>simulator<TAB>hex-bytes",
|
|
] {
|
|
assert!(help.contains(marker), "help omitted {marker}:\n{help}");
|
|
}
|
|
|
|
let output = run(&[]);
|
|
assert_exit(&output, EXIT_USAGE);
|
|
assert!(text(&output.stderr).contains("Usage: packet-dump"));
|
|
}
|
|
|
|
#[test]
|
|
fn fake_capture_decodes_directions_types_headers_raw_bytes_and_malformed_data() {
|
|
let directory = TestDir::new("capture");
|
|
let script = script(&directory);
|
|
let output = run(&["--fake-script", path_text(&script), "--raw"]);
|
|
assert!(output.status.success(), "{}", text(&output.stderr));
|
|
assert!(output.stderr.is_empty());
|
|
let output = text(&output.stdout);
|
|
assert!(output.contains(
|
|
"IN type=UseCircuitCode simulator=Alpha Region bytes=46 sequence=0 frequency=Low id=3 reliable=true resent=false zerocoded=false appended_acks=false"
|
|
));
|
|
assert!(output.contains(&format!("raw={USE_CIRCUIT_CODE}")));
|
|
assert!(output.contains("OUT type=UseCircuitCode simulator=Beta Region bytes=46"));
|
|
assert!(output.contains("IN malformed-or-unknown simulator=Alpha Region bytes=7"));
|
|
assert!(output.contains("Capture complete; packets=3 dropped=0 active_tasks=0 open_sockets=0"));
|
|
|
|
let output = run(&[
|
|
"--fake-script",
|
|
path_text(&script),
|
|
"--direction",
|
|
"incoming",
|
|
"--packet-type",
|
|
"UseCircuitCode",
|
|
]);
|
|
assert!(output.status.success());
|
|
let output = text(&output.stdout);
|
|
assert!(output.contains("IN type=UseCircuitCode"));
|
|
assert!(!output.contains("OUT type="));
|
|
assert!(!output.contains("malformed-or-unknown"));
|
|
assert!(output.contains("Capture complete; packets=1"));
|
|
}
|
|
|
|
#[test]
|
|
fn output_file_packet_and_byte_limits_are_enforced_without_unbounded_writes() {
|
|
let directory = TestDir::new("limits");
|
|
let script = script(&directory);
|
|
let capture = directory.path("capture.log");
|
|
let output = run(&[
|
|
"--fake-script",
|
|
path_text(&script),
|
|
"--max-packets",
|
|
"1",
|
|
"--output",
|
|
path_text(&capture),
|
|
]);
|
|
assert!(output.status.success(), "{}", text(&output.stderr));
|
|
assert!(output.stdout.is_empty());
|
|
let saved = fs::read_to_string(capture).expect("read capture output");
|
|
assert!(saved.contains("Packet limit reached"));
|
|
assert!(saved.contains("Capture complete; packets=1"));
|
|
|
|
let limited = directory.path("limited.log");
|
|
let output = run(&[
|
|
"--fake-script",
|
|
path_text(&script),
|
|
"--max-output-bytes",
|
|
"10",
|
|
"--output",
|
|
path_text(&limited),
|
|
]);
|
|
assert_exit(&output, EXIT_OUTPUT);
|
|
assert!(text(&output.stderr).contains("output byte limit"));
|
|
assert_eq!(
|
|
fs::metadata(limited).expect("limited file metadata").len(),
|
|
0
|
|
);
|
|
|
|
let output = run(&["--fake-script", path_text(&script), "--max-packets", "0"]);
|
|
assert_exit(&output, EXIT_USAGE);
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_scripts_filters_and_credentials_fail_without_leaking_secrets() {
|
|
let directory = TestDir::new("invalid");
|
|
let invalid = directory.path("invalid.tsv");
|
|
fs::write(&invalid, "incoming\tRegion\tzz\n").expect("write invalid script");
|
|
let output = run(&["--fake-script", path_text(&invalid)]);
|
|
assert_exit(&output, EXIT_INPUT);
|
|
assert!(output.stdout.is_empty());
|
|
assert!(text(&output.stderr).contains("non-hex digit"));
|
|
|
|
let valid = script(&directory);
|
|
let output = run(&[
|
|
"--fake-script",
|
|
path_text(&valid),
|
|
"--packet-type",
|
|
"NotAPacket",
|
|
]);
|
|
assert_exit(&output, EXIT_USAGE);
|
|
assert!(text(&output.stderr).contains("unknown packet type"));
|
|
|
|
let password = "do-not-print-this-password";
|
|
let output = run(&[
|
|
"First",
|
|
"Last",
|
|
password,
|
|
"10",
|
|
"--fake-script",
|
|
path_text(&valid),
|
|
]);
|
|
assert_exit(&output, EXIT_USAGE);
|
|
assert!(!text(&output.stdout).contains(password));
|
|
assert!(!text(&output.stderr).contains(password));
|
|
}
|