fix(tool): Windows UNC 路径规范 — PathValidator 路径比较一致性
Some checks are pending
CI / Lint & TypeCheck (push) Waiting to run
CI / Unit Tests (push) Waiting to run
CI / Build Frontend (push) Waiting to run
CI / Rust Check (push) Waiting to run
CI / Security Scan (push) Waiting to run
CI / E2E Tests (push) Blocked by required conditions
Some checks are pending
CI / Lint & TypeCheck (push) Waiting to run
CI / Unit Tests (push) Waiting to run
CI / Build Frontend (push) Waiting to run
CI / Rust Check (push) Waiting to run
CI / Security Scan (push) Waiting to run
CI / E2E Tests (push) Blocked by required conditions
- with_workspace() 对 workspace_root 做 canonicalize,确保与 resolve_and_validate 产出的 canonical 路径格式一致 - 新增 normalize_windows_path() 剥离 \?\ 前缀,解决 Windows 上 starts_with 比较失败问题 - check_blocked/check_allowed 统一使用规范化路径比较
This commit is contained in:
@@ -97,6 +97,17 @@ fn default_blocked_paths() -> Vec<PathBuf> {
|
||||
]
|
||||
}
|
||||
|
||||
/// Normalize Windows UNC path prefix for consistent comparison.
|
||||
/// `\\?\C:\Users\...` → `C:\Users\...`
|
||||
fn normalize_windows_path(path: &Path) -> std::borrow::Cow<'_, Path> {
|
||||
let s = path.to_string_lossy();
|
||||
if s.starts_with(r"\\?\") {
|
||||
std::borrow::Cow::Owned(PathBuf::from(&s[4..]))
|
||||
} else {
|
||||
std::borrow::Cow::Borrowed(path)
|
||||
}
|
||||
}
|
||||
|
||||
/// Expand tilde in path to home directory
|
||||
fn expand_tilde(path: &str) -> PathBuf {
|
||||
if path.starts_with('~') {
|
||||
@@ -154,9 +165,16 @@ impl PathValidator {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the workspace root directory
|
||||
/// Set the workspace root directory.
|
||||
/// Canonicalizes the path to ensure consistent comparison on Windows
|
||||
/// (where canonicalize() returns `\\?\C:\...` UNC paths).
|
||||
pub fn with_workspace(mut self, workspace: PathBuf) -> Self {
|
||||
self.workspace_root = Some(workspace);
|
||||
let canonical = if workspace.exists() {
|
||||
workspace.canonicalize().unwrap_or(workspace)
|
||||
} else {
|
||||
workspace
|
||||
};
|
||||
self.workspace_root = Some(canonical);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -287,10 +305,14 @@ impl PathValidator {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check if path is in blocked list
|
||||
/// Check if path is in blocked list.
|
||||
/// Normalizes Windows UNC prefix (`\\?\`) for consistent comparison.
|
||||
fn check_blocked(&self, path: &Path) -> Result<()> {
|
||||
// Strip Windows UNC prefix for consistent matching
|
||||
let normalized = normalize_windows_path(path);
|
||||
for blocked in &self.config.blocked_paths {
|
||||
if path.starts_with(blocked) || path == blocked {
|
||||
let blocked_norm = normalize_windows_path(blocked);
|
||||
if normalized.starts_with(&*blocked_norm) || normalized == blocked_norm {
|
||||
return Err(ZclawError::InvalidInput(format!(
|
||||
"Access to this path is blocked: {}",
|
||||
path.display()
|
||||
@@ -310,11 +332,15 @@ impl PathValidator {
|
||||
/// - This prevents accidental exposure of the entire filesystem
|
||||
/// when the validator is misconfigured or used without setup
|
||||
fn check_allowed(&self, path: &Path) -> Result<()> {
|
||||
let path_norm = normalize_windows_path(path);
|
||||
|
||||
// If no allowed paths specified, check workspace
|
||||
if self.config.allowed_paths.is_empty() {
|
||||
if let Some(ref workspace) = self.workspace_root {
|
||||
// Workspace is configured - validate path is within it
|
||||
if !path.starts_with(workspace) {
|
||||
// Both sides are canonicalized (workspace via with_workspace, path via resolve_and_validate)
|
||||
let ws_norm = normalize_windows_path(workspace);
|
||||
if !path_norm.starts_with(&*ws_norm) {
|
||||
return Err(ZclawError::InvalidInput(format!(
|
||||
"Path outside workspace: {} (workspace: {})",
|
||||
path.display(),
|
||||
@@ -336,7 +362,8 @@ impl PathValidator {
|
||||
|
||||
// Check against allowed paths
|
||||
for allowed in &self.config.allowed_paths {
|
||||
if path.starts_with(allowed) {
|
||||
let allowed_norm = normalize_windows_path(allowed);
|
||||
if path_norm.starts_with(&*allowed_norm) {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user