lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

commit 72053f8de902c4c758157d15ac63fba7c0332a2e
parent 5ff49ddc6d28ca32cc798f4abf1cfb8c7112229d
Author: triesap <tyson@radroots.org>
Date:   Mon,  7 Sep 2026 06:41:46 +0000

test: bind the Step 306 harness gate

- Pin the exact reproducibility and install harness source bytes.
- Run detached-clone, store-isolation, lifecycle, and diff vectors.
- Deny warnings and require the standalone contract validator.
- Emit the typed pre-candidate Step 306 gate result.

Diffstat:
Acontracts/rshr-202-step-306-gates.v1.json | 1+
Mtools/xtask/src/main.rs | 35+++++++++++++++++++++++++++++++++++
Atools/xtask/src/rshr_202_step_306_gate.rs | 264+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 300 insertions(+), 0 deletions(-)

diff --git a/contracts/rshr-202-step-306-gates.v1.json b/contracts/rshr-202-step-306-gates.v1.json @@ -0,0 +1 @@ +{"gate_command_contract":[{"argv_template":["cargo","extbuild","run","--","cargo","run","--offline","--locked","-q","-p","xtask","--","rshr-step-306-gate","--step={step}","--check-id={check_id}","--source-revision={source_revision}","--source-tree={source_tree}","--candidate-digest={candidate_digest}","--platform=macos_aarch64","--execution-request-sha256={execution_request_sha256}"],"assertion_id":["step_306_gate_01_9d88a1ac22999cbb6ec8d2537a0c94614841cae1df303dc49dde045745eb7bf2"],"check_id":"gate-01-9d88a1ac22999cbb6ec8d2537a0c94614841cae1df303dc49dde045745eb7bf2","environment_authority":{"cache_policy_id":"rshr-200-step-287-cache-policy.v1","cache_policy_sha256":"3e81d178bce97b6c349dfbb00c68fd6f620ac00b1a1c8d37b12e9998f3c9eaaa","cadence_policy_id":"rshr-200-step-287-cadence-policy.v1","cadence_policy_sha256":"d24903df8659ee3772297c84994911efe7d21cb8b988320ddc6ddce0431892a1","isolation":"extbuild_host_constrained","network":"disabled","network_policy_id":"none","network_policy_sha256":"none","resource_policy_id":"rshr-200-step-287-resource-policy.v1","resource_policy_sha256":"05d3c7a89185d3c55678d97955193fce2ed92b1eee5af99083d77ea64c98d14e"},"environment_names":["EXT_BUILD_CONFIG","EXT_BUILD_MACHINE_CONFIG","EXT_BUILD_ROOT","HOME","PATH","RUSTUP_TOOLCHAIN","TMPDIR"],"gate_definition_sha256":"9d88a1ac22999cbb6ec8d2537a0c94614841cae1df303dc49dde045745eb7bf2","required_platforms":["macos_aarch64"],"required_tools":["git","rustc"],"result_schema":"radroots.services-hardening.rshr-200-step-check-result.v1","schema":"radroots.services-hardening.rshr-200-step-check-command.v1","step":306,"verifier_path":"tools/xtask/src/rshr_202_step_306_gate.rs","verifier_sha256":"51942eb554799907b3107bf4b497d1e3628c59dfbf2417047e1d14e7dfae224c"}],"schema":"radroots.lib.rshr-202-step-306-gates.v1","step":[306]} diff --git a/tools/xtask/src/main.rs b/tools/xtask/src/main.rs @@ -45,6 +45,7 @@ mod rshr_202_step_298_platform; mod rshr_202_step_299_gate; mod rshr_202_step_304_gate; mod rshr_202_step_305_gate; +mod rshr_202_step_306_gate; mod safe_artifact_io; #[cfg_attr(coverage_nightly, coverage(off))] mod safety_qualification; @@ -207,6 +208,23 @@ enum XtaskCommand { #[arg(long)] execution_request_sha256: String, }, + #[command(name = "rshr-step-306-gate", hide = true)] + RshrStep306Gate { + #[arg(long)] + step: u16, + #[arg(long)] + check_id: String, + #[arg(long)] + source_revision: String, + #[arg(long)] + source_tree: String, + #[arg(long)] + candidate_digest: String, + #[arg(long)] + platform: String, + #[arg(long)] + execution_request_sha256: String, + }, SourceLock { #[arg(long)] consumer_root: PathBuf, @@ -745,6 +763,23 @@ fn run(args: &[String]) -> Result<(), String> { platform, execution_request_sha256, }), + XtaskCommand::RshrStep306Gate { + step, + check_id, + source_revision, + source_tree, + candidate_digest, + platform, + execution_request_sha256, + } => rshr_202_step_306_gate::run(rshr_202_step_306_gate::Arguments { + step, + check_id, + source_revision, + source_tree, + candidate_digest, + platform, + execution_request_sha256, + }), XtaskCommand::SourceLock { consumer_root } => { build_control::validate_consumer(&consumer_root).map(|_| ()) } diff --git a/tools/xtask/src/rshr_202_step_306_gate.rs b/tools/xtask/src/rshr_202_step_306_gate.rs @@ -0,0 +1,264 @@ +use std::{ + env, fs, + path::{Path, PathBuf}, + process::{Command, Output}, +}; + +use serde_json::{Value, json}; +use sha2::{Digest as _, Sha256}; + +const STEP: u16 = 306; +const GATE_DIGEST: &str = "9d88a1ac22999cbb6ec8d2537a0c94614841cae1df303dc49dde045745eb7bf2"; +const MAX_OUTPUT_BYTES: usize = 32 * 1024 * 1024; +const EXACT_SOURCES: &[(&str, &str)] = &[ + ( + "Cargo.lock", + "a8edafae2d2b26465b2b99038ce55d80fa603481ebb5ebebbd810708ba4a894a", + ), + ( + "Cargo.toml", + "322b975e60004df42de5b1b9460bf84255d2210e97f4f1cf1ede84a068b7519e", + ), + ( + "contracts/architecture/decisions/services_hardening_repro_install.v1.json", + "69844a67fdc35345fd8d44f95516d238f868a14dcee3d5338aabeb17ca006903", + ), + ( + "tools/xtask/Cargo.toml", + "bf5442895085225a571bf8aa45b2316e732a5c9925911de26147a924466f8ba0", + ), + ( + "tools/xtask/src/bounded_process.rs", + "56b8de0c3c34b7481f0f8c792e37dfb0d9fc12a5b990287c979e89fd6989202e", + ), + ( + "tools/xtask/src/main.rs", + "1e2101e74985fe6a37762d4d32002221d659b7d5307f3788b6171683b46bf48a", + ), + ( + "tools/xtask/src/service_repro_install.rs", + "55fba278db8b4eec333181a307c70036a0b0fde077fc7254af386ed940dc117e", + ), +]; + +pub(crate) struct Arguments { + pub(crate) step: u16, + pub(crate) check_id: String, + pub(crate) source_revision: String, + pub(crate) source_tree: String, + pub(crate) candidate_digest: String, + pub(crate) platform: String, + pub(crate) execution_request_sha256: String, +} + +fn root() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")) + .parent() + .and_then(Path::parent) + .expect("xtask must remain under tools/xtask") + .to_path_buf() +} + +fn sha256(bytes: &[u8]) -> String { + hex::encode(Sha256::digest(bytes)) +} + +fn canonical(value: &Value) -> Result<Vec<u8>, String> { + serde_json::to_vec(value).map_err(|_| "Step 306 JSON encoding failed".to_owned()) +} + +fn bounded(command: &mut Command, label: &str) -> Result<Output, String> { + let output = command + .current_dir(root()) + .env("CARGO_NET_OFFLINE", "true") + .env("CARGO_TERM_COLOR", "never") + .output() + .map_err(|_| format!("{label} could not start"))?; + if output.stdout.len() > MAX_OUTPUT_BYTES || output.stderr.len() > MAX_OUTPUT_BYTES { + return Err(format!("{label} exceeded its output bound")); + } + if output.status.success() { + Ok(output) + } else { + Err(format!("{label} failed")) + } +} + +fn expected_contract(verifier_sha256: &str) -> Value { + json!({ + "argv_template": [ + "cargo", "extbuild", "run", "--", "cargo", "run", "--offline", "--locked", + "-q", "-p", "xtask", "--", "rshr-step-306-gate", "--step={step}", + "--check-id={check_id}", "--source-revision={source_revision}", + "--source-tree={source_tree}", "--candidate-digest={candidate_digest}", + "--platform=macos_aarch64", "--execution-request-sha256={execution_request_sha256}" + ], + "assertion_id": [format!("step_306_gate_01_{GATE_DIGEST}")], + "check_id": format!("gate-01-{GATE_DIGEST}"), + "environment_authority": { + "cache_policy_id": "rshr-200-step-287-cache-policy.v1", + "cache_policy_sha256": "3e81d178bce97b6c349dfbb00c68fd6f620ac00b1a1c8d37b12e9998f3c9eaaa", + "cadence_policy_id": "rshr-200-step-287-cadence-policy.v1", + "cadence_policy_sha256": "d24903df8659ee3772297c84994911efe7d21cb8b988320ddc6ddce0431892a1", + "isolation": "extbuild_host_constrained", + "network": "disabled", + "network_policy_id": "none", + "network_policy_sha256": "none", + "resource_policy_id": "rshr-200-step-287-resource-policy.v1", + "resource_policy_sha256": "05d3c7a89185d3c55678d97955193fce2ed92b1eee5af99083d77ea64c98d14e" + }, + "environment_names": ["EXT_BUILD_CONFIG", "EXT_BUILD_MACHINE_CONFIG", "EXT_BUILD_ROOT", "HOME", "PATH", "RUSTUP_TOOLCHAIN", "TMPDIR"], + "gate_definition_sha256": GATE_DIGEST, + "required_platforms": ["macos_aarch64"], + "required_tools": ["git", "rustc"], + "result_schema": "radroots.services-hardening.rshr-200-step-check-result.v1", + "schema": "radroots.services-hardening.rshr-200-step-check-command.v1", + "step": STEP, + "verifier_path": "tools/xtask/src/rshr_202_step_306_gate.rs", + "verifier_sha256": verifier_sha256 + }) +} + +pub(crate) fn run(arguments: Arguments) -> Result<(), String> { + let check_id = format!("gate-01-{GATE_DIGEST}"); + if arguments.step != STEP + || arguments.check_id != check_id + || arguments.candidate_digest != "none" + || arguments.platform != "macos_aarch64" + || !valid_hex(&arguments.source_revision, 40) + || !valid_hex(&arguments.source_tree, 40) + || !valid_hex(&arguments.execution_request_sha256, 64) + { + return Err("Step 306 gate arguments differ".to_owned()); + } + let root = root(); + if root.join(".github").exists() { + return Err("forbidden .github surface is present".to_owned()); + } + for (relative, expected) in EXACT_SOURCES { + let observed = sha256( + &fs::read(root.join(relative)) + .map_err(|_| "Step 306 governed source is unreadable".to_owned())?, + ); + if observed != *expected { + return Err(format!("Step 306 governed source bytes differ: {relative}")); + } + } + let verifier_path = root.join("tools/xtask/src/rshr_202_step_306_gate.rs"); + let verifier_sha256 = + sha256(&fs::read(verifier_path).map_err(|_| "Step 306 verifier is unreadable".to_owned())?); + let authority_bytes = fs::read(root.join("contracts/rshr-202-step-306-gates.v1.json")) + .map_err(|_| "Step 306 gate authority is unreadable".to_owned())?; + let authority: Value = serde_json::from_slice(&authority_bytes) + .map_err(|_| "Step 306 gate authority is invalid".to_owned())?; + let mut canonical_authority = canonical(&authority)?; + canonical_authority.push(b'\n'); + let contracts = authority + .get("gate_command_contract") + .and_then(Value::as_array) + .ok_or_else(|| "Step 306 gate contract is absent".to_owned())?; + if authority_bytes != canonical_authority + || authority.get("schema") != Some(&json!("radroots.lib.rshr-202-step-306-gates.v1")) + || authority.get("step") != Some(&json!([STEP])) + || contracts.as_slice() != [expected_contract(&verifier_sha256)] + { + return Err("Step 306 gate authority differs".to_owned()); + } + + for (arguments, label) in [ + ( + vec!["+1.97.1", "fmt", "--all", "--", "--check"], + "Step 306 formatting", + ), + ( + vec!["+1.97.1", "check", "--offline", "--locked", "-p", "xtask"], + "Step 306 verifier check", + ), + ( + vec![ + "+1.97.1", + "test", + "--offline", + "--locked", + "-p", + "xtask", + "service_repro_install::tests", + ], + "Step 306 reproducibility and install tests", + ), + ( + vec![ + "+1.97.1", + "test", + "--offline", + "--locked", + "-p", + "xtask", + "tests::typed_build_control_cli_requires_explicit_modes_and_known_values", + ], + "Step 306 typed command test", + ), + ] { + bounded(Command::new("cargo").args(arguments), label)?; + } + bounded( + Command::new("cargo").args([ + "+1.97.1", + "clippy", + "--offline", + "--locked", + "-p", + "xtask", + "--all-targets", + "--", + "-D", + "warnings", + ]), + "Step 306 clippy", + )?; + bounded( + Command::new("cargo").args([ + "+1.97.1", + "run", + "--offline", + "--locked", + "-q", + "-p", + "xtask", + "--", + "contract", + "validate", + ]), + "Step 306 contracts", + )?; + + let contract = &contracts[0]; + let assertion = json!([{ "id": format!("step_306_gate_01_{GATE_DIGEST}"), "result": "pass" }]); + let result = json!({ + "schema": "radroots.services-hardening.rshr-200-step-check-result.v1", + "step": STEP, + "check_id": check_id, + "gate_definition_sha256": GATE_DIGEST, + "source_revision": arguments.source_revision, + "source_tree": arguments.source_tree, + "candidate_generation": 0, + "candidate_digest": "none", + "command_contract_sha256": sha256(&canonical(contract)?), + "verifier_sha256": verifier_sha256, + "execution_request": [{"platform": arguments.platform, "sha256": arguments.execution_request_sha256}], + "assertion_inventory_sha256": sha256(&canonical(&assertion)?), + "assertion": assertion, + "result": "pass" + }); + let mut bytes = canonical(&result)?; + bytes.push(b'\n'); + std::io::Write::write_all(&mut std::io::stdout().lock(), &bytes) + .map_err(|_| "Step 306 result write failed".to_owned()) +} + +fn valid_hex(value: &str, length: usize) -> bool { + value.len() == length + && value + .bytes() + .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte)) +}