"""Redaction applied to monitoring data before egress. One unconditional scrub, no modes, no knobs. Every string that leaves the process passes through ``redact_for_export``: secrets first (wraps ``agent/redact.py::redact_sensitive_text(force=True)`` plus bearer/token shapes, failing CLOSED so a broken redactor never emits the raw string), then PII (e-mail, phone, UUID-shaped ids -> ``[email]`` / ``[phone]`` / ``[id]``). There is deliberately no setting to weaken this. """ from __future__ import annotations import re from typing import Any, Optional # ── secret shapes (belt-and-suspenders on top of agent/redact.py) ─────────── _BEARER_RE = re.compile(r"\bBearer\s+[A-Za-z0-9._~+\-/]+=*", re.IGNORECASE) _TOKEN_RE = re.compile( r"\b(xox[baprs]-[A-Za-z0-9-]+|sk-[A-Za-z0-9_-]{8,}|gh[pousr]_[A-Za-z0-9_]{8,})\b" ) _SECRET_LITERAL_RE = re.compile(r"\*{3,}") _BEARER_RESIDUE_RE = re.compile(r"\bBearer\s+\[[^\]]+\]", re.IGNORECASE) # ── PII shapes ─────────────────────────────────────────────────────────────── _EMAIL_RE = re.compile(r"[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}") # E.164-ish and common separators; conservative to avoid nuking code/IDs. _PHONE_RE = re.compile( r"(? str: """Always-on secret redaction. force=True so user config can't disable it.""" try: from agent.redact import redact_sensitive_text out = redact_sensitive_text(text, force=True) except Exception: # Fail CLOSED: if the redactor can't run, do not emit the raw string. return UNAVAILABLE for pattern in (_BEARER_RE, _TOKEN_RE, _SECRET_LITERAL_RE, _BEARER_RESIDUE_RE): out = pattern.sub("[redacted]", out) return out def redact_for_export(text: Optional[str]) -> Optional[str]: """Scrub a string for egress: secrets, then PII. Unconditional.""" if text is None: return None out = _secret_redact(str(text)) out = _EMAIL_RE.sub("[email]", out) out = _UUID_RE.sub("[id]", out) out = _PHONE_RE.sub("[phone]", out) return out def redact_bounded( raw: Any, *, limit: int = 500, empty: str = "[redacted]", unavailable: str = UNAVAILABLE, ) -> str: """Redact ``str(raw or "")`` and length-bound it; ``empty`` replaces an empty result, ``unavailable`` is returned if redaction itself raises.""" try: return (redact_for_export(str(raw or "")) or empty)[:limit] except Exception: return unavailable __all__ = [ "redact_for_export", "redact_bounded", ]