Add case-insensitive path validation for Windows/Docker compatibility, improve error handling in file API with binary file support and enhanced logging for path resolution and file read operations
This commit is contained in:
parent
1129d1bca5
commit
db70d51c65
@ -191,12 +191,29 @@ def _resolve_safe_path(vault_root: Path, relative_path: str) -> Path:
|
||||
Raises:
|
||||
HTTPException(403): When the resolved path escapes the vault root.
|
||||
"""
|
||||
resolved = (vault_root / relative_path).resolve()
|
||||
vault_resolved = vault_root.resolve()
|
||||
# Construct the full path without resolving symlinks first
|
||||
full_path = vault_root / relative_path
|
||||
|
||||
# Resolve both paths to handle symlinks
|
||||
try:
|
||||
resolved = full_path.resolve(strict=False)
|
||||
vault_resolved = vault_root.resolve(strict=False)
|
||||
except Exception as e:
|
||||
logger.error(f"Path resolution error - vault_root: {vault_root}, relative_path: {relative_path}, error: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"Path resolution error: {str(e)}")
|
||||
|
||||
# Check if resolved path is within vault using string comparison (case-insensitive on Windows)
|
||||
try:
|
||||
# This will raise ValueError if resolved is not relative to vault_resolved
|
||||
resolved.relative_to(vault_resolved)
|
||||
except ValueError:
|
||||
raise HTTPException(status_code=403, detail="Access denied: path outside vault")
|
||||
# Try case-insensitive comparison for Windows/Docker compatibility
|
||||
resolved_str = str(resolved).lower()
|
||||
vault_str = str(vault_resolved).lower()
|
||||
if not resolved_str.startswith(vault_str):
|
||||
logger.warning(f"Path outside vault - vault: {vault_resolved}, requested: {relative_path}, resolved: {resolved}")
|
||||
raise HTTPException(status_code=403, detail="Access denied: path outside vault")
|
||||
|
||||
return resolved
|
||||
|
||||
|
||||
@ -529,10 +546,18 @@ async def api_file(vault_name: str, path: str = Query(..., description="Relative
|
||||
|
||||
try:
|
||||
raw = file_path.read_text(encoding="utf-8", errors="replace")
|
||||
except PermissionError:
|
||||
except PermissionError as e:
|
||||
logger.error(f"Permission denied reading file {path}: {e}")
|
||||
raise HTTPException(status_code=403, detail=f"Permission denied: cannot read file {path}")
|
||||
except UnicodeDecodeError:
|
||||
# Binary file - try to read as binary and decode with errors='replace'
|
||||
try:
|
||||
raw = file_path.read_bytes().decode("utf-8", errors="replace")
|
||||
except Exception as e:
|
||||
logger.error(f"Error reading binary file {path}: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"Cannot read file: {str(e)}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error reading file {path}: {e}")
|
||||
logger.error(f"Unexpected error reading file {path}: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"Error reading file: {str(e)}")
|
||||
|
||||
ext = file_path.suffix.lower()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user