228 lines
7.5 KiB
Rust
228 lines
7.5 KiB
Rust
use std::collections::BTreeSet;
|
|
use std::fs;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
const ALLOWED_DEPENDENCIES: [&str; 17] = [
|
|
"base64",
|
|
"crossterm",
|
|
"libremetaverse",
|
|
"libremetaverse-imaging",
|
|
"libremetaverse-rendering-simple",
|
|
"metacrate-lsl-tools",
|
|
"png",
|
|
"libremetaverse-types",
|
|
"reqwest",
|
|
"rustls",
|
|
"serde",
|
|
"serde_json",
|
|
"sha2",
|
|
"tokio",
|
|
"tokio-rustls",
|
|
"url",
|
|
"unicode-width",
|
|
];
|
|
|
|
#[test]
|
|
fn package_has_only_reviewed_rust_dependencies_and_no_build_script() {
|
|
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
assert!(
|
|
!root.join("build.rs").exists(),
|
|
"agent must not have a build script"
|
|
);
|
|
let manifest = fs::read_to_string(root.join("Cargo.toml")).expect("read package manifest");
|
|
let dependency_section = manifest
|
|
.split("[dependencies]")
|
|
.nth(1)
|
|
.expect("dependency section")
|
|
.split("\n[")
|
|
.next()
|
|
.expect("end of dependency section");
|
|
let observed = dependency_section
|
|
.lines()
|
|
.filter_map(|line| line.split_once('=').map(|(name, _)| name.trim()))
|
|
.filter(|name| !name.is_empty())
|
|
.collect::<BTreeSet<_>>();
|
|
assert_eq!(
|
|
observed,
|
|
ALLOWED_DEPENDENCIES.into_iter().collect(),
|
|
"every direct dependency needs explicit policy review"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn runtime_source_has_no_subprocess_or_native_abi_escape_hatch() {
|
|
let source = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("src");
|
|
let mut files = Vec::with_capacity(38);
|
|
collect_rust_files(&source, &mut files);
|
|
assert!(
|
|
files.len() <= 38,
|
|
"source-file count needs a reviewed bound update"
|
|
);
|
|
for path in files {
|
|
let text = fs::read_to_string(&path).expect("read runtime source");
|
|
for forbidden in [
|
|
"std::process::Command",
|
|
"tokio::process",
|
|
"Command::new(",
|
|
"extern \"C\"",
|
|
"#[link(",
|
|
"unsafe fn ",
|
|
"unsafe impl ",
|
|
] {
|
|
assert!(
|
|
!text.contains(forbidden),
|
|
"{} contains forbidden runtime boundary {forbidden:?}",
|
|
path.display()
|
|
);
|
|
}
|
|
assert!(
|
|
!text.lines().any(|line| {
|
|
let line = line.trim_start();
|
|
line.starts_with("unsafe {") || line.contains("= unsafe {")
|
|
}),
|
|
"{} contains an unsafe block",
|
|
path.display()
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn vision_path_selects_only_the_pure_rust_renderer_and_codec() {
|
|
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
let manifest = fs::read_to_string(root.join("Cargo.toml")).expect("agent manifest");
|
|
assert!(manifest.contains("default-features = false, features = [\"rust-j2k\"]"));
|
|
for forbidden in ["imaging-skia", "openjpeg", "skia-safe"] {
|
|
assert!(!manifest.contains(forbidden), "vision enables {forbidden}");
|
|
}
|
|
let renderer = fs::read_to_string(root.join("../libremetaverse-rendering-simple/Cargo.toml"))
|
|
.expect("simple renderer manifest");
|
|
assert!(!renderer.contains("imaging-skia"));
|
|
assert!(!renderer.contains("openjpeg"));
|
|
let vision = fs::read_to_string(root.join("src/vision.rs")).expect("vision source");
|
|
for forbidden in ["screenshot crate", "gpu adapter", "std::process"] {
|
|
assert!(
|
|
!vision.to_ascii_lowercase().contains(forbidden),
|
|
"vision must remain synthetic and pure Rust: {forbidden}"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn compatibility_crates_never_depend_on_or_reexport_metacrate_crates() {
|
|
let workspace = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
|
.parent()
|
|
.and_then(Path::parent)
|
|
.expect("workspace")
|
|
.to_path_buf();
|
|
for entry in fs::read_dir(workspace.join("crates")).expect("crates") {
|
|
let path = entry.expect("crate entry").path();
|
|
let Some(name) = path.file_name().and_then(|value| value.to_str()) else {
|
|
continue;
|
|
};
|
|
if !name.starts_with("libremetaverse") || !path.is_dir() {
|
|
continue;
|
|
}
|
|
let manifest = fs::read_to_string(path.join("Cargo.toml")).expect("compat manifest");
|
|
assert!(
|
|
!manifest.lines().any(|line| {
|
|
let line = line.trim_start();
|
|
line.starts_with("metacrate-") || line.contains("../metacrate-")
|
|
}),
|
|
"{name} must not depend on metacrate crates"
|
|
);
|
|
let lib = path.join("src/lib.rs");
|
|
if lib.exists() {
|
|
let source = fs::read_to_string(lib).expect("compat lib.rs");
|
|
assert!(
|
|
!source.lines().any(|line| {
|
|
let line = line.trim_start();
|
|
(line.starts_with("pub use") || line.starts_with("pub mod"))
|
|
&& line.contains("metacrate")
|
|
}),
|
|
"{name} must not reexport metacrate crates"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn world_backend_requires_the_opaque_policy_authorization() {
|
|
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
let backend = fs::read_to_string(root.join("src/backend.rs")).expect("backend source");
|
|
assert!(backend.contains("action: AuthorizedAction"));
|
|
assert!(!backend.contains("call: ProposedToolCall"));
|
|
assert!(!backend.contains("decision: PolicyDecision"));
|
|
}
|
|
|
|
#[test]
|
|
fn live_session_adapter_reuses_native_lifecycle_and_messaging_managers() {
|
|
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
let backend = fs::read_to_string(root.join("src/backend.rs")).expect("backend source");
|
|
for required in [
|
|
".native_default_login_params(",
|
|
".native_login(",
|
|
".native_subscribe_disconnected(",
|
|
".native_subscribe_event_queue_running(",
|
|
".native_logout_async(",
|
|
".subscribe_chat_from_simulator(",
|
|
".subscribe_im(",
|
|
".chat(",
|
|
".instant_message_with_uuid_string(",
|
|
".objects_avatars",
|
|
".objects_primitives",
|
|
".parcels",
|
|
".environment()",
|
|
".inventory()",
|
|
".store()",
|
|
".native_subscribe_sim_changed(",
|
|
] {
|
|
assert!(
|
|
backend.contains(required),
|
|
"missing native lifecycle {required}"
|
|
);
|
|
}
|
|
assert!(!backend.contains("reqwest"));
|
|
}
|
|
|
|
#[test]
|
|
fn embodied_adapter_uses_only_reviewed_high_level_native_movement() {
|
|
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
let backend = fs::read_to_string(root.join("src/backend.rs")).expect("backend source");
|
|
for required in [
|
|
".movement\n .turn_toward(",
|
|
".auto_pilot_local(",
|
|
".auto_pilot_cancel()",
|
|
".get_parcel_local_id(",
|
|
".sit()",
|
|
".stand()",
|
|
] {
|
|
assert!(
|
|
backend.contains(required),
|
|
"missing native embodiment mapping {required}"
|
|
);
|
|
}
|
|
let behavior = fs::read_to_string(root.join("src/behavior.rs")).expect("behavior source");
|
|
for forbidden in [
|
|
"Teleport(",
|
|
"TouchObject(",
|
|
"set_agent_controls(",
|
|
"FollowAvatar(",
|
|
] {
|
|
assert!(
|
|
!behavior.contains(forbidden),
|
|
"model-facing behavior contains forbidden primitive {forbidden}"
|
|
);
|
|
}
|
|
}
|
|
|
|
fn collect_rust_files(directory: &Path, output: &mut Vec<PathBuf>) {
|
|
for entry in fs::read_dir(directory).expect("read source directory") {
|
|
let path = entry.expect("source entry").path();
|
|
if path.is_dir() {
|
|
collect_rust_files(&path, output);
|
|
} else if path.extension().is_some_and(|extension| extension == "rs") {
|
|
output.push(path);
|
|
}
|
|
}
|
|
}
|