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:
parent
549156e55f
commit
e0673b6192
3 changed files with 195 additions and 3 deletions
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue