forked from Crockan/MercuryToolbox
125 lines
3.5 KiB
Rust
125 lines
3.5 KiB
Rust
//! CLI contract tests for PE analysis commands.
|
|
|
|
use assert_cmd::Command;
|
|
use predicates::prelude::*;
|
|
use serde_json::Value;
|
|
|
|
const COMMANDS: &[&str] = &[
|
|
"peexports",
|
|
"peimports",
|
|
"pecalls",
|
|
"pesig",
|
|
"pestrrefs",
|
|
"drvshape",
|
|
"ioctlscan",
|
|
];
|
|
|
|
fn peimports_command() -> Command {
|
|
Command::cargo_bin("peimports").expect("binary")
|
|
}
|
|
|
|
fn peexports_command() -> Command {
|
|
Command::cargo_bin("peexports").expect("binary")
|
|
}
|
|
|
|
#[test]
|
|
fn help_mentions_shared_flags_and_examples() {
|
|
for command_name in COMMANDS {
|
|
let mut command = Command::cargo_bin(command_name).expect("binary");
|
|
command
|
|
.arg("--help")
|
|
.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("Examples"))
|
|
.stdout(predicate::str::contains("--json"))
|
|
.stdout(predicate::str::contains("--toon"))
|
|
.stdout(predicate::str::contains("--input-format"));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn non_pe_input_is_runtime_error() {
|
|
let temp = tempfile::NamedTempFile::new().expect("temp file");
|
|
std::fs::write(temp.path(), b"not a pe").expect("write fixture");
|
|
|
|
let mut command = peimports_command();
|
|
command
|
|
.arg(temp.path())
|
|
.arg("--json")
|
|
.assert()
|
|
.code(3)
|
|
.stderr(predicate::str::contains("not a PE"));
|
|
}
|
|
|
|
#[test]
|
|
fn imports_json_includes_guided_triage_metadata() {
|
|
let sample = assert_cmd::cargo::cargo_bin("peimports");
|
|
let mut command = peimports_command();
|
|
let output = command
|
|
.arg(sample)
|
|
.arg("--json")
|
|
.assert()
|
|
.success()
|
|
.get_output()
|
|
.stdout
|
|
.clone();
|
|
|
|
let json = serde_json::from_slice::<Value>(&output).expect("json output");
|
|
assert_eq!(json["report_quality"]["confidence"], "high");
|
|
assert_eq!(json["report_quality"]["completeness"], "complete");
|
|
assert!(
|
|
json["report_quality"]["evidence"]
|
|
.as_array()
|
|
.is_some_and(|items| {
|
|
items
|
|
.iter()
|
|
.any(|item| item == "PE headers and import table parsed")
|
|
})
|
|
);
|
|
assert!(json["next_actions"].as_array().is_some_and(|items| {
|
|
items.iter().any(|item| {
|
|
item["label"] == "Hunt imported API callsites"
|
|
&& item["command"].as_str().is_some_and(|command| {
|
|
command.contains("pecalls") && command.contains("--category")
|
|
})
|
|
})
|
|
}));
|
|
}
|
|
|
|
#[test]
|
|
fn imports_text_is_answer_first_and_actionable() {
|
|
let sample = assert_cmd::cargo::cargo_bin("peimports");
|
|
let mut command = peimports_command();
|
|
command
|
|
.arg(sample)
|
|
.assert()
|
|
.success()
|
|
.stdout(predicate::str::contains("answer="))
|
|
.stdout(predicate::str::contains(
|
|
"trust confidence=high completeness=complete",
|
|
))
|
|
.stdout(predicate::str::contains(
|
|
"next_action label=\"Hunt imported API callsites\" command=\"pecalls",
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn exports_ordinal_filter_matches_the_real_export_ordinal() {
|
|
let mut command = peexports_command();
|
|
let output = command
|
|
.arg(r"C:\Windows\System32\kernel32.dll")
|
|
.arg("--ordinal")
|
|
.arg("126")
|
|
.arg("--json")
|
|
.assert()
|
|
.success()
|
|
.get_output()
|
|
.stdout
|
|
.clone();
|
|
|
|
let json = serde_json::from_slice::<Value>(&output).expect("json output");
|
|
let export = json["files"][0]["exports"][0].clone();
|
|
assert_eq!(export["ordinal"], 126);
|
|
assert_eq!(export["name"], "BuildIoRingCancelRequest");
|
|
}
|