commit d64ca18d797f7528c1d3173ed5304c706e1edde4
parent 44f51cf1e3c3392550b70789eb219dbb57e93ed5
Author: triesap <tyson@radroots.org>
Date: Tue, 8 Sep 2026 23:25:23 +0000
cli: verify immutable source lock agreement
- correct the stale digest for the unchanged cargo dependency lock
- validate exact public revisions versions and resolved shared sources
- reject malformed source locks and dependency overrides offline
- verify workspace tests strict clippy api and supply chain checks
Diffstat:
6 files changed, 262 insertions(+), 3 deletions(-)
diff --git a/README b/README
@@ -82,11 +82,15 @@ cargo test --all-targets --locked
cargo clippy --all-targets --locked -- -D warnings
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --locked
scripts/verify-boundaries.sh
+scripts/verify-source-lock.sh
scripts/verify-supply-chain.sh
tools/verify-repository-boundary.sh
```
-The boundary command byte-compares the root-only public API against its
+The source-lock command checks the exact Cargo lock digest, public dependency
+revision, declared version and resolved shared package sources. It runs offline
+and rejects local overrides or mismatched inputs. The boundary command includes
+this check and byte-compares the root-only public API against its
reviewed baseline and rejects forbidden repository roots or credential
material. The supply-chain command validates the exact locked dependency
graph, license policy, approved registries and immutable Lib source, then
diff --git a/radroots.lib.source-lock.v1.toml b/radroots.lib.source-lock.v1.toml
@@ -6,4 +6,4 @@ workspace_catalog_sha256 = "deca0c080deae187ff8186c0708903e42f41ea57f77c5f91581e
version = "0.1.0-alpha"
source_archive_sha256 = "2da62bd0112d43b634ba5e87dfc09d048f7ea1836ab89d2fd18fcfce5186ad6b"
lockfile = "Cargo.lock"
-lockfile_sha256 = "ef183844b032b7baf9d607d43eaecac944ad42e819e7247508130296d2543769"
+lockfile_sha256 = "e0d86121841778d6ebb802bbfb807e7f74490a03b2a9593b15a7ac39020300ac"
diff --git a/scripts/verify-boundaries.sh b/scripts/verify-boundaries.sh
@@ -5,6 +5,7 @@ repo_root="$(git rev-parse --show-toplevel)"
cd "$repo_root"
tools/verify-repository-boundary.sh
+scripts/verify-source-lock.sh
test "$(cargo public-api --version)" = "cargo-public-api 0.52.0"
temporary_api="$(mktemp)"
trap 'rm -f "$temporary_api"' EXIT
diff --git a/scripts/verify-source-lock.sh b/scripts/verify-source-lock.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+repo_root="$(git rev-parse --show-toplevel)"
+cd "$repo_root"
+cargo run --locked -p radroots_cli_xtask -- source-lock-check
diff --git a/tools/xtask/src/main.rs b/tools/xtask/src/main.rs
@@ -2,10 +2,15 @@
mod rshr_202_step_302_gate;
mod rshr_202_step_302_platform;
+mod source_lock;
fn main() {
if run().is_err() {
- eprintln!("step_302_gate_failed");
+ if std::env::args().nth(1).as_deref() == Some("source-lock-check") {
+ eprintln!("source_lock_check_failed");
+ } else {
+ eprintln!("step_302_gate_failed");
+ }
std::process::exit(1);
}
}
@@ -13,6 +18,7 @@ fn main() {
fn run() -> Result<(), ()> {
let mut arguments = std::env::args().skip(1);
match arguments.next().as_deref() {
+ Some("source-lock-check") if arguments.next().is_none() => source_lock::run(),
Some("rshr-step-302-gate") => {
let args = parse_gate_args(arguments.collect())?;
rshr_202_step_302_gate::run(args).map_err(|_| ())
diff --git a/tools/xtask/src/source_lock.rs b/tools/xtask/src/source_lock.rs
@@ -0,0 +1,242 @@
+use serde_json::Value;
+use sha2::{Digest, Sha256};
+use std::collections::BTreeMap;
+use std::io::Read;
+use std::path::Path;
+use std::process::{Command, Stdio};
+
+const MAX_TEXT: u64 = 2 * 1024 * 1024;
+const MAX_METADATA: u64 = 4 * MAX_TEXT;
+const FIELDS: [&str; 9] = [
+ "schema",
+ "repository",
+ "revision",
+ "architecture",
+ "workspace_catalog_sha256",
+ "version",
+ "source_archive_sha256",
+ "lockfile",
+ "lockfile_sha256",
+];
+
+pub fn run() -> Result<(), ()> {
+ let root = Path::new(env!("CARGO_MANIFEST_DIR"))
+ .parent()
+ .and_then(Path::parent)
+ .ok_or(())?;
+ let lock = read(root.join("radroots.lib.source-lock.v1.toml").as_path())?;
+ let cargo_lock = read(root.join("Cargo.lock").as_path())?;
+ let metadata = metadata(root)?;
+ validate(&lock, &cargo_lock, &metadata)?;
+ println!("source lock ok: exact digest, public revision and dependency agreement");
+ Ok(())
+}
+
+fn read(path: &Path) -> Result<Vec<u8>, ()> {
+ let file_type = std::fs::symlink_metadata(path).map_err(|_| ())?;
+ if !file_type.is_file() || file_type.len() > MAX_TEXT {
+ return Err(());
+ }
+ let mut bytes = Vec::new();
+ std::fs::File::open(path)
+ .map_err(|_| ())?
+ .take(MAX_TEXT + 1)
+ .read_to_end(&mut bytes)
+ .map_err(|_| ())?;
+ if bytes.len() as u64 > MAX_TEXT {
+ return Err(());
+ }
+ Ok(bytes)
+}
+
+fn metadata(root: &Path) -> Result<Value, ()> {
+ let mut child = Command::new("cargo")
+ .args(["metadata", "--locked", "--offline", "--format-version", "1"])
+ .current_dir(root)
+ .stdin(Stdio::null())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::null())
+ .spawn()
+ .map_err(|_| ())?;
+ let mut bytes = Vec::new();
+ let read = child
+ .stdout
+ .take()
+ .ok_or(())?
+ .take(MAX_METADATA + 1)
+ .read_to_end(&mut bytes);
+ if read.is_err() || bytes.len() as u64 > MAX_METADATA {
+ let _ = child.kill();
+ let _ = child.wait();
+ return Err(());
+ }
+ if !child.wait().map_err(|_| ())?.success() {
+ return Err(());
+ }
+ serde_json::from_slice(&bytes).map_err(|_| ())
+}
+
+fn fields(bytes: &[u8]) -> Result<BTreeMap<&str, &str>, ()> {
+ let text = std::str::from_utf8(bytes).map_err(|_| ())?;
+ let mut fields = BTreeMap::new();
+ for line in text.lines() {
+ let (key, quoted) = line.split_once(" = ").ok_or(())?;
+ let value = quoted
+ .strip_prefix('"')
+ .and_then(|value| value.strip_suffix('"'))
+ .ok_or(())?;
+ if !FIELDS.contains(&key)
+ || value.is_empty()
+ || value
+ .bytes()
+ .any(|byte| !byte.is_ascii_graphic() || matches!(byte, b'"' | b'\\'))
+ || fields.insert(key, value).is_some()
+ {
+ return Err(());
+ }
+ }
+ if fields.len() != FIELDS.len() {
+ return Err(());
+ }
+ Ok(fields)
+}
+
+fn validate(lock: &[u8], cargo_lock: &[u8], metadata: &Value) -> Result<(), ()> {
+ let fields = fields(lock)?;
+ for (key, expected) in [
+ ("schema", "radroots.lib.source-lock.v1"),
+ ("repository", "https://github.com/radrootslabs/lib"),
+ ("architecture", "radroots.crates.release.v2"),
+ ("lockfile", "Cargo.lock"),
+ ] {
+ if fields[key] != expected {
+ return Err(());
+ }
+ }
+ for (key, length) in [
+ ("revision", 40),
+ ("workspace_catalog_sha256", 64),
+ ("source_archive_sha256", 64),
+ ("lockfile_sha256", 64),
+ ] {
+ if fields[key].len() != length
+ || !fields[key]
+ .bytes()
+ .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
+ {
+ return Err(());
+ }
+ }
+ if fields["lockfile_sha256"] != hex::encode(Sha256::digest(cargo_lock)) {
+ return Err(());
+ }
+ let source = format!(
+ "git+https://github.com/radrootslabs/lib.git?rev={}",
+ fields["revision"]
+ );
+ let resolved = format!("{source}#{}", fields["revision"]);
+ let packages = metadata["packages"].as_array().ok_or(())?;
+ let cli = packages
+ .iter()
+ .find(|package| package["name"] == "radroots_cli")
+ .ok_or(())?;
+ let dependencies = cli["dependencies"].as_array().ok_or(())?;
+ let facade = dependencies
+ .iter()
+ .find(|dependency| dependency["name"] == "radroots")
+ .ok_or(())?;
+ if facade["source"] != source
+ || facade["req"] != format!("={}", fields["version"])
+ || !facade["rename"].is_null()
+ {
+ return Err(());
+ }
+ let mut shared_count = 0;
+ for package in packages {
+ let name = package["name"].as_str().ok_or(())?;
+ if (name == "radroots" || name.starts_with("radroots_"))
+ && !matches!(name, "radroots_cli" | "radroots_cli_xtask")
+ {
+ if package["source"] != resolved || package["version"] != fields["version"] {
+ return Err(());
+ }
+ shared_count += 1;
+ }
+ }
+ if shared_count == 0 {
+ return Err(());
+ }
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use serde_json::json;
+
+ fn fixture() -> (String, Value) {
+ let lock = include_str!("../../../radroots.lib.source-lock.v1.toml");
+ let lock = lock.replace(
+ fields(lock.as_bytes()).expect("canonical lock")["lockfile_sha256"],
+ &hex::encode(Sha256::digest(b"synthetic cargo lock")),
+ );
+ let revision = fields(lock.as_bytes()).expect("canonical lock")["revision"];
+ let source = format!("git+https://github.com/radrootslabs/lib.git?rev={revision}");
+ let metadata = json!({"packages": [
+ {"name": "radroots_cli", "dependencies": [{"name": "radroots", "source": source, "req": "=0.1.0-alpha", "rename": null}]},
+ {"name": "radroots", "source": format!("{source}#{revision}"), "version": "0.1.0-alpha"}
+ ]});
+ (lock, metadata)
+ }
+
+ #[test]
+ fn exact_lock_and_dependency_inputs_pass() {
+ let (lock, metadata) = fixture();
+ assert!(validate(lock.as_bytes(), b"synthetic cargo lock", &metadata).is_ok());
+ }
+
+ #[test]
+ fn stale_lock_bytes_fail_even_when_revision_agrees() {
+ let (lock, metadata) = fixture();
+ assert!(validate(lock.as_bytes(), b"changed cargo lock", &metadata).is_err());
+ }
+
+ #[test]
+ fn local_or_mismatched_resolved_shared_sources_fail() {
+ let (lock, metadata) = fixture();
+ for source in [
+ Value::Null,
+ json!("git+https://github.com/radrootslabs/lib.git?branch=master"),
+ ] {
+ let mut changed = metadata.clone();
+ changed["packages"][1]["source"] = source;
+ assert!(validate(lock.as_bytes(), b"synthetic cargo lock", &changed).is_err());
+ }
+ }
+
+ #[test]
+ fn manifest_revision_or_version_drift_fails() {
+ let (lock, metadata) = fixture();
+ for (field, value) in [
+ ("source", "git+https://example.invalid/lib"),
+ ("req", "^0.1"),
+ ] {
+ let mut changed = metadata.clone();
+ changed["packages"][0]["dependencies"][0][field] = json!(value);
+ assert!(validate(lock.as_bytes(), b"synthetic cargo lock", &changed).is_err());
+ }
+ }
+
+ #[test]
+ fn duplicate_unknown_missing_or_unsafe_lock_fields_fail() {
+ let (lock, metadata) = fixture();
+ for changed in [
+ format!("{lock}revision = \"{}\"\n", "a".repeat(40)),
+ format!("{lock}extra = \"value\"\n"),
+ lock.lines().skip(1).collect::<Vec<_>>().join("\n"),
+ lock.replace("lockfile = \"Cargo.lock\"", "lockfile = \"../Cargo.lock\""),
+ ] {
+ assert!(validate(changed.as_bytes(), b"synthetic cargo lock", &metadata).is_err());
+ }
+ }
+}