[pitboss] phase 02: M2 — Python end-to-end excellence with all hardening baked in

This commit is contained in:
pitboss 2026-05-11 22:56:43 -04:00
parent 894f587b60
commit 0bf39047b9
50 changed files with 4167 additions and 170 deletions

View file

@ -4,26 +4,31 @@
//! It is the only function the rest of the crate needs to know about.
use crate::commands::scan::Diag;
use crate::dynamic::corpus::payloads_for;
use crate::dynamic::report::{AttemptSummary, VerifyResult, VerifyStatus};
use crate::dynamic::runner::{run_spec, RunError};
use crate::dynamic::sandbox::SandboxOptions;
use crate::dynamic::spec::HarnessSpec;
use crate::evidence::UnsupportedReason;
use crate::dynamic::telemetry::{self, TelemetryEvent};
use crate::dynamic::toolchain;
use crate::evidence::{InconclusiveReason, UnsupportedReason};
use crate::utils::config::Config;
use std::path::Path;
use std::time::Instant;
#[derive(Debug, Clone, Default)]
pub struct VerifyOptions {
pub sandbox: SandboxOptions,
/// Project root for repro artifact symlinks (optional).
pub project_root: Option<std::path::PathBuf>,
}
impl VerifyOptions {
/// Build `VerifyOptions` from scanner config.
///
/// Currently forwards sandbox timeout from `config.scanner`; future
/// milestones will add image/resource limits here.
pub fn from_config(_config: &Config) -> Self {
Self {
sandbox: SandboxOptions::default(),
project_root: None,
}
}
}
@ -33,8 +38,6 @@ impl VerifyOptions {
/// 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 {
// Use the stable hash to identify the finding so the VerifyResult's
// finding_id matches HarnessSpec::finding_id (both use the same hex form).
let finding_id = format!("{:016x}", diag.stable_hash);
let spec = match HarnessSpec::from_finding(diag) {
@ -45,18 +48,56 @@ pub fn verify_finding(diag: &Diag, opts: &VerifyOptions) -> VerifyResult {
status: VerifyStatus::Unsupported,
triggered_payload: None,
reason: Some(reason),
inconclusive_reason: None,
detail: None,
attempts: vec![],
toolchain_match: None,
};
}
};
// Spec derivable, but no backend implementation exists yet.
// Phase M1 always lands here; real execution starts in Phase M2.
let _ = &opts.sandbox;
match run_spec(&spec, &opts.sandbox) {
// Resolve toolchain information.
let toolchain_res = toolchain::resolve_python(Path::new("."));
let toolchain_match = if toolchain_res.toolchain_drift { "drift" } else { "exact" };
let start = Instant::now();
let result = run_spec(&spec, &opts.sandbox);
let elapsed = start.elapsed();
let verdict = build_verdict(
&finding_id,
&spec,
result,
toolchain_match,
opts,
elapsed,
);
// Emit telemetry (best-effort; never affects verdict).
let event = TelemetryEvent::new(
&spec,
verdict.status,
verdict.inconclusive_reason,
toolchain_match,
elapsed,
1, // build_attempts tracked in RunOutcome but not exposed here for simplicity
);
telemetry::emit(&event);
verdict
}
fn build_verdict(
finding_id: &str,
spec: &HarnessSpec,
result: Result<crate::dynamic::runner::RunOutcome, RunError>,
toolchain_match: &str,
opts: &VerifyOptions,
_elapsed: std::time::Duration,
) -> VerifyResult {
match result {
Ok(run) => {
let attempts = run
let attempts: Vec<AttemptSummary> = run
.attempts
.iter()
.map(|a| AttemptSummary {
@ -64,51 +105,138 @@ pub fn verify_finding(diag: &Diag, opts: &VerifyOptions) -> VerifyResult {
exit_code: a.outcome.exit_code,
timed_out: a.outcome.timed_out,
triggered: a.triggered,
sink_hit: a.outcome.sink_hit,
})
.collect();
match run.triggered_by {
Some(i) => VerifyResult {
finding_id,
if let Some(i) = run.triggered_by {
let triggered_payload = run.attempts[i].payload_label.to_string();
let payloads = payloads_for(spec.expected_cap);
let vuln_payloads: Vec<_> = payloads.iter().filter(|p| !p.is_benign).collect();
let payload_bytes = vuln_payloads
.get(i)
.map(|p| p.bytes)
.unwrap_or(b"");
// Emit repro artifact.
let repro_result = crate::dynamic::repro::write(
spec,
&opts.sandbox,
&run.attempts[i].outcome,
&VerifyResult {
finding_id: finding_id.to_owned(),
status: VerifyStatus::Confirmed,
triggered_payload: Some(triggered_payload.clone()),
reason: None,
inconclusive_reason: None,
detail: None,
attempts: attempts.clone(),
toolchain_match: Some(toolchain_match.to_owned()),
},
&run.harness_source,
&run.entry_source,
payload_bytes,
run.attempts[i].payload_label,
opts.project_root.as_deref(),
);
// If repro write fails, downgrade to NonReproducible.
if repro_result.is_err() {
return VerifyResult {
finding_id: finding_id.to_owned(),
status: VerifyStatus::Inconclusive,
triggered_payload: None,
reason: None,
inconclusive_reason: Some(InconclusiveReason::NonReproducible),
detail: Some(format!("repro write failed: {}", repro_result.unwrap_err())),
attempts,
toolchain_match: Some(toolchain_match.to_owned()),
};
}
VerifyResult {
finding_id: finding_id.to_owned(),
status: VerifyStatus::Confirmed,
triggered_payload: Some(run.attempts[i].payload_label.to_string()),
triggered_payload: Some(triggered_payload),
reason: None,
inconclusive_reason: None,
detail: None,
attempts,
},
None => VerifyResult {
finding_id,
toolchain_match: Some(toolchain_match.to_owned()),
}
} else if run.oracle_collision {
// Oracle fired but probe didn't — likely collision.
VerifyResult {
finding_id: finding_id.to_owned(),
status: VerifyStatus::Inconclusive,
triggered_payload: None,
reason: None,
inconclusive_reason: Some(InconclusiveReason::OracleCollisionSuspected),
detail: Some("oracle fired but sink-reachability probe did not".to_owned()),
attempts,
toolchain_match: Some(toolchain_match.to_owned()),
}
} else {
VerifyResult {
finding_id: finding_id.to_owned(),
status: VerifyStatus::NotConfirmed,
triggered_payload: None,
reason: None,
inconclusive_reason: None,
detail: None,
attempts,
},
toolchain_match: Some(toolchain_match.to_owned()),
}
}
}
Err(RunError::NoPayloadsForCap) => VerifyResult {
finding_id,
finding_id: finding_id.to_owned(),
status: VerifyStatus::Unsupported,
triggered_payload: None,
reason: Some(UnsupportedReason::NoPayloadsForCap),
inconclusive_reason: None,
detail: None,
attempts: vec![],
toolchain_match: None,
},
Err(RunError::Harness(_)) => VerifyResult {
finding_id,
status: VerifyStatus::Unsupported,
triggered_payload: None,
reason: Some(UnsupportedReason::BackendUnavailable),
detail: None,
attempts: vec![],
},
Err(RunError::Sandbox(e)) => VerifyResult {
finding_id,
Err(RunError::Harness(e)) => {
// Typed `Unsupported(reason)` carries its semantics in `reason`; the
// free-form `detail` is reserved for `Inconclusive`/unexpected paths
// (cf. §10 decision 14 and the verify_result_json_shape contract).
let (reason, detail) = match &e {
crate::dynamic::harness::HarnessError::Unsupported(r) => (Some(r.clone()), None),
_ => (Some(UnsupportedReason::BackendUnavailable), Some(format!("{e}"))),
};
VerifyResult {
finding_id: finding_id.to_owned(),
status: VerifyStatus::Unsupported,
triggered_payload: None,
reason,
inconclusive_reason: None,
detail,
attempts: vec![],
toolchain_match: None,
}
}
Err(RunError::BuildFailed { stderr, attempts: build_att }) => VerifyResult {
finding_id: finding_id.to_owned(),
status: VerifyStatus::Inconclusive,
triggered_payload: None,
reason: None,
inconclusive_reason: Some(InconclusiveReason::BuildFailed),
detail: Some(format!("build failed after {build_att} attempts: {stderr}")),
attempts: vec![],
toolchain_match: None,
},
Err(RunError::Sandbox(e)) => VerifyResult {
finding_id: finding_id.to_owned(),
status: VerifyStatus::Inconclusive,
triggered_payload: None,
reason: None,
inconclusive_reason: Some(InconclusiveReason::SandboxError),
detail: Some(format!("sandbox failed: {e:?}")),
attempts: vec![],
toolchain_match: None,
},
}
}