""" Shared helpers for sanitizing error messages before they reach clients. """ from __future__ import annotations import os import re from typing import Any from .log import get_logger logger = get_logger(__name__) _DEBUG_MODE = os.getenv("MJR_DEBUG", "").strip().lower() in ("1", "true", "yes", "on") _WINDOWS_PATH_RE = re.compile(r"[A-Za-z]:\\[^\s]+") _UNC_PATH_RE = re.compile(r"\\\\[^\s\\]+\\[^\s]+") _UNIX_PATH_RE = re.compile(r"(? str: """Mask path-looking substrings to avoid leaking filesystem structure.""" cleaned = _WINDOWS_PATH_RE.sub("[path]", value) cleaned = _UNC_PATH_RE.sub("[path]", cleaned) cleaned = _UNIX_PATH_RE.sub("[path]", cleaned) return cleaned def sanitize_error_message(exc: Any, fallback: str) -> str: """ Build a safe error message for clients. Args: exc: Exception or raw value to sanitize. fallback: Fallback message to show when nothing meaningful remains. Returns: A string suitable for inclusion in API responses. """ if not fallback: fallback = "An error occurred" if exc is None: return fallback try: raw = str(exc) except Exception: raw = "" if not raw: return fallback # Never call expandvars — it would expand env-var references like %USERNAME% # or $HOME embedded in exception messages, leaking real values to clients. try: cwd = os.getcwd() except Exception: cwd = "" cleaned = raw.replace(cwd, "[cwd]") if cwd else raw sanitized = _mask_paths(cleaned) sanitized = " ".join(sanitized.splitlines()).strip() if _DEBUG_MODE: logger.debug("Sanitized error payload: %s", sanitized, exc_info=True) if sanitized: return f"{fallback}: {sanitized[:200]}" return fallback