"""File-system document storage for BadNote.""" import os import shutil from .config import settings def _safe_filename(filename: str) -> str: """Reduce a client-supplied filename to a safe basename. Prevents path traversal (e.g. ``../../etc/passwd``) by stripping any directory components and parent references before the name is joined onto the storage path. """ name = os.path.basename(filename or "") name = name.replace("\\", "").replace("/", "").strip() if not name or name in (".", ".."): name = "document" return name def _resolve_within(base: str, *parts: str) -> str: """Join ``parts`` onto ``base`` and ensure the result stays inside ``base``.""" base_abs = os.path.abspath(base) target = os.path.abspath(os.path.join(base_abs, *parts)) if os.path.commonpath([base_abs, target]) != base_abs: raise ValueError("Resolved path escapes the storage directory") return target def save_document(file_bytes: bytes, doc_id: str, filename: str) -> str: """Save uploaded file bytes to storage. Returns the stored file path.""" safe_doc_id = _safe_filename(doc_id) safe_name = _safe_filename(filename) doc_dir = _resolve_within(settings.storage_path, safe_doc_id) os.makedirs(doc_dir, exist_ok=True) file_path = _resolve_within(doc_dir, safe_name) with open(file_path, "wb") as f: f.write(file_bytes) return file_path def get_document_path(doc_id: str, filename: str) -> str: """Return the full path to a stored document file.""" safe_doc_id = _safe_filename(doc_id) safe_name = _safe_filename(filename) return _resolve_within(settings.storage_path, safe_doc_id, safe_name) def delete_document(doc_id: str) -> None: """Remove a document's directory and all its contents.""" safe_doc_id = _safe_filename(doc_id) doc_dir = _resolve_within(settings.storage_path, safe_doc_id) if os.path.isdir(doc_dir): shutil.rmtree(doc_dir)