Update: 将子项目从 submodule 转为完整内容

- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用
- 添加所有子项目的完整源代码
- 保留原始 .git 为 .git.bak 备份
This commit is contained in:
freedak
2026-07-04 19:20:46 +08:00
parent 54d6465fa7
commit f7a720204a
3360 changed files with 802660 additions and 3 deletions
@@ -0,0 +1,233 @@
//! **P2 E4:下载沙箱 + MOTW 端到端集成**`#[ignore]`,本机/打包 chrome)。
//!
//! 验证:
//! 1. **下载落隔离 workspace**navigate fixture(含 `download` 属性链接)→ observe → 取链接 ref
//! → `act(Click)` → 文件落进 per-pet workspace 的 `downloads/` 子目录(**非用户 Downloads**+
//! size>0。`allowAndName` 下文件名是下载 GUID。
//! 2. **Win MOTW**:下载完成后该文件有 `Zone.Identifier` ADS 且含 `ZoneId=3``std::fs::read` 那个
//! ADS 路径校验)。mac/linux 跳过 MOTW 断言(write_motw 空实现,登记在 PLATFORM-VERIFICATION.md)。
//!
//! 可执行 denylist 红线**拒绝判定**走纯逻辑单测(`download::tests`,不需真浏览器)——这里只验「良性
//! 文件真落盘 + MOTW 标记」的端到端链路。
//!
//! 手动跑(本机 Windows 有系统 Chrome):
//! set NOMIFUN_CHROME_BINARY=C:\Program Files\Google\Chrome\Application\chrome.exe
//! cargo nextest run -p nomi-browser-engine --run-ignored all -E 'test(download_) | test(motw)'
//! 跑完核对任务管理器无残留 chromeBuilder kill_on_drop 应自动清)。
use std::time::Duration;
use nomi_browser_engine::progress::Progress;
use nomi_browser_engine::{ActSpec, BrowserEngine, ObserveOpts};
mod common;
/// 端到端:触发一次真实下载 → 文件落隔离 workspace/downloads + size>0Windows 上额外验
/// `Zone.Identifier` ADS 含 ZoneId=3。一个测试覆盖「沙箱落点 + MOTW」全链(建一次 chrome)。
#[tokio::test]
#[ignore = "需本机/打包 chromeset NOMIFUN_CHROME_BINARY 后 --run-ignored all"]
async fn download_lands_in_sandbox_and_gets_motw() {
let (backend, download_dir) =
common::build_backend_for_fixture_with_downloads("e4").await;
eprintln!("download_dir = {}", download_dir.display());
backend
.navigate(&common::fixture_url("download.html"), false)
.await
.expect("navigate download.html");
// observe 填 ref 表(act Click 反查的前置)。
let obs = backend
.observe(&ObserveOpts::default())
.await
.expect("observe");
eprintln!("=== download fixture entries ===");
for e in &obs.entries {
eprintln!(" ref={} role={} name={:?}", e.r#ref, e.role, e.name);
}
// 取下载链接的 reffixture 固定有一个 link "Download report.txt")。
let link = obs
.entries
.iter()
.find(|e| e.role == "link" && e.name.contains("report.txt"))
.expect("fixture should expose a download link");
eprintln!("download link ref = {}", link.r#ref);
// act(Click):点击 download 链接触发下载(data: URL + download 属性 → chrome 落盘到沙箱目录)。
let p = Progress::new(Duration::from_secs(30));
let res = backend
.act(&ActSpec::Click { r#ref: link.r#ref.clone() }, &p)
.await;
eprintln!("click result = {res:?}");
// 点击本身可能因「下载导致导航被打断」返回各种良性态——不强断言 success;下载是否落盘才是验收点。
// 轮询下载目录直到出现一个非空文件(下载异步;最长等 ~10s)。allowAndName → 文件名是 GUID。
let mut found: Option<std::path::PathBuf> = None;
for _ in 0..100 {
if let Ok(rd) = std::fs::read_dir(&download_dir) {
for entry in rd.flatten() {
let path = entry.path();
// 跳过 chrome 下载中途的 .crdownload 临时文件,只认最终落盘文件。
if path.extension().and_then(|e| e.to_str()) == Some("crdownload") {
continue;
}
if let Ok(meta) = std::fs::metadata(&path)
&& meta.is_file()
&& meta.len() > 0
{
found = Some(path);
break;
}
}
}
if found.is_some() {
break;
}
tokio::time::sleep(Duration::from_millis(100)).await;
}
let file = found.expect("a non-empty downloaded file should land in the sandbox download dir");
let size = std::fs::metadata(&file).unwrap().len();
eprintln!("downloaded file = {} ({} bytes)", file.display(), size);
assert!(size > 0, "downloaded file must be non-empty");
// ── 红线:落点必须在隔离 download_dir 下(绝不在用户真实 Downloads)──
assert!(
file.starts_with(&download_dir),
"downloaded file must be inside the sandbox dir {}, got {}",
download_dir.display(),
file.display()
);
// ── Win MOTW:等下载循环打上 Zone.Identifier ADS(异步,downloadProgress completed 后才写)──
#[cfg(windows)]
{
let ads = format!("{}:Zone.Identifier", file.display());
let mut motw: Option<String> = None;
for _ in 0..50 {
if let Ok(s) = std::fs::read_to_string(&ads) {
motw = Some(s);
break;
}
tokio::time::sleep(Duration::from_millis(100)).await;
}
let content = motw.expect("Zone.Identifier ADS should be written by the download loop (MOTW)");
eprintln!("MOTW Zone.Identifier content = {content:?}");
assert!(content.contains("[ZoneTransfer]"), "MOTW must have [ZoneTransfer] header: {content:?}");
assert!(content.contains("ZoneId=3"), "MOTW must mark Internet zone (ZoneId=3): {content:?}");
}
// ── macOS MOTW 等价:下载文件必带 com.apple.quarantineGatekeeper 打开可执行/归档时校验)。
// 两个写者:① chrome 原生 LSFileQuarantineagent=Chrome,形态更全含源URL+事件UUID;
// ② 我方 write_motw 兜底(XATTR_CREATE,仅 chrome 未落时填,agent=NomiFun)。二者异步竞争,
// 最终 agent 不定——但**安全不变量=quarantine 存在且标 web-download0081;**,与 agent 无关。
// 故断言「存在 + 0081; 标志」,agent 接受 Chrome(原生)或 NomiFun(兜底)两者。轮询等其落盘。──
#[cfg(target_os = "macos")]
{
let mut q: Option<String> = None;
for _ in 0..100 {
let out = std::process::Command::new("/usr/bin/xattr")
.args(["-p", "com.apple.quarantine"])
.arg(&file)
.output();
if let Ok(o) = out
&& o.status.success()
{
q = Some(String::from_utf8_lossy(&o.stdout).trim().to_string());
break;
}
tokio::time::sleep(Duration::from_millis(100)).await;
}
let value = q.expect("com.apple.quarantine should be present (chrome native or write_motw fallback) on macOS");
eprintln!("macOS quarantine = {value:?}");
assert!(
value.starts_with("0081;"),
"quarantine flags must mark web download (0081;...): {value:?}"
);
// agent 是 Chromechrome 原生先写)或 NomiFun(chrome 未写时我方兜底)——两者都满足
// 「文件已被 quarantine」这一安全不变量,不耦合具体 agent(消除两写者竞争致的伪 flake)。
assert!(
value.contains("Chrome") || value.contains("NomiFun"),
"quarantine agent should be Chrome (native) or NomiFun (fallback): {value:?}"
);
}
#[cfg(all(not(windows), not(target_os = "macos")))]
{
// linuxwrite_motw 空实现,无内核级 quarantine 等价——不断言。见 PLATFORM-VERIFICATION.md
// TODO(verify-linux)。
eprintln!("linux: MOTW/quarantine is a no-op (no kernel equivalent); skip assertion");
}
// 清理:删下载文件(连带其 ADS)。download_dir 由 build helper 下次跑前清。
let _ = std::fs::remove_file(&file);
}
/// **F1-sec:可执行下载红线 enforcement 端到端**。触发一次 `setup.exe` 下载 → 引擎的下载循环在
/// `Browser.downloadWillBegin` 命中 `reject_executable_download`.exe denylist)→ `cancelDownload`
/// 取消(**fail-closed,红线,不看 session_mode**)→ 沙箱目录里**不出现**任何非空最终落盘文件。
/// 证「可执行下载在红线会话也拒」(这道判定不吃 session_mode,故即便 yolo/companion 也取消)。
#[tokio::test]
#[ignore = "需本机/打包 chromeset NOMIFUN_CHROME_BINARY 后 --run-ignored all"]
async fn executable_download_is_cancelled_red_line() {
let (backend, download_dir) =
common::build_backend_for_fixture_with_downloads("f1sec-exe").await;
eprintln!("download_dir = {}", download_dir.display());
backend
.navigate(&common::fixture_url("download_exe.html"), false)
.await
.expect("navigate download_exe.html");
let obs = backend
.observe(&ObserveOpts::default())
.await
.expect("observe");
let link = obs
.entries
.iter()
.find(|e| e.role == "link" && e.name.contains("setup.exe"))
.expect("fixture should expose an executable download link");
eprintln!("exe download link ref = {}", link.r#ref);
// 点击触发 .exe 下载。引擎应在 downloadWillBegin 时取消(denylist)。
let p = Progress::new(Duration::from_secs(30));
let res = backend
.act(&ActSpec::Click { r#ref: link.r#ref.clone() }, &p)
.await;
eprintln!("click result = {res:?}");
// 等待窗口:给下载循环时间收到 downloadWillBegin + 发 cancelDownload;其间反复确认沙箱目录里
// **没有**非空 .exe 最终文件落盘(被取消 → 不应有 completed 落盘)。
let mut leaked: Option<std::path::PathBuf> = None;
for _ in 0..50 {
if let Ok(rd) = std::fs::read_dir(&download_dir) {
for entry in rd.flatten() {
let path = entry.path();
// .crdownload 是中途临时文件(取消后会被清理)——只认最终落盘的非空文件。
if path.extension().and_then(|e| e.to_str()) == Some("crdownload") {
continue;
}
if let Ok(meta) = std::fs::metadata(&path)
&& meta.is_file()
&& meta.len() > 0
{
leaked = Some(path);
break;
}
}
}
if leaked.is_some() {
break;
}
tokio::time::sleep(Duration::from_millis(100)).await;
}
if let Some(p) = &leaked {
let _ = std::fs::remove_file(p);
}
assert!(
leaked.is_none(),
"executable download (.exe) must be cancelled by the red-line (no final file should land), \
but a file landed: {leaked:?}"
);
}