mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-09 19:45:13 +02:00
[pitboss] phase 26: Track G.3 — End-to-end chain re-verification
This commit is contained in:
parent
4228be2db6
commit
8a801953e2
21 changed files with 991 additions and 15 deletions
|
|
@ -27,7 +27,7 @@
|
|||
//! - `PayloadSlot::EnvVar(name)` — set env var before invoking entry.
|
||||
//! - `PayloadSlot::Argv(n)` — `main(argc, argv)` shape: appended to argv.
|
||||
|
||||
use crate::dynamic::lang::{HarnessSource, LangEmitter};
|
||||
use crate::dynamic::lang::{ChainStepHarness, HarnessSource, LangEmitter};
|
||||
use crate::dynamic::spec::{EntryKind, HarnessSpec, PayloadSlot};
|
||||
use crate::evidence::UnsupportedReason;
|
||||
use std::path::PathBuf;
|
||||
|
|
@ -307,6 +307,28 @@ impl LangEmitter for CEmitter {
|
|||
"c emitter supports {SUPPORTED:?}; this finding's enclosing context is `EntryKind::{attempted}` — see Phase 16 shape dispatch (main / libFuzzer / free function)"
|
||||
)
|
||||
}
|
||||
|
||||
fn compose_chain_step(&self, prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
chain_step(prev_output)
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 26 — C chain-step harness.
|
||||
fn chain_step(prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
let source = "#include <stdio.h>\n#include <stdlib.h>\n\nint main(void) {\n const char *prev = getenv(\"NYX_PREV_OUTPUT\");\n if (prev) fputs(prev, stdout);\n return 0;\n}\n".to_owned();
|
||||
ChainStepHarness {
|
||||
source,
|
||||
filename: "step.c".to_owned(),
|
||||
command: vec!["cc".to_owned(), "step.c".to_owned(), "-o".to_owned(), "step".to_owned()],
|
||||
extra_env: prev_output
|
||||
.map(|bytes| {
|
||||
vec![(
|
||||
ChainStepHarness::PREV_OUTPUT_ENV.to_owned(),
|
||||
String::from_utf8_lossy(bytes).into_owned(),
|
||||
)]
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit a C harness for `spec`.
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
//! Build step: `prepare_cpp()` in `build_sandbox.rs` runs
|
||||
//! `g++ -O0 -std=c++17 -o nyx_harness main.cpp` in the workdir.
|
||||
|
||||
use crate::dynamic::lang::{HarnessSource, LangEmitter};
|
||||
use crate::dynamic::lang::{ChainStepHarness, HarnessSource, LangEmitter};
|
||||
use crate::dynamic::spec::{EntryKind, HarnessSpec, PayloadSlot};
|
||||
use crate::evidence::UnsupportedReason;
|
||||
use std::path::PathBuf;
|
||||
|
|
@ -280,6 +280,28 @@ impl LangEmitter for CppEmitter {
|
|||
"cpp emitter supports {SUPPORTED:?}; this finding's enclosing context is `EntryKind::{attempted}` — see Phase 16 shape dispatch (main / libFuzzer / free function)"
|
||||
)
|
||||
}
|
||||
|
||||
fn compose_chain_step(&self, prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
chain_step(prev_output)
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 26 — C++ chain-step harness.
|
||||
fn chain_step(prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
let source = "#include <cstdio>\n#include <cstdlib>\n\nint main() {\n const char *prev = std::getenv(\"NYX_PREV_OUTPUT\");\n if (prev) std::fputs(prev, stdout);\n return 0;\n}\n".to_owned();
|
||||
ChainStepHarness {
|
||||
source,
|
||||
filename: "step.cpp".to_owned(),
|
||||
command: vec!["c++".to_owned(), "step.cpp".to_owned(), "-o".to_owned(), "step".to_owned()],
|
||||
extra_env: prev_output
|
||||
.map(|bytes| {
|
||||
vec![(
|
||||
ChainStepHarness::PREV_OUTPUT_ENV.to_owned(),
|
||||
String::from_utf8_lossy(bytes).into_owned(),
|
||||
)]
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit a C++ harness for `spec`.
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
//! Build container: `nyx-build-go:{toolchain_id}` (deferred; §19.1).
|
||||
|
||||
use crate::dynamic::environment::{Environment, RuntimeArtifacts};
|
||||
use crate::dynamic::lang::{HarnessSource, LangEmitter};
|
||||
use crate::dynamic::lang::{ChainStepHarness, HarnessSource, LangEmitter};
|
||||
use crate::dynamic::spec::{EntryKind, HarnessSpec, PayloadSlot};
|
||||
use crate::evidence::UnsupportedReason;
|
||||
use std::path::PathBuf;
|
||||
|
|
@ -75,6 +75,35 @@ impl LangEmitter for GoEmitter {
|
|||
fn materialize_runtime(&self, env: &Environment) -> RuntimeArtifacts {
|
||||
materialize_go(env)
|
||||
}
|
||||
|
||||
fn compose_chain_step(&self, prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
chain_step(prev_output)
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 26 — Go chain-step harness.
|
||||
///
|
||||
/// Emits a `main.go` driver that reads `NYX_PREV_OUTPUT` and forwards it
|
||||
/// on stdout. The Go probe shim (`__nyx_probe`) is top-level Go code
|
||||
/// requiring extra stdlib imports; chain steps keep the harness minimal
|
||||
/// and rely on the sandbox runner's outer probe channel to observe the
|
||||
/// final sink fire. Wiring the probe shim into chain steps is tracked
|
||||
/// alongside the Phase 15 emitter follow-up about probe shim splicing.
|
||||
fn chain_step(prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
let source = "package main\n\nimport (\n \"fmt\"\n \"os\"\n)\n\nfunc main() {\n prev := os.Getenv(\"NYX_PREV_OUTPUT\")\n fmt.Print(prev)\n}\n".to_owned();
|
||||
ChainStepHarness {
|
||||
source,
|
||||
filename: "step.go".to_owned(),
|
||||
command: vec!["go".to_owned(), "run".to_owned(), "step.go".to_owned()],
|
||||
extra_env: prev_output
|
||||
.map(|bytes| {
|
||||
vec![(
|
||||
ChainStepHarness::PREV_OUTPUT_ENV.to_owned(),
|
||||
String::from_utf8_lossy(bytes).into_owned(),
|
||||
)]
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
// ── Phase 15: shape detector ─────────────────────────────────────────────────
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
//! Build container: `nyx-build-java:{toolchain_id}` (deferred; §19.1).
|
||||
|
||||
use crate::dynamic::environment::{Environment, RuntimeArtifacts};
|
||||
use crate::dynamic::lang::{HarnessSource, LangEmitter};
|
||||
use crate::dynamic::lang::{ChainStepHarness, HarnessSource, LangEmitter};
|
||||
use crate::dynamic::spec::{EntryKind, HarnessSpec, PayloadSlot};
|
||||
use crate::evidence::UnsupportedReason;
|
||||
use std::path::PathBuf;
|
||||
|
|
@ -74,6 +74,34 @@ impl LangEmitter for JavaEmitter {
|
|||
fn materialize_runtime(&self, env: &Environment) -> RuntimeArtifacts {
|
||||
materialize_java(env)
|
||||
}
|
||||
|
||||
fn compose_chain_step(&self, prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
chain_step(prev_output)
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 26 — Java chain-step harness.
|
||||
///
|
||||
/// Emits a `Step.java` class whose `main` reads `NYX_PREV_OUTPUT` and
|
||||
/// forwards it on stdout. The Java probe shim is class-level and
|
||||
/// requires `System`/`java.io.*` imports the chain step already pulls in
|
||||
/// implicitly; wiring the full shim is tracked alongside the Phase 14
|
||||
/// emitter follow-up about probe shim splicing.
|
||||
fn chain_step(prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
let source = "public class Step {\n public static void main(String[] args) {\n String prev = System.getenv(\"NYX_PREV_OUTPUT\");\n if (prev == null) prev = \"\";\n System.out.print(prev);\n }\n}\n".to_owned();
|
||||
ChainStepHarness {
|
||||
source,
|
||||
filename: "Step.java".to_owned(),
|
||||
command: vec!["java".to_owned(), "Step".to_owned()],
|
||||
extra_env: prev_output
|
||||
.map(|bytes| {
|
||||
vec![(
|
||||
ChainStepHarness::PREV_OUTPUT_ENV.to_owned(),
|
||||
String::from_utf8_lossy(bytes).into_owned(),
|
||||
)]
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
// ── Phase 14: shape detector ─────────────────────────────────────────────────
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
//! - [`PayloadSlot::Argv`] — coerced to positional `Param(0)` by build_call.
|
||||
|
||||
use crate::dynamic::environment::{Environment, RuntimeArtifacts};
|
||||
use crate::dynamic::lang::{js_shared, HarnessSource, LangEmitter};
|
||||
use crate::dynamic::lang::{js_shared, ChainStepHarness, HarnessSource, LangEmitter};
|
||||
use crate::dynamic::spec::{EntryKind, HarnessSpec};
|
||||
use crate::evidence::UnsupportedReason;
|
||||
|
||||
|
|
@ -43,6 +43,10 @@ impl LangEmitter for JavaScriptEmitter {
|
|||
fn materialize_runtime(&self, env: &Environment) -> RuntimeArtifacts {
|
||||
materialize_node(env)
|
||||
}
|
||||
|
||||
fn compose_chain_step(&self, prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
js_shared::chain_step(prev_output, /* typescript = */ false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit a JS harness for `spec`.
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
//! which preserves the pre-Phase-13 behaviour.
|
||||
|
||||
use crate::dynamic::environment::{Environment, RuntimeArtifacts};
|
||||
use crate::dynamic::lang::HarnessSource;
|
||||
use crate::dynamic::lang::{ChainStepHarness, HarnessSource};
|
||||
use crate::dynamic::spec::{EntryKind, HarnessSpec, PayloadSlot};
|
||||
use crate::evidence::UnsupportedReason;
|
||||
use crate::utils::project::DetectedFramework;
|
||||
|
|
@ -394,6 +394,41 @@ pub fn emit(spec: &HarnessSpec, is_typescript: bool) -> Result<HarnessSource, Un
|
|||
})
|
||||
}
|
||||
|
||||
/// Phase 26 — Node chain-step harness (shared between JS + TS emitters).
|
||||
///
|
||||
/// Splices the Node probe shim ([`probe_shim`]) in front of a minimal
|
||||
/// driver that reads `NYX_PREV_OUTPUT` and forwards it on stdout. The
|
||||
/// composite re-verifier swaps the trailing forward for the next member's
|
||||
/// payload-injection prologue when running a multi-step chain.
|
||||
pub fn chain_step(prev_output: Option<&[u8]>, is_typescript: bool) -> ChainStepHarness {
|
||||
let probe = probe_shim();
|
||||
let driver = "\nprocess.stdout.write(process.env.NYX_PREV_OUTPUT || '');\n";
|
||||
let (filename, command) = if is_typescript {
|
||||
(
|
||||
"step.ts".to_owned(),
|
||||
vec!["node".to_owned(), "step.ts".to_owned()],
|
||||
)
|
||||
} else {
|
||||
(
|
||||
"step.js".to_owned(),
|
||||
vec!["node".to_owned(), "step.js".to_owned()],
|
||||
)
|
||||
};
|
||||
ChainStepHarness {
|
||||
source: format!("{probe}{driver}"),
|
||||
filename,
|
||||
command,
|
||||
extra_env: prev_output
|
||||
.map(|bytes| {
|
||||
vec![(
|
||||
ChainStepHarness::PREV_OUTPUT_ENV.to_owned(),
|
||||
String::from_utf8_lossy(bytes).into_owned(),
|
||||
)]
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Public wrapper to detect the shape for a finalised [`HarnessSpec`].
|
||||
pub fn detect_shape(spec: &HarnessSpec) -> JsShape {
|
||||
let entry_source = read_entry_source(&spec.entry_file);
|
||||
|
|
|
|||
|
|
@ -48,6 +48,33 @@ pub struct HarnessSource {
|
|||
pub entry_subpath: Option<String>,
|
||||
}
|
||||
|
||||
/// Phase 26 — one step in a chain-composite harness.
|
||||
///
|
||||
/// The composite re-verifier walks every member of a chain and assembles
|
||||
/// a sequence of per-step harnesses. Each step is invoked with the
|
||||
/// previous step's stdout threaded into the
|
||||
/// [`ChainStepHarness::PREV_OUTPUT_ENV`] env var so the harness can fold
|
||||
/// the chained input into its payload (e.g. browser-fetch → websocket
|
||||
/// message → shell tool).
|
||||
///
|
||||
/// `extra_env` is additive on top of the sandbox's own
|
||||
/// [`crate::dynamic::sandbox::SandboxOptions::extra_env`]; the runner is
|
||||
/// responsible for splicing both in.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ChainStepHarness {
|
||||
pub source: String,
|
||||
pub filename: String,
|
||||
pub command: Vec<String>,
|
||||
pub extra_env: Vec<(String, String)>,
|
||||
}
|
||||
|
||||
impl ChainStepHarness {
|
||||
/// Env-var name the previous step's stdout is bound to in the next
|
||||
/// step's environment. Stable surface — kept distinct from
|
||||
/// `NYX_PAYLOAD` so a chain step can read both at once.
|
||||
pub const PREV_OUTPUT_ENV: &'static str = "NYX_PREV_OUTPUT";
|
||||
}
|
||||
|
||||
/// Per-language harness emitter contract.
|
||||
///
|
||||
/// Implementations are zero-sized unit structs (one per `src/dynamic/lang/*.rs`
|
||||
|
|
@ -96,6 +123,49 @@ pub trait LangEmitter {
|
|||
fn materialize_runtime(&self, _env: &Environment) -> RuntimeArtifacts {
|
||||
RuntimeArtifacts::default()
|
||||
}
|
||||
|
||||
/// Phase 26 — Track G.3: build one step of a chain-composite harness.
|
||||
///
|
||||
/// `prev_output` carries the previous step's stdout (or `None` for
|
||||
/// the chain's entry step). The returned [`ChainStepHarness`]
|
||||
/// reads `NYX_PREV_OUTPUT` from its env to fold the chained input
|
||||
/// into the step's behaviour and (when the step terminates at a
|
||||
/// sink) invokes the Phase 06 `__nyx_probe` shim so the runner's
|
||||
/// probe channel observes the sink fire.
|
||||
///
|
||||
/// Default impl produces a portable POSIX-shell stub that echoes
|
||||
/// the previous step's output verbatim. Concrete emitters override
|
||||
/// to splice in the language-native probe shim.
|
||||
fn compose_chain_step(&self, prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
default_chain_step(prev_output)
|
||||
}
|
||||
}
|
||||
|
||||
/// Default chain-step harness. Emitted by [`LangEmitter::compose_chain_step`]
|
||||
/// when an emitter does not override the trait method.
|
||||
pub fn default_chain_step(prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
ChainStepHarness {
|
||||
source: "#!/bin/sh\nprintf '%s' \"${NYX_PREV_OUTPUT:-}\"\n".to_owned(),
|
||||
filename: "step.sh".to_owned(),
|
||||
command: vec!["sh".to_owned(), "step.sh".to_owned()],
|
||||
extra_env: prev_output
|
||||
.map(|bytes| {
|
||||
vec![(
|
||||
ChainStepHarness::PREV_OUTPUT_ENV.to_owned(),
|
||||
String::from_utf8_lossy(bytes).into_owned(),
|
||||
)]
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Public free-fn dispatcher for [`LangEmitter::compose_chain_step`].
|
||||
///
|
||||
/// Returns the lang-agnostic shell stub when `lang` has no registered
|
||||
/// emitter so callers do not need to special-case that path.
|
||||
pub fn compose_chain_step(lang: Lang, prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
dispatch(lang, |e| e.compose_chain_step(prev_output))
|
||||
.unwrap_or_else(|| default_chain_step(prev_output))
|
||||
}
|
||||
|
||||
/// Public free-fn dispatcher for [`LangEmitter::materialize_runtime`].
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
//! Build container: `nyx-build-php:{toolchain_id}` (deferred; §19.1).
|
||||
|
||||
use crate::dynamic::environment::{Environment, RuntimeArtifacts};
|
||||
use crate::dynamic::lang::{HarnessSource, LangEmitter};
|
||||
use crate::dynamic::lang::{ChainStepHarness, HarnessSource, LangEmitter};
|
||||
use crate::dynamic::spec::{EntryKind, HarnessSpec, PayloadSlot};
|
||||
use crate::evidence::UnsupportedReason;
|
||||
use std::path::PathBuf;
|
||||
|
|
@ -67,6 +67,33 @@ impl LangEmitter for PhpEmitter {
|
|||
fn materialize_runtime(&self, env: &Environment) -> RuntimeArtifacts {
|
||||
materialize_php(env)
|
||||
}
|
||||
|
||||
fn compose_chain_step(&self, prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
chain_step(prev_output)
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 26 — PHP chain-step harness.
|
||||
///
|
||||
/// Emits a `step.php` script that reads `NYX_PREV_OUTPUT` via
|
||||
/// `getenv()` and forwards it on stdout. The PHP probe shim is kept
|
||||
/// outside the chain step for now and wired in alongside the Phase 15
|
||||
/// emitter follow-up about probe shim splicing.
|
||||
fn chain_step(prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
let source = "<?php\n$prev = getenv(\"NYX_PREV_OUTPUT\");\nif ($prev === false) { $prev = \"\"; }\necho $prev;\n".to_owned();
|
||||
ChainStepHarness {
|
||||
source,
|
||||
filename: "step.php".to_owned(),
|
||||
command: vec!["php".to_owned(), "step.php".to_owned()],
|
||||
extra_env: prev_output
|
||||
.map(|bytes| {
|
||||
vec![(
|
||||
ChainStepHarness::PREV_OUTPUT_ENV.to_owned(),
|
||||
String::from_utf8_lossy(bytes).into_owned(),
|
||||
)]
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
// ── Phase 15: shape detector ─────────────────────────────────────────────────
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
//! - Other slots produce [`UnsupportedReason::PayloadSlotUnsupported`].
|
||||
|
||||
use crate::dynamic::environment::{Environment, RuntimeArtifacts};
|
||||
use crate::dynamic::lang::{HarnessSource, LangEmitter};
|
||||
use crate::dynamic::lang::{ChainStepHarness, HarnessSource, LangEmitter};
|
||||
use crate::dynamic::spec::{EntryKind, HarnessSpec, PayloadSlot};
|
||||
use crate::evidence::UnsupportedReason;
|
||||
use crate::utils::project::DetectedFramework;
|
||||
|
|
@ -65,6 +65,34 @@ impl LangEmitter for PythonEmitter {
|
|||
fn materialize_runtime(&self, env: &Environment) -> RuntimeArtifacts {
|
||||
materialize_python(env)
|
||||
}
|
||||
|
||||
fn compose_chain_step(&self, prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
chain_step(prev_output)
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 26 — Python chain-step harness.
|
||||
///
|
||||
/// Splices the Python probe shim ([`probe_shim`]) in front of a minimal
|
||||
/// driver that reads `NYX_PREV_OUTPUT` and forwards it on stdout. The
|
||||
/// composite re-verifier swaps the trailing forward for the next member's
|
||||
/// payload-injection prologue when running a multi-step chain.
|
||||
fn chain_step(prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
let probe = probe_shim();
|
||||
let driver = "\nimport os, sys\nprev = os.environ.get('NYX_PREV_OUTPUT', '')\nsys.stdout.write(prev)\nsys.stdout.flush()\n";
|
||||
ChainStepHarness {
|
||||
source: format!("{probe}{driver}"),
|
||||
filename: "step.py".to_owned(),
|
||||
command: vec!["python3".to_owned(), "step.py".to_owned()],
|
||||
extra_env: prev_output
|
||||
.map(|bytes| {
|
||||
vec![(
|
||||
ChainStepHarness::PREV_OUTPUT_ENV.to_owned(),
|
||||
String::from_utf8_lossy(bytes).into_owned(),
|
||||
)]
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
// ── Phase 12: shape detector ─────────────────────────────────────────────────
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
//! Build: no compilation step. Command is `ruby harness.rb`.
|
||||
|
||||
use crate::dynamic::environment::{Environment, RuntimeArtifacts};
|
||||
use crate::dynamic::lang::{HarnessSource, LangEmitter};
|
||||
use crate::dynamic::lang::{ChainStepHarness, HarnessSource, LangEmitter};
|
||||
use crate::dynamic::spec::{EntryKind, HarnessSpec, PayloadSlot};
|
||||
use crate::evidence::UnsupportedReason;
|
||||
use std::path::PathBuf;
|
||||
|
|
@ -64,6 +64,28 @@ impl LangEmitter for RubyEmitter {
|
|||
fn materialize_runtime(&self, env: &Environment) -> RuntimeArtifacts {
|
||||
materialize_ruby(env)
|
||||
}
|
||||
|
||||
fn compose_chain_step(&self, prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
chain_step(prev_output)
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 26 — Ruby chain-step harness.
|
||||
fn chain_step(prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
let source = "prev = ENV[\"NYX_PREV_OUTPUT\"] || \"\"\n$stdout.write(prev)\n".to_owned();
|
||||
ChainStepHarness {
|
||||
source,
|
||||
filename: "step.rb".to_owned(),
|
||||
command: vec!["ruby".to_owned(), "step.rb".to_owned()],
|
||||
extra_env: prev_output
|
||||
.map(|bytes| {
|
||||
vec![(
|
||||
ChainStepHarness::PREV_OUTPUT_ENV.to_owned(),
|
||||
String::from_utf8_lossy(bytes).into_owned(),
|
||||
)]
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
// ── Phase 15: shape detector ─────────────────────────────────────────────────
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
//! HTML_ESCAPE is n/a for Rust (§15.4).
|
||||
|
||||
use crate::dynamic::environment::{Environment, RuntimeArtifacts};
|
||||
use crate::dynamic::lang::{HarnessSource, LangEmitter};
|
||||
use crate::dynamic::lang::{ChainStepHarness, HarnessSource, LangEmitter};
|
||||
use crate::dynamic::spec::{EntryKind, HarnessSpec, PayloadSlot};
|
||||
use crate::evidence::UnsupportedReason;
|
||||
use crate::labels::Cap;
|
||||
|
|
@ -63,6 +63,34 @@ impl LangEmitter for RustEmitter {
|
|||
fn materialize_runtime(&self, env: &Environment) -> RuntimeArtifacts {
|
||||
materialize_rust(env)
|
||||
}
|
||||
|
||||
fn compose_chain_step(&self, prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
chain_step(prev_output)
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 26 — Rust chain-step harness.
|
||||
///
|
||||
/// Emits a minimal `step.rs` file that reads `NYX_PREV_OUTPUT` and writes
|
||||
/// it on stdout. The chain composer drives the step with `rustc step.rs`
|
||||
/// (single-file build) — full Cargo crate scaffolding is reserved for
|
||||
/// chain members whose underlying finding already produced a HarnessSpec
|
||||
/// via the standard emit path.
|
||||
fn chain_step(prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
let source = "use std::env;\nuse std::io::{self, Write};\n\nfn main() {\n let prev = env::var(\"NYX_PREV_OUTPUT\").unwrap_or_default();\n let _ = io::stdout().write_all(prev.as_bytes());\n}\n".to_owned();
|
||||
ChainStepHarness {
|
||||
source,
|
||||
filename: "step.rs".to_owned(),
|
||||
command: vec!["rustc".to_owned(), "step.rs".to_owned(), "-o".to_owned(), "step".to_owned()],
|
||||
extra_env: prev_output
|
||||
.map(|bytes| {
|
||||
vec![(
|
||||
ChainStepHarness::PREV_OUTPUT_ENV.to_owned(),
|
||||
String::from_utf8_lossy(bytes).into_owned(),
|
||||
)]
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 09 — Track D.2: synthesise a `Cargo.toml` that pins every
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
//! runtime ignores.
|
||||
|
||||
use crate::dynamic::environment::{Environment, RuntimeArtifacts};
|
||||
use crate::dynamic::lang::{js_shared, HarnessSource, LangEmitter};
|
||||
use crate::dynamic::lang::{js_shared, ChainStepHarness, HarnessSource, LangEmitter};
|
||||
use crate::dynamic::spec::{EntryKind, HarnessSpec};
|
||||
use crate::evidence::UnsupportedReason;
|
||||
|
||||
|
|
@ -46,6 +46,10 @@ impl LangEmitter for TypeScriptEmitter {
|
|||
fn materialize_runtime(&self, env: &Environment) -> RuntimeArtifacts {
|
||||
js_shared::materialize_node(env)
|
||||
}
|
||||
|
||||
fn compose_chain_step(&self, prev_output: Option<&[u8]>) -> ChainStepHarness {
|
||||
js_shared::chain_step(prev_output, /* typescript = */ true)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue