//! Signed self-update tests — the security-critical part is signature //! verification: a valid signature is accepted, anything tampered is rejected. //! Fixtures (tests/fixtures/sample.bin + .minisig) were signed with the real //! release private key, so these run with no key present (as in CI). use corrosion_host_agent::update; const SAMPLE: &[u8] = include_bytes!("fixtures/sample.bin"); const SAMPLE_SIG: &str = include_str!("fixtures/sample.bin.minisig"); #[test] fn accepts_a_validly_signed_binary() { update::verify_signature(SAMPLE, SAMPLE_SIG).expect("valid signature must verify"); } #[test] fn rejects_a_tampered_binary() { let mut tampered = SAMPLE.to_vec(); tampered[0] ^= 0xFF; // flip a byte let err = update::verify_signature(&tampered, SAMPLE_SIG) .expect_err("tampered binary must be rejected"); assert!(err.to_string().contains("verification failed"), "got: {err}"); } #[test] fn rejects_a_garbage_signature() { assert!(update::verify_signature(SAMPLE, "not a real minisig blob").is_err()); } #[test] fn rejects_empty_binary_against_real_sig() { assert!(update::verify_signature(b"", SAMPLE_SIG).is_err()); } #[test] fn url_allowlist_enforced() { // Allowed. update::assert_url_allowed("https://cdn.corrosionmgmt.com/host-agent/alpha/corrosion-host-agent-linux-amd64") .expect("the real CDN host must be allowed"); // http rejected. assert!(update::assert_url_allowed("http://cdn.corrosionmgmt.com/x").is_err()); // wrong host rejected. assert!(update::assert_url_allowed("https://evil.example.com/x").is_err()); // credential-in-URL (userinfo bypass) rejected. assert!(update::assert_url_allowed("https://cdn.corrosionmgmt.com:[email protected]/x").is_err()); // host as userinfo trick rejected (real host is evil.com). assert!(update::assert_url_allowed("https://[email protected]/x").is_err()); } #[test] fn swap_binary_replaces_and_backs_up() { let dir = tempfile::tempdir().expect("tempdir"); let current = dir.path().join("corrosion-host-agent"); std::fs::write(¤t, b"OLD BINARY").unwrap(); update::swap_binary(¤t, b"NEW BINARY").expect("swap should succeed"); assert_eq!(std::fs::read(¤t).unwrap(), b"NEW BINARY", "current is the new binary"); let backup = dir.path().join("corrosion-host-agent.old"); assert_eq!(std::fs::read(&backup).unwrap(), b"OLD BINARY", ".old holds the previous binary"); // the .new scratch file is consumed by the rename assert!(!dir.path().join("corrosion-host-agent.new").exists()); }