[pitboss/grind] deferred session-0023 (20260516T052512Z-20f8)

This commit is contained in:
pitboss 2026-05-16 13:05:27 -05:00
parent 1d1975a2ea
commit 6189c4a4c5
20 changed files with 297 additions and 1 deletions

View file

@ -76,6 +76,7 @@ fn verdict(status: VerifyStatus, reason: Option<InconclusiveReason>) -> VerifyRe
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
}
}

View file

@ -584,6 +584,7 @@ pub fn run_shape_fixture_lang(
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
}
}
Err(RunError::NoPayloadsForCap) => VerifyResult {
@ -598,6 +599,7 @@ pub fn run_shape_fixture_lang(
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
},
Err(e) => VerifyResult {
finding_id: spec.finding_id.clone(),
@ -611,6 +613,7 @@ pub fn run_shape_fixture_lang(
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
},
}
}

View file

@ -74,6 +74,7 @@ fn diag_with_verdict(status: VerifyStatus) -> Diag {
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
},
VerifyStatus::NotConfirmed => VerifyResult {
finding_id: "abc123".into(),
@ -93,6 +94,7 @@ fn diag_with_verdict(status: VerifyStatus) -> Diag {
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
},
VerifyStatus::Unsupported => VerifyResult {
finding_id: "abc123".into(),
@ -106,6 +108,7 @@ fn diag_with_verdict(status: VerifyStatus) -> Diag {
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
},
VerifyStatus::Inconclusive => VerifyResult {
finding_id: "abc123".into(),
@ -119,6 +122,7 @@ fn diag_with_verdict(status: VerifyStatus) -> Diag {
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
},
};

View file

@ -55,6 +55,7 @@ fn set_verdict(
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
});
}
}
@ -170,6 +171,7 @@ fn new_confirmed_fails_no_new_confirmed_gate() {
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
});
}
}

View file

@ -61,6 +61,7 @@ mod go_fixture_tests {
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
};
}

View file

@ -69,6 +69,7 @@ mod java_fixture_tests {
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
};
}

View file

@ -62,6 +62,7 @@ mod js_fixture_tests {
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
};
}

View file

@ -60,6 +60,7 @@ fn json_dynamic_verdict_confirmed_serialises_correctly() {
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
}),
..Default::default()
});
@ -100,6 +101,7 @@ fn json_dynamic_verdict_not_confirmed_serialises_correctly() {
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
}),
..Default::default()
});
@ -165,6 +167,7 @@ fn json_unsupported_verdict_has_reason() {
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
}),
..Default::default()
});

View file

@ -61,6 +61,7 @@ mod php_fixture_tests {
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
};
}

View file

@ -70,6 +70,7 @@ mod repro_determinism_tests {
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
}
}

View file

@ -89,6 +89,7 @@ mod repro_hermetic_tests {
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
}
}

View file

@ -350,6 +350,111 @@ except Exception as exc:
"refuse_filesystem_confirm should be false when sandbox-exec is reachable"
);
}
/// Phase 18 verifier-side projection: when a real strict run lands a
/// macOS `HardeningRecord`, `summarize_hardening` collapses it into
/// the portable [`crate::evidence::HardeningSummary`] that
/// `build_verdict` stamps on a `Confirmed` `VerifyResult`. Drives
/// the same `sandbox::run` path the existing
/// `path_traversal_payload_blocked_under_strict` test uses, then
/// asserts on the projection that would land on
/// `VerifyResult::hardening_outcome` if this run had triggered the
/// finding's oracle.
#[test]
fn summarize_hardening_lands_path_traversal_on_strict_file_io_run() {
unsafe { std::env::remove_var(SANDBOX_EXEC_BIN_ENV) };
if !sandbox_exec_available() {
eprintln!("SKIP: /usr/bin/sandbox-exec missing — cannot exercise wrap");
return;
}
const FILE_IO: u32 = 1 << 5;
let tmp = workdir();
let harness = build_harness(tmp.path());
let opts = strict_opts(FILE_IO);
let result = sandbox::run(&harness, b"", &opts).expect("sandbox::run");
let summary = nyx_scanner::dynamic::verify::summarize_hardening(&result)
.expect("hardening summary should populate after a strict macOS run");
assert_eq!(summary.backend, "macos-process");
assert_eq!(summary.level, "sandboxed");
assert_eq!(
summary.profile, "path_traversal",
"FILE_IO-cap strict run should select the path_traversal profile"
);
assert!(
summary.primitives.is_empty(),
"macOS backend records no per-primitive entries"
);
}
/// Standard-profile runs leave `SandboxOutcome::hardening_outcome`
/// unset, so `summarize_hardening` returns `None` and
/// `VerifyResult::hardening_outcome` stays `None`. Companion to
/// `standard_profile_does_not_wrap_with_sandbox_exec`.
#[test]
fn summarize_hardening_returns_none_for_standard_profile_run() {
unsafe { std::env::remove_var(SANDBOX_EXEC_BIN_ENV) };
let tmp = workdir();
let harness = build_harness(tmp.path());
let opts = standard_opts();
let result = sandbox::run(&harness, b"", &opts).expect("sandbox::run");
assert!(
nyx_scanner::dynamic::verify::summarize_hardening(&result).is_none(),
"standard profile should leave hardening_outcome unset"
);
}
/// Round-trip the portable summary through JSON to lock in the
/// repro-bundle wire shape: `VerifyResult::hardening_outcome` lands
/// on `expected/verdict.json` so the eval-corpus tabulator and any
/// downstream replay reads the same fields back.
#[test]
fn hardening_summary_round_trips_through_json() {
use nyx_scanner::evidence::{HardeningSummary, HardeningPrimitive};
let summary = HardeningSummary {
backend: "macos-process".into(),
level: "sandboxed".into(),
profile: "path_traversal".into(),
primitives: vec![],
};
let json = serde_json::to_string(&summary).expect("serialize");
let parsed: HardeningSummary = serde_json::from_str(&json).expect("deserialize");
assert_eq!(parsed, summary);
// Defaults: missing `profile` and `primitives` must decode as
// empty so older `verdict.json` payloads keep round-tripping.
let minimal: HardeningSummary =
serde_json::from_str(r#"{"backend":"linux-process","level":"full"}"#)
.expect("minimal decode");
assert_eq!(minimal.profile, "");
assert!(minimal.primitives.is_empty());
// Linux-shape: per-primitive entries decode + re-encode with
// their `errno` field intact when populated.
let with_primitives = HardeningSummary {
backend: "linux-process".into(),
level: "partial".into(),
profile: "strict".into(),
primitives: vec![
HardeningPrimitive {
name: "no_new_privs".into(),
status: "applied".into(),
errno: None,
},
HardeningPrimitive {
name: "seccomp".into(),
status: "failed".into(),
errno: Some(1),
},
],
};
let json = serde_json::to_string(&with_primitives).expect("serialize primitives");
assert!(
json.contains("\"errno\":1"),
"errno field should survive JSON round-trip; got: {json}"
);
let parsed: HardeningSummary = serde_json::from_str(&json).expect("decode primitives");
assert_eq!(parsed, with_primitives);
}
}
// Non-macOS placeholder so `cargo nextest run --test sandbox_hardening_macos`

View file

@ -76,6 +76,7 @@ fn sarif_confirmed_verdict_sets_partial_fingerprint() {
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
};
let result = sarif_result(diag_with_verdict(verdict));
@ -111,6 +112,7 @@ fn sarif_not_confirmed_verdict_sets_partial_fingerprint() {
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
};
let result = sarif_result(diag_with_verdict(verdict));
@ -140,6 +142,7 @@ fn sarif_unsupported_verdict_sets_partial_fingerprint() {
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
};
let result = sarif_result(diag_with_verdict(verdict));
@ -174,6 +177,7 @@ fn sarif_inconclusive_verdict_sets_partial_fingerprint() {
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
};
let result = sarif_result(diag_with_verdict(verdict));
@ -224,6 +228,7 @@ fn sarif_confirmed_verdict_nyx_dynamic_verdict_contains_triggered_payload() {
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
};
let result = sarif_result(diag_with_verdict(verdict));
@ -257,6 +262,7 @@ fn sarif_all_four_statuses_produce_partial_fingerprint() {
differential: None,
replay_stable: None,
wrong: None,
hardening_outcome: None,
};
let result = sarif_result(diag_with_verdict(verdict));