Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,209 @@
|
||||
use super::super::Directory;
|
||||
use super::fixture::digest;
|
||||
use std::io::{Read, Write};
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::os::unix::net::UnixListener;
|
||||
|
||||
#[test]
|
||||
fn cache_client_authenticates_receipts_and_rejects_missing_corrupt_or_truncated_bytes() {
|
||||
for behavior in [
|
||||
"valid",
|
||||
"missing",
|
||||
"receipt",
|
||||
"digest",
|
||||
"truncated",
|
||||
"oversized",
|
||||
] {
|
||||
let directory = Directory::new();
|
||||
let state = directory.0.join("state");
|
||||
std::fs::create_dir(&state).unwrap();
|
||||
std::fs::set_permissions(&state, std::fs::Permissions::from_mode(0o700)).unwrap();
|
||||
let token = "a".repeat(64);
|
||||
let token_path = state.join("deverd.token");
|
||||
std::fs::write(&token_path, &token).unwrap();
|
||||
std::fs::set_permissions(token_path, std::fs::Permissions::from_mode(0o600)).unwrap();
|
||||
let endpoint = directory.0.join("cache.sock");
|
||||
let server = UnixListener::bind(&endpoint).unwrap();
|
||||
let bytes = b"archive fixture";
|
||||
let sha256 = digest(bytes);
|
||||
let expected = sha256.clone();
|
||||
let worker = std::thread::spawn(move || {
|
||||
let (mut stream, _) = server.accept().unwrap();
|
||||
let mut length = [0; 4];
|
||||
stream.read_exact(&mut length).unwrap();
|
||||
let mut payload = vec![0; u32::from_be_bytes(length) as usize];
|
||||
stream.read_exact(&mut payload).unwrap();
|
||||
let request: serde_json::Value = serde_json::from_slice(&payload).unwrap();
|
||||
assert_eq!(request["token"], token);
|
||||
assert_eq!(request["operation"]["artifact_get"]["sha256"], expected);
|
||||
assert_eq!(request["operation"]["artifact_get"]["bytes"], bytes.len());
|
||||
if behavior == "oversized" {
|
||||
stream
|
||||
.write_all(&(1024_u32 * 1024 + 1).to_be_bytes())
|
||||
.unwrap();
|
||||
return;
|
||||
}
|
||||
let artifact = if behavior == "missing" {
|
||||
serde_json::Value::Null
|
||||
} else {
|
||||
serde_json::json!({"sha256":if behavior == "receipt" { "0".repeat(64) } else { expected }, "bytes":bytes.len()})
|
||||
};
|
||||
let response = serde_json::to_vec(
|
||||
&serde_json::json!({"ok":true, "status":null, "error":null, "artifact":artifact}),
|
||||
)
|
||||
.unwrap();
|
||||
stream
|
||||
.write_all(&(response.len() as u32).to_be_bytes())
|
||||
.unwrap();
|
||||
stream.write_all(&response).unwrap();
|
||||
if !matches!(behavior, "missing" | "receipt") {
|
||||
let mut content = bytes.to_vec();
|
||||
if behavior == "digest" {
|
||||
content[0] ^= 1;
|
||||
}
|
||||
if behavior == "truncated" {
|
||||
content.pop();
|
||||
}
|
||||
stream.write_all(&content).unwrap();
|
||||
}
|
||||
});
|
||||
let result = dever::process::cache::read(&state, &endpoint, &sha256, bytes.len() as u64);
|
||||
worker.join().unwrap();
|
||||
if behavior == "valid" {
|
||||
assert_eq!(result.unwrap(), bytes);
|
||||
} else {
|
||||
assert!(result.is_err(), "accepted {behavior}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_client_rejects_untrusted_state_before_connecting() {
|
||||
let directory = Directory::new();
|
||||
std::fs::set_permissions(&directory.0, std::fs::Permissions::from_mode(0o777)).unwrap();
|
||||
let error = dever::process::cache::read(
|
||||
&directory.0,
|
||||
&directory.0.join("absent.sock"),
|
||||
&"a".repeat(64),
|
||||
1,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert!(error.contains("not writable"), "{error}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_upload_requires_both_authenticated_admission_and_final_receipts() {
|
||||
for rejected in ["none", "admission", "publication"] {
|
||||
let directory = Directory::new();
|
||||
let state = directory.0.join("state");
|
||||
std::fs::create_dir(&state).unwrap();
|
||||
std::fs::set_permissions(&state, std::fs::Permissions::from_mode(0o700)).unwrap();
|
||||
let token = "b".repeat(64);
|
||||
let path = state.join("deverd.token");
|
||||
std::fs::write(&path, &token).unwrap();
|
||||
std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o600)).unwrap();
|
||||
let endpoint = directory.0.join("cache.sock");
|
||||
let listener = UnixListener::bind(&endpoint).unwrap();
|
||||
let bytes = b"content addressed upload";
|
||||
let server = std::thread::spawn(move || {
|
||||
let (mut stream, _) = listener.accept().unwrap();
|
||||
stream
|
||||
.set_read_timeout(Some(std::time::Duration::from_secs(5)))
|
||||
.unwrap();
|
||||
let mut prefix = [0; 4];
|
||||
stream.read_exact(&mut prefix).unwrap();
|
||||
let mut payload = vec![0; u32::from_be_bytes(prefix) as usize];
|
||||
stream.read_exact(&mut payload).unwrap();
|
||||
let request: serde_json::Value = serde_json::from_slice(&payload).unwrap();
|
||||
assert_eq!(request["token"], token);
|
||||
let expected = serde_json::json!({"sha256":digest(bytes), "bytes":bytes.len()});
|
||||
assert_eq!(request["operation"]["artifact_put"], expected);
|
||||
for phase in ["admission", "publication"] {
|
||||
let artifact = if rejected == phase {
|
||||
serde_json::json!({"sha256":"0".repeat(64),"bytes":bytes.len()})
|
||||
} else {
|
||||
expected.clone()
|
||||
};
|
||||
let response = serde_json::to_vec(
|
||||
&serde_json::json!({"ok":true,"status":null,"error":null,"artifact":artifact}),
|
||||
)
|
||||
.unwrap();
|
||||
stream
|
||||
.write_all(&(response.len() as u32).to_be_bytes())
|
||||
.unwrap();
|
||||
stream.write_all(&response).unwrap();
|
||||
if phase == "admission" {
|
||||
if rejected == phase {
|
||||
assert_eq!(stream.read(&mut prefix).unwrap(), 0);
|
||||
break;
|
||||
}
|
||||
let mut received = vec![0; bytes.len()];
|
||||
stream.read_exact(&mut received).unwrap();
|
||||
assert_eq!(received, bytes);
|
||||
}
|
||||
}
|
||||
});
|
||||
let result = dever::process::cache::write(&state, &endpoint, bytes);
|
||||
server.join().unwrap();
|
||||
assert_eq!(result.is_ok(), rejected == "none");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signed_resource_get_uses_the_historical_deverd_envelope_and_exact_receipt() {
|
||||
// 原 service.rs::resource 请求版本/catalog/扩展身份;服务先回复 receipt,再写裸资产。
|
||||
for corrupt in [false, true] {
|
||||
let directory = Directory::new();
|
||||
let state = directory.0.join("state");
|
||||
std::fs::create_dir(&state).unwrap();
|
||||
let token = "c".repeat(64);
|
||||
let token_path = state.join("deverd.token");
|
||||
std::fs::write(&token_path, &token).unwrap();
|
||||
std::fs::set_permissions(token_path, std::fs::Permissions::from_mode(0o600)).unwrap();
|
||||
let endpoint = directory.0.join("resources.sock");
|
||||
let listener = UnixListener::bind(&endpoint).unwrap();
|
||||
let bytes = b"signed runtime fixture";
|
||||
let catalog = "d".repeat(64);
|
||||
let extension = serde_json::json!({"kind":{"type":"runtime","ecosystem":"npm"},"target":"linux-x86_64"});
|
||||
let expected = serde_json::json!({"token":token,"operation":{"resource_get":{"version":"0.0.1","catalog":catalog,"extension":extension,"artifact":{"sha256":digest(bytes),"bytes":bytes.len()}}},"installed":[]});
|
||||
let worker = std::thread::spawn(move || {
|
||||
let (mut stream, _) = listener.accept().unwrap();
|
||||
stream
|
||||
.set_read_timeout(Some(std::time::Duration::from_secs(5)))
|
||||
.unwrap();
|
||||
let mut prefix = [0; 4];
|
||||
stream.read_exact(&mut prefix).unwrap();
|
||||
let mut request = vec![0; u32::from_be_bytes(prefix) as usize];
|
||||
stream.read_exact(&mut request).unwrap();
|
||||
assert_eq!(
|
||||
serde_json::from_slice::<serde_json::Value>(&request).unwrap(),
|
||||
expected
|
||||
);
|
||||
let response = serde_json::to_vec(&serde_json::json!({"ok":true,"status":null,"error":null,"artifact":{"sha256":digest(bytes),"bytes":bytes.len()}})).unwrap();
|
||||
stream
|
||||
.write_all(&(response.len() as u32).to_be_bytes())
|
||||
.unwrap();
|
||||
stream.write_all(&response).unwrap();
|
||||
let mut content = bytes.to_vec();
|
||||
if corrupt {
|
||||
content[0] ^= 1;
|
||||
}
|
||||
stream.write_all(&content).unwrap();
|
||||
});
|
||||
let result = dever::process::cache::resource(
|
||||
&state,
|
||||
&endpoint,
|
||||
"0.0.1",
|
||||
&catalog,
|
||||
Some(&extension),
|
||||
&digest(bytes),
|
||||
bytes.len() as u64,
|
||||
);
|
||||
worker.join().unwrap();
|
||||
if corrupt {
|
||||
assert!(result.unwrap_err().contains("SHA-256"));
|
||||
} else {
|
||||
assert_eq!(result.unwrap(), bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user