mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-15 20:05:13 +02:00
[pitboss/grind] deferred session-0023 (20260517T044708Z-e058)
This commit is contained in:
parent
f4793b0439
commit
b638cade34
8 changed files with 458 additions and 26 deletions
|
|
@ -21,7 +21,9 @@ mod hardening_tests {
|
|||
|
||||
use nyx_scanner::dynamic::harness::BuiltHarness;
|
||||
use nyx_scanner::dynamic::sandbox::process_macos::{
|
||||
profile_for_caps, sandbox_exec_available, HardeningLevel, SANDBOX_EXEC_BIN_ENV,
|
||||
clear_profile_path_cache_for_tests, profile_for_caps, profile_path,
|
||||
sandbox_exec_available, HardeningLevel, SANDBOX_EXEC_BIN_ENV, SB_DENY_DEFAULT_ENV,
|
||||
SB_SEED_DIR_ENV,
|
||||
};
|
||||
use nyx_scanner::dynamic::sandbox::{
|
||||
self, HardeningRecord, ProcessHardeningProfile, SandboxBackend, SandboxOptions,
|
||||
|
|
@ -686,6 +688,98 @@ except Exception as exc:
|
|||
);
|
||||
}
|
||||
|
||||
/// Phase 18 follow-up smoke test: a synthetic seed under
|
||||
/// `NYX_SB_SEED_DIR` rewrites the materialised `.sb` profile to
|
||||
/// `(deny default)` and appends the seed body verbatim. Exercises
|
||||
/// the splice path through the production [`profile_path`] call
|
||||
/// site so the env-var → seed-dir → splice → on-disk file flow is
|
||||
/// validated end-to-end, not just via the unit tests on
|
||||
/// [`splice_deny_default`].
|
||||
///
|
||||
/// Uses the `ssrf` profile because no other test in this file
|
||||
/// touches it; the cache-clear helper resets state regardless so
|
||||
/// the assertion holds even if a future test materialises ssrf
|
||||
/// before this one.
|
||||
#[test]
|
||||
fn deny_default_seed_loads_under_strict() {
|
||||
let seed_dir = tempfile::TempDir::new().expect("seed tempdir");
|
||||
// The seed body is intentionally over-permissive so the
|
||||
// /usr/bin/true probe at the end of the test can clear without
|
||||
// tripping on missing allowances. A real seed generated by
|
||||
// `tools/sb-trace.sh` would be much tighter (only the rules
|
||||
// each interpreter cold-start needs).
|
||||
let seed_body = ";; synthetic seed for end-to-end smoke test\n\
|
||||
(allow process-fork)\n\
|
||||
(allow process-exec*)\n\
|
||||
(allow file-read*)\n\
|
||||
(allow file-read-metadata)\n\
|
||||
(allow file-write-data (literal \"/dev/null\"))\n\
|
||||
(allow mach-lookup)\n\
|
||||
(allow signal (target self))\n\
|
||||
(allow sysctl-read)\n\
|
||||
(allow ipc-posix-shm-read*)\n";
|
||||
std::fs::write(seed_dir.path().join("ssrf.allow"), seed_body)
|
||||
.expect("write synthetic seed");
|
||||
|
||||
clear_profile_path_cache_for_tests();
|
||||
unsafe {
|
||||
std::env::set_var(SB_DENY_DEFAULT_ENV, "1");
|
||||
std::env::set_var(SB_SEED_DIR_ENV, seed_dir.path());
|
||||
}
|
||||
|
||||
let materialised = profile_path("ssrf").expect("profile materialises");
|
||||
let body = std::fs::read_to_string(&materialised).expect("read profile body");
|
||||
|
||||
unsafe {
|
||||
std::env::remove_var(SB_DENY_DEFAULT_ENV);
|
||||
std::env::remove_var(SB_SEED_DIR_ENV);
|
||||
}
|
||||
clear_profile_path_cache_for_tests();
|
||||
|
||||
assert!(
|
||||
body.contains("(deny default)"),
|
||||
"splice should rewrite (allow default) -> (deny default); got: {body}",
|
||||
);
|
||||
assert!(
|
||||
!body.contains("(allow default)"),
|
||||
"no (allow default) directive should survive the splice; got: {body}",
|
||||
);
|
||||
assert!(
|
||||
body.contains(";; ── deny-default seed (spliced by NYX_SB_DENY_DEFAULT=1) ──"),
|
||||
"splice banner should appear once; got: {body}",
|
||||
);
|
||||
assert!(
|
||||
body.contains("(allow process-fork)"),
|
||||
"synthetic seed body should land verbatim; got: {body}",
|
||||
);
|
||||
assert!(
|
||||
body.contains("(allow mach-lookup)"),
|
||||
"later seed rule should also appear verbatim; got: {body}",
|
||||
);
|
||||
|
||||
// The spliced profile should still parse as valid sandbox-exec
|
||||
// syntax when the host has the binary on PATH; skip when it
|
||||
// is missing (stripped CI images) since this assertion is the
|
||||
// only one that needs the live binary.
|
||||
if sandbox_exec_available() {
|
||||
let probe = std::process::Command::new("/usr/bin/sandbox-exec")
|
||||
.arg("-f")
|
||||
.arg(&materialised)
|
||||
.arg("-D")
|
||||
.arg("WORKDIR=/tmp")
|
||||
.arg("/usr/bin/true")
|
||||
.output()
|
||||
.expect("invoke sandbox-exec on spliced profile");
|
||||
assert!(
|
||||
probe.status.success(),
|
||||
"spliced profile should be valid sandbox-exec syntax; \
|
||||
status={:?}, stderr={}",
|
||||
probe.status,
|
||||
String::from_utf8_lossy(&probe.stderr),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue