Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,182 @@
|
||||
//! 自有签名、归档和 Lib 输入验证;不连接生产 deverd 或修改机器缓存。
|
||||
|
||||
mod central;
|
||||
mod pep517;
|
||||
mod pep517_fixture;
|
||||
mod python;
|
||||
mod python_fixture;
|
||||
|
||||
use super::{Fixture, fixture::digest, write};
|
||||
use dever::compiler::project::dependency::{
|
||||
library::{Spec, archive},
|
||||
worker::signed::SignedResources,
|
||||
};
|
||||
use ring::signature::{Ed25519KeyPair, KeyPair};
|
||||
use std::io::Write;
|
||||
|
||||
#[test]
|
||||
fn lib_identities_normalize_extras_and_reject_ranges_and_path_injection() {
|
||||
assert_eq!(
|
||||
"pip:Some_Name[TEST,docs]@1.2.3"
|
||||
.parse::<Spec>()
|
||||
.unwrap()
|
||||
.key(),
|
||||
"pip:some-name[docs,test]@1.2.3"
|
||||
);
|
||||
assert_eq!(
|
||||
"go:example.org/widget@v1.2.3"
|
||||
.parse::<Spec>()
|
||||
.unwrap()
|
||||
.version,
|
||||
"1.2.3"
|
||||
);
|
||||
assert_eq!(
|
||||
"npm:@scope/name@1.2.3".parse::<Spec>().unwrap().name,
|
||||
"@scope/name"
|
||||
);
|
||||
for value in [
|
||||
"npm:thing@^1",
|
||||
"npm:../thing@1.2.3",
|
||||
"pip:thing[]@1",
|
||||
"pip:thing@>=1",
|
||||
"go:../thing@1.2.3",
|
||||
"npm:@scope/../thing@1.2.3",
|
||||
] {
|
||||
assert!(value.parse::<Spec>().is_err(), "accepted {value}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn installed_resources_require_independent_signature_and_exact_catalog_bytes() {
|
||||
let fixture = Fixture::new(&[]);
|
||||
let root = fixture.directory.0.join("installation");
|
||||
let key = Ed25519KeyPair::from_seed_unchecked(&[91; 32]).unwrap();
|
||||
let hex = |bytes: &[u8]| {
|
||||
bytes
|
||||
.iter()
|
||||
.map(|byte| format!("{byte:02x}"))
|
||||
.collect::<String>()
|
||||
};
|
||||
write(
|
||||
&root,
|
||||
"state/trusted-release-key",
|
||||
hex(key.public_key().as_ref()),
|
||||
);
|
||||
let bytes = b"signed fixture contents";
|
||||
let path = "runtime/npm/linux-x86_64/runtime.pack";
|
||||
let target = dever::compiler::toolchain::BuildTarget::host().unwrap();
|
||||
let catalog = serde_json::to_vec(&serde_json::json!({"format":"dever-release-v2","version":"0.0.1","platform":target.name(),"artifacts":[{"path":"base.txt","bytes":bytes.len(),"sha256":digest(bytes)}],"extensions":[{"kind":{"type":"runtime","ecosystem":"npm"},"target":"linux-x86_64","artifacts":[{"path":path,"bytes":bytes.len(),"sha256":digest(bytes)}]}]})).unwrap();
|
||||
write(&root, "versions/0.0.1/base.txt", bytes);
|
||||
write(&root, "versions/0.0.1/manifest.json", &catalog);
|
||||
write(
|
||||
&root,
|
||||
"versions/0.0.1/manifest.sig",
|
||||
hex(key.sign(&catalog).as_ref()),
|
||||
);
|
||||
let signed = SignedResources::load(&root, "0.0.1").unwrap();
|
||||
assert_eq!(signed.read("base.txt").unwrap(), bytes);
|
||||
let actual = signed.read_with(path, |version, catalog_hash, extension, artifact| {
|
||||
assert_eq!(version, "0.0.1");
|
||||
assert_eq!(catalog_hash, digest(&catalog));
|
||||
assert_eq!(extension, &serde_json::json!({"kind":{"type":"runtime","ecosystem":"npm"},"target":"linux-x86_64"}));
|
||||
assert_eq!(artifact.path, path);
|
||||
Ok(bytes.to_vec())
|
||||
}).unwrap();
|
||||
assert_eq!(actual, bytes);
|
||||
assert!(
|
||||
signed
|
||||
.read_with(path, |_, _, _, _| Ok(b"changed".to_vec()))
|
||||
.is_err()
|
||||
);
|
||||
write(&root, "versions/0.0.1/base.txt", b"different signed file");
|
||||
assert!(signed.read("base.txt").is_err());
|
||||
write(&root, "versions/0.0.1/manifest.json", b"{}");
|
||||
assert!(SignedResources::load(&root, "0.0.1").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn archive_consumption_rejects_links_and_file_directory_collisions() {
|
||||
let make = |entries: &[(&str, &[u8], tar::EntryType)]| {
|
||||
let gzip = flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::fast());
|
||||
let mut archive = tar::Builder::new(gzip);
|
||||
for (path, bytes, kind) in entries {
|
||||
let mut header = tar::Header::new_gnu();
|
||||
header.set_entry_type(*kind);
|
||||
header.set_size(bytes.len() as u64);
|
||||
header.set_mode(0o755);
|
||||
header.set_cksum();
|
||||
archive.append_data(&mut header, path, *bytes).unwrap();
|
||||
}
|
||||
archive.into_inner().unwrap().finish().unwrap()
|
||||
};
|
||||
let bytes = make(&[(
|
||||
"package/main.js",
|
||||
b"export const value = 42;",
|
||||
tar::EntryType::Regular,
|
||||
)]);
|
||||
assert_eq!(archive::files(&bytes, false).unwrap().len(), 1);
|
||||
assert!(archive::entries(&bytes, false).unwrap()["package/main.js"].1);
|
||||
let bytes = make(&[("package/main.js", b"", tar::EntryType::Symlink)]);
|
||||
assert!(archive::files(&bytes, false).unwrap_err().contains("links"));
|
||||
let bytes = make(&[
|
||||
("package", b"not a directory", tar::EntryType::Regular),
|
||||
("package/main.js", b"", tar::EntryType::Regular),
|
||||
]);
|
||||
assert!(
|
||||
archive::files(&bytes, false)
|
||||
.unwrap_err()
|
||||
.contains("collision")
|
||||
);
|
||||
let mut zip = zip::ZipWriter::new(std::io::Cursor::new(Vec::new()));
|
||||
zip.start_file("../escape", zip::write::SimpleFileOptions::default())
|
||||
.unwrap();
|
||||
zip.write_all(b"bad").unwrap();
|
||||
assert!(archive::files(&zip.finish().unwrap().into_inner(), true).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lib_zip_rejects_duplicate_central_paths() {
|
||||
let mut zip = zip::ZipWriter::new(std::io::Cursor::new(Vec::new()));
|
||||
for name in ["package/first", "package/other"] {
|
||||
zip.start_file(
|
||||
name,
|
||||
zip::write::SimpleFileOptions::default().unix_permissions(0o755),
|
||||
)
|
||||
.unwrap();
|
||||
zip.write_all(name.as_bytes()).unwrap();
|
||||
}
|
||||
let mut bytes = zip.finish().unwrap().into_inner();
|
||||
let files = archive::entries(&bytes, true).unwrap();
|
||||
assert_eq!(files.len(), 2);
|
||||
assert!(files["package/first"].1);
|
||||
let mut cursor = 0;
|
||||
while cursor + 5 <= bytes.len() {
|
||||
if &bytes[cursor..cursor + 5] == b"other" {
|
||||
bytes[cursor..cursor + 5].copy_from_slice(b"first");
|
||||
}
|
||||
cursor += 1;
|
||||
}
|
||||
assert!(
|
||||
archive::entries(&bytes, true)
|
||||
.unwrap_err()
|
||||
.contains("duplicate or inconsistent")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn python_wheel_identity_abi_and_dependency_edges_are_checked_before_installation() {
|
||||
let bytes = python_fixture::wheel("tiny", "1.0.0", "");
|
||||
let lib = serde_json::from_value(serde_json::json!({"spec":{"ecosystem":"pip","name":"tiny","version":"1.0.0"},"dependencies":[],"runtime":{"name":"python","version":"3.12.1","sha256":"0".repeat(64)},"artifacts":[],"schema":"pypi-wheel-v1"})).unwrap();
|
||||
let markers = serde_json::json!({"implementation_name":"cpython","implementation_version":"3.12.1","os_name":"posix","platform_machine":"x86_64","platform_python_implementation":"CPython","platform_release":"","platform_system":"Linux","platform_version":"","python_full_version":"3.12.1","python_version":"3.12","sys_platform":"linux"});
|
||||
let mut runtime = serde_json::from_value::<dever::compiler::project::dependency::library::runtime::Manifest>(serde_json::json!({"format":"dever-worker-runtime-v1","ecosystem":"pip","target":"linux-x86_64","executable":"bin/python3","arguments":["-I","-S","-B"],"python_wheel_tags":["py3-none-any"],"python_extension_suffixes":[".so"],"python_markers":markers})).unwrap();
|
||||
let files =
|
||||
dever::compiler::project::dependency::library::python::install(&lib, &bytes, &runtime, &[])
|
||||
.unwrap();
|
||||
assert_eq!(files["tiny/__init__.py"], b"VALUE = 42\n");
|
||||
runtime.python_wheel_tags.clear();
|
||||
assert!(
|
||||
dever::compiler::project::dependency::library::python::install(&lib, &bytes, &runtime, &[])
|
||||
.unwrap_err()
|
||||
.contains("tags")
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user