Initial commit — Pedrito Hechakuaa v0

Bot de Telegram para monitoreo de notificaciones judiciales del PJ Paraguay.
Multi-tenant con onboarding conversacional, audit log, y polling automático.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
markosbenitez
2026-05-25 10:58:32 -03:00
commit 7743919695
24 changed files with 5242 additions and 0 deletions

66
utils/sanitize.py Normal file
View File

@@ -0,0 +1,66 @@
"""
utils/sanitize.py — Sanitización de datos antes de loggear.
REGLA: todo log que incluya datos externos (responses del PJ, inputs del usuario)
debe pasar por credential_scrubber() antes de escribirse.
Uso:
from utils.sanitize import scrub
logger.info("respuesta_pj", data=scrub(response_text))
logger.error("error_en_request", body=scrub(request_body))
"""
from __future__ import annotations
import re
# Patrones que NUNCA deben aparecer en logs
_PATTERNS = [
# Bearer tokens (opacos base64url, ~128 chars)
(re.compile(r"Bearer\s+[A-Za-z0-9\-_]{20,}"), "Bearer [REDACTED]"),
# Authorization header completo
(re.compile(r'"[Aa]uthorization"\s*:\s*"[^"]+"'), '"Authorization": "[REDACTED]"'),
# bearerToken en JSON
(re.compile(r'"bearerToken"\s*:\s*"[^"]+"'), '"bearerToken": "[REDACTED]"'),
# refreshToken en JSON
(re.compile(r'"refreshToken"\s*:\s*"[^"]+"'), '"refreshToken": "[REDACTED]"'),
# permisoFirmado JWT (eyJ...)
(re.compile(r'"permisoFirmado"\s*:\s*"eyJ[A-Za-z0-9\.\-_=+/]+"'), '"permisoFirmado": "[REDACTED]"'),
# clave/password en JSON (campo "clave" del body de login)
(re.compile(r'"clave"\s*:\s*"[^"]+"'), '"clave": "[REDACTED]"'),
(re.compile(r'"password"\s*:\s*"[^"]+"'), '"password": "[REDACTED]"'),
# cookiesession (hexadecimal 32 chars)
(re.compile(r"cookiesession1=[A-Fa-f0-9]{32}"), "cookiesession1=[REDACTED]"),
# Cualquier JWT genérico (eyJ...)
(re.compile(r"\beyJ[A-Za-z0-9\.\-_=+/]{50,}"), "[JWT_REDACTED]"),
]
def scrub(value: object) -> str:
"""
Convierte value a string y aplica todos los patrones de sanitización.
Usar siempre antes de pasar datos externos a structlog.
Ejemplo:
logger.debug("raw_response", body=scrub(response.text))
"""
text = str(value)
for pattern, replacement in _PATTERNS:
text = pattern.sub(replacement, text)
return text
def scrub_dict(d: dict) -> dict:
"""
Aplica scrub() recursivamente a todos los valores string de un dict.
Útil para sanitizar dicts de headers o payloads antes de loggear.
"""
result = {}
for key, value in d.items():
if isinstance(value, str):
result[key] = scrub(value)
elif isinstance(value, dict):
result[key] = scrub_dict(value)
else:
result[key] = value
return result