libgsh: complete scenario coverage for corpus_check execution paths
Adds the ReadFailed scenario (binary path resolves to a directory so exists() succeeds but read() fails) and a scenarios coverage map at the top of the test module. The map links each test to the audit fix scenarios: - valid CID, content matches: Allowed - valid CID at admission, tampered content at execution: ContentMismatch - missing binary where directory exists: Denied (sanity preserved) - binary present but unreadable: ReadFailed (fail-closed) Plus the existing sentinels for ungoverned-CID and corpus-not-mounted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Tyler J King <tking@guildhouse.dev>
This commit is contained in:
parent
13b393a7f1
commit
91f027ae61
1 changed files with 48 additions and 3 deletions
|
|
@ -149,11 +149,30 @@ pub fn corpus_check_with_base(corpus_cid: &str, command: &str, base_dir: &str) -
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
//! Scenario coverage map (execution half of the CID-content
|
||||
//! verification audit fix):
|
||||
//!
|
||||
//! - **Valid CID, content matches: execution allowed** —
|
||||
//! `binary_with_matching_content_is_allowed`.
|
||||
//! - **Valid CID at admission, tampered content at execution:
|
||||
//! execution denies** — `tampered_content_triggers_content_mismatch`.
|
||||
//! - **Missing binary where directory exists: denied (existing
|
||||
//! behavior preserved as sanity check)** —
|
||||
//! `missing_binary_in_corpus_is_denied`.
|
||||
//! - **Binary present but unreadable: denied fail-closed** —
|
||||
//! `unreadable_binary_triggers_read_failed`.
|
||||
//! - **Sentinel: ungoverned CID** — `ungoverned_skips_check`.
|
||||
//! - **Sentinel: corpus directory not mounted on host** —
|
||||
//! `missing_corpus_dir_reports_not_mounted`.
|
||||
//!
|
||||
//! The admission half (forged CID rejected at CRD reconcile) is
|
||||
//! covered in corpus-operator::verifier.
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Write bytes to `dir/cid/name` and return the CID derived from those
|
||||
/// bytes so the caller can pass a matching CID for the happy path or
|
||||
/// a different one to simulate tamper.
|
||||
/// Write bytes to `dir/cid/name` and return the path so the caller can
|
||||
/// pass a matching CID for the happy path or a different one to
|
||||
/// simulate tamper.
|
||||
fn write_binary(dir: &Path, cid: &str, name: &str, contents: &[u8]) -> PathBuf {
|
||||
let corpus_dir = dir.join(cid);
|
||||
std::fs::create_dir_all(&corpus_dir).unwrap();
|
||||
|
|
@ -231,4 +250,30 @@ mod tests {
|
|||
other => panic!("expected ContentMismatch, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Place a directory at the path where the binary should live; the
|
||||
/// `exists()` check passes but `read()` fails. Verifies the fail-closed
|
||||
/// path: an unreadable binary is denied rather than allowed.
|
||||
#[test]
|
||||
fn unreadable_binary_triggers_read_failed() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let claimed = cid_of(b"any-content");
|
||||
let corpus_dir = dir.path().join(&claimed);
|
||||
// Make a directory at the binary path — it satisfies `exists()` but
|
||||
// `read()` will fail with EISDIR or similar.
|
||||
std::fs::create_dir_all(corpus_dir.join("kubectl")).unwrap();
|
||||
|
||||
let base = dir.path().to_str().unwrap();
|
||||
match corpus_check_with_base(&claimed, "kubectl", base) {
|
||||
CorpusCheckResult::ReadFailed {
|
||||
corpus_cid,
|
||||
command,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(corpus_cid, claimed);
|
||||
assert_eq!(command, "kubectl");
|
||||
}
|
||||
other => panic!("expected ReadFailed, got {other:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue