"""Shared path validation helpers for tool implementations. Extracts the ``resolve() + relative_to()`` and ``..`` traversal check patterns previously duplicated across skill_manager_tool, skills_tool, skills_hub, cronjob_tools, and credential_files. """ import logging from pathlib import Path from typing import Optional logger = logging.getLogger(__name__) def validate_within_dir(path: Path, root: Path) -> Optional[str]: """Ensure *path* resolves to a location within *root*. Returns an error message string if validation fails, or ``None`` if the path is safe. Uses ``Path.resolve()`` to follow symlinks and normalize ``..`` components. Usage:: error = validate_within_dir(user_path, allowed_root) if error: return json.dumps({"error": error}) """ try: resolved = path.resolve() root_resolved = root.resolve() resolved.relative_to(root_resolved) except (ValueError, OSError) as exc: return f"Path escapes allowed directory: {exc}" return None def has_traversal_component(path_str: str) -> bool: """Return True if *path_str* contains ``..`` traversal components. Quick check for obvious traversal attempts before doing full resolution. """ parts = Path(path_str).parts return ".." in parts