mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-09 19:45:13 +02:00
[pitboss] sweep after phase 02: 2 deferred items resolved
This commit is contained in:
parent
3ebdb5e33b
commit
fdb42c0b75
4 changed files with 107 additions and 3 deletions
|
|
@ -67,6 +67,7 @@ fn make_rust_sqli_spec() -> HarnessSpec {
|
|||
spec_hash: "benchrustsqli0001".into(),
|
||||
derivation: SpecDerivationStrategy::FromFlowSteps,
|
||||
stubs_required: vec![],
|
||||
framework: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -87,6 +88,7 @@ fn make_sqli_spec() -> HarnessSpec {
|
|||
spec_hash: "benchsqli000001".into(),
|
||||
derivation: SpecDerivationStrategy::FromFlowSteps,
|
||||
stubs_required: vec![],
|
||||
framework: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -285,6 +287,7 @@ fn make_js_sqli_spec() -> HarnessSpec {
|
|||
spec_hash: "benchjssqli000001".into(),
|
||||
derivation: SpecDerivationStrategy::FromFlowSteps,
|
||||
stubs_required: vec![],
|
||||
framework: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -305,6 +308,7 @@ fn make_go_sqli_spec() -> HarnessSpec {
|
|||
spec_hash: "benchgosqli000001".into(),
|
||||
derivation: SpecDerivationStrategy::FromFlowSteps,
|
||||
stubs_required: vec![],
|
||||
framework: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -325,6 +329,7 @@ fn make_java_sqli_spec() -> HarnessSpec {
|
|||
spec_hash: "benchjavasqli00001".into(),
|
||||
derivation: SpecDerivationStrategy::FromFlowSteps,
|
||||
stubs_required: vec![],
|
||||
framework: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -345,6 +350,7 @@ fn make_php_sqli_spec() -> HarnessSpec {
|
|||
spec_hash: "benchphpsqli000001".into(),
|
||||
derivation: SpecDerivationStrategy::FromFlowSteps,
|
||||
stubs_required: vec![],
|
||||
framework: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -98,6 +98,13 @@ pub struct VerifyOptions {
|
|||
/// `NYX_VERIFY_REPLAY_DOCKER` environment variable (`1` / `true`).
|
||||
/// The flag is inert when `replay_stable_check == false`.
|
||||
pub replay_use_docker: bool,
|
||||
/// Test/observability hook: when `Some`, [`verify_finding`] records
|
||||
/// every [`crate::dynamic::trace::TraceEvent`] into this trace handle
|
||||
/// instead of constructing a fresh internal one. Lets integration
|
||||
/// tests inspect the verifier's stage timeline (e.g. the Track L.0
|
||||
/// `framework_adapter_*` events) without scraping stderr or writing
|
||||
/// a repro bundle. `None` in production paths.
|
||||
pub trace_sink: Option<Arc<crate::dynamic::trace::VerifyTrace>>,
|
||||
}
|
||||
|
||||
impl VerifyOptions {
|
||||
|
|
@ -175,6 +182,7 @@ impl VerifyOptions {
|
|||
trace_verbose: false,
|
||||
replay_stable_check,
|
||||
replay_use_docker,
|
||||
trace_sink: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -483,7 +491,14 @@ pub fn verify_finding(diag: &Diag, opts: &VerifyOptions) -> VerifyResult {
|
|||
// Phase 30 (Track C observability): one trace per finding, threaded
|
||||
// into [`SandboxOptions`] so the runner can append `build_*` /
|
||||
// `sandbox_started` / `oracle_*` stages from inside `run_spec`.
|
||||
let trace = Arc::new(crate::dynamic::trace::VerifyTrace::new());
|
||||
//
|
||||
// Tests may pre-seed `opts.trace_sink` with their own `Arc<VerifyTrace>`
|
||||
// handle; when present we reuse it instead of allocating a fresh one
|
||||
// so assertions can inspect the recorded stages after the call returns.
|
||||
let trace = opts
|
||||
.trace_sink
|
||||
.clone()
|
||||
.unwrap_or_else(|| Arc::new(crate::dynamic::trace::VerifyTrace::new()));
|
||||
trace.record(
|
||||
crate::dynamic::trace::TraceStage::SpecStarted,
|
||||
Some(format!("rule={} path={}", diag.id, diag.path)),
|
||||
|
|
|
|||
|
|
@ -149,6 +149,20 @@ mod escape_tests {
|
|||
assert_no_escape(result, $fixture, None);
|
||||
}
|
||||
};
|
||||
($name:ident, $fixture:literal, linux_only) => {
|
||||
// macOS Docker Desktop does not enforce host /tmp isolation or
|
||||
// pid-cgroup limits the way the Linux backend does, so these
|
||||
// fixtures escape on macOS. The `linux-with-docker` CI row is
|
||||
// the authoritative gate (see module docstring).
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn $name() {
|
||||
if !docker_available() { return; }
|
||||
let (_tmpdir, harness) = harness_for_fixture($fixture);
|
||||
let result = sandbox::run(&harness, &noop_payload(), &escape_opts());
|
||||
assert_no_escape(result, $fixture, None);
|
||||
}
|
||||
};
|
||||
($name:ident, $fixture:literal, marker = $marker:expr) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
|
|
@ -163,14 +177,28 @@ mod escape_tests {
|
|||
let _ = fs::remove_file(&marker);
|
||||
}
|
||||
};
|
||||
($name:ident, $fixture:literal, marker = $marker:expr, linux_only) => {
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn $name() {
|
||||
if !docker_available() { return; }
|
||||
let marker: PathBuf = PathBuf::from($marker);
|
||||
let _ = fs::remove_file(&marker);
|
||||
let (_tmpdir, harness) = harness_for_fixture($fixture);
|
||||
let result = sandbox::run(&harness, &noop_payload(), &escape_opts());
|
||||
assert_no_escape(result, $fixture, Some(&marker));
|
||||
let _ = fs::remove_file(&marker);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
escape_test!(
|
||||
escape_file_write_outside_workdir,
|
||||
"file_write_outside_workdir.py",
|
||||
marker = "/tmp/nyx_escape_file_write_marker"
|
||||
marker = "/tmp/nyx_escape_file_write_marker",
|
||||
linux_only
|
||||
);
|
||||
escape_test!(escape_fork_bomb, "fork_bomb.py");
|
||||
escape_test!(escape_fork_bomb, "fork_bomb.py", linux_only);
|
||||
escape_test!(escape_raw_socket, "raw_socket.py");
|
||||
escape_test!(escape_proc_mem_write, "proc_mem_write.py");
|
||||
escape_test!(escape_ptrace_attach, "ptrace_attach.py");
|
||||
|
|
|
|||
|
|
@ -158,6 +158,61 @@ mod verify_e2e {
|
|||
assert_eq!(result.reason, Some(UnsupportedReason::ConfidenceTooLow));
|
||||
}
|
||||
|
||||
/// Phase 01 / Track L.0 acceptance: every spec the verifier
|
||||
/// finalises must emit either `framework_adapter_detected` or
|
||||
/// `framework_adapter_none` into the [`VerifyTrace`]. The Phase 01
|
||||
/// adapter registry is empty, so the baseline contract is that
|
||||
/// every successfully-derived spec records a `framework_adapter_none`
|
||||
/// event whose `detail` carries `lang=<Lang> entry=<entry_name>`.
|
||||
///
|
||||
/// We drive `verify_finding` through the `NoPayloadsForCap` short-circuit
|
||||
/// (CRYPTO has no curated payload corpus) so the trace is recorded
|
||||
/// without needing a working toolchain or sandbox backend.
|
||||
#[test]
|
||||
fn verify_finding_emits_framework_adapter_none_for_empty_registry() {
|
||||
use nyx_scanner::dynamic::trace::{TraceStage, VerifyTrace};
|
||||
use std::sync::Arc;
|
||||
|
||||
let diag = taint_diag_with_cap(Cap::CRYPTO);
|
||||
let trace = Arc::new(VerifyTrace::new());
|
||||
let mut opts = VerifyOptions::default();
|
||||
opts.trace_sink = Some(Arc::clone(&trace));
|
||||
|
||||
let _result = verify_finding(&diag, &opts);
|
||||
|
||||
let events = trace.events();
|
||||
let adapter_event = events
|
||||
.iter()
|
||||
.find(|e| e.stage == TraceStage::FrameworkAdapterNone)
|
||||
.expect(
|
||||
"Phase 01 / Track L.0 contract: every finalised spec must emit \
|
||||
a `framework_adapter_none` event when the adapter registry is empty",
|
||||
);
|
||||
let detail = adapter_event
|
||||
.detail
|
||||
.as_deref()
|
||||
.expect("framework_adapter_none must carry a detail string");
|
||||
assert!(
|
||||
detail.contains("lang="),
|
||||
"framework_adapter_none detail must include `lang=…`, got: {detail:?}"
|
||||
);
|
||||
assert!(
|
||||
detail.contains("entry="),
|
||||
"framework_adapter_none detail must include `entry=…`, got: {detail:?}"
|
||||
);
|
||||
assert!(
|
||||
detail.contains("entry=handle_request"),
|
||||
"framework_adapter_none detail must name the spec's entry function, got: {detail:?}"
|
||||
);
|
||||
assert!(
|
||||
!events
|
||||
.iter()
|
||||
.any(|e| e.stage == TraceStage::FrameworkAdapterDetected),
|
||||
"Phase 01 ships zero adapters, so no `framework_adapter_detected` event \
|
||||
can fire on the baseline path"
|
||||
);
|
||||
}
|
||||
|
||||
/// The JSON shape of `VerifyResult` for an evidence-less finding
|
||||
/// matches the documented contract: `status` present; transient
|
||||
/// fields like `triggered_payload`, `detail`, `attempts` absent
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue