flowrs-engine/crates/flowrs-cli/tests/pkg_custody.rs
flowRS fd958c0547 flowRS engine, cut from flowRS 3451b065
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-02 19:04:39 -05:00

404 lines
13 KiB
Rust

//! The registry's custody, on the command line: verification runs on one machine and writes an
//! unsigned record, signing is a separate light step on another, and nothing signs without being
//! given the key by its path. A throwaway key throughout: no test needs the production key.
use flowrs_registry::package::Package;
use flowrs_registry::record::{
Condition, ConditionId, Method, PackageRef, Record, SCHEMA, SignedRecord, Status, Toolchain,
};
use serde_json::Value;
use std::path::{Path, PathBuf};
use std::process::{Command, Output};
use tempfile::TempDir;
fn flowrsc(dir: &Path, args: &[&str], keys: Option<&str>) -> Output {
let mut c = Command::new(env!("CARGO_BIN_EXE_flowrsc"));
c.args(args).current_dir(dir);
match keys {
Some(k) => c.env("FLOWRS_REGISTRY_KEYS", k),
None => c.env_remove("FLOWRS_REGISTRY_KEYS"),
};
c.output().expect("flowrsc runs")
}
fn text(o: &Output) -> String {
format!(
"{}{}",
String::from_utf8_lossy(&o.stdout),
String::from_utf8_lossy(&o.stderr)
)
}
const MANIFEST: &str = r#"schema = "flowrs.package/1"
name = "tiny"
version = "0.1.0"
kind = "widget"
licence = "Apache-2.0"
description = "A package for the custody tests."
provides = ["tiny.node"]
tests = ["tests::it_runs"]
[needs]
network = []
filesystem = []
[[ports]]
of = "tiny.node"
port = "in"
unit = "1"
"#;
/// A package tree, its archive, and a record of it with all five conditions as given.
fn package(dir: &Path) -> Package {
let p = dir.join("tiny");
std::fs::create_dir_all(p.join("src")).unwrap();
std::fs::write(
p.join("Cargo.toml"),
"[package]\nname = \"tiny\"\nversion = \"0.1.0\"\nedition = \"2024\"\n",
)
.unwrap();
std::fs::write(
p.join("src/lib.rs"),
"#[cfg(test)]\nmod tests {\n #[test]\n fn it_runs() {}\n}\n",
)
.unwrap();
std::fs::write(p.join("flowrs-package.toml"), MANIFEST).unwrap();
Package::from_dir(&p).unwrap()
}
fn record(pkg: &Package, status: Status) -> Record {
let manifest = pkg.manifest().unwrap();
Record {
schema: SCHEMA.into(),
package: PackageRef {
name: manifest.name.clone(),
version: manifest.version.clone(),
kind: manifest.kind,
},
hash: pkg.hash.clone(),
manifest,
conditions: ConditionId::ALL
.iter()
.map(|&id| Condition {
id,
status,
how: vec![Method::Observed],
detail: "checked on another machine".into(),
evidence: vec![],
})
.collect(),
toolchain: Toolchain {
rustc: "rustc".into(),
cargo: "cargo".into(),
host: "aarch64-apple-darwin".into(),
sandbox: "seatbelt".into(),
observation: "kernel reports (canary seen)".into(),
},
flowrs_commit: "0000000".into(),
verified_at: "2026-11-02T00:00:00Z".into(),
verifier: "flowrs-registry test".into(),
}
}
/// Stage `pkg` as `pkg submit` does on the verifying machine.
fn stage(reg: &Path, pkg: &Package, r: Record) -> PathBuf {
let hex = flowrs_registry::archive::hex_of(&pkg.hash).unwrap();
let incoming = reg.join("incoming");
std::fs::create_dir_all(&incoming).unwrap();
std::fs::write(incoming.join(format!("{hex}.fpkg")), &pkg.archive).unwrap();
let file = incoming.join(format!("{hex}.json"));
std::fs::write(
&file,
serde_json::to_string_pretty(&SignedRecord {
record: r,
signature: None,
})
.unwrap(),
)
.unwrap();
file
}
/// A key made by the command, and its id.
fn key(dir: &Path) -> (PathBuf, String) {
let f = dir.join("key.json");
let o = flowrsc(dir, &["pkg", "keygen", f.to_str().unwrap()], None);
assert!(o.status.success(), "{}", text(&o));
let public: Value =
serde_json::from_str(&std::fs::read_to_string(dir.join("key.json.pub")).unwrap()).unwrap();
(f, public["id"].as_str().unwrap().to_string())
}
fn signature(file: &Path) -> Option<Value> {
let v: Value = serde_json::from_str(&std::fs::read_to_string(file).unwrap()).unwrap();
v.get("signature").cloned()
}
/// `keygen` writes a key only its owner can read, and never over one that exists.
#[test]
fn keygen_writes_an_owners_key_and_never_over_another() {
let d = TempDir::new().unwrap();
let (f, id) = key(d.path());
assert!(id.starts_with("v2:"));
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mode = std::fs::metadata(&f).unwrap().permissions().mode();
assert_eq!(mode & 0o777, 0o600, "{mode:o}");
}
let before = std::fs::read(&f).unwrap();
let o = flowrsc(d.path(), &["pkg", "keygen", f.to_str().unwrap()], None);
assert!(!o.status.success());
assert!(text(&o).contains("never overwritten"), "{}", text(&o));
assert_eq!(std::fs::read(&f).unwrap(), before);
// The public half holds no secret.
let public = std::fs::read_to_string(d.path().join("key.json.pub")).unwrap();
assert!(
!public.contains("ml_dsa_65") && !public.contains("ed25519\""),
"{public}"
);
}
/// Verifying and staging take no key at all: there is no way to sign on the machine that builds.
#[test]
fn verify_and_submit_take_no_key() {
let d = TempDir::new().unwrap();
for args in [
&["pkg", "verify", "x", "--key", "k.json"][..],
&["pkg", "submit", "x", "--registry", "r", "--key", "k.json"][..],
] {
let o = flowrsc(d.path(), args, None);
assert_eq!(o.status.code(), Some(2), "{args:?}");
assert!(text(&o).contains("--key"), "{}", text(&o));
}
}
/// `sign` needs a key named on its command line; a mistyped path signs nothing and falls back to
/// nothing.
#[test]
fn a_mistyped_key_path_signs_nothing() {
let d = TempDir::new().unwrap();
let (_, id) = key(d.path());
let pkg = package(d.path());
let reg = d.path().join("reg");
let staged = stage(&reg, &pkg, record(&pkg, Status::Passed));
let reg_s = reg.to_str().unwrap();
// With no --key at all.
let o = flowrsc(d.path(), &["pkg", "sign", "--registry", reg_s], Some(&id));
assert_eq!(o.status.code(), Some(2));
assert!(text(&o).contains("--key"), "{}", text(&o));
assert_eq!(signature(&staged), None);
// With a path that is not there.
let o = flowrsc(
d.path(),
&["pkg", "sign", "--key", "kye.json", "--registry", reg_s],
Some(&id),
);
assert_eq!(o.status.code(), Some(2));
assert!(text(&o).contains("kye.json"), "{}", text(&o));
assert_eq!(signature(&staged), None);
}
/// A real key this host does not trust signs nothing: it would be a record no host admits.
#[test]
fn a_key_this_host_does_not_trust_signs_nothing() {
let d = TempDir::new().unwrap();
let (f, _) = key(d.path());
let pkg = package(d.path());
let reg = d.path().join("reg");
let staged = stage(&reg, &pkg, record(&pkg, Status::Passed));
let o = flowrsc(
d.path(),
&[
"pkg",
"sign",
"--key",
f.to_str().unwrap(),
"--registry",
reg.to_str().unwrap(),
],
None,
);
assert_eq!(o.status.code(), Some(2));
assert!(text(&o).contains("does not trust"), "{}", text(&o));
assert_eq!(signature(&staged), None);
}
/// The whole split: a record verified elsewhere, signed here by a command that compiles nothing,
/// published, and then admitted for this exact tree.
#[test]
fn verified_elsewhere_signed_here_published_and_admitted() {
let d = TempDir::new().unwrap();
let (f, id) = key(d.path());
let pkg = package(d.path());
let reg = d.path().join("reg");
let staged = stage(&reg, &pkg, record(&pkg, Status::Passed));
let (reg_s, key_s) = (reg.to_str().unwrap(), f.to_str().unwrap());
let tree = d.path().join("tiny");
let tree_s = tree.to_str().unwrap();
// Not signed: publishing holds it back.
let o = flowrsc(
d.path(),
&["pkg", "publish", "--registry", reg_s],
Some(&id),
);
assert!(o.status.success(), "{}", text(&o));
assert!(text(&o).contains("left out"), "{}", text(&o));
let o = flowrsc(
d.path(),
&["pkg", "check", tree_s, "--registry", reg_s],
Some(&id),
);
assert_eq!(o.status.code(), Some(1), "{}", text(&o));
let o = flowrsc(
d.path(),
&["pkg", "sign", "--key", key_s, "--registry", reg_s],
Some(&id),
);
assert!(o.status.success(), "{}", text(&o));
assert!(text(&o).contains("signed tiny"), "{}", text(&o));
assert_eq!(
signature(&staged).unwrap()["key_id"].as_str(),
Some(id.as_str())
);
let o = flowrsc(
d.path(),
&["pkg", "publish", "--registry", reg_s],
Some(&id),
);
assert!(o.status.success(), "{}", text(&o));
let o = flowrsc(
d.path(),
&["pkg", "check", tree_s, "--registry", reg_s],
Some(&id),
);
assert!(o.status.success(), "{}", text(&o));
assert!(text(&o).contains("is admitted"), "{}", text(&o));
// One byte of the tree changed: the signed record no longer covers it.
std::fs::write(tree.join("src/lib.rs"), "// changed\n").unwrap();
let o = flowrsc(
d.path(),
&["pkg", "check", tree_s, "--registry", reg_s],
Some(&id),
);
assert_eq!(o.status.code(), Some(1), "{}", text(&o));
}
/// Signing one record file, for a record carried by hand, holds to the same rule: a record that
/// did not pass is not signed, and the file stays as it was.
#[test]
fn one_record_file_is_signed_only_if_it_passed() {
let d = TempDir::new().unwrap();
let (f, id) = key(d.path());
let pkg = package(d.path());
let key_s = f.to_str().unwrap();
let write = |name: &str, status| {
let file = d.path().join(name);
std::fs::write(
&file,
serde_json::to_string_pretty(&SignedRecord {
record: record(&pkg, status),
signature: None,
})
.unwrap(),
)
.unwrap();
file
};
let bad = write("failed.json", Status::Failed);
let o = flowrsc(
d.path(),
&["pkg", "sign", bad.to_str().unwrap(), "--key", key_s],
Some(&id),
);
assert_eq!(o.status.code(), Some(2), "{}", text(&o));
assert_eq!(signature(&bad), None);
let good = write("passed.json", Status::Passed);
let out = d.path().join("signed.json");
let o = flowrsc(
d.path(),
&[
"pkg",
"sign",
good.to_str().unwrap(),
"--key",
key_s,
"-o",
out.to_str().unwrap(),
],
Some(&id),
);
assert!(o.status.success(), "{}", text(&o));
assert_eq!(signature(&good), None, "the input stays as it was with -o");
assert_eq!(
signature(&out).unwrap()["key_id"].as_str(),
Some(id.as_str())
);
}
/// `check` with no registry is what a contributor runs: no key, and a package whose manifest does
/// not read stops there with the reason.
#[test]
fn a_contributors_check_stops_at_a_manifest_that_does_not_read() {
let d = TempDir::new().unwrap();
let p = d.path().join("broken");
std::fs::create_dir_all(&p).unwrap();
std::fs::write(p.join("Cargo.toml"), "[package]\nname = \"broken\"\n").unwrap();
std::fs::write(
p.join("flowrs-package.toml"),
"schema = \"flowrs.package/9\"\n",
)
.unwrap();
let o = flowrsc(d.path(), &["pkg", "check", p.to_str().unwrap()], None);
assert_eq!(o.status.code(), Some(2), "{}", text(&o));
assert!(text(&o).contains("error"), "{}", text(&o));
}
/// The whole local check, with no key on the machine: a package whose test fails is told so, with
/// a non-zero status, and nothing is written anywhere. It builds flowrs-core confined, once, in the
/// verifier's shared target.
#[test]
fn a_contributors_check_runs_with_no_key_and_says_what_failed() {
let d = TempDir::new().unwrap();
let p = d.path().join("failing");
std::fs::create_dir_all(p.join("src")).unwrap();
std::fs::write(
p.join("Cargo.toml"),
"[package]\nname = \"failing\"\nversion = \"0.1.0\"\nedition = \"2024\"\npublish = false\n\n[dependencies]\nflowrs-core = { path = \"../../crates/flowrs-core\" }\n",
)
.unwrap();
std::fs::write(
p.join("src/lib.rs"),
"pub fn install() -> Result<(), String> {\n Ok(())\n}\n#[cfg(test)]\nmod tests {\n #[test]\n fn it_runs() {\n panic!(\"this package is broken\");\n }\n}\n",
)
.unwrap();
std::fs::write(
p.join("flowrs-package.toml"),
MANIFEST.replace("\"tiny\"", "\"failing\""),
)
.unwrap();
let work = std::env::temp_dir().join("flowrs-registry-verify");
let o = flowrsc(
d.path(),
&[
"pkg",
"check",
p.to_str().unwrap(),
"--work",
work.to_str().unwrap(),
"--commit",
"custody-test",
],
None,
);
let t = text(&o);
assert_eq!(o.status.code(), Some(1), "{t}");
assert!(t.contains("the manifest reads"), "{t}");
assert!(t.contains("FAILED tests"), "{t}");
assert!(t.contains("fix it before opening a pull request"), "{t}");
assert!(!p.join("record.json").exists());
}