radrootsd

JSON-RPC bridge for Radroots event publishing
git clone https://radroots.dev/git/radrootsd.git
Log | Files | Refs | README | LICENSE

source_lock.rs (5786B)


      1 #![forbid(unsafe_code)]
      2 
      3 use serde::Deserialize;
      4 use serde_json::{Value, json};
      5 use sha2::{Digest, Sha256};
      6 use std::process::Command;
      7 
      8 #[derive(Deserialize)]
      9 #[serde(deny_unknown_fields)]
     10 struct SourceLock {
     11     schema: String,
     12     repository: String,
     13     revision: String,
     14     architecture: String,
     15     workspace_catalog_sha256: String,
     16     version: String,
     17     source_archive_sha256: String,
     18     lockfile: String,
     19     lockfile_sha256: String,
     20 }
     21 
     22 fn validate(lock: &str, cargo_lock: &[u8], metadata: &Value) -> Result<(), &'static str> {
     23     let lock: SourceLock = toml::from_str(lock).map_err(|_| "invalid source lock")?;
     24     if lock.schema != "radroots.lib.source-lock.v1"
     25         || lock.repository != "https://github.com/radrootslabs/lib"
     26         || lock.architecture != "radroots.crates.release.v2"
     27         || lock.lockfile != "Cargo.lock"
     28     {
     29         return Err("invalid source identity");
     30     }
     31     for (value, length) in [
     32         (&lock.revision, 40),
     33         (&lock.workspace_catalog_sha256, 64),
     34         (&lock.source_archive_sha256, 64),
     35         (&lock.lockfile_sha256, 64),
     36     ] {
     37         if value.len() != length
     38             || !value
     39                 .bytes()
     40                 .all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))
     41         {
     42             return Err("invalid source digest");
     43         }
     44     }
     45     if lock.lockfile_sha256 != format!("{:x}", Sha256::digest(cargo_lock)) {
     46         return Err("stale Cargo.lock digest");
     47     }
     48     let source = format!(
     49         "git+https://github.com/radrootslabs/lib.git?rev={}",
     50         lock.revision
     51     );
     52     let resolved = format!("{source}#{}", lock.revision);
     53     let packages = metadata["packages"].as_array().ok_or("invalid metadata")?;
     54     let daemon = packages
     55         .iter()
     56         .find(|p| p["name"] == "radrootsd")
     57         .ok_or("missing daemon")?;
     58     let dependencies = daemon["dependencies"]
     59         .as_array()
     60         .ok_or("missing dependencies")?;
     61     let mut direct = 0;
     62     for dependency in dependencies {
     63         let name = dependency["name"]
     64             .as_str()
     65             .ok_or("missing dependency name")?;
     66         if name.starts_with("radroots_") {
     67             if dependency["source"] != source
     68                 || dependency["req"] != format!("={}", lock.version)
     69                 || !dependency["rename"].is_null()
     70             {
     71                 return Err("unlocked direct foundation dependency");
     72             }
     73             direct += 1;
     74         }
     75     }
     76     let mut shared = 0;
     77     for package in packages {
     78         let name = package["name"].as_str().ok_or("missing package name")?;
     79         if name == "radroots" || name.starts_with("radroots_") {
     80             if package["source"] != resolved || package["version"] != lock.version {
     81                 return Err("unlocked resolved foundation package");
     82             }
     83             shared += 1;
     84         }
     85     }
     86     if direct == 0 || shared == 0 {
     87         return Err("missing foundation dependencies");
     88     }
     89     Ok(())
     90 }
     91 
     92 #[test]
     93 fn committed_source_lock_matches_actual_lock_and_offline_resolved_graph() {
     94     let metadata = Command::new("cargo")
     95         .args(["metadata", "--locked", "--offline", "--format-version", "1"])
     96         .current_dir(env!("CARGO_MANIFEST_DIR"))
     97         .output()
     98         .expect("offline locked metadata");
     99     assert!(metadata.status.success(), "offline locked metadata failed");
    100     let metadata = serde_json::from_slice(&metadata.stdout).expect("metadata JSON");
    101     validate(
    102         include_str!("../radroots.lib.source-lock.v1.toml"),
    103         include_bytes!("../Cargo.lock"),
    104         &metadata,
    105     )
    106     .expect("exact source-lock agreement");
    107 }
    108 
    109 fn fixture() -> (String, Value) {
    110     let text = include_str!("../radroots.lib.source-lock.v1.toml");
    111     let lock: SourceLock = toml::from_str(text).expect("source lock");
    112     let source = format!(
    113         "git+https://github.com/radrootslabs/lib.git?rev={}",
    114         lock.revision
    115     );
    116     let metadata = json!({"packages": [
    117         {"name": "radrootsd", "dependencies": [{"name": "radroots_protocol", "source": source, "req": "=0.1.0-alpha", "rename": null}]},
    118         {"name": "radroots_protocol", "source": format!("{source}#{}", lock.revision), "version": "0.1.0-alpha"}
    119     ]});
    120     (
    121         text.replace(
    122             &lock.lockfile_sha256,
    123             &format!("{:x}", Sha256::digest(b"fixture lock")),
    124         ),
    125         metadata,
    126     )
    127 }
    128 
    129 #[test]
    130 fn changed_lock_bytes_and_malformed_source_identity_are_rejected() {
    131     let (lock, metadata) = fixture();
    132     assert!(validate(&lock, b"fixture lock", &metadata).is_ok());
    133     assert_eq!(
    134         validate(&lock, b"different lock", &metadata),
    135         Err("stale Cargo.lock digest")
    136     );
    137     for changed in [
    138         format!("{lock}unknown = true\n"),
    139         format!("{lock}schema = \"duplicate\"\n"),
    140         lock.replace("lockfile = \"Cargo.lock\"", "lockfile = \"../Cargo.lock\""),
    141     ] {
    142         assert!(validate(&changed, b"fixture lock", &metadata).is_err());
    143     }
    144 }
    145 
    146 #[test]
    147 fn path_or_unlocked_dependency_overrides_are_rejected() {
    148     let (lock, metadata) = fixture();
    149     for (field, value) in [
    150         ("source", Value::Null),
    151         (
    152             "source",
    153             json!("git+https://github.com/radrootslabs/lib.git?branch=master"),
    154         ),
    155         ("req", json!("^0.1")),
    156         ("rename", json!("hidden_dependency")),
    157     ] {
    158         let mut changed = metadata.clone();
    159         changed["packages"][0]["dependencies"][0][field] = value;
    160         assert!(validate(&lock, b"fixture lock", &changed).is_err());
    161     }
    162     for (field, value) in [("source", Value::Null), ("version", json!("0.2.0"))] {
    163         let mut changed = metadata.clone();
    164         changed["packages"][1][field] = value;
    165         assert!(validate(&lock, b"fixture lock", &changed).is_err());
    166     }
    167 }