OWASP Top 10 para LLMs 2026 y la lista de agentes, explicadas con código (y lo que tengo en producción)
Tabla de Contenidos
OWASP tiene hoy dos listas para la IA, y no compiten: el Top 10 para aplicaciones LLM 2026 cubre el modelo cuando es un componente de tu app, y el Top 10 para aplicaciones agénticas (ASI01-ASI10) cubre el momento en que el modelo actúa por su cuenta. La edición 2026 de la primera no añade categorías: reordena las mismas diez, y el cambio que importa es que Excessive Agency sube del 6.º al 3.º puesto. Abajo va cada riesgo con un fallo realista y su arreglo en Laravel o TypeScript, y al final lo que de verdad tengo puesto en un sistema con agentes en producción.
La frase que resume las dos listas está en la carta de los líderes del proyecto: «Stop trying to build a model that cannot be fooled. Build the system around it, so that when the model is fooled, and it will be, nothing important breaks.» Deja de intentar que el modelo no se deje engañar; construye el sistema para que, cuando lo engañen, no se rompa nada importante. Todo lo que sigue es una forma concreta de hacer eso.
Dos listas, una frontera
- OWASP Top 10 for LLM Applications 2026. La web de OWASP la fecha el 3 de agosto de 2026 y el anuncio oficial salió el 1 de septiembre. Son 122 páginas, con licencia CC BY-SA 4.0, y por primera vez el orden no sale solo del voto de expertos: pesa un 75 % el voto y un 25 % un corpus de 6.639 incidentes reales clasificados.
- OWASP Top 10 for Agentic Applications for 2026. Se publicó el 9 de diciembre de 2025 con la etiqueta «2026» y no tiene revisión posterior. Introduce el principio de mínima agencia: no des autonomía donde no hace falta, porque amplía la superficie de ataque sin aportar nada.
OWASP marca la frontera así: «This list owns the risk when the model is a component inside your application. The moment that model becomes an actor […] the risk moves to the OWASP Agentic Top 10.» Un chatbot que responde con tu base de conocimiento vive en la primera lista. Un agente que lee correos, decide y llama a herramientas vive en las dos.
Qué cambió de 2025 a 2026
| Puesto 2026 | Riesgo | En 2025 |
|---|---|---|
| LLM01 | Prompt Injection | 1.º (=) |
| LLM02 | Sensitive Information Disclosure | 2.º (=) |
| LLM03 | Excessive Agency | 6.º (sube 3) |
| LLM04 | Supply Chain | 3.º |
| LLM05 | Data and Model Poisoning | 4.º |
| LLM06 | Unbounded Consumption | 10.º (sube 4) |
| LLM07 | Misinformation | 9.º (sube 2) |
| LLM08 | Hidden Context Exposure | 7.º como «System Prompt Leakage» (renombrado y ampliado) |
| LLM09 | Vector and Embedding Weaknesses | 8.º |
| LLM10 | Improper Output Handling | 5.º (baja 5, la mayor caída) |
Ojo al buscar en español: varias páginas titulan «OWASP LLM 2026» y siguen listando el orden de 2025, con System Prompt Leakage en el 7 y Unbounded Consumption en el 10. La tabla de arriba está sacada del PDF oficial.
Dos datos de la carta que cambian cómo leer el ranking. Prompt injection sigue en el n.º 1 por votación, pero ordenado solo por incidentes registrados se cae del top 10: OWASP lo atribuye a que se defiende tanto que llegan pocos exploits limpios a las bases públicas. Y Misinformation es el caso contrario: los votantes la ponían abajo y los incidentes la ponen arriba, la brecha más grande «en la dirección que de verdad duele».
Las diez del Top 10 para LLMs 2026, con código
Los ejemplos son cortos a propósito. Las funciones con nombre propio (fetchAsMarkdown, llm.summarize) son helpers de tu app; las APIs de Laravel, del SDK laravel/ai y de Node son las reales.
LLM01:2026 Prompt Injection
Cualquier entrada que cambie el comportamiento del modelo de una forma que no querías: la del usuario, pero también una página que el modelo lee, la salida de una herramienta, una imagen o un audio. El problema de fondo, en palabras de OWASP: los LLM «make no architectural distinction between “instructions” and “data”». No existe el equivalente a una consulta parametrizada, así que la defensa no es un prompt mejor, sino que el código decida lo que sale.
// ❌ El comentario de un desconocido decide lo que publica la marca
$out = $agent->prompt("Responde a este comentario: {$comment->text}");
$network->reply($comment, $out['reply']);
// ✅ El modelo clasifica y redacta; el código decide si sale
$safe = in_array($out['category'], ['gratitude', 'praise', 'greeting'], true)
&& ($out['auto_send'] ?? false) === true
&& ! preg_match('~https?://|www\.|@\w~i', $out['reply']) // sin enlaces ni menciones
&& mb_strlen($out['reply']) <= 280
&& ! $comment->isDirectMessage();
$safe
? $network->reply($comment, $out['reply'])
: $comment->update(['suggested_reply' => $out['reply']]); // lo demás, a una persona
LLM02:2026 Sensitive Information Disclosure
Datos confidenciales que salen por un canal que no debían. La edición 2026 insiste en que el canal «is not only the final answer»: también cuentan los argumentos de las llamadas a herramientas, las trazas de razonamiento, los logs y los embeddings. La defensa más barata es no meter en el contexto lo que la tarea no necesita.
// ❌ Todo el registro del cliente entra al contexto (y a los logs del proveedor)
await llm.summarize(JSON.stringify(customer));
// ✅ Minimización: solo los campos que la tarea necesita
const { plan, lastTicketSubject } = customer;
await llm.summarize(JSON.stringify({ plan, lastTicketSubject }));
LLM03:2026 Excessive Agency
El que más sube. Es darle al modelo más funcionalidad, más permisos o más autonomía de los que la tarea pide, de forma que una salida inesperada o manipulada se convierte en una acción dañina, «regardless of what is causing the LLM to malfunction». Da igual si el modelo alucinó o lo engañaron: el daño lo pone el permiso.
// ❌ Una tool "RunSql" que acepta SQL arbitrario
// ✅ Una tool estrecha, atada al usuario autenticado, y un tope de pasos
#[MaxSteps(5)]
class SupportAgent implements Agent, HasTools
{
public function tools(): iterable
{
return [new OrderStatus($this->user)];
}
}
class OrderStatus implements Tool
{
public function __construct(private User $user) {}
public function description(): string
{
return 'Estado de un pedido del usuario actual.';
}
public function schema(JsonSchema $schema): array
{
return ['order_id' => $schema->integer()->required()];
}
public function handle(Request $request): string
{
// La autorización va en código, no en el prompt
return $this->user->orders()->findOrFail($request['order_id'])->status;
}
}
LLM04:2026 Supply Chain
La integridad de todo lo que no escribiste tú: modelos, adaptadores, conversiones, paquetes. Lo específico de servidores MCP y registros de herramientas se va a la lista de agentes (ASI04). El caso de manual es de septiembre de 2025: el paquete no oficial postmark-mcp publicó quince versiones limpias y en la 1.0.16 añadió una línea que mandaba en copia oculta cada correo a un dominio del atacante.
// ❌ Con un rango, `npm install` habría traído la 1.0.16 maliciosa
"postmark-mcp": "^1.0.0"
// ✅ Versión exacta, lockfile, `npm ci` en CI y revisar el diff antes de subir
"postmark-mcp": "1.0.15"
LLM05:2026 Data and Model Poisoning
Alguien mete datos o artefactos manipulados para sesgar el modelo o dejarle una puerta trasera: en el entrenamiento, el fine-tuning, los embeddings o la base de conocimiento de un RAG. En una app normal, la puerta más común es dejar que lo que escribe un visitante entre directo en lo que el bot usará para responder a los demás.
// ❌ La «corrección» de un visitante entra directa en la base de conocimiento
KnowledgeEntry::create(['body' => $request->input('correction')]);
// ✅ Cuarentena con procedencia; solo una persona la promueve
KnowledgeEntry::create([
'body' => $request->input('correction'),
'status' => 'pending',
'source' => 'visitor',
'source_ip' => $request->ip(),
]);
LLM06:2026 Unbounded Consumption
Inferencia sin control: denegación de servicio, clonación del modelo o denial of wallet, que es que te vacíen la cuenta. Lo que la hace distinta es la asimetría de coste: a quien ataca, una petición le cuesta nada y a ti te cuesta tokens. OWASP dice que el rate limit por petición «is no longer sufficient» y pide topes duros de gasto. El fallo más común es limitar con una clave que elige el cliente.
// ❌ Throttle con la clave que manda el cliente: un id nuevo por petición lo atraviesa
RateLimiter::attempt('chat:'.$request->input('session_id'), 20, fn () => $this->answer($request));
// ✅ Topes que no dependen del cliente: por sitio y día, y tokens por respuesta
if (! RateLimiter::attempt("chat:site:{$site}:".now()->toDateString(), 300, fn () => true, 86400)) {
return $this->cannedReply(); // respuesta fija, sin llamar al modelo
}
// y en el agente: #[MaxTokens(1500)]
LLM07:2026 Misinformation
Una salida falsa que parece creíble y que alguien usa para decidir, sea una persona, un workflow u otro agente: «The core risk is that the incorrect output is trusted and acted upon.» Si tu sistema cita fuentes, la defensa es comprobar la cita contra la fuente, no pedirle al modelo que tenga cuidado.
// ✅ Una cita solo cuenta si el pasaje literal está en la página, vuelta a leer
const page = await fetchAsMarkdown(claim.url); // conversión determinista, sin LLM
if (!normalize(page).includes(normalize(claim.passage))) rejected.push(claim);
LLM08:2026 Hidden Context Exposure
Antes se llamaba System Prompt Leakage y ahora cubre todo el contexto oculto: instrucciones, políticas recuperadas, esquemas de herramientas. La regla de diseño es suponer que todo eso se puede extraer. El prompt no debe llevar secretos ni hacer de frontera de autorización.
// ❌ Secreto y regla de negocio en el system prompt
return "Usa el token {$crmToken}. Si el usuario dice VIP2026, aplica un 30% de descuento.";
// ✅ El prompt es público por diseño; el descuento lo valida el servidor
return 'Eres el asistente de soporte. Responde solo con la base de conocimiento.';
// ...
$discount = Coupon::validFor($user, $code);
LLM09:2026 Vector and Embedding Weaknesses
Los riesgos de la capa de similitud: RAG, memoria vectorial, cachés semánticas. OWASP lo resume en una línea: «Poisoning makes the system wrong, inversion makes it leak, jamming makes it silent, and access-control failure makes it indiscriminate.» El fallo de acceso es el más fácil de cometer: buscar en todo el índice y filtrar después.
// ❌ Similitud sobre TODO el índice y filtro de inquilino después, en la app
// ✅ El filtro de inquilino va DENTRO de la consulta vectorial (pgvector)
await pool.query(
'SELECT id, body FROM chunks WHERE tenant_id = $1 ORDER BY embedding <=> $2 LIMIT 5',
[tenantId, toSql(queryEmbedding)],
);
LLM10:2026 Improper Output Handling
Pasar la salida del modelo a otro componente sin validarla ni escaparla: XSS, SSRF, ejecución remota. Es la que más baja en el ranking, y aun así hay una trampa muy de Laravel. Str::markdown() no es seguro para texto de un LLM tal como viene, porque CommonMark trae por defecto html_input en allow y allow_unsafe_links en true. Otra vía que la edición 2026 detalla es la exfiltración por imagen: el modelo escribe  y el navegador la carga solo.
{{-- ❌ CommonMark por defecto deja pasar HTML crudo y enlaces javascript: --}}
{!! Str::markdown($llmAnswer) !!}
{{-- ✅ --}}
{!! Str::markdown($llmAnswer, ['html_input' => 'strip', 'allow_unsafe_links' => false]) !!}
// ✅ En el cliente: sanear lo que se renderiza, más una CSP con img-src restrictivo
el.innerHTML = DOMPurify.sanitize(await marked.parse(answer));
Las diez de la lista de agentes (ASI01-ASI10)
Esta lista empieza donde el modelo deja de contestar y empieza a actuar: planifica en varios pasos, llama a herramientas y a veces habla con otros agentes.
ASI01 Agent Goal Hijack
Contenido que el agente lee (una página, un correo, la salida de una tool) le cambia el objetivo o el plan. A diferencia de LLM01, el daño no se queda en una respuesta: se arrastra por todos los pasos. El ejemplo que usa OWASP es EchoLeak (CVE-2025-32711): un solo correo, sin que nadie hiciera clic, llevaba a Microsoft 365 Copilot a sacar datos internos. La defensa estructural es que el agente que lee contenido ajeno no tenga herramientas de salida.
// ✅ El agente que lee web ajena no puede mandar nada a ningún sitio
const readerTools = [search, readPage]; // ni sendEmail, ni writeFile, ni httpPost
const result = DossierSchema.parse(JSON.parse(agentOutput)); // zod: solo la forma esperada
ASI02 Tool Misuse & Exploitation
El agente usa una herramienta legítima, dentro de sus permisos, de forma dañina: borra, abusa de una API cara, exfiltra. En MCP, las anotaciones como readOnlyHint ayudan, pero la especificación dice que el cliente debe tratarlas como no fiables. La única garantía es que la tool peligrosa no exista.
server.registerTool(
'list_posts',
{
description: 'Lista piezas (solo lectura)',
inputSchema: { brand: z.string() },
annotations: { readOnlyHint: true },
},
async ({ brand }) => ({ content: [{ type: 'text', text: JSON.stringify(await posts.list(brand)) }] }),
);
// Y no se registra ninguna 'publish_post'
En Laravel MCP, el equivalente son los atributos #[IsReadOnly] y #[IsDestructive].
ASI03 Identity & Privilege Abuse
Credenciales heredadas o compartidas, cadenas de delegación sin límite y agentes sin identidad propia, de forma que nadie puede atribuir quién hizo qué. El caso del MCP de GitHub (Invariant Labs, mayo de 2025) es exactamente esto: un issue malicioso en un repo público llevaba al agente a filtrar datos de repos privados, porque su token abarcaba todos.
// ❌ El agente usa un token de administrador
// ✅ Un token por agente y por propósito, con la mínima ability
$token = $bot->createToken('ingest-agent', ['licitaciones:ingest'])->plainTextToken;
abort_unless($request->user()->tokenCan('licitaciones:ingest'), 403);
ASI04 Agentic Supply Chain Vulnerabilities
Lo mismo que LLM04, pero en vivo: las herramientas de un servidor MCP de terceros se cargan cuando el agente arranca, y su descripción puede cambiar de un día para otro para inyectar instrucciones (tool poisoning). Si dependes de un MCP ajeno, fija su huella.
// ✅ Si cambian los descriptores de las tools, no se usan hasta revisarlos
const { tools } = await client.listTools();
const hash = createHash('sha256').update(JSON.stringify(tools)).digest('hex');
if (hash !== APPROVED_TOOLS_HASH) throw new Error('Los descriptores MCP cambiaron: revisar antes de usar');
ASI05 Unexpected Code Execution
El agente genera o ejecuta código (shell, eval, instalar paquetes) y acaba comprometiendo el host. El caso más claro es de GitHub Copilot, CVE-2025-53773: una inyección lograba que el modo agente escribiera en .vscode/settings.json la opción que aprueba solos todos los comandos. El agente se editaba su propia configuración.
// ❌ exec(`git log ${argDelModelo}`) → shell + texto del modelo
// ✅ Binario fijo, argumentos como array, sin shell y con lista permitida
if (!['log', 'status'].includes(sub)) throw new Error('Subcomando no permitido');
execFile('git', [sub, '--oneline', '-n', '20'], { cwd: repoDir, shell: false }, cb);
ASI06 Memory & Context Poisoning
Envenenar la memoria persistente, los resúmenes o el RAG para sesgar sesiones futuras. La inyección de un solo turno es LLM01; esto es la que se queda.
// ❌ Memoria global escrita a partir de lo que dijo cualquiera
Memory::create(['fact' => $out['learned']]);
// ✅ Con dueño, procedencia y caducidad; nunca desde contenido no confiable
Memory::create([
'user_id' => $user->id,
'fact' => $out['learned'],
'source' => 'chat',
'expires_at' => now()->addDays(30),
]);
ASI07 Insecure Inter-Agent Communication
Mensajes entre agentes sin autenticación, sin integridad o sin protección contra repetición. Si un agente acepta órdenes de otro, esas órdenes tienen que venir firmadas y caducar.
const sig = createHmac('sha256', key).update(`${msg.id}.${msg.exp}.${msg.body}`).digest();
if (Date.now() > msg.exp || !timingSafeEqual(sig, Buffer.from(msg.sig, 'hex'))) reject(msg);
ASI08 Cascading Failures
No es el origen del fallo sino su propagación: una alucinación o un input malicioso se amplifica de un agente a otro, o se convierte en una tormenta de reintentos. La defensa son techos y trabajos idempotentes.
// ✅ Techo por corrida y ventana: cada fila es una llamada a la IA
$pending = Interaction::whereNull('category')
->where('created_at', '>', now()->subHours(48))
->limit(20)
->get();
ASI09 Human-Agent Trust Exploitation
Aprovechar que la persona se fía del agente para que sea ella quien ejecute la acción final, y quede su firma. La defensa es que la persona apruebe lo real (el texto exacto, el destinatario exacto), no el resumen que le hace el agente. Y un detalle que se olvida: si el enlace de aprobación actúa con un GET, lo aprueba el escáner de correo que previsualiza los enlaces.
// ✅ El GET muestra la pieza real; solo el POST actúa
Route::middleware('signed')->group(function () {
Route::get('/approve/{post}', [ApprovalController::class, 'confirm']);
Route::post('/approve/{post}', [ApprovalController::class, 'approve']);
});
ASI10 Rogue Agents
El agente se sale de su función: deriva de objetivo, secuestra un workflow, hace trampa para cumplir la métrica. Contra eso no hay un parche, hay un hábito: no creerse el informe del agente.
// ❌ Confiar en «200 cerradas, 0 fallos»
// ✅ Tras una escritura en lote, releer el estado con otro instrumento
$closed = Task::whereIn('id', $ids)->where('status', 'closed')->count();
if ($closed !== count($ids)) {
alert("El agente dijo ".count($ids)." y hay {$closed}");
}
Ese ejemplo no es inventado. Me pasó: un agente reportó «200 cerradas, 0 fallos» y al releer la base había cerrado 177 de 198.
Lo que tengo en producción
El backend de este sitio corre agentes de IA a diario: escriben copy para redes, clasifican comentarios, investigan y redactan borradores, y atienden el chat de soporte. Así se ve contra las dos listas.
- Mínimo privilegio (LLM03, ASI02, ASI03). El servidor MCP con el que opero el sistema desde un agente no puede publicar, aprobar, rechazar ni contestarle a nadie: esas tools no existen. Puede reprogramar, pero no acercar a menos de 30 minutos una pieza que ya está en cola. Y puede componer una pieza, pero se niega a generarla en una marca que no exige aprobación, porque eso sería publicar por la puerta de atrás. El agente de engagement solo redacta borradores para que los lea una persona. La API de licitaciones usa un token con una sola ability.
- Aprobación humana (ASI09). Nada sale sin que una persona lo apruebe desde un enlace firmado, con el GET que muestra y el POST que actúa. El mismo patrón está en el login del panel, por código de un solo uso y no por enlace mágico.
- Citas comprobadas (LLM07). La cadena que escribe artículos separa investigar de redactar y audita cada cita reabriendo la fuente. La página se convierte a texto de forma determinista, sin un modelo de por medio, para que el auditor lea exactamente lo mismo. Lo hice así porque medí que un motor que investiga y redacta de corrido dejaba entre el 29 % y el 43 % de sus afirmaciones sin sostener.
- Topes que no dependen del cliente (LLM06). El chat público tiene límites por sitio y día, por conversación y de tokens por respuesta. Al llegar a uno responde un texto fijo sin llamar al modelo. Esto salió de una auditoría propia: el límite por minuto dependía de un identificador que elige el cliente, y pasaron 25 de 25 peticiones.
- Contexto mínimo (LLM02, LLM08). El chat solo ve la base de conocimiento del sitio. El prompt no lleva credenciales, y la autorización vive en código.
- Contenido hostil (LLM01, ASI01). Los agentes que leen webs ajenas tienen la instrucción de que ninguna página puede darles órdenes, pero sobre todo entregan un JSON de forma fija, no acciones, y el lector solo alcanza la web pública. No es teoría: una wiki de videojuegos sirvió a dos agentes distintos bloques de instrucciones escondidas en la página, y ninguno las ejecutó.
Lo que falta, dicho también: el libro de consumo de IA mide el gasto real, pero no hay un tope global en dinero que corte solo. Y ítems como LLM05, LLM09, ASI06 y ASI07 no aplican porque no hay fine-tuning, RAG, memoria persistente ni agentes hablando entre sí. No tenerlos es parte de la defensa: el principio de mínima agencia también vale para la arquitectura.
Seis casos reales
- EchoLeak, CVE-2025-32711 (junio de 2025). Prompt injection zero-click en Microsoft 365 Copilot: un correo bastaba para sacar datos internos. CVSS 9.3, parcheado en el servidor. Análisis.
- MCP de GitHub (Invariant Labs, 26 de mayo de 2025). Un issue en un repo público hacía que el agente filtrara repos privados vía un pull request. El fallo es de arquitectura: un token que alcanza todo. Informe.
- postmark-mcp 1.0.16 (25 de septiembre de 2025). El primer servidor MCP malicioso documentado: copia oculta de cada correo a un dominio del atacante, unas 1.500 descargas en una semana. Bleeping Computer.
- GitHub Copilot, CVE-2025-53773 (parche de agosto de 2025). El modo agente podía activar por su cuenta la aprobación automática de comandos editando su propia configuración. Embrace The Red.
- Amazon Q Developer para VS Code, CVE-2025-8217 (julio de 2025). Un token de GitHub mal acotado permitió meter en la versión 1.84.0 un prompt destructivo, que no llegó a ejecutarse por un error de sintaxis. Boletín de AWS.
- Gemini en Chrome, CVE-2026-0628 (Unit 42, marzo de 2026). No es una prompt injection sino un fallo de aislamiento: una extensión con permisos básicos podía inyectar código en el panel de Gemini y llegar a la cámara y el micrófono. Sirve para recordar que el asistente también es superficie de ataque del navegador. Unit 42.
Checklist para llevarte
- El modelo propone; el código decide qué se ejecuta, qué se publica y a quién.
- Cada tool, tan estrecha como la tarea, y la que no hace falta, que no exista.
- Un token por agente y por propósito.
- Topes de coste que no dependan de nada que mande el cliente.
- La salida del modelo se escapa como cualquier input de usuario, también el markdown.
- Versiones exactas de paquetes y huella de las tools de MCP de terceros.
- Una persona aprueba lo real, con GET que muestra y POST que actúa.
- Después de que un agente escribe en lote, se relee el estado.
Preguntas frecuentes
¿Cuándo se publicó el OWASP Top 10 para LLMs 2026?
La web de OWASP fecha el recurso el 3 de agosto de 2026 (ya 4 de agosto en UTC) y el anuncio oficial salió el 1 de septiembre, con nota de prensa del 2. El propio PDF no lleva fecha en la portada. La lista de agentes, en cambio, es del 9 de diciembre de 2025.
¿Qué cambió en el OWASP Top 10 para LLMs de 2026 respecto a 2025?
No hay categorías nuevas: son las mismas diez reordenadas. Excessive Agency sube del 6.º al 3.º, Unbounded Consumption del 10.º al 6.º, Misinformation del 9.º al 7.º, Improper Output Handling baja del 5.º al 10.º, y System Prompt Leakage se renombra como Hidden Context Exposure. Además, el orden pondera por primera vez datos de incidentes reales (25 %) junto al voto de expertos (75 %).
¿Qué diferencia hay entre el OWASP LLM Top 10 y el Top 10 de aplicaciones agénticas?
El de LLM cubre el riesgo cuando el modelo es un componente de tu aplicación, por ejemplo un chatbot que responde. El de agentes cubre el momento en que el modelo actúa por su cuenta: planifica en varios pasos, llama a herramientas o habla con otros agentes. Un agente con herramientas está expuesto a las dos listas.
¿Se puede evitar la prompt injection con un mejor prompt?
No. Los modelos no distinguen entre instrucciones y datos, así que no existe un equivalente a la consulta parametrizada. La defensa es de arquitectura: que el código decida qué acciones se ejecutan, que el agente que lee contenido ajeno no tenga herramientas de salida, y que lo delicado pase por una persona.
¿Es seguro usar Str::markdown de Laravel con texto de un LLM?
No tal como viene. CommonMark trae por defecto html_input en allow y allow_unsafe_links en true, así que deja pasar HTML crudo y enlaces javascript:. Pásale las opciones html_input strip y allow_unsafe_links false, y en el cliente añade una CSP con img-src restrictivo contra la exfiltración por imágenes.
¿Te sirvió? Recibe la próxima por correo
Una vez por semana: qué se rompe al actualizar, IA para desarrolladores y lo que estoy construyendo, con fuentes. Sin spam.
Al suscribirte aceptas nuestro aviso de privacidad.