24 Commits

Author SHA1 Message Date
markosbenitez
b3a644669e fix de /pdf 2026-06-19 18:17:55 -03:00
markosbenitez
76a5e58477 fix: listar_actuaciones usa ':' como separador en URL — corrige 404 en /pdf y /exp 2026-06-19 18:10:19 -03:00
markosbenitez
816296a7a0 admin/app.py — FastAPI con rutas /tenants, /tenants/{id}, /tenants/{id}/resetear, /audit
admin/auth.py — dependencia X-Admin-Token con secrets.compare_digest
4 templates HTML — funcionales, sin framework frontend
main.py — arranca admin en thread daemon; si ADMIN_TOKEN está vacío, no inicia el panel
Puerto 9090 (separado de health en 8080)
Cédula nunca expuesta — aparece como ***
2026-05-30 01:40:57 -03:00
markosbenitez
8bd1a0dd5b feat: F1 — panel admin web (FastAPI, solo 127.0.0.1, X-Admin-Token, SSH tunnel) 2026-05-30 01:37:14 -03:00
markosbenitez
61a15c5c50 feat: F3 — extracto de PDF en notificaciones y comando /pdf (pypdf, sin disco) 2026-05-30 01:13:31 -03:00
markosbenitez
64cfc4d1b7 poller.py: la revisión al arrancar ahora respeta _en_horario_habitual() — si son las 06:18 PYT no revisa hasta las 07:00
health.py: el watcher calcula la hora actual en America/Asuncion antes de alertar — fuera de horario hábil (noches, fines de semana) no manda alerta aunque el último poll sea de hace horas
2026-05-30 01:07:02 -03:00
markosbenitez
37b6b82016 fix: revisión inicial respeta horario; health alert solo en horario hábil PYT 2026-05-29 10:35:14 -03:00
markosbenitez
a868eb5b69 fix: chown /app completo + entrypoint directo al venv para evitar permisos con uv 2026-05-28 23:17:09 -03:00
markosbenitez
2957a6b4fd pusheado manual 2026-05-28 22:57:51 -03:00
markosbenitez
4be7bf13c3 fix: UV_NO_CACHE=1 para evitar error de permisos al arrancar como UID 1000 2026-05-28 22:52:17 -03:00
markosbenitez
ec751e5414 docs: DEPLOY.md adaptado al estado real del servidor
Versión para VPS donde Docker, Nginx y usuario pedrito ya existen.
Elimina: instalación de Docker, Nginx, creación de usuario, configuración DNS.
Agrega: tabla de estado inicial, nota sobre docker-compose v1 vs v2.
De 20 secciones (60-90 min) a 17 secciones (30-45 min).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 19:48:29 -03:00
markosbenitez
9c60972bcd prompt sprint 4 2026-05-28 01:12:25 -03:00
markosbenitez
f3fd871874 feat: F5 — frases libres mapeadas a comandos sin LLM
Antes: texto libre de tenants activos → siempre "no entendí eso".
Ahora: normalización (lowercase, sin acentos, sin puntuación) + lookup
en diccionario de frases y regex para exp/pdf con número.

"novedades", "hay algo nuevo?" → /notif
"dame el estado" → /estado
"necesito ayuda" → /ayuda
"expediente 198/2026", "exp 198" → /exp <arg>
"pdf 198/2026" → /pdf <arg>
Texto irrelevante → respuesta "no entendí" sin cambios.
Onboarding en curso: sin interferencia.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 00:27:15 -03:00
markosbenitez
f5f7fdc179 docs: arquitectura con seguridad en CLAUDE.md + SECURITY.md dedicado
CLAUDE.md: diagrama Mermaid reemplaza file-tree desactualizado; incluye
todos los componentes actuales con controles de seguridad anotados.
Stack actualizado con aiohttp y Docker. Referencia a SECURITY.md.

SECURITY.md (nuevo): diagrama defensa en profundidad (7 vectores → controles),
diagrama vault.py v3 con capas KEK+DEK, diagrama de ciclo de vida de
credenciales (secuencia), tabla de controles transversales y limitaciones.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 11:14:33 -03:00
markosbenitez
0aa1365659 docs: guía de migración vault v3 en sección de actualización del bot
Agrega nota en sección 18 de DEPLOY.md sobre correr migrar_vault_v3.py
al actualizar desde versiones anteriores a v0.4.0 con tenants existentes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 11:07:06 -03:00
markosbenitez
05b2f84eb8 security: vault v3 — PBKDF2 + envelope encryption (DEK/KEK)
Reemplaza derivación SHA256 por PBKDF2-HMAC-SHA256 (600k iter) con salt
aleatorio por tenant + DEK de 32 bytes aleatoria por cifrado. Robar la DB
sin FERNET_KEY o FERNET_KEY sin la DB ya no alcanza para comprometer credenciales.
Compatibilidad v1 preservada para migración. Agrega migrar_vault_v3.py con
dry-run, pre-flight check y backup atómico.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v0.4.0
2026-05-26 11:04:51 -03:00
markosbenitez
5eb934d4a1 chore: release v0.3.1 — CHANGELOG + política de versiones en CLAUDE.md
- CHANGELOG.md: historial completo desde v0.1.0 hasta v0.3.1
- CLAUDE.md: sección 'Gestión de versiones' con reglas semver,
  tabla MAJOR/MINOR/PATCH, proceso de release copy-paste
- Scope y archivos del proyecto actualizados al estado real (v0.3.1)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v0.3.1
2026-05-26 10:43:59 -03:00
markosbenitez
fa72ced5a6 security: hardening contra 7 vectores de ataque identificados
Análisis ofensivo → defensas implementadas:

1. /health/history sin auth (CRÍTICO)
   - Requiere Bearer token (HEALTH_AUTH_TOKEN en .env)
   - chat_ids enmascarados como hash SHA256[:8] opaco ("usr_a3b4c5d6")
   - Accesos denegados loggeados con IP + User-Agent

2. /health revela inteligencia operacional (MEDIO)
   - Sin auth: solo {"status", "timestamp"} — sin conteo de tenants ni timing
   - Con auth: respuesta completa con métricas internas
   - Misma URL, auth desbloquea detalle

3. Variables sensibles en proceso env (CRÍTICO)
   - Después de cargar Settings(), se limpian FERNET_KEY, TELEGRAM_BOT_TOKEN,
     PJ_USUARIO_ENC, PJ_PASSWORD_ENC, WEBHOOK_SECRET, HEALTH_AUTH_TOKEN, INVITE_CODE
   - Protege contra /proc/<pid>/environ y herencia por subprocesos

4. Race condition TOCTOU en invite codes (MEDIO)
   - SELECT+UPDATE reemplazado por UPDATE WHERE usado=0
   - rowcount=0 → False; imposible que dos requests simultáneos consuman el mismo código

5. Timing oracle en invite codes (BAJO-MEDIO)
   - Delay mínimo de 800ms en _paso_codigo() independientemente del resultado
   - secrets.compare_digest() para comparación del código del .env

6. Information disclosure en respuestas a no-registrados (BAJO)
   - _rechazar_no_registrado: "Para usar este bot escribí /start." (sin revelar comandos)
   - handle_texto_libre: silencio total para usuarios sin registro (no confirmar que el bot existe)

7. Nginx rate limiting (DEPLOY.md)
   - limit_req_zone pedrito_webhook: 30r/s, burst=50
   - limit_req_zone pedrito_health: 10r/m, burst=5
   - Headers de seguridad: X-Content-Type-Options, X-Frame-Options

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 08:06:36 -03:00
markosbenitez
6aa8b8fa62 docs: guía de deploy completa con Docker, Nginx, SSL, DNS y webhook
- Estructura en 4 partes: preparación, servidor, HTTPS/producción, operación
- Sección DNS detallada para Bluehost cPanel + mensaje exacto en inglés
  para delegar a un asistente técnico
- Configuración Nginx como reverse proxy con SSL terminado en el servidor
- Certbot + Let's Encrypt con renovación automática
- docker-compose en modo producción (bind 127.0.0.1:8080)
- Activación del modo webhook paso a paso
- Health check HTTP integrado en Nginx
- Tabla de rutas absolutas de todos los archivos
- Troubleshooting expandido para 8 escenarios de error

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v0.3.0
2026-05-25 23:55:36 -03:00
markosbenitez
076bc100af feat: Docker, health check, schema migrations y webhook opt-in
- migrations/001_initial_schema.sql: DDL idempotente con CREATE TABLE/INDEX IF NOT EXISTS
- database.py: run_migrations() aplica .sql pendientes en orden; init_db() queda como alias
- health.py: servidor aiohttp en puerto 8080 — GET /health, GET /health/history, POST /webhook
  Watcher loop alerta al admin por Telegram si el poller no actualiza en N horas
- config.py: webhook_url, webhook_secret, health_port, health_alert_hours
- poller.py: on_poll_done callback (conecta con health.record_poll)
- main.py: arranca HealthServer, llama run_migrations, modo webhook opt-in via WEBHOOK_URL
- Dockerfile + docker-compose.yml + .dockerignore
- pyproject.toml: aiohttp>=3.9

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 23:31:18 -03:00
markosbenitez
537ed6931d Fix: mapeo de campos reales del PJ y PDF desde notificaciones
El endpoint de notificaciones del PJ usa nombres distintos a los documentados:
- fechaNotificacion (no fecha)
- descripcionDespacho (no juzgado)
- descripcionCircunscripcion (no circunscripcion)
- codActuacionCaso — campo nuevo, necesario para descargar PDF

El botón PDF usaba codNotificacionOrigen como ID de actuación;
corregido a codActuacionCaso.

Manejo explícito de 403 en listar_notificaciones como CSJAuthError.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v0.2.1
2026-05-25 23:01:13 -03:00
markosbenitez
7865008618 Seguridad: rate limiting, códigos de un solo uso, rotación de FERNET_KEY
- utils/rate_limiter.py: ventana deslizante 20 req/60s por chat_id,
  aplicado en todos los handlers de telegram_bot.py
- database.py: tabla invite_codes (un solo uso por código), funciones
  crear_invite_code() y verificar_y_consumir_invite_code()
- onboarding.py: verifica primero en DB (un solo uso), fallback al
  INVITE_CODE del .env (ilimitado, para uso interno)
- scripts/generar_codigo.py: genera 1 o N códigos de invitación
- scripts/rotar_fernet_key.py: re-cifra todos los tenants y el .env
  con nueva FERNET_KEY, backup automático de .env antes de escribir

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v0.2.0
2026-05-25 22:33:50 -03:00
markosbenitez
6487a7857c Add DEPLOY.md — guía completa de despliegue en VPS Ubuntu
Paso a paso para delegar el deploy a equipo técnico:
usuario dedicado, uv, .env seguro, cifrado de credenciales,
systemd service, firewall UFW, monitoreo con journalctl,
procedimiento de actualización y troubleshooting.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v0.1.0
2026-05-25 11:20:48 -03:00
markosbenitez
7743919695 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>
2026-05-25 10:58:32 -03:00