ironveil 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. ironveil-1.0.0/LICENSE +24 -0
  2. ironveil-1.0.0/PKG-INFO +341 -0
  3. ironveil-1.0.0/README.md +315 -0
  4. ironveil-1.0.0/ironveil.egg-info/PKG-INFO +341 -0
  5. ironveil-1.0.0/ironveil.egg-info/SOURCES.txt +145 -0
  6. ironveil-1.0.0/ironveil.egg-info/dependency_links.txt +1 -0
  7. ironveil-1.0.0/ironveil.egg-info/entry_points.txt +2 -0
  8. ironveil-1.0.0/ironveil.egg-info/requires.txt +8 -0
  9. ironveil-1.0.0/ironveil.egg-info/top_level.txt +1 -0
  10. ironveil-1.0.0/proxy/adaptador_clasificador.py +229 -0
  11. ironveil-1.0.0/proxy/auth.py +589 -0
  12. ironveil-1.0.0/proxy/backends_entrada.py +543 -0
  13. ironveil-1.0.0/proxy/cli.py +87 -0
  14. ironveil-1.0.0/proxy/cola.py +373 -0
  15. ironveil-1.0.0/proxy/complementos.py +47 -0
  16. ironveil-1.0.0/proxy/configuraciones.py +54 -0
  17. ironveil-1.0.0/proxy/demo_chat.py +43 -0
  18. ironveil-1.0.0/proxy/espera_revision.py +149 -0
  19. ironveil-1.0.0/proxy/herramientas.py +157 -0
  20. ironveil-1.0.0/proxy/main.py +1524 -0
  21. ironveil-1.0.0/proxy/mecanismos.py +966 -0
  22. ironveil-1.0.0/proxy/memoria_decisiones/__init__.py +63 -0
  23. ironveil-1.0.0/proxy/memoria_decisiones/almacenes/__init__.py +17 -0
  24. ironveil-1.0.0/proxy/memoria_decisiones/almacenes/jsonl.py +90 -0
  25. ironveil-1.0.0/proxy/memoria_decisiones/almacenes/sqlite.py +103 -0
  26. ironveil-1.0.0/proxy/memoria_decisiones/composicion.py +118 -0
  27. ironveil-1.0.0/proxy/memoria_decisiones/dominio.py +70 -0
  28. ironveil-1.0.0/proxy/memoria_decisiones/puerto.py +48 -0
  29. ironveil-1.0.0/proxy/memoria_decisiones/registro.py +60 -0
  30. ironveil-1.0.0/proxy/memoria_decisiones/servicio.py +182 -0
  31. ironveil-1.0.0/proxy/modelos_protegidos.py +380 -0
  32. ironveil-1.0.0/proxy/normalizacion/__init__.py +42 -0
  33. ironveil-1.0.0/proxy/normalizacion/composicion.py +130 -0
  34. ironveil-1.0.0/proxy/normalizacion/decodificadores/__init__.py +21 -0
  35. ironveil-1.0.0/proxy/normalizacion/decodificadores/en_base64.py +42 -0
  36. ironveil-1.0.0/proxy/normalizacion/decodificadores/en_hex.py +38 -0
  37. ironveil-1.0.0/proxy/normalizacion/decodificadores/en_rot13.py +33 -0
  38. ironveil-1.0.0/proxy/normalizacion/dominio.py +150 -0
  39. ironveil-1.0.0/proxy/normalizacion/puerto.py +31 -0
  40. ironveil-1.0.0/proxy/normalizacion/registro.py +54 -0
  41. ironveil-1.0.0/proxy/normalizacion/servicio.py +127 -0
  42. ironveil-1.0.0/proxy/notificaciones.py +283 -0
  43. ironveil-1.0.0/proxy/revision_panel.py +683 -0
  44. ironveil-1.0.0/proxy/siem/__init__.py +69 -0
  45. ironveil-1.0.0/proxy/siem/composicion.py +149 -0
  46. ironveil-1.0.0/proxy/siem/evento.py +89 -0
  47. ironveil-1.0.0/proxy/siem/exportador.py +77 -0
  48. ironveil-1.0.0/proxy/siem/formatos/__init__.py +53 -0
  49. ironveil-1.0.0/proxy/siem/formatos/cef.py +94 -0
  50. ironveil-1.0.0/proxy/siem/formatos/ecs.py +67 -0
  51. ironveil-1.0.0/proxy/siem/formatos/json_plano.py +46 -0
  52. ironveil-1.0.0/proxy/siem/formatos/leef.py +63 -0
  53. ironveil-1.0.0/proxy/siem/puertos.py +66 -0
  54. ironveil-1.0.0/proxy/siem/registro.py +148 -0
  55. ironveil-1.0.0/proxy/siem/transportes/__init__.py +46 -0
  56. ironveil-1.0.0/proxy/siem/transportes/archivo.py +43 -0
  57. ironveil-1.0.0/proxy/siem/transportes/asincrono.py +125 -0
  58. ironveil-1.0.0/proxy/siem/transportes/aws.py +128 -0
  59. ironveil-1.0.0/proxy/siem/transportes/azure.py +147 -0
  60. ironveil-1.0.0/proxy/siem/transportes/credenciales.py +49 -0
  61. ironveil-1.0.0/proxy/siem/transportes/elastic.py +59 -0
  62. ironveil-1.0.0/proxy/siem/transportes/https_generico.py +121 -0
  63. ironveil-1.0.0/proxy/siem/transportes/sigv4.py +104 -0
  64. ironveil-1.0.0/proxy/siem/transportes/splunk.py +32 -0
  65. ironveil-1.0.0/proxy/siem/transportes/syslog_red.py +175 -0
  66. ironveil-1.0.0/proxy/static/consola.css +453 -0
  67. ironveil-1.0.0/proxy/static/demo.css +101 -0
  68. ironveil-1.0.0/proxy/static/demo.html +64 -0
  69. ironveil-1.0.0/proxy/static/demo.js +280 -0
  70. ironveil-1.0.0/proxy/static/login.html +47 -0
  71. ironveil-1.0.0/proxy/static/login.js +64 -0
  72. ironveil-1.0.0/proxy/static/logo.svg +19 -0
  73. ironveil-1.0.0/proxy/static/revision.html +73 -0
  74. ironveil-1.0.0/proxy/static/revision.js +646 -0
  75. ironveil-1.0.0/pyproject.toml +55 -0
  76. ironveil-1.0.0/setup.cfg +4 -0
  77. ironveil-1.0.0/tests/test_adaptador_clasificador.py +283 -0
  78. ironveil-1.0.0/tests/test_agencia_excesiva.py +446 -0
  79. ironveil-1.0.0/tests/test_agregar_resultados_desde_jsonl.py +131 -0
  80. ironveil-1.0.0/tests/test_auth.py +207 -0
  81. ironveil-1.0.0/tests/test_backends_entrada.py +495 -0
  82. ironveil-1.0.0/tests/test_calentamiento.py +167 -0
  83. ironveil-1.0.0/tests/test_chat_autenticacion.py +152 -0
  84. ironveil-1.0.0/tests/test_clasificacion.py +356 -0
  85. ironveil-1.0.0/tests/test_cli.py +73 -0
  86. ironveil-1.0.0/tests/test_cola.py +224 -0
  87. ironveil-1.0.0/tests/test_comparar_prompt_guard_fp16.py +95 -0
  88. ironveil-1.0.0/tests/test_comparar_v5_c0_c5.py +159 -0
  89. ironveil-1.0.0/tests/test_config.py +85 -0
  90. ironveil-1.0.0/tests/test_consola_accesibilidad.py +647 -0
  91. ironveil-1.0.0/tests/test_consolidar.py +603 -0
  92. ironveil-1.0.0/tests/test_costo_mecanismos.py +92 -0
  93. ironveil-1.0.0/tests/test_costo_por_mecanismo.py +460 -0
  94. ironveil-1.0.0/tests/test_costo_por_pieza.py +345 -0
  95. ironveil-1.0.0/tests/test_delimitacion.py +134 -0
  96. ironveil-1.0.0/tests/test_demo_chat.py +75 -0
  97. ironveil-1.0.0/tests/test_demo_estaticos.py +417 -0
  98. ironveil-1.0.0/tests/test_detalle_clasificador.py +589 -0
  99. ironveil-1.0.0/tests/test_espera_revision.py +560 -0
  100. ironveil-1.0.0/tests/test_exportar_lote_siem.py +68 -0
  101. ironveil-1.0.0/tests/test_extension_agencia_excesiva.py +123 -0
  102. ironveil-1.0.0/tests/test_fusionar_latencia_clasificador.py +208 -0
  103. ironveil-1.0.0/tests/test_graficas_finales.py +97 -0
  104. ironveil-1.0.0/tests/test_limpiar_dataset.py +122 -0
  105. ironveil-1.0.0/tests/test_main.py +1698 -0
  106. ironveil-1.0.0/tests/test_matriz_real_vs_hipotesis.py +210 -0
  107. ironveil-1.0.0/tests/test_mecanismos.py +164 -0
  108. ironveil-1.0.0/tests/test_medir_clasificador_entrada.py +360 -0
  109. ironveil-1.0.0/tests/test_medir_normalizacion.py +95 -0
  110. ironveil-1.0.0/tests/test_memoria_almacenes.py +568 -0
  111. ironveil-1.0.0/tests/test_memoria_decisiones.py +377 -0
  112. ironveil-1.0.0/tests/test_memoria_dominio.py +95 -0
  113. ironveil-1.0.0/tests/test_memoria_servicio.py +428 -0
  114. ironveil-1.0.0/tests/test_minimo_privilegio.py +85 -0
  115. ironveil-1.0.0/tests/test_modelos_protegidos.py +644 -0
  116. ironveil-1.0.0/tests/test_mover_a_descartados.py +191 -0
  117. ironveil-1.0.0/tests/test_normalizacion_decodificadores.py +222 -0
  118. ironveil-1.0.0/tests/test_normalizacion_integracion.py +272 -0
  119. ironveil-1.0.0/tests/test_normalizacion_servicio.py +373 -0
  120. ironveil-1.0.0/tests/test_notebook_garak.py +185 -0
  121. ironveil-1.0.0/tests/test_notebook_garak_jev.py +214 -0
  122. ironveil-1.0.0/tests/test_notebook_jev.py +112 -0
  123. ironveil-1.0.0/tests/test_notificaciones.py +531 -0
  124. ironveil-1.0.0/tests/test_resumen_garak.py +307 -0
  125. ironveil-1.0.0/tests/test_revision_en_vivo.py +449 -0
  126. ironveil-1.0.0/tests/test_revision_panel.py +493 -0
  127. ironveil-1.0.0/tests/test_rutas.py +61 -0
  128. ironveil-1.0.0/tests/test_rutas_por_entorno.py +43 -0
  129. ironveil-1.0.0/tests/test_seleccionar_config.py +70 -0
  130. ironveil-1.0.0/tests/test_siem.py +342 -0
  131. ironveil-1.0.0/tests/test_siem_arquitectura.py +154 -0
  132. ironveil-1.0.0/tests/test_siem_asincrono.py +178 -0
  133. ironveil-1.0.0/tests/test_siem_composicion.py +207 -0
  134. ironveil-1.0.0/tests/test_siem_formatos.py +115 -0
  135. ironveil-1.0.0/tests/test_siem_integracion.py +219 -0
  136. ironveil-1.0.0/tests/test_siem_registro.py +284 -0
  137. ironveil-1.0.0/tests/test_siem_sigv4.py +66 -0
  138. ironveil-1.0.0/tests/test_siem_transportes.py +611 -0
  139. ironveil-1.0.0/tests/test_tabla_maestra.py +407 -0
  140. ironveil-1.0.0/tests/test_tiempo_revision_humana.py +47 -0
  141. ironveil-1.0.0/tests/test_validar_dataset.py +260 -0
  142. ironveil-1.0.0/tests/test_validar_formato_siem.py +54 -0
  143. ironveil-1.0.0/tests/test_vector4_movimiento_lateral.py +208 -0
  144. ironveil-1.0.0/tests/test_vector5_agotamiento.py +213 -0
  145. ironveil-1.0.0/tests/test_vector5_carga.py +200 -0
  146. ironveil-1.0.0/tests/test_vector6_adaptativo.py +248 -0
  147. ironveil-1.0.0/tests/test_vectores_1_2_3.py +142 -0
ironveil-1.0.0/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anderson Fabian Garcia Nieto, Ana Gabriela Fiquitiva Poveda, Carlos Mario Piedrahita Arango, Andres Felipe Sabogal Wilches
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ AVISO: este es un trabajo academico. Los "canarios" (SPT-DEMO-*, RRHH-DEMO-*) son
24
+ credenciales ficticias; el framework solo debe usarse contra el laboratorio propio.
@@ -0,0 +1,341 @@
1
+ Metadata-Version: 2.2
2
+ Name: ironveil
3
+ Version: 1.0.0
4
+ Summary: Defensa en profundidad para LLMs autoalojados (Ollama): proxy FastAPI con 5 mecanismos activables por bandera
5
+ Author: Equipo IronVeil (Fiquitiva, García, Sabogal, Piedrahita)
6
+ License: MIT
7
+ Project-URL: Repository, https://github.com/Anderfg13/IronVeil
8
+ Project-URL: Documentation, https://anderfg13.github.io/IronVeil/
9
+ Keywords: llm,security,prompt-injection,ollama,guardrails,fastapi
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Security
15
+ Classifier: Framework :: FastAPI
16
+ Requires-Python: >=3.11
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: fastapi==0.115.0
20
+ Requires-Dist: uvicorn[standard]==0.30.6
21
+ Requires-Dist: httpx==0.27.2
22
+ Requires-Dist: PyYAML==6.0.2
23
+ Requires-Dist: transformers==5.17.0
24
+ Provides-Extra: prompt-guard
25
+ Requires-Dist: torch; extra == "prompt-guard"
26
+
27
+ <div align="center">
28
+
29
+ <img src="proxy/static/logo.svg" alt="Logo de IronVeil" width="96">
30
+
31
+ # IronVeil
32
+
33
+ **Defensa en profundidad para proteger un LLM, local o de frontera, de la inyección de prompt y la extracción de datos.**
34
+ Pensado para los modelos autoalojados con Ollama que quedan expuestos por accidente en internet, y válido para cualquier otro modelo detrás del proxy.
35
+ Cinco mecanismos defensivos activables por bandera, siete configuraciones experimentales y una pregunta:
36
+ *¿qué combinación ofrece la mejor relación protección / utilidad / costo?*
37
+
38
+ [![CI](https://github.com/Anderfg13/IronVeil/actions/workflows/ci.yml/badge.svg)](https://github.com/Anderfg13/IronVeil/actions/workflows/ci.yml)
39
+ [![Licencia: MIT](https://img.shields.io/badge/licencia-MIT-22c55e.svg)](LICENSE)
40
+ ![Python](https://img.shields.io/badge/python-3.11%2B-3776ab)
41
+ ![Docker](https://img.shields.io/badge/docker%20compose-un%20solo%20comando-2496ed)
42
+ ![Laboratorio aislado](https://img.shields.io/badge/laboratorio-100%25%20aislado-fb7185)
43
+
44
+ [Arquitectura](#arquitectura) · [Inicio rápido](#inicio-rápido-un-solo-comando) · [Las 7 configuraciones](#las-7-configuraciones) · [Resultados](#dónde-están-los-resultados) · [Estructura](#estructura-del-repositorio) · [Reproducir](#reproducir-los-resultados)
45
+
46
+ <img src="docs/assets/arquitectura-animada.svg" alt="Animación: una petición legítima atraviesa la cadena de mecanismos; un ataque es bloqueado por la clasificación y encolado por la aprobación humana; y el mismo mensaje, al volver, es rechazado al instante por la memoria de decisiones. El modelo protegido y los clasificadores pueden ser locales o de frontera" width="900">
47
+
48
+ *Una petición legítima (verde) atraviesa la cadena y vuelve revisada; un ataque (rojo) lo detiene la clasificación y la aprobación humana lo encola (429).*
49
+
50
+ </div>
51
+
52
+ ---
53
+
54
+ ## Qué es IronVeil
55
+
56
+ Un modelo de lenguaje expuesto sin protección permite a cualquiera extraer el *system prompt*, inyectar instrucciones, moverse entre modelos o agotar los recursos. El caso más común y el que motiva el proyecto es un servidor **Ollama autoalojado** publicado en internet por accidente, pero el problema es el mismo con cualquier modelo. IronVeil es un **proxy FastAPI** que se sienta entre el cliente y el modelo e implementa cinco defensas, **cada una apagada o encendida con una sola línea de `config.yaml`**:
57
+
58
+ | # | Mecanismo | Bandera | Naturaleza |
59
+ |---|-----------|---------|------------|
60
+ | 1 | **Filtrado** | `filtrado` | Determinista: patrones/regex sobre entrada y salida |
61
+ | 2 | **Delimitación** (spotlighting) | `delimitacion` | Determinista: reestructura el prompt con marcadores aleatorios; no bloquea |
62
+ | 3 | **Clasificación** | `clasificacion` | Probabilístico: por defecto Prompt Guard 2 (entrada) + Llama Guard 3 (salida), intercambiables por otro modelo local o de frontera; falla cerrada |
63
+ | 4 | **Mínimo privilegio** | `minimo_privilegio` | Determinista: rechaza una credencial de otro dominio |
64
+ | 5 | **Aprobación humana + rate limit** | `aprobacion_humana` | Humano en el bucle: encola (429) en vez de rechazar (400) |
65
+
66
+ El experimento mide cuánto reduce cada mecanismo —solo o combinado— la **Tasa de Éxito de Ataque (ASR)** frente a cinco vectores: **V1** reconocimiento · **V2** extracción de system prompt · **V3** inyección de prompt · **V4** movimiento lateral · **V5** agotamiento de recursos. Dos modelos (`soporte` y `rrhh`) guardan un secreto ficticio (canario) en su *system prompt*; si el canario aparece en la respuesta, el ataque tuvo éxito.
67
+
68
+ ### Qué modelos puede proteger
69
+
70
+ Los cinco mecanismos no dependen del modelo: trabajan sobre el texto que entra y el que sale, así que el proxy sirve igual para:
71
+
72
+ - **Modelos locales con Ollama** (el caso del experimento: `soporte` y `rrhh`).
73
+ - **Cualquier servidor compatible con la API de OpenAI** (vLLM, LM Studio…) y **modelos de frontera** (OpenAI, Anthropic). Es una extensión opcional (`MODELOS_REMOTOS`): los terceros están apagados salvo permiso explícito, porque el texto sale de tu red. Guía: [`docs/MODELO_PROTEGIDO_REMOTO.md`](docs/MODELO_PROTEGIDO_REMOTO.md).
74
+
75
+ El **clasificador** también es intercambiable (Prompt Guard, Llama Guard, Jev, un modelo de frontera como juez…): [`docs/CAMBIAR_MODELO_CLASIFICADOR.md`](docs/CAMBIAR_MODELO_CLASIFICADOR.md). Lo que está **medido** en este repositorio es el caso de Ollama con `llama3.2:1b`; los demás modelos se admiten pero no se evaluaron con la misma batería (ver [`docs/GUIA_PARA_EL_PAPER.md`](docs/GUIA_PARA_EL_PAPER.md)).
76
+
77
+ > **Es un trabajo académico.** Todo el laboratorio corre en Docker, en tu propia máquina. Nunca apuntes estos ataques contra servidores de terceros. Las credenciales (`SPT-DEMO-*`, `RRHH-DEMO-*`) son **ficticias** y viven solo en `.env` / Modelfiles.
78
+
79
+ ## Arquitectura
80
+
81
+ <details open>
82
+ <summary><b>Flujo de una petición</b> (Mermaid, se renderiza en GitHub)</summary>
83
+
84
+ ```mermaid
85
+ flowchart LR
86
+ C([Cliente<br/>POST /chat]) --> CFG[cargar_config<br/>config.yaml]
87
+ subgraph PROXY["Proxy FastAPI :8000 — único puerto publicado"]
88
+ direction LR
89
+ CFG --> RL{{"5 · límite de<br/>peticiones/min"}}
90
+ RL --> E1["1 · Filtrado"] --> E3["3 · Clasificación<br/>Prompt Guard 2"] --> E4["4 · Mín. privilegio"]
91
+ E4 --> B{¿bloqueó<br/>alguien?}
92
+ B -- "sí + 5 activo" --> Q[("Cola de revisión<br/>humana · 429")]
93
+ B -- "sí" --> R400[/"400 rechazada"/]
94
+ B -- no --> D["2 · Delimitación<br/>(no bloquea)"]
95
+ D --> OL
96
+ OL --> S1["1 · Filtrado salida"] --> S3["3 · Clasificación salida<br/>Llama Guard 3"]
97
+ S3 --> LOG[("eventos.jsonl")]
98
+ end
99
+ OL[("Modelo protegido<br/>hoy: Ollama :11434 (red interna)<br/>o servidor OpenAI-compatible / frontera")]
100
+ Q -. "persona aprueba" .-> OL
101
+ S3 --> C
102
+ style OL fill:#2e1065,stroke:#a78bfa,color:#fff
103
+ style Q fill:#422006,stroke:#fbbf24,color:#fff
104
+ style R400 fill:#4c0519,stroke:#fb7185,color:#fff
105
+ ```
106
+
107
+ **Reglas que gobiernan la cadena** (detalle en [`docs/arquitectura.md`](docs/arquitectura.md)):
108
+ - La cadena es una **lista de pasos recorrida en bucle**, no `if` anidados; el primer mecanismo que bloquea corta la cadena (`mecanismo_que_bloqueo` en el log).
109
+ - La **delimitación** no bloquea: se aplica aparte, justo antes de llamar a Ollama.
110
+ - La **aprobación humana** no es un paso más: *intercepta* el bloqueo y lo convierte en cola (429). Encolar es una demora, no una caída.
111
+ - La **clasificación** evalúa el texto original del usuario, no el ya envuelto por la delimitación.
112
+ - **Fail closed:** un timeout o error del clasificador cuenta como `unsafe`.
113
+ - El mismo código ejecuta las 7 configuraciones; solo cambia `config.yaml`.
114
+
115
+ </details>
116
+
117
+ <details>
118
+ <summary><b>Secuencia: petición bloqueada y encolada</b></summary>
119
+
120
+ ```mermaid
121
+ sequenceDiagram
122
+ autonumber
123
+ actor A as Atacante
124
+ participant P as Proxy (:8000)
125
+ participant K as Cadena (1·3·4)
126
+ participant Q as Cola humana (5)
127
+ actor H as Revisor
128
+ participant O as Modelo protegido
129
+ A->>P: POST /chat {modelo, mensaje}
130
+ P->>K: filtrar → clasificar → validar_privilegio
131
+ K-->>P: clasificación = unsafe (primer bloqueo gana)
132
+ P->>Q: enviar_a_revision(petición)
133
+ P-->>A: 429 pendiente (cabecera X-Revision-Id)
134
+ H->>Q: GET /revision/ui → rechazar
135
+ Q-->>A: GET /chat/revision/{id} → rechazada
136
+ Note over P,O: el modelo nunca fue llamado
137
+ P->>P: eventos.jsonl (resultado=bloqueado, mecanismo_que_bloqueo=clasificacion)
138
+ ```
139
+
140
+ </details>
141
+
142
+ ### Diagrama de clases (animado)
143
+
144
+ <p align="center"><img src="docs/assets/diagrama-clases.svg" alt="Diagrama de clases de IronVeil: núcleo del proxy y las extensiones memoria_decisiones, siem y normalizacion con sus puertos y adaptadores" width="100%"></p>
145
+
146
+ El núcleo depende de **puertos** (`Protocol`) y los **adaptadores** (almacenes, formatos y destinos SIEM, decodificadores) se registran por configuración, sin editar el núcleo. Los paquetes `memoria_decisiones/` y `siem/` son la referencia del patrón (SOLID, ver [`CLAUDE.md`](CLAUDE.md) §7).
147
+
148
+ ### Versión interactiva
149
+
150
+ Abre **[`docs/diagramas/index.html`](docs/diagramas/index.html)** (o, con GitHub Pages activado, <https://anderfg13.github.io/IronVeil/diagramas/>): eliges una configuración C0–C6, envías una petición legítima o un ataque (V2–V5) y ves qué pieza se ilumina, cuál bloquea y si termina en 400, en cola (429) o exitosa. Es una **simulación didáctica**; las cifras reales están en [`analisis/resultados_finales.md`](analisis/resultados_finales.md).
151
+
152
+ ## Inicio rápido (un solo comando)
153
+
154
+ **Requisitos:** Docker con Compose v2 (`docker compose`), ~10 GB de disco libre e internet la primera vez (imagen de Ollama, modelo base y, para la clasificación de entrada, Prompt Guard).
155
+
156
+ ```bash
157
+ git clone https://github.com/Anderfg13/IronVeil.git && cd IronVeil
158
+ cp .env.example .env # variables y canarios ficticios (nunca se sube a git)
159
+ docker compose up --build # levanta Ollama + inicializa modelos + proxy
160
+ ```
161
+
162
+ La primera vez descarga el modelo base (`llama3.2`), el clasificador `llama-guard3:1b` y construye la imagen del proxy; puede tardar varios minutos. El proxy no arranca hasta que `ollama-init` termina con éxito. Cuando los logs digan que `ironveil-proxy` escucha en el puerto 8000, está listo:
163
+
164
+ ```bash
165
+ curl -X POST http://localhost:8000/chat -H 'Content-Type: application/json' \
166
+ -d '{"modelo":"soporte","mensaje":"hola, ¿en qué me puedes ayudar?"}'
167
+ ```
168
+
169
+ > **Clasificación de entrada (C3 y C6):** usa *Llama Prompt Guard 2*, un modelo con licencia restringida en Hugging Face. Acepta la licencia, crea un token y ponlo en `HF_TOKEN` dentro de `.env`. Sin token, esa pieza **falla cerrada** (bloquea todo), por diseño. Las demás configuraciones no lo necesitan.
170
+
171
+ **Usuario de prueba (opcional).** El laboratorio arranca abierto, como lo necesitan los experimentos. Para probar la
172
+ autenticación del proxy hay un usuario `admin` con una clave **aleatoria** (no existe una clave por defecto publicada):
173
+
174
+ ```bash
175
+ python -m proxy.auth --demo # crea la cuenta; la clave queda en credenciales-demo.local.txt
176
+ # en .env: CHAT_REQUIERE_AUTENTICACION=1 y IRONVEIL_DEMO=1
177
+ docker compose up -d proxy
178
+ curl -u admin:LA_CLAVE -X POST http://localhost:8000/chat -H 'Content-Type: application/json' \
179
+ -d '{"modelo":"soporte","mensaje":"hola"}' # sin -u responde 401
180
+ ```
181
+
182
+ `/chat` y la consola `/revision/ui` usan las mismas cuentas; el chat de `/demo` pide login. Detalles en
183
+ [`docs/AUTENTICACION.md`](docs/AUTENTICACION.md).
184
+
185
+ **Comprobar el aislamiento** (debe fallar con *connection refused*; Ollama no se publica al host):
186
+
187
+ ```bash
188
+ curl http://localhost:11434
189
+ ```
190
+
191
+ **Apagar:** `docker compose down` · borrar también los modelos: `docker compose down -v`.
192
+
193
+ ## Las 7 configuraciones
194
+
195
+ | Config. | Mecanismos activos | Para qué sirve |
196
+ |---------|--------------------|----------------|
197
+ | **C0** | ninguno | línea base (passthrough puro) |
198
+ | **C1** | filtrado | |
199
+ | **C2** | delimitación | |
200
+ | **C3** | clasificación | |
201
+ | **C4** | mínimo privilegio | |
202
+ | **C5** | aprobación humana + rate limit | |
203
+ | **C6** | los cinco | defensa en profundidad |
204
+
205
+ Cambiar de configuración es **editar `config.yaml`** (el proxy lo relee en cada petición; no hay que reconstruir nada). Un atajo que escribe las cinco banderas por ti:
206
+
207
+ ```bash
208
+ python scripts/seleccionar_config.py C3 # escribe config.yaml con C3 (solo clasificación)
209
+ ```
210
+
211
+ o a mano (`config.yaml`, las cinco claves son obligatorias; si falta una el proxy falla ruidosamente):
212
+
213
+ ```yaml
214
+ filtrado: false
215
+ delimitacion: false
216
+ clasificacion: true # ← C3
217
+ minimo_privilegio: false
218
+ aprobacion_humana: false
219
+ ```
220
+
221
+ Luego lanza los ataques contra el proxy local, indicando la configuración que declaras (el script verifica que coincida con `config.yaml`):
222
+
223
+ ```bash
224
+ pip install -r requirements-dev.txt # solo para los scripts del host
225
+ python -m ataques.vectores_1_2_3 --configuracion C3 # V1, V2 y V3
226
+ python -m ataques.vector4_movimiento_lateral --configuracion C3
227
+ python -m ataques.vector5_agotamiento --modelo soporte --configuracion C3
228
+ python -m ataques.vector6_adaptativo --configuracion C6 # adaptativo, contra la cadena completa
229
+ ```
230
+
231
+ Los eventos quedan en `resultados/AAAA-MM-DD/eventos.jsonl` (esquema de 8 campos base, ver [`CLAUDE.md`](CLAUDE.md) §4). Para el recorrido completo C0→C6 en GPU: [`resultados/RECORRIDA_GPU_C0_C6.ipynb`](resultados/RECORRIDA_GPU_C0_C6.ipynb).
232
+
233
+ ## Dónde están los resultados
234
+
235
+ | Qué | Dónde |
236
+ |-----|-------|
237
+ | **Respuesta a la pregunta de investigación** y tabla maestra | [`analisis/resultados_finales.md`](analisis/resultados_finales.md) |
238
+ | Tabla maestra (CSV / Markdown / LaTeX) | `analisis/tabla_maestra.{csv,md,tex}` |
239
+ | Hipótesis vs. ASR medido, celda por celda | [`analisis/matriz_real_vs_hipotesis.md`](analisis/matriz_real_vs_hipotesis.md) |
240
+ | Costo de latencia por mecanismo | [`analisis/costo_por_mecanismo.md`](analisis/costo_por_mecanismo.md) |
241
+ | Gráficas finales | [`resultados/graficas_finales/`](resultados/graficas_finales/) |
242
+ | **Datos crudos** (evidencia, no se editan a mano) | [`resultados/resultados_template.csv`](resultados/resultados_template.csv) y `resultados/AAAA-MM-DD/` |
243
+ | Filas descartadas con su razón | [`resultados/descartados.csv`](resultados/descartados.csv) · [`docs/LIMPIEZA_DATOS.md`](docs/LIMPIEZA_DATOS.md) |
244
+ | De dónde sale cada tabla y figura del informe | [`docs/DE_DONDE_SALE_CADA_TABLA.md`](docs/DE_DONDE_SALE_CADA_TABLA.md) |
245
+ | Decisiones de diseño y conflictos resueltos | [`docs/FUENTE_DE_VERDAD.md`](docs/FUENTE_DE_VERDAD.md) · [`docs/CONFLICTOS_RESUELTOS.md`](docs/CONFLICTOS_RESUELTOS.md) |
246
+
247
+ Lectura honesta de los resultados (sobre `llama3.2:1b`, corridas en GPU T4 de Colab): el ASR promedio V1–V4 baja de **31,2 % en C0 a 15,0 % en C1 y en C6**; la clasificación es la única pieza con costo apreciable (**≈ 75 ms** de sobrecosto propio, p95 ≈ 95 ms), y **ninguna combinación llega a 0 %**, como anticipa la literatura. Las diferencias de uno o dos eventos no se distinguen del ruido del LLM (el modelo no es determinista); cada comparación lleva su valor p. Las cifras, sus límites y lo que **no** se puede afirmar están en `analisis/resultados_finales.md`.
248
+
249
+ ## Estructura del repositorio
250
+
251
+ ```text
252
+ IronVeil/
253
+ ├── config.yaml # las 5 banderas: lo único que cambia entre C0..C6
254
+ ├── docker-compose.yml # Ollama + ollama-init + proxy (solo :8000 se publica)
255
+ ├── .env.example # variables y canarios ficticios (copiar a .env)
256
+ ├── proxy/ # el framework
257
+ │ ├── main.py # FastAPI: POST /chat y la cadena de mecanismos
258
+ │ ├── mecanismos.py # los 5 mecanismos (contrato estable, CLAUDE.md §4)
259
+ │ ├── cola.py # cola de revisión humana + rate limit
260
+ │ ├── siem/ # extensión: export a Wazuh/Splunk/Elastic/… (puertos y adaptadores)
261
+ │ ├── memoria_decisiones/ # extensión: recuerda los rechazos humanos
262
+ │ ├── normalizacion/ # extensión: decodifica Base64/hex/ROT13 antes de la cadena
263
+ │ └── static/ # consola de revisión humana (/revision/ui) y logo
264
+ ├── ollama/ # init.sh y Modelfiles (plantillas, sin credenciales)
265
+ ├── ataques/ # vectores V1–V7, medición de costo, validación externa con Garak
266
+ ├── analisis/ # consolidación, estadística, tablas y gráficas
267
+ ├── resultados/ # datos crudos (CSV/JSONL), gráficas y notebooks de corrida
268
+ ├── scripts/ # utilidades (seleccionar_config.py)
269
+ ├── tests/ # pytest: sin Docker ni Ollama (clasificador con mocks)
270
+ ├── docs/ # arquitectura, decisiones, guías, diagramas
271
+ │ ├── assets/ # diagramas animados (SVG)
272
+ │ └── diagramas/ # visor interactivo (HTML)
273
+ ├── .github/ # CI, publicación de release/paquete, plantilla de PR
274
+ ├── CLAUDE.md # contrato y reglas del equipo
275
+ └── LICENSE
276
+ ```
277
+
278
+ ## Reproducir los resultados
279
+
280
+ ```bash
281
+ python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
282
+ pip install -r requirements-dev.txt # versiones exactas
283
+ python -m pytest -q # suite completa: no necesita Docker ni Ollama
284
+ ruff check . && black --check . # lint y formato
285
+
286
+ python analisis/validar_dataset.py # valida el CSV crudo
287
+ python analisis/consolidar.py # ASR por configuración y vector
288
+ python analisis/tabla_maestra.py # tabla maestra del informe
289
+ python analisis/graficas_finales.py # gráficas en resultados/graficas_finales/
290
+ ```
291
+
292
+ Reglas del dato: los CSV y logs de `resultados/` son **evidencia**; toda transformación pasa por un script reproducible y ninguna fila se borra (se mueve a `descartados.csv` con su razón).
293
+
294
+ ## Extensiones opcionales (apagadas por defecto)
295
+
296
+ | Extensión | Qué hace | Documentación |
297
+ |-----------|----------|---------------|
298
+ | Consola de revisión humana | UI en tiempo real (SSE) con login opcional: `/revision/ui` | [`docs/DEMO.md`](docs/DEMO.md) |
299
+ | Chat de demo | Pantalla `/demo` (`IRONVEIL_DEMO=1`) | [`docs/DEMO.md`](docs/DEMO.md) |
300
+ | SIEM | JSON/CEF/LEEF/ECS hacia archivo, syslog, HTTPS, Splunk, Elastic, Azure, CloudWatch | [`docs/SIEM.md`](docs/SIEM.md) |
301
+ | Memoria de decisiones | Rechaza solo lo que un humano ya rechazó | [`docs/MEMORIA_DE_DECISIONES.md`](docs/MEMORIA_DE_DECISIONES.md) |
302
+ | Normalización de entrada | Decodifica lo que el atacante escondió | [`docs/NORMALIZACION_DE_ENTRADA.md`](docs/NORMALIZACION_DE_ENTRADA.md) |
303
+ | Otro clasificador (p. ej. Jev) | Cambiar de modelo clasificador sin tocar `clasificar()` | [`docs/CAMBIAR_MODELO_CLASIFICADOR.md`](docs/CAMBIAR_MODELO_CLASIFICADOR.md) |
304
+ | Modelo protegido remoto | Proteger un modelo servido por otra API | [`docs/MODELO_PROTEGIDO_REMOTO.md`](docs/MODELO_PROTEGIDO_REMOTO.md) |
305
+ | Validación externa con Garak | Segunda instancia del proxy atacada por Garak | [`docs/GARAK.md`](docs/GARAK.md) |
306
+ | Excessive Agency (V7) | Herramientas **simuladas** de `rrhh-agente` | `ataques/variantes_ataque.md` |
307
+
308
+ ## Seguridad y ética
309
+
310
+ - **Laboratorio aislado:** todo ataque, carga o escaneo va contra el stack propio en localhost/Docker, nunca contra terceros.
311
+ - **Ollama nunca se publica al host:** solo el proxy expone el puerto 8000.
312
+ - **Autenticación opcional:** `CHAT_REQUIERE_AUTENTICACION=1` exige usuario y clave en `/chat` (falla cerrada si no hay cuentas); la consola de revisión tiene login con sesión y CSRF. Apagada por defecto para no alterar lo medido.
313
+ - **Sin secretos en el código:** los canarios viven en `.env`/Modelfiles; las dependencias están fijadas con versión exacta (y con hashes en la imagen del proxy).
314
+ - **Mensajes de error genéricos** hacia el cliente; el detalle se registra solo en el servidor.
315
+ - Las herramientas de la extensión V7 están **simuladas** y marcadas como tales en el código.
316
+
317
+ ## Instalar como paquete
318
+
319
+ ```bash
320
+ pip install ironveil # en un entorno virtual; necesita un Ollama alcanzable
321
+ ironveil config C3 && ironveil serve
322
+ ```
323
+
324
+ Publicación en PyPI y límites: [`docs/PUBLICAR_PYPI.md`](docs/PUBLICAR_PYPI.md). El sitio con el visor interactivo y los
325
+ diagramas animados se publica con GitHub Pages: <https://anderfg13.github.io/IronVeil/>.
326
+
327
+ ## Versiones y paquetes
328
+
329
+ La entrega final está marcada con la etiqueta [`entrega-final-2026-10-31`](https://github.com/Anderfg13/IronVeil/releases) (ver [`docs/RELEASE_NOTES.md`](docs/RELEASE_NOTES.md)). Al subir una etiqueta, GitHub Actions publica la *release* y la imagen del proxy en GitHub Packages:
330
+
331
+ ```bash
332
+ docker pull ghcr.io/anderfg13/ironveil-proxy:entrega-final-2026-10-31
333
+ ```
334
+
335
+ ## Equipo y contribución
336
+
337
+ **Anderson Fabian Garcia Nieto** (infraestructura, filtrado, clasificación, mínimo privilegio, cola y costo) · **Carlos Mario Piedrahita Arango** (configuración, delimitación, integración, interfaz de aprobación y SIEM) · **Andres Felipe Sabogal Wilches** (ejecución de los vectores de ataque) · **Ana Gabriela Fiquitiva Poveda** (esquema de log, consolidación, gráficas e informe). Las reglas de trabajo, los contratos estables y la lista de comprobación de cada tarea están en [`CLAUDE.md`](CLAUDE.md); las PR usan la [plantilla](.github/PULL_REQUEST_TEMPLATE.md).
338
+
339
+ [Cómo contribuir](CONTRIBUTING.md) · [Seguridad](SECURITY.md) · [Código de conducta](CODE_OF_CONDUCT.md) · [Cómo citar](CITATION.cff)
340
+
341
+ Licencia [MIT](LICENSE) · uso exclusivamente académico y en laboratorio propio.