tests: cover the three security boundaries that had none (#3950)

- hive-c0re::webhook_secret::verify_signature — the HMAC comparison
  verify_hmac (the only gate on the public webhook endpoint) delegates
  to. Correct-signature and mismatched-signature (wrong secret, tampered
  body) cases.
- hive-forge credential-helper get (host= check) — subprocess
  integration tests since the check is inlined in run(), which reads
  real stdin/env and prints real stdout. Host mismatch (error, token
  withheld), host match (credentials printed), and no host= line
  (backwards compat) cases.
- hive-priv::{validate_credential_name, validate_snapshot_name,
  ensure_plain_filename} — the only gate on the root-privileged socket.
  Empty/charset/dot/slash rejection, hive- prefix requirement, and
  ./../slash rejection respectively.
This commit is contained in:
atlas 2026-09-23 14:37:54 +02:00
commit e0673b6192
3 changed files with 195 additions and 3 deletions

View file

@ -97,3 +97,41 @@ fn hex_decode(s: &str) -> Option<Vec<u8>> {
}
Some(out)
}
#[cfg(test)]
mod tests {
use super::{hex_encode, verify_signature};
/// Compute the `sha256=<hex>` header Forgejo would send for `secret` +
/// `body`, so the "matches" test below isn't asserting against a
/// hand-picked string that happens to look like a signature.
fn sign(secret: &str, body: &[u8]) -> String {
use hmac::{Hmac, KeyInit, Mac};
use sha2::Sha256;
let mut mac = Hmac::<Sha256>::new_from_slice(secret.as_bytes()).unwrap();
mac.update(body);
format!("sha256={}", hex_encode(&mac.finalize().into_bytes()))
}
/// `verify_hmac` (`dashboard/webhook.rs`) delegates the actual HMAC
/// comparison here — this is the only gate on an inbound Forgejo
/// webhook reachable via the public gateway. A signature computed
/// with the right secret over the right body must verify.
#[test]
fn verify_signature_accepts_the_correct_hmac() {
let header = sign("s3cr3t", b"payload");
assert!(verify_signature("s3cr3t", b"payload", &header).is_ok());
}
/// If `mac.verify_slice`'s result were ever inverted (accept on
/// mismatch), an unauthenticated actor could queue an operator
/// approval through the public webhook endpoint. Cover both ways a
/// signature can stop matching: wrong secret, and a body that no
/// longer matches the one that was signed.
#[test]
fn verify_signature_rejects_a_mismatched_hmac() {
let header = sign("s3cr3t", b"payload");
assert!(verify_signature("different-secret", b"payload", &header).is_err());
assert!(verify_signature("s3cr3t", b"tampered-payload", &header).is_err());
}
}