From d6696730399c82144dff126dbe8b18a8dd530a9f Mon Sep 17 00:00:00 2001 From: KiriAky 107 Date: Sat, 12 Sep 2026 02:31:44 +0800 Subject: [PATCH] =?UTF-8?q?test:=20=E8=A1=A5=E9=BD=90C-01=E6=9C=AA?= =?UTF-8?q?=E6=8E=88=E6=9D=83=E6=96=87=E4=BB=B6=E6=94=BB=E5=87=BB=E7=9F=A9?= =?UTF-8?q?=E9=98=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src-tauri/src/extension_container.rs | 78 +++++++++++++++++++ .../tests/fixtures/sandbox_network_probe.rs | 12 +++ 2 files changed, 90 insertions(+) diff --git a/frontend/src-tauri/src/extension_container.rs b/frontend/src-tauri/src/extension_container.rs index afb0229..d325ad3 100644 --- a/frontend/src-tauri/src/extension_container.rs +++ b/frontend/src-tauri/src/extension_container.rs @@ -708,6 +708,84 @@ mod tests { fn real_container_cannot_reach_ipv4_or_ipv6_loopback_listeners() { real_native_protocol_probes(false, false); } + #[test] + fn real_malicious_binary_cannot_read_or_write_four_ungranted_file_scopes() { + use sha2::{Digest, Sha256}; + use std::os::windows::fs::OpenOptionsExt; + use windows_sys::Win32::Storage::FileSystem::*; + + let profile = Profile::create().unwrap(); + let package = tempfile::tempdir().unwrap(); + let executable = package.path().join("file-probe.exe"); + let fixture = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/sandbox_network_probe.rs"); + let compile = std::process::Command::new("rustc") + .arg("--edition=2021") + .arg(&fixture) + .arg("-o") + .arg(&executable) + .output() + .unwrap(); + assert!( + compile.status.success(), + "{}", + String::from_utf8_lossy(&compile.stderr) + ); + let open = |path: &std::path::Path| { + std::fs::OpenOptions::new() + .access_mode(READ_CONTROL | WRITE_DAC) + .share_mode(FILE_SHARE_READ | FILE_SHARE_WRITE) + .custom_flags(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT) + .open(path) + .unwrap() + }; + let package_root = open(package.path()); + let entry = open(&executable); + profile.grant_package_read_execute(&package_root).unwrap(); + profile.grant_package_read_execute(&entry).unwrap(); + + let bait_root = tempfile::tempdir().unwrap(); + let scopes = ["vault", "home", "credentials", "other-package"]; + let bait: Vec<_> = scopes + .iter() + .map(|scope| { + let directory = bait_root.path().join(scope); + std::fs::create_dir(&directory).unwrap(); + let path = directory.join("bait.txt"); + std::fs::write(&path, format!("OpenNexus C-01 {scope} bait")).unwrap(); + path + }) + .collect(); + let before: Vec<_> = bait + .iter() + .map(|path| Sha256::digest(std::fs::read(path).unwrap())) + .collect(); + let folder = profile.folder().unwrap(); + let system = std::path::PathBuf::from(std::env::var_os("SystemRoot").unwrap()); + let mut arguments = vec!["file_denied_100".to_owned()]; + arguments.extend(bait.iter().map(|path| path.to_string_lossy().into_owned())); + let data = crate::extension_launch_data::LaunchData::new( + &executable, + &arguments, + &system, + &folder, + &folder.join("Temp"), + &std::collections::BTreeMap::new(), + ) + .unwrap(); + assert_eq!( + checked_executable_data(&profile, &executable, None, Some(data)), + Some(0) + ); + let after: Vec<_> = bait + .iter() + .map(|path| Sha256::digest(std::fs::read(path).unwrap())) + .collect(); + assert_eq!(after, before); + drop(entry); + drop(package_root); + profile.remove().unwrap(); + } #[cfg(feature = "desktop")] #[test] #[ignore = "real MCP tools/call 60-second deadline acceptance; run explicitly"] diff --git a/frontend/src-tauri/tests/fixtures/sandbox_network_probe.rs b/frontend/src-tauri/tests/fixtures/sandbox_network_probe.rs index 8ad217f..ed29864 100644 --- a/frontend/src-tauri/tests/fixtures/sandbox_network_probe.rs +++ b/frontend/src-tauri/tests/fixtures/sandbox_network_probe.rs @@ -3,6 +3,18 @@ use std::net::{SocketAddr, TcpStream, UdpSocket}; use std::time::Duration; fn main() { let args: Vec<_> = std::env::args().collect(); + if args.get(1).is_some_and(|value| value == "file_denied_100") { + for path in &args[2..] { + for _ in 0..100 { + if std::fs::File::open(path).is_ok() + || std::fs::OpenOptions::new().write(true).open(path).is_ok() + { + std::process::exit(88); + } + } + } + std::process::exit(0); + } if args.get(1).is_some_and(|s| s == "cpu_burn") { std::thread::scope(|scope| { for _ in 0..8 {