mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-21 20:18:06 +02:00
feat: Implement dynamic verification layer with harness generation and payload orchestration
This commit is contained in:
parent
fb698d2c27
commit
56e934656c
10 changed files with 582 additions and 0 deletions
86
src/dynamic/verify.rs
Normal file
86
src/dynamic/verify.rs
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
//! Top-level entry point for the dynamic layer.
|
||||
//!
|
||||
//! The CLI subcommand and any library consumer call [`verify_finding`].
|
||||
//! It is the only function the rest of the crate needs to know about.
|
||||
|
||||
use crate::commands::scan::Diag;
|
||||
use crate::dynamic::report::{AttemptSummary, VerifyResult, VerifyStatus};
|
||||
use crate::dynamic::runner::{run_spec, RunError};
|
||||
use crate::dynamic::sandbox::SandboxOptions;
|
||||
use crate::dynamic::spec::HarnessSpec;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct VerifyOptions {
|
||||
pub sandbox: SandboxOptions,
|
||||
}
|
||||
|
||||
/// Try to dynamically confirm a static finding.
|
||||
///
|
||||
/// Never fails: every error path collapses into a [`VerifyStatus`] so the
|
||||
/// caller can treat dynamic verification as best-effort enrichment.
|
||||
pub fn verify_finding(diag: &Diag, opts: &VerifyOptions) -> VerifyResult {
|
||||
let finding_id = diag.id.clone();
|
||||
|
||||
let Some(spec) = HarnessSpec::from_finding(diag) else {
|
||||
return VerifyResult {
|
||||
finding_id,
|
||||
status: VerifyStatus::Unsupported,
|
||||
triggered_payload: None,
|
||||
reason: Some("no harness spec derivable from finding".into()),
|
||||
attempts: vec![],
|
||||
};
|
||||
};
|
||||
|
||||
match run_spec(&spec, &opts.sandbox) {
|
||||
Ok(run) => {
|
||||
let attempts = run
|
||||
.attempts
|
||||
.iter()
|
||||
.map(|a| AttemptSummary {
|
||||
payload_label: a.payload_label.to_string(),
|
||||
exit_code: a.outcome.exit_code,
|
||||
timed_out: a.outcome.timed_out,
|
||||
triggered: a.triggered,
|
||||
})
|
||||
.collect();
|
||||
|
||||
match run.triggered_by {
|
||||
Some(i) => VerifyResult {
|
||||
finding_id,
|
||||
status: VerifyStatus::Confirmed,
|
||||
triggered_payload: Some(run.attempts[i].payload_label.to_string()),
|
||||
reason: None,
|
||||
attempts,
|
||||
},
|
||||
None => VerifyResult {
|
||||
finding_id,
|
||||
status: VerifyStatus::NotConfirmed,
|
||||
triggered_payload: None,
|
||||
reason: None,
|
||||
attempts,
|
||||
},
|
||||
}
|
||||
}
|
||||
Err(RunError::NoPayloadsForCap) => VerifyResult {
|
||||
finding_id,
|
||||
status: VerifyStatus::Unsupported,
|
||||
triggered_payload: None,
|
||||
reason: Some("no payload corpus for sink cap".into()),
|
||||
attempts: vec![],
|
||||
},
|
||||
Err(RunError::Harness(e)) => VerifyResult {
|
||||
finding_id,
|
||||
status: VerifyStatus::Inconclusive,
|
||||
triggered_payload: None,
|
||||
reason: Some(format!("harness build failed: {e:?}")),
|
||||
attempts: vec![],
|
||||
},
|
||||
Err(RunError::Sandbox(e)) => VerifyResult {
|
||||
finding_id,
|
||||
status: VerifyStatus::Inconclusive,
|
||||
triggered_payload: None,
|
||||
reason: Some(format!("sandbox failed: {e:?}")),
|
||||
attempts: vec![],
|
||||
},
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue