""" Structured error envelope for the sidecar. Mirrors the Node redactMessage so a Python failure reaches Sentry with its type, a redacted message, and our own stack frames (basename only) instead of a bare "Error: Error". """ import os import re import traceback # _CTRL: matches ASCII control chars 0x00-0x1F and 0x7F only. Written with \x # hex escapes so no literal control bytes appear in this file. It must NOT match # spaces, "/", ".", or any printable character. _CTRL = re.compile(r"[\x00-\x1f\x7f]") _BLOB = re.compile(r"blob:[^\s\"')]+") _DATA = re.compile(r"data:[^\s\"')]+") _URL = re.compile(r"https?://[^\s\"')]+") _PATH = re.compile(r"(?:/(?:Users|home|root|data|tmp|var|app|opt|mnt|srv)|[A-Za-z]:\\)[^\s\"')]*") # Relative object-storage keys (uploads//…, outputs/…, previews/…) carry a # user-supplied filename tail; mask them like absolute paths. Runs after _PATH, # which already swallows the absolute /data/uploads/… form. _RELKEY = re.compile(r"\b(?:uploads|outputs|previews)/[^\s\"')]+") _IP = re.compile(r"\b\d{1,3}(?:\.\d{1,3}){3}\b") # IPv6: a full 8-group form, or any ::-compressed form (::1, fe80::…, …::). The # negative lookbehind/lookahead ((?", s) s = _DATA.sub("", s) s = _URL.sub("", s) s = _PATH.sub("", s) s = _RELKEY.sub("", s) s = _IP.sub("", s) s = _IPV6.sub("", s) s = _EMAIL.sub("", s) s = _QUOTED.sub(lambda m: m.group(1) + "" + m.group(1), s) s = _HEX.sub("", s) s = _FILE.sub("", s) s = re.sub(r"\s+", " ", s).strip() return (s[:_MAX_LEN] + "…") if len(s) > _MAX_LEN else s def _our_frames(exc): frames = [] for fr in traceback.extract_tb(exc.__traceback__): # Keep only our sidecar-script frames; drop stdlib and venv/site-packages. if os.path.dirname(os.path.abspath(fr.filename)) != _SIDECAR_DIR: continue frames.append({"file": os.path.basename(fr.filename), "line": fr.lineno, "func": fr.name}) return frames[-20:] def build_error_envelope(exc): """A JSON-serializable {type, message, frames} describing a caught exception.""" return { "type": type(exc).__name__, "message": redact(str(exc)), "frames": _our_frames(exc), }