Implement configuration management, drag-and-drop functionality, and logging utilities for WebDrop Bridge
This commit is contained in:
parent
04ef84cf9a
commit
6bef2f6119
9 changed files with 1154 additions and 0 deletions
97
src/webdrop_bridge/core/validator.py
Normal file
97
src/webdrop_bridge/core/validator.py
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
"""Path validation for secure file operations."""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
|
||||
class ValidationError(Exception):
|
||||
"""Raised when path validation fails."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class PathValidator:
|
||||
"""Validates file paths against security whitelist.
|
||||
|
||||
Ensures that only files within allowed root directories can be accessed.
|
||||
All paths are resolved to absolute form before validation to prevent
|
||||
directory traversal attacks.
|
||||
"""
|
||||
|
||||
def __init__(self, allowed_roots: List[Path]):
|
||||
"""Initialize validator with allowed root directories.
|
||||
|
||||
Args:
|
||||
allowed_roots: List of Path objects representing allowed root dirs
|
||||
|
||||
Raises:
|
||||
ValidationError: If any root doesn't exist or isn't a directory
|
||||
"""
|
||||
self.allowed_roots = []
|
||||
|
||||
for root in allowed_roots:
|
||||
root_path = Path(root).resolve()
|
||||
if not root_path.exists():
|
||||
raise ValidationError(
|
||||
f"Allowed root '{root}' does not exist"
|
||||
)
|
||||
if not root_path.is_dir():
|
||||
raise ValidationError(
|
||||
f"Allowed root '{root}' is not a directory"
|
||||
)
|
||||
self.allowed_roots.append(root_path)
|
||||
|
||||
def validate(self, path: Path) -> bool:
|
||||
"""Validate that path is within an allowed root directory.
|
||||
|
||||
Args:
|
||||
path: File path to validate
|
||||
|
||||
Returns:
|
||||
True if path is valid and accessible
|
||||
|
||||
Raises:
|
||||
ValidationError: If path fails validation
|
||||
"""
|
||||
try:
|
||||
# Resolve to absolute path (handles symlinks, .., etc)
|
||||
file_path = Path(path).resolve()
|
||||
except (OSError, ValueError) as e:
|
||||
raise ValidationError(f"Cannot resolve path '{path}': {e}") from e
|
||||
|
||||
# Check file exists
|
||||
if not file_path.exists():
|
||||
raise ValidationError(f"File does not exist: {path}")
|
||||
|
||||
# Check it's a regular file (not directory, symlink to dir, etc)
|
||||
if not file_path.is_file():
|
||||
raise ValidationError(f"Path is not a regular file: {path}")
|
||||
|
||||
# Check path is within an allowed root
|
||||
for allowed_root in self.allowed_roots:
|
||||
try:
|
||||
# This raises ValueError if file_path is not relative to root
|
||||
file_path.relative_to(allowed_root)
|
||||
return True
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
# Not in any allowed root
|
||||
raise ValidationError(
|
||||
f"Path '{file_path}' is not within allowed roots: "
|
||||
f"{self.allowed_roots}"
|
||||
)
|
||||
|
||||
def is_valid(self, path: Path) -> bool:
|
||||
"""Check if path is valid without raising exception.
|
||||
|
||||
Args:
|
||||
path: File path to check
|
||||
|
||||
Returns:
|
||||
True if valid, False otherwise
|
||||
"""
|
||||
try:
|
||||
return self.validate(path)
|
||||
except ValidationError:
|
||||
return False
|
||||
Loading…
Add table
Add a link
Reference in a new issue