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

@ -3114,9 +3114,10 @@ mod tests {
use super::{
AgentTmpfilesEntry, BindMount, OwnedFd, PAUSED_MARKER_FILE, PrivRequest,
agent_tmpfiles_content, check_fd_agreement, clear_runner_credentials,
contains_secret_shaped_run, git_overlay_flags, limits_dropin_body, matrix_token_filename,
partial_name, redact_secret_line, remove_marker_in, single_output_path, toplevel_attr,
validate_account_name, write_agent_dir_file, write_state_file_nofollow,
contains_secret_shaped_run, ensure_plain_filename, git_overlay_flags, limits_dropin_body,
matrix_token_filename, partial_name, redact_secret_line, remove_marker_in,
single_output_path, toplevel_attr, validate_account_name, validate_credential_name,
validate_snapshot_name, write_agent_dir_file, write_state_file_nofollow,
};
use std::path::PathBuf;
use std::sync::atomic::{AtomicU32, Ordering};
@ -3735,4 +3736,44 @@ mod tests {
);
std::fs::remove_dir_all(&dir).ok();
}
/// The only gate on the root-privileged socket for a `--load-credential`
/// name. The doc comment is explicit that `.` is excluded on purpose
/// ("this name gets interpolated into filesystem paths") — a future
/// "let's allow dots for version numbers" loosening must fail this,
/// not just the `/` case below.
#[test]
fn validate_credential_name_rejects_dot_slash_and_empty_but_allows_the_charset() {
assert!(
validate_credential_name("").is_err(),
"empty must be rejected"
);
assert!(validate_credential_name("valid-name_123").is_ok());
assert!(
validate_credential_name("bad.name").is_err(),
"dot must be rejected — see the fn's doc comment"
);
assert!(validate_credential_name("bad/name").is_err());
}
/// A snapshot label must additionally carry the `hive-` prefix (it
/// doubles as the allow-list gating the btrfs snapshot/delete
/// shellouts) on top of [`validate_credential_name`]'s charset rule.
#[test]
fn validate_snapshot_name_requires_hive_prefix() {
assert!(validate_snapshot_name("not-hive-prefixed").is_err());
assert!(validate_snapshot_name("hive-valid").is_ok());
}
/// `ensure_plain_filename` is the gate between an agent-writable
/// directory and a root-privileged file write. `.`/`..` would resolve
/// to the directory itself or its parent; any `/` climbs into a path
/// component the caller never named.
#[test]
fn ensure_plain_filename_rejects_dot_dotdot_and_any_slash() {
assert!(ensure_plain_filename("test", ".").is_err());
assert!(ensure_plain_filename("test", "..").is_err());
assert!(ensure_plain_filename("test", "sub/dir").is_err());
assert!(ensure_plain_filename("test", "matrix-token").is_ok());
}
}