From 4e4eefdde142b39dc781f6954eb7c46055520158 Mon Sep 17 00:00:00 2001 From: iven Date: Sat, 18 Apr 2026 09:11:15 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=B7=B1=E5=BA=A6=E5=AE=A1=E8=AE=A1?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20=E2=80=94=20WASM=20=E5=AE=89=E5=85=A8?= =?UTF-8?q?=E5=8A=A0=E5=9B=BA=20+=20A2A=20=E7=BC=96=E8=AF=91=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=20+=20=E6=B5=8B=E8=AF=95=E7=BC=96=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CRITICAL: - zclaw_file_read: 路径遍历修复 — 组件级过滤替代前缀检查 - zclaw_http_fetch: SSRF 防护 — URL scheme 白名单 + 私有IP段阻止 - Phase 4A: 移除 zclaw-protocols a2a feature gate, A2A 始终编译 - 移除 kernel/desktop multi-agent feature (不再控制任何代码) MEDIUM: - user_profiler: FactCategory cfg(test) 导入修复 (563 测试全通过) --- crates/zclaw-kernel/Cargo.toml | 4 +- crates/zclaw-protocols/src/lib.rs | 5 -- crates/zclaw-skills/Cargo.toml | 3 +- crates/zclaw-skills/src/wasm_runner.rs | 65 +++++++++++++++++-- desktop/src-tauri/Cargo.toml | 4 +- .../src/intelligence/user_profiler.rs | 2 + 6 files changed, 66 insertions(+), 17 deletions(-) diff --git a/crates/zclaw-kernel/Cargo.toml b/crates/zclaw-kernel/Cargo.toml index e8fa997..e1286a8 100644 --- a/crates/zclaw-kernel/Cargo.toml +++ b/crates/zclaw-kernel/Cargo.toml @@ -8,9 +8,7 @@ rust-version.workspace = true description = "ZCLAW kernel - central coordinator for all subsystems" [features] -default = ["multi-agent"] -# Enable multi-agent orchestration (Director, A2A protocol) -multi-agent = ["zclaw-protocols/a2a"] +default = [] [dependencies] zclaw-types = { workspace = true } diff --git a/crates/zclaw-protocols/src/lib.rs b/crates/zclaw-protocols/src/lib.rs index 5c2b57b..76d69c8 100644 --- a/crates/zclaw-protocols/src/lib.rs +++ b/crates/zclaw-protocols/src/lib.rs @@ -1,20 +1,15 @@ //! ZCLAW Protocols //! //! Protocol support for MCP (Model Context Protocol) and A2A (Agent-to-Agent). -//! -//! A2A is gated behind the `a2a` feature flag (reserved for future multi-agent scenarios). -//! MCP is always available as a framework for tool integration. mod mcp; mod mcp_types; mod mcp_tool_adapter; mod mcp_transport; -#[cfg(feature = "a2a")] mod a2a; pub use mcp::*; pub use mcp_types::*; pub use mcp_tool_adapter::*; pub use mcp_transport::*; -#[cfg(feature = "a2a")] pub use a2a::*; diff --git a/crates/zclaw-skills/Cargo.toml b/crates/zclaw-skills/Cargo.toml index 3fd2b6b..bb4029d 100644 --- a/crates/zclaw-skills/Cargo.toml +++ b/crates/zclaw-skills/Cargo.toml @@ -9,7 +9,7 @@ description = "ZCLAW skill system" [features] default = [] -wasm = ["wasmtime", "wasmtime-wasi/p1", "ureq"] +wasm = ["wasmtime", "wasmtime-wasi/p1", "ureq", "url"] [dependencies] zclaw-types = { workspace = true } @@ -28,3 +28,4 @@ shlex = { workspace = true } wasmtime = { workspace = true, optional = true } wasmtime-wasi = { workspace = true, optional = true } ureq = { workspace = true, optional = true } +url = { workspace = true, optional = true } diff --git a/crates/zclaw-skills/src/wasm_runner.rs b/crates/zclaw-skills/src/wasm_runner.rs index a75f876..f84f283 100644 --- a/crates/zclaw-skills/src/wasm_runner.rs +++ b/crates/zclaw-skills/src/wasm_runner.rs @@ -267,6 +267,46 @@ fn add_host_functions(linker: &mut Linker, network_allowed: bool) -> return -1; } + // Security: validate URL scheme to prevent SSRF. + // Only http:// and https:// are allowed. + let parsed = match url::Url::parse(&url) { + Ok(u) => u, + Err(_) => { + warn!("[WasmSkill] http_fetch denied — invalid URL: {}", url); + return -1; + } + }; + let scheme = parsed.scheme(); + if scheme != "http" && scheme != "https" { + warn!("[WasmSkill] http_fetch denied — unsupported scheme: {}", scheme); + return -1; + } + // Block private/loopback hosts to prevent SSRF + if let Some(host) = parsed.host_str() { + let lower = host.to_lowercase(); + if lower == "localhost" + || lower.starts_with("127.") + || lower.starts_with("10.") + || lower.starts_with("192.168.") + || lower.starts_with("169.254.") + || lower.starts_with("0.") + || lower.ends_with(".internal") + || lower.ends_with(".local") + { + warn!("[WasmSkill] http_fetch denied — private/loopback host: {}", host); + return -1; + } + // Also block 172.16.0.0/12 range + if lower.starts_with("172.") { + if let Ok(second) = lower.split('.').nth(1).unwrap_or("0").parse::() { + if (16..=31).contains(&second) { + warn!("[WasmSkill] http_fetch denied — private host (172.16-31.x.x): {}", host); + return -1; + } + } + } + } + debug!("[WasmSkill] guest http_fetch: {}", url); // Synchronous HTTP GET (we're already on a blocking thread) @@ -309,15 +349,30 @@ fn add_host_functions(linker: &mut Linker, network_allowed: bool) -> return -1; } - // Security: only allow reads under /workspace (preopen root) - if path.starts_with("..") || path.starts_with('/') { + // Security: validate path stays within /workspace sandbox. + // Reject absolute paths, and filter any path component that + // is ".." (e.g. "foo/../../etc/passwd"). + let joined = std::path::Path::new("/workspace").join(&path); + let mut safe = true; + for comp in joined.components() { + match comp { + std::path::Component::ParentDir => { + safe = false; + break; + } + std::path::Component::RootDir | std::path::Component::Prefix(_) => { + safe = false; + break; + } + _ => {} // Normal, CurDir — ok + } + } + if !safe { warn!("[WasmSkill] guest file_read denied — path escapes sandbox: {}", path); return -1; } - let full_path = format!("/workspace/{}", path); - - match std::fs::read(&full_path) { + match std::fs::read(&joined) { Ok(data) => write_guest_bytes(&mut caller, out_ptr, out_cap, &data), Err(e) => { debug!("[WasmSkill] file_read error for {}: {}", path, e); diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index 7750857..db57251 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -16,9 +16,7 @@ crate-type = ["staticlib", "cdylib", "rlib"] tauri-build = { version = "2", features = [] } [features] -default = ["multi-agent"] -# Multi-agent orchestration (A2A protocol, Director, agent delegation) -multi-agent = ["zclaw-kernel/multi-agent"] +default = [] dev-server = ["dep:axum", "dep:tower-http"] [dependencies] diff --git a/desktop/src-tauri/src/intelligence/user_profiler.rs b/desktop/src-tauri/src/intelligence/user_profiler.rs index 322c4e5..e46f207 100644 --- a/desktop/src-tauri/src/intelligence/user_profiler.rs +++ b/desktop/src-tauri/src/intelligence/user_profiler.rs @@ -10,6 +10,8 @@ use std::sync::Arc; use chrono::Utc; use tracing::{debug, warn}; use zclaw_memory::fact::Fact; +#[cfg(test)] +use zclaw_memory::fact::FactCategory; use zclaw_memory::user_profile_store::{ CommStyle, Level, UserProfile, UserProfileStore, };