use super::*; use dever_cli::libs::Ecosystem; use dever_cli::toolchain::{ BuildTarget, Extension, ExtensionId, ExtensionKind, ExtensionSource, SignedResources, artifact_get_optional, prepare_extension_with, }; use std::sync::atomic::AtomicUsize; fn id(ecosystem: Ecosystem) -> ExtensionId { ExtensionId { kind: ExtensionKind::Runtime(ecosystem), target: BuildTarget::host().unwrap(), } } fn logical_path(id: &ExtensionId) -> String { let ExtensionKind::Runtime(ecosystem) = &id.kind else { panic!("runtime fixture") }; format!( "runtime/{}/{}/runtime.pack", ecosystem.as_str(), id.target.platform() ) } struct Source { requests: AtomicUsize, fail: bool, } impl Source { fn new(fail: bool) -> Self { Self { requests: AtomicUsize::new(0), fail, } } } impl ExtensionSource for Source { fn open(&self, version: &Version, extension: &ExtensionId) -> Result, String> { self.requests.fetch_add(1, Ordering::Relaxed); if self.fail { return Err("fixture extension download failed".into()); } let mut tar = tar::Builder::new(Vec::new()); let mut header = tar::Header::new_ustar(); header.set_size(version.as_str().len() as u64); header.set_mode(0o644); header.set_cksum(); tar.append_data( &mut header, logical_path(extension), version.as_str().as_bytes(), ) .unwrap(); let mut encoder = zstd::stream::write::Encoder::new(Vec::new(), 1).unwrap(); encoder.include_checksum(true).unwrap(); encoder.write_all(&tar.into_inner().unwrap()).unwrap(); Ok(Box::new(std::io::Cursor::new(encoder.finish().unwrap()))) } } fn release(layout: &Layout, key: &Ed25519KeyPair, version: &str) { write_release( layout, key, version, &layout.root().join("dispatch"), true, true, ); let directory = layout.downloads().join(version); let path = directory.join("manifest.json"); let mut manifest: ReleaseManifest = serde_json::from_slice(&fs::read(&path).unwrap()).unwrap(); for ecosystem in [Ecosystem::Pip, Ecosystem::Npm] { let id = id(ecosystem); manifest.extensions.push(Extension { kind: id.kind.clone(), target: id.target, artifacts: vec![Artifact { path: logical_path(&id), bytes: version.len() as u64, sha256: hex(&Sha256::digest(version.as_bytes())), }], }); } let bytes = serde_json::to_vec(&manifest).unwrap(); fs::write(path, &bytes).unwrap(); fs::write( directory.join("manifest.sig"), hex(key.sign(&bytes).as_ref()), ) .unwrap(); } fn machine() -> (TemporaryDirectory, Layout, MachineManager, Ed25519KeyPair) { let root = TemporaryDirectory::new(); let layout = Layout::new(root.path().join("machine")); layout.initialize().unwrap(); let key = signing_key(); fs::write( layout.state().join("trusted-release-key"), hex(key.public_key().as_ref()), ) .unwrap(); let manager = MachineManager::new(layout.clone()); (root, layout, manager, key) } #[test] fn signed_extension_preparation_is_shared_immutable_and_concurrent() { let (_root, layout, manager, key) = machine(); release(&layout, &key, "1.0.0"); let version = manager.install_requested("1.0.0").unwrap(); let extension = id(Ecosystem::Pip); let logical = logical_path(&extension); let resources = SignedResources::load(&layout, &version).unwrap(); assert!( resources .read(&logical) .unwrap_err() .contains("dever lib install") ); let source = Source::new(false); std::thread::scope(|scope| { for _ in 0..2 { scope.spawn(|| prepare_extension_with(&layout, &version, &extension, &source).unwrap()); } }); assert_eq!(source.requests.load(Ordering::Relaxed), 1); assert_eq!(resources.read(&logical).unwrap(), b"1.0.0"); let path = resources.path(&logical).unwrap(); assert!(path.starts_with(layout.extensions())); assert!(!layout.versions().join("1.0.0").join(&logical).exists()); fs::write(&path, b"other").unwrap(); assert!(resources.read(&logical).unwrap_err().contains("integrity")); assert!( prepare_extension_with(&layout, &version, &extension, &source) .unwrap_err() .contains("integrity") ); assert_eq!(source.requests.load(Ordering::Relaxed), 1); } #[test] fn signed_resources_cross_machine_roots_without_sharing_the_cache() { let (root, layout, manager, key) = machine(); release(&layout, &key, "1.0.0"); let version = manager.install_requested("1.0.0").unwrap(); let mut daemon = TestDaemon::start(&layout); let client = Layout::new(root.path().join("client")); client.initialize().unwrap(); fs::copy( layout.state().join("trusted-release-key"), client.state().join("trusted-release-key"), ) .unwrap(); let source = layout.versions().join(version.as_str()); let manifest: ReleaseManifest = serde_json::from_slice(&fs::read(source.join("manifest.json")).unwrap()).unwrap(); for path in manifest .artifacts .iter() .map(|artifact| artifact.path.as_str()) .chain(["manifest.json", "manifest.sig"]) { let destination = client.downloads().join(version.as_str()).join(path); fs::create_dir_all(destination.parent().unwrap()).unwrap(); fs::copy(source.join(path), destination).unwrap(); } MachineManager::new(client.clone()) .install_requested(version.as_str()) .unwrap(); fs::hard_link(layout.service_socket(), client.service_socket()).unwrap(); fs::copy(layout.service_token(), client.service_token()).unwrap(); let resources = SignedResources::load(&client, &version).unwrap(); let extension = id(Ecosystem::Pip); let logical = logical_path(&extension); assert!( resources .read_shared(&logical) .unwrap_err() .contains("not installed") ); assert!(!layout.extensions().exists()); let source = Source::new(false); prepare_extension_with(&layout, &version, &extension, &source).unwrap(); assert_eq!(resources.read_shared(&logical).unwrap(), b"1.0.0"); assert_eq!(resources.read_shared(&logical).unwrap(), b"1.0.0"); assert_eq!(source.requests.load(Ordering::Relaxed), 1); assert!(!client.extensions().exists()); assert!( resources .read(&logical) .unwrap_err() .contains("not installed") ); assert!( resources .read_shared(&logical_path(&id(Ecosystem::Npm))) .unwrap_err() .contains("not installed") ); assert!( resources .read_shared("../../state/service-token") .unwrap_err() .contains("does not declare") ); let path = SignedResources::load(&layout, &version) .unwrap() .path(&logical) .unwrap(); fs::write(&path, b"other").unwrap(); assert!( resources .read_shared(&logical) .unwrap_err() .contains("integrity") ); daemon.stop(); assert!(resources.read_shared(&logical).is_err()); } #[test] fn signed_resource_ipc_rejects_unbound_receipts_catalogs_and_paths() { let (_root, layout, manager, key) = machine(); release(&layout, &key, "1.0.0"); let version = manager.install_requested("1.0.0").unwrap(); prepare_extension_with(&layout, &version, &id(Ecosystem::Pip), &Source::new(false)).unwrap(); let _daemon = TestDaemon::start(&layout); let token = fs::read_to_string(layout.service_token()).unwrap(); let catalog = hex(&Sha256::digest( fs::read(layout.versions().join("1.0.0/manifest.json")).unwrap(), )); let request = serde_json::json!({"version":"1.0.0", "catalog":catalog, "extension":id(Ecosystem::Pip), "artifact":{"sha256":hex(&Sha256::digest(b"1.0.0")), "bytes":5}}); for field in ["catalog", "artifact", "extension", "path"] { let mut invalid = request.clone(); invalid[field] = match field { "catalog" => serde_json::json!("0".repeat(64)), "artifact" => serde_json::json!({"sha256":"0".repeat(64),"bytes":u64::MAX}), "extension" => serde_json::Value::Null, _ => serde_json::json!("../../state/service-token"), }; let result = raw_operation( &layout, token.trim(), serde_json::json!({"resource_get":invalid}), ); assert_eq!(result["ok"], false, "{field}: {result}"); } } #[test] fn update_prepares_only_installed_extensions_and_preserves_active_on_failure() { let (_root, layout, manager, key) = machine(); for version in ["1.0.0", "2.0.0"] { release(&layout, &key, version); } let old = manager.install_requested("1.0.0").unwrap(); let extension = id(Ecosystem::Pip); prepare_extension_with(&layout, &old, &extension, &Source::new(false)).unwrap(); fs::write(layout.downloads().join("latest"), "2.0.0").unwrap(); let journal = fs::read(layout.state().join("active-version")).unwrap(); assert!( manager .update_with(&Source::new(true)) .unwrap_err() .contains("download failed") ); assert_eq!( fs::read(layout.state().join("active-version")).unwrap(), journal ); assert_eq!( SignedResources::load(&layout, &old) .unwrap() .read(&logical_path(&extension)) .unwrap(), b"1.0.0" ); let source = Source::new(false); let new = manager.update_with(&source).unwrap(); assert_eq!(new.as_str(), "2.0.0"); assert_eq!(source.requests.load(Ordering::Relaxed), 1); let resources = SignedResources::load(&layout, &new).unwrap(); assert_eq!(resources.read(&logical_path(&extension)).unwrap(), b"2.0.0"); assert!( resources .read(&logical_path(&id(Ecosystem::Npm))) .unwrap_err() .contains("not installed") ); assert_eq!(manager.active_version().unwrap(), Some(new)); } #[test] fn optional_artifact_ipc_distinguishes_absence_corruption_and_service_failure() { let (_root, layout, _manager, _key) = machine(); let mut daemon = TestDaemon::start(&layout); let digest = hex(&Sha256::digest(b"payload")); assert!( artifact_get_optional(&layout, &digest, 7) .unwrap() .is_none() ); artifact_put(&layout, b"payload").unwrap(); assert_eq!( artifact_get_optional(&layout, &digest, 7).unwrap().unwrap(), b"payload" ); fs::write( layout.native_cache().join("artifacts").join(&digest), b"changed", ) .unwrap(); assert!( artifact_get_optional(&layout, &digest, 7) .unwrap_err() .contains("SHA-256") ); daemon.stop(); assert!(artifact_get_optional(&layout, &digest, 7).is_err()); } #[test] fn extension_ipc_rejects_caller_paths_and_invalid_typed_identity() { let (_root, layout, _manager, _key) = machine(); let _daemon = TestDaemon::start(&layout); let token = fs::read_to_string(layout.service_token()).unwrap(); for selection in [ serde_json::json!({"version":"1.0.0","extension":{"kind":{"type":"runtime","ecosystem":"pip"},"target":"linux-x86_64"},"url":"http://localhost/"}), serde_json::json!({"version":"../../outside","extension":{"kind":{"type":"target"},"target":"linux-aarch64"}}), serde_json::json!({"version":"1.0.0","extension":{"kind":{"type":"shell"},"target":"linux-x86_64"}}), ] { let result = raw_operation( &layout, token.trim(), serde_json::json!({"ensure_extension":selection}), ); assert_eq!(result["ok"], false, "{result}"); } }