[pitboss/grind] deferred session-0030 (20260517T044708Z-e058)

This commit is contained in:
pitboss 2026-05-17 10:54:04 -05:00
parent 19d13a085d
commit b41b24c416
5 changed files with 124 additions and 9 deletions

View file

@ -307,14 +307,43 @@ fn source_ext_for_lang(lang: &crate::symbol::Lang) -> &'static str {
}
}
fn dockerfile_for_spec(spec: &HarnessSpec) -> String {
/// Resolve the `FROM` reference for `toolchain_id`.
///
/// Prefers the pinned digest from
/// [`crate::dynamic::toolchain::pinned_image_ref`] so the emitted
/// Dockerfile is hermetic across hosts. Falls back to a tag-only
/// reference derived from `toolchain_id` when the catalogue has no
/// digest for the toolchain.
fn resolve_dockerfile_from(spec: &HarnessSpec) -> String {
use crate::symbol::Lang;
if let Some(pinned) = crate::dynamic::toolchain::pinned_image_ref(&spec.toolchain_id) {
return pinned.to_owned();
}
match spec.lang {
Lang::Rust => {
let toolchain = spec.toolchain_id.strip_prefix("rust-").unwrap_or("stable");
format!("rust:{toolchain}-slim")
}
Lang::Python => {
format!("python:{}", spec.toolchain_id.strip_prefix("python-").unwrap_or("3"))
}
_ => "ubuntu:latest".to_owned(),
}
}
fn dockerfile_for_spec(spec: &HarnessSpec) -> String {
use crate::symbol::Lang;
let image = resolve_dockerfile_from(spec);
match spec.lang {
Lang::Rust => {
// Multi-stage: build with Rust, run the binary directly.
// The builder stage uses the resolved (pinned-or-tag) image;
// the runtime stage stays on debian:bookworm-slim because the
// resulting nyx_harness binary is self-contained.
format!(
"FROM rust:{toolchain}-slim AS builder\n\
"FROM {image} AS builder\n\
WORKDIR /harness\n\
COPY Cargo.toml Cargo.lock* ./\n\
COPY src/ src/\n\
@ -326,13 +355,12 @@ fn dockerfile_for_spec(spec: &HarnessSpec) -> String {
)
}
Lang::Python => {
let image = format!("python:{}", spec.toolchain_id.strip_prefix("python-").unwrap_or("3"));
format!(
"FROM {image}\nWORKDIR /harness\nCOPY harness.py .\nCMD [\"python3\", \"harness.py\"]\n"
)
}
_ => {
format!("# Unsupported language: {:?}\nFROM ubuntu:latest\n", spec.lang)
format!("# Unsupported language: {:?}\nFROM {image}\n", spec.lang)
}
}
}
@ -759,6 +787,47 @@ mod tests {
unsafe { std::env::remove_var("NYX_REPRO_BASE") };
}
#[test]
fn dockerfile_for_pinned_toolchain_uses_pinned_digest() {
// python-3.11 is in the image catalogue with a pinned digest, so the
// emitted Dockerfile must `FROM <base>@sha256:…` for hermeticity.
let spec = make_spec();
let pinned = crate::dynamic::toolchain::pinned_image_ref(&spec.toolchain_id)
.expect("python-3.11 should resolve to a pinned digest in images.toml");
assert!(
pinned.contains("@sha256:"),
"pinned_image_ref returned a non-pinned value: {pinned}",
);
let dockerfile = dockerfile_for_spec(&spec);
let expected_from = format!("FROM {pinned}");
assert!(
dockerfile.contains(&expected_from),
"dockerfile did not embed pinned digest;\n expected substring: {expected_from}\n got:\n{dockerfile}",
);
}
#[test]
fn dockerfile_falls_back_to_tag_when_toolchain_absent_from_catalogue() {
// Unpinned toolchain id: no entry in IMAGE_DIGESTS, so the emitter
// must fall back to a tag-only `FROM` so an operator can still build
// the bundle (with a docker_pull.sh that is not emitted in this case).
let mut spec = make_spec();
spec.toolchain_id = "python-2.7".into();
assert!(
crate::dynamic::toolchain::pinned_image_ref(&spec.toolchain_id).is_none(),
"test precondition: python-2.7 must NOT be in the catalogue",
);
let dockerfile = dockerfile_for_spec(&spec);
assert!(
dockerfile.contains("FROM python:2.7"),
"fallback dockerfile missing tag-only FROM line:\n{dockerfile}",
);
assert!(
!dockerfile.contains("@sha256:"),
"fallback dockerfile must not invent a digest:\n{dockerfile}",
);
}
#[test]
fn reproduce_sh_contains_toolchain_check_and_exit_codes() {
let dir = TempDir::new().unwrap();

View file

@ -85,6 +85,19 @@ pub struct VerifyOptions {
/// Default `false`. [`Self::from_config`] honours the
/// `NYX_VERIFY_REPLAY_STABLE` environment variable (`1` / `true`).
pub replay_stable_check: bool,
/// Phase 31 follow-up: when `true` and `replay_stable_check` is also
/// `true`, the verifier passes `--docker` to `reproduce.sh` instead of
/// running it through the host's process backend. Lets the eval-corpus
/// driver mark `replay_stable` based on the bare-image replay path so
/// the M7 ship-gate's Gate 5 reflects the docker bundle's green/red
/// signal — required when the corpus walks a host that has stripped
/// the language toolchains (the bare-image CI matrix at
/// `.github/workflows/repro-bare.yml`).
///
/// Default `false`. [`Self::from_config`] honours the
/// `NYX_VERIFY_REPLAY_DOCKER` environment variable (`1` / `true`).
/// The flag is inert when `replay_stable_check == false`.
pub replay_use_docker: bool,
}
impl VerifyOptions {
@ -141,6 +154,9 @@ impl VerifyOptions {
let replay_stable_check = std::env::var("NYX_VERIFY_REPLAY_STABLE")
.map(|v| matches!(v.as_str(), "1" | "true" | "TRUE"))
.unwrap_or(false);
let replay_use_docker = std::env::var("NYX_VERIFY_REPLAY_DOCKER")
.map(|v| matches!(v.as_str(), "1" | "true" | "TRUE"))
.unwrap_or(false);
Self {
sandbox: SandboxOptions {
@ -158,6 +174,7 @@ impl VerifyOptions {
telemetry_policy: SamplingPolicy::from_config(&config.telemetry),
trace_verbose: false,
replay_stable_check,
replay_use_docker,
}
}
}
@ -760,7 +777,8 @@ pub fn verify_finding(diag: &Diag, opts: &VerifyOptions) -> VerifyResult {
&& let Some(bundle) = crate::dynamic::repro::bundle_root_for(&spec.spec_hash)
&& bundle.join("reproduce.sh").exists()
{
let replay = crate::dynamic::repro::replay_bundle(&bundle, &[]);
let replay_args: &[&str] = if opts.replay_use_docker { &["--docker"] } else { &[] };
let replay = crate::dynamic::repro::replay_bundle(&bundle, replay_args);
verdict.replay_stable = crate::dynamic::repro::replay_stability(&replay);
}
@ -1273,6 +1291,33 @@ mod tests {
unsafe { std::env::remove_var("NYX_VERIFY_REPLAY_STABLE") };
}
#[test]
fn from_config_defaults_replay_use_docker_off() {
// Same hermeticity concern as `replay_stable_check`: clear any
// stale process-wide setting so the default is observable.
unsafe { std::env::remove_var("NYX_VERIFY_REPLAY_DOCKER") };
let opts = VerifyOptions::from_config(&Config::default());
assert!(
!opts.replay_use_docker,
"NYX_VERIFY_REPLAY_DOCKER absent must leave the opt-in off so \
interactive `nyx scan` does not require docker for the replay step"
);
}
#[test]
fn from_config_picks_up_replay_docker_env_flag() {
unsafe { std::env::set_var("NYX_VERIFY_REPLAY_DOCKER", "1") };
let opts = VerifyOptions::from_config(&Config::default());
assert!(opts.replay_use_docker);
unsafe { std::env::set_var("NYX_VERIFY_REPLAY_DOCKER", "true") };
let opts = VerifyOptions::from_config(&Config::default());
assert!(opts.replay_use_docker);
unsafe { std::env::set_var("NYX_VERIFY_REPLAY_DOCKER", "0") };
let opts = VerifyOptions::from_config(&Config::default());
assert!(!opts.replay_use_docker);
unsafe { std::env::remove_var("NYX_VERIFY_REPLAY_DOCKER") };
}
#[test]
fn from_config_defaults_process_hardening_to_standard() {
use crate::dynamic::sandbox::ProcessHardeningProfile;

View file

@ -258,8 +258,9 @@ fn python_3_11_flask_eval_bundle_structural_invariants() {
let dockerfile = std::fs::read_to_string(root.join("harness/Dockerfile.harness")).unwrap();
assert!(
dockerfile.contains("FROM python:3.11"),
"dockerfile missing pinned FROM line",
dockerfile.contains("FROM python:3.11-slim@sha256:"),
"dockerfile missing pinned FROM line (expected `FROM python:3.11-slim@sha256:…` so the \
bundle is hermetic across hosts); got:\n{dockerfile}",
);
let payload = std::fs::read(root.join("payload/payload.bin")).unwrap();

View file

@ -1,4 +1,4 @@
FROM python:3.11
FROM python:3.11-slim@sha256:9a7765b36773a37061455b332f18e265e7f58f6fea9c419a550d2a8b0e9db834
WORKDIR /harness
COPY harness.py .
CMD ["python3", "harness.py"]

View file

@ -1,7 +1,7 @@
{
"files": {
"entry/extracted_source.py": "d18631435ec059c8cabafe7854f18d45e06a5c62da6274710712cf862cf9afa8",
"harness/Dockerfile.harness": "88bfe406a6305222207469e68777e09e68c558e66b4b15ca7f31670cb74f91b5",
"harness/Dockerfile.harness": "9ae78bdafc9cf11e9530f8c88deebc62b4c754c7ffa4759a40c80049c5a84586",
"harness/harness.py": "15cc817251cf0c8915be782996b4af9b5b456f0b8fd75c360dcda153e071961c",
"payload/payload.bin": "f3dc1d1a3d5a282cb6f171544ad5c8a5e78a6065a6decf6955c20763302bd574"
},