#![cfg(unix)]
use keyhog_scanner::execution_pack::{ExecutionPackSignature, ExecutionPackSigningKey};
use serde_json::Value;
use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::process::Command;
#[test]
fn hidden_install_command_publishes_authenticated_policy_generation() {
let directory = tempfile::tempdir().expect("temporary install root");
let cache_home = directory.path().join("cache");
let pack_root = cache_home.join("keyhog/execution-packs");
fs::create_dir_all(&pack_root).expect("execution-pack root");
let key_path = pack_root.join("signing.key");
let key_bytes = [0x4d; 32];
fs::write(&key_path, key_bytes).expect("write signing key");
fs::set_permissions(&key_path, fs::Permissions::from_mode(0o600)).expect("protect signing key");
let output = pack_root.join("current");
let result = Command::new(env!("CARGO_BIN_EXE_keyhog"))
.arg("compile-execution-packs")
.arg("--output-dir")
.arg(&output)
.arg("--signing-key")
.arg(&key_path)
.output()
.expect("run install pack compiler");
assert!(
result.status.success(),
"install pack compiler failed: {}",
String::from_utf8_lossy(&result.stderr)
);
let manifest_bytes = fs::read(output.join("manifest.json")).expect("read manifest");
let manifest: Value = serde_json::from_slice(&manifest_bytes).expect("parse manifest");
assert_eq!(manifest["version"], 1);
let packs = manifest["packs"].as_array().expect("pack rows");
let policies: std::collections::BTreeSet<_> = packs
.iter()
.map(|row| row["policy"].as_str().expect("policy"))
.collect();
assert_eq!(
policies,
["deep", "default", "fast", "precision"]
.into_iter()
.collect()
);
let allowed_backends = ["cpu", "simd", "gpu-cuda", "gpu-wgpu", "gpu-metal"];
assert!(packs
.iter()
.all(|row| { allowed_backends.contains(&row["backend"].as_str().expect("backend")) }));
for backend in packs
.iter()
.map(|row| row["backend"].as_str().expect("backend"))
.collect::<std::collections::BTreeSet<_>>()
{
assert_eq!(
packs.iter().filter(|row| row["backend"] == backend).count(),
4,
"each eligible backend must publish one pack for every policy"
);
}
let key = ExecutionPackSigningKey::from_bytes(key_bytes).expect("load signing key");
for row in packs {
let pack_path = output.join(row["file"].as_str().expect("pack file"));
let signature_path = output.join(row["signature_file"].as_str().expect("signature file"));
let pack_bytes = fs::read(&pack_path).expect("read pack");
assert_eq!(
pack_bytes.len(),
row["bytes"].as_u64().expect("pack bytes") as usize
);
let signature_bytes = fs::read(signature_path).expect("read signature");
let signature = ExecutionPackSignature::decode(&signature_bytes).expect("decode signature");
key.verify(&pack_bytes, &signature)
.expect("authenticate installed pack");
assert_eq!(
keyhog_core::hex_encode(&signature.pack_digest),
row["signed_pack_digest"].as_str().expect("signed digest")
);
}
let calibrated = Command::new(env!("CARGO_BIN_EXE_keyhog"))
.args([
"calibrate-autoroute",
"--policy",
"default",
"--quiet",
"--execution-packs",
])
.arg(&output)
.output()
.expect("validate packs before calibration");
assert!(
calibrated.status.success(),
"calibration rejected current authenticated packs: {}",
String::from_utf8_lossy(&calibrated.stderr)
);
let scan_input = directory.path().join("runtime-pack-input.txt");
fs::write(
&scan_input,
b"GITHUB_TOKEN=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\n",
)
.expect("write runtime pack scan input");
let mut scan_backends = vec!["cpu"];
if packs
.iter()
.any(|row| row["policy"] == "default" && row["backend"] == "simd")
{
scan_backends.push("simd");
}
for backend in scan_backends {
let scan = Command::new(env!("CARGO_BIN_EXE_keyhog"))
.args([
"scan",
"--backend",
backend,
"--format",
"json-envelope",
"--no-config",
])
.arg(&scan_input)
.env("XDG_CACHE_HOME", &cache_home)
.env("KEYHOG_REQUIRE_EXECUTION_PACKS", "1")
.current_dir(directory.path())
.output()
.expect("scan through installed detector and matcher pack");
assert_eq!(
scan.status.code(),
Some(1),
"{backend} pack scan failed: {}",
String::from_utf8_lossy(&scan.stderr)
);
let scan_json: Value =
serde_json::from_slice(&scan.stdout).expect("pack scan JSON envelope");
assert_eq!(
scan_json["findings"].as_array().map(Vec::len),
Some(1),
"{backend} pack must preserve exact finding parity"
);
}
let cpu_pack = output.join("default-cpu.khpack");
let hidden_cpu_pack = output.join("default-cpu.khpack.hidden");
fs::rename(&cpu_pack, &hidden_cpu_pack).expect("hide runtime detector pack");
let missing = Command::new(env!("CARGO_BIN_EXE_keyhog"))
.args([
"scan",
"--backend",
"cpu",
"--format",
"json-envelope",
"--no-config",
])
.arg(&scan_input)
.env("XDG_CACHE_HOME", &cache_home)
.env("KEYHOG_REQUIRE_EXECUTION_PACKS", "1")
.output()
.expect("reject missing runtime detector pack");
fs::rename(&hidden_cpu_pack, &cpu_pack).expect("restore runtime detector pack");
assert_eq!(missing.status.code(), Some(2));
assert!(String::from_utf8_lossy(&missing.stderr)
.contains("loading authenticated detector execution pack"));
let healthy = Command::new(env!("CARGO_BIN_EXE_keyhog"))
.args(["doctor", "--autoroute-cache", "off"])
.env("XDG_CACHE_HOME", &cache_home)
.output()
.expect("inspect installed execution generation");
assert!(
healthy.status.success(),
"doctor rejected authenticated packs: {}",
String::from_utf8_lossy(&healthy.stdout)
);
let healthy_stdout = String::from_utf8_lossy(&healthy.stdout);
assert!(healthy_stdout.contains("pack state") && healthy_stdout.contains("AUTHENTICATED"));
let tampered_path = output.join("default-cpu.khpack");
let mut tampered = fs::read(&tampered_path).expect("read pack for tamper");
let last = tampered.len() - 1;
tampered[last] ^= 0x01;
fs::write(&tampered_path, tampered).expect("tamper installed pack");
let rejected = Command::new(env!("CARGO_BIN_EXE_keyhog"))
.args([
"calibrate-autoroute",
"--policy",
"default",
"--quiet",
"--execution-packs",
])
.arg(&output)
.output()
.expect("reject tampered packs before calibration");
assert!(!rejected.status.success());
let rejected_stderr = String::from_utf8_lossy(&rejected.stderr);
assert!(
rejected_stderr.contains("content digest mismatch")
|| rejected_stderr.contains("signed digest does not match the pack bytes"),
"corrupt pack diagnostic: {rejected_stderr}"
);
let unhealthy = Command::new(env!("CARGO_BIN_EXE_keyhog"))
.args(["doctor", "--autoroute-cache", "off"])
.env("XDG_CACHE_HOME", &cache_home)
.output()
.expect("reject corrupt installed execution generation");
assert_eq!(unhealthy.status.code(), Some(4));
let unhealthy_stdout = String::from_utf8_lossy(&unhealthy.stdout);
assert!(unhealthy_stdout.contains("pack state") && unhealthy_stdout.contains("INVALID"));
}
#[test]
fn hidden_install_command_rejects_exposed_signing_key() {
let directory = tempfile::tempdir().expect("temporary install root");
let key_path = directory.path().join("signing.key");
fs::write(&key_path, [0x5e; 32]).expect("write signing key");
fs::set_permissions(&key_path, fs::Permissions::from_mode(0o644)).expect("expose signing key");
let output = directory.path().join("current");
let result = Command::new(env!("CARGO_BIN_EXE_keyhog"))
.arg("compile-execution-packs")
.arg("--output-dir")
.arg(&output)
.arg("--signing-key")
.arg(&key_path)
.output()
.expect("run install pack compiler");
assert!(!result.status.success());
assert!(String::from_utf8_lossy(&result.stderr)
.contains("must not grant group or other permissions"));
assert!(!output.exists());
}