Inicio Linux & Systems Cybersecurity Cloud & DevOps Networks & Infrastructure SIEM & Monitoring DFIR & Threat Intel Development & Other Todas las categorias Herramientas

OpenCode Security Agent: Proteccion Runtime para Agentes IA

OpenCode Security Agent: Proteccion Runtime para Agentes IA

Tabla de contenidos

El problema: agentes IA con acceso total a tu sistema

Los agentes de codigo IA como OpenCode tienen acceso a herramientas potentes: shell, lectura/escritura de ficheros, acceso a red. Esto es necesario para que sean utiles, pero abre un vector de ataque real.

El ecosistema de Skills y MCP servers es joven. Estudios recientes muestran que ~36% de los skills de agentes IA contienen fallos de seguridad, se han rastreado mas de 138 CVEs, y miles de skills maliciosos han sido identificados en registros publicos. Un solo skill comprometido puede:

  • Exfiltrar claves SSH, tokens de API o ficheros cloud
  • Inyectar codigo malicioso en tu repositorio
  • Establecer reverse shells a servidores externos
  • Modificar tu configuracion de shell sin que lo notes

El caso Postmark MCP (septiembre 2025)

El ejemplo canonico es el incidente del MCP de Postmark. Un skill que fue limpio y de confianza durante quince versiones envio una actualizacion silenciosa (v1.0.16) que hacia BCC de cada email que el usuario enviaba a un dominio externo controlado por el atacante. Los analisis estaticos de la v1.0.15 no habrian encontrado nada — el ataque vino en una actualizacion aparentemente rutinaria.

Este tipo de ataque de supply-chain es el mas dificil de detectar: no es que instales algo malicioso, es que algo que ya confiabas se vuelve malicioso.

Que es OpenCode Security Agent

OpenCode Security Agent es un plugin de seguridad runtime para OpenCode que implementa dos capas de proteccion:

  1. Capa v1 (analisis estatico): escanea los skills y MCP servers instalados contra multiples bases de datos de vulnerabilidades y analiza el codigo en busca de patrones sospechosos.
  2. Capa v2 (proteccion runtime): un plugin de OpenCode con hook tool.execute.before que inspecciona cada llamada de herramienta antes de que se ejecute y bloquea las peligrosas.

Arquitectura

CODE
+---------------------------------------------+
|              OpenCode Runtime                |
|                                              |
|  +-------------+     +-------------------+  |
|  | Agent (LLM) |---->| tool.execute.     |  |
|  +-------------+     |  before hook      |  |
|                       |  security-agent.ts|  |
|                       +--------+----------+  |
|                                |              |
|                       +--------v----------+  |
|                       | sentinel_preflight|  |
|                       |      .py          |  |
|                       +--------+----------+  |
|                                |              |
|                       +--------v----------+  |
|                       |   iocs.json       |  |
|                       |  (IOC library)    |  |
|                       +-------------------+  |
|                                |              |
|                          ALLOW / DENY        |
+---------------------------------------------+

El flujo es:

  1. El agente solicita ejecutar una herramienta (bash, read, write, etc.)
  2. El plugin security-agent.ts intercepta la llamada via tool.execute.before
  3. Pasa los argumentos a sentinel_preflight.py
  4. Python evalua contra la libreria de IOCs y reglas de patrones
  5. Si hay match: bloqueo inmediato con explicacion del motivo
  6. Si no hay match: la herramienta se ejecuta normalmente

Coste: cero tokens LLM. ~30-80ms por llamada.

Modo de fallo: fail-open. Si el fichero de IOCs no existe o el script falla, la decision por defecto es permitir la ejecucion.

Que detecta

El motor de deteccion evalua cada llamada de herramienta contra multiples categorias de amenazas. Esta lista refleja el estado tras la auditoria de seguridad v1.3, donde se parchearon 14 bypasses encontrados mediante red teaming.

Rutas sensibles (CRITICAL)

Cualquier acceso de lectura o escritura a directorios y ficheros que contienen material criptografico, tokens de acceso o configuracion de servicios cloud. Esto incluye:

  • Directorios de claves SSH y credenciales Git
  • Configuracion de AWS, Azure, GCP y Kubernetes
  • Claves GPG, certificados (PEM, P12, PFX, KEY)
  • Hashes de sistema, ficheros de service accounts
  • Ficheros con patron de variables de entorno
  • Configuracion de Docker y PyPI

Ademas, detecta enumeracion de dot-dirs — comandos que intentan listar recursivamente todos los directorios ocultos del home del usuario para descubrir que configuraciones existen.

Variables de entorno sensibles (HIGH)

Comandos que referencian variables de entorno que contienen secretos. El plugin detecta nombres especificos de providers de IA, cloud y servicios de pago, ademas de patrones genericos como variables que terminan en _KEY, _SECRET, _TOKEN o _PASSWORD.

Tambien detecta volcados de entorno: comandos que intentan extraer variables de entorno en bloque o buscar claves en el historial del shell.

Dominios maliciosos conocidos (CRITICAL)

IOCs confirmados de incidentes reales, como el dominio usado en el backdoor del Postmark MCP. Tambien detecta:

  • Servicios de exfiltracion tipo pastebin
  • URLs con IP raw sin dominio (IPv4 e IPv6)
  • Servicios de tunneling (para evadir firewalls corporativos)
  • Typosquatting: genera automaticamente variantes por homoglifos, transposiciones, eliminacion de caracteres, insercion de guiones y cambios de TLD de cada dominio malicioso conocido. Solo coincide con variantes que aparecen en contexto de URL para evitar falsos positivos

Patrones de shell peligrosos (CRITICAL)

  • Pipes de descarga a interprete (descarga remota piped a shell)
  • Reverse shells (netcat, bash con redirects TCP)
  • Cadenas de exfiltracion ofuscada (base64 combinado con envio de red)
  • Uso de eval/exec con entrada externa
  • Permisos excesivamente abiertos (world-writable)
  • Modificacion de ficheros de perfil de shell (persistencia)
  • Exfiltracion DNS/ICMP: comandos como nslookup, dig, ping o host combinados con sustitucion de comandos para enviar datos por canales secundarios
  • Interpretes alternativos: ejecucion de codigo via interpretes de scripting con imports o llamadas peligrosas (sockets, subprocesos, system calls)
  • Conexiones a IP raw: uso de herramientas de conexion directa (netcat, socat, telnet) hacia direcciones IP sin nombre de dominio

Exfiltracion de datos (CRITICAL)

Detecta cadenas de comandos que combinan archivado (tar, zip, 7z) con subida por red, y comandos de envio de ficheros sensibles mediante upload HTTP.

Criptomineria (CRITICAL)

Detecta binarios de mineria conocidos, URLs de pools de minado, y patrones asociados a criptomineros embebidos.

Inyeccion de prompts (HIGH)

Frases de manipulacion embebidas en argumentos de herramientas, como instrucciones para que el agente descarte su contexto previo, actue con privilegios elevados, desactive mecanismos de seguridad u oculte acciones al usuario.

Auto-proteccion (CRITICAL)

El plugin se protege a si mismo: bloquea cualquier intento del agente de escribir en los ficheros de configuracion de seguridad (allowlist, IOCs, threats database). Esta proteccion opera en doble capa (TypeScript + Python) y no puede ser desactivada desde dentro de OpenCode.

Instalacion

Requisitos previos

  • OpenCode instalado y funcional
  • Python 3 disponible en el sistema
  • Git para clonar el repositorio

Paso 1: clonar el repositorio

BASH
git clone https://github.com/red-orbita/opencode-security-agent.git
cd opencode-security-agent

Paso 2: instalar el plugin

El repositorio incluye un script de instalacion que configura tanto el plugin runtime como el skill de analisis estatico.

Instalacion global (recomendado — protege todos tus proyectos):

BASH
bash scripts/install.sh --user

Instalacion por proyecto (solo protege el proyecto actual):

BASH
bash scripts/install.sh --project

Que hace el instalador

El script install.sh realiza estas acciones:

  1. Copia security-agent.ts al directorio de plugins de OpenCode:
  • Global: ~/.config/opencode/plugins/security-agent.ts
  • Proyecto: .opencode/plugins/security-agent.ts
  1. Copia sentinel_preflight.py junto al plugin para que pueda ser invocado
  1. Copia iocs.json (la libreria de indicadores de compromiso) a la ubicacion correcta
  1. Copia el skill SKILL.md al directorio de skills:
  • Global: ~/.config/opencode/skills/security-agent/SKILL.md
  • Proyecto: .opencode/skills/security-agent/SKILL.md

Paso 3: verificar la instalacion

Reinicia OpenCode y prueba pidiendo al agente que lea un fichero de claves SSH o que ejecute un comando que referencie una API key. Si el plugin esta activo, veras un mensaje de bloqueo con la razon y una sugerencia de allowlist:

CODE
OpenCode Security Agent blocked a bash call.
Reason: [CRITICAL] sensitive path: ~/.ssh/
If this is a false positive, ask the human to manually add
an exception to .security/sentinel-allowlist.json
(this file cannot be edited by the agent).
Suggested exception (for the human to add manually):
  Add to "paths": ["~/.ssh/id_rsa"]
  Then re-run the operation.

Como funciona el plugin (detalle tecnico)

security-agent.ts

El plugin de OpenCode es un modulo TypeScript que exporta una funcion plugin. Usa el hook tool.execute.before que OpenCode ejecuta antes de cada llamada a herramienta:

TYPESCRIPT
export const SecurityAgent = async ({ project, client, $ }) => {
  return {
    "tool.execute.before": async (input, output) => {
      // Serializa los argumentos de la herramienta
      // Invoca sentinel_preflight.py con el JSON
      // Si el veredicto es "deny", lanza un Error
      // Si es "allow", no hace nada (la herramienta se ejecuta)
    },
  }
}

Puntos clave del diseno:

  • No modifica argumentos: solo inspecciona y bloquea o permite
  • Sin estado: cada llamada es evaluada de forma independiente
  • Sin red: toda la evaluacion es local
  • Sin LLM: no consume tokens

sentinel_preflight.py

El script Python es el motor de deteccion. Recibe un JSON con la herramienta y sus argumentos, y evalua contra:

  1. Patrones de rutas sensibles — expresiones regulares contra paths
  2. Variables de entorno — deteccion de referencias a secretos en comandos
  3. IOCs de dominios — comparacion contra la libreria de dominios maliciosos
  4. Patrones de shell — deteccion de cadenas de exfiltracion y reverse shells
  5. Frases de inyeccion — deteccion de prompt injection en argumentos

El veredicto se devuelve como JSON a security-agent.ts:

JSON
{
  "decision": "deny",
  "reason": "[CRITICAL] sensitive path detected",
  "tool": "bash",
  "timestamp": "2026-04-22T10:30:00Z"
}

iocs.json

La libreria de indicadores de compromiso contiene dominios maliciosos confirmados de incidentes reales (nunca overrideables), patrones de rutas sensibles, patrones de variables de entorno y expresiones regulares para detectar comandos peligrosos.

Los dominios maliciosos confirmados nunca son overrideables — ni siquiera con allowlist. Esto es por diseno.

Integracion con OpenSpec

Si tu proyecto usa OpenSpec (como Red Orbita), el Security Agent se integra de forma natural en el flujo de trabajo.

El skill como capa de auditoria

El Security Agent incluye un skill (SKILL.md) que OpenCode puede cargar bajo demanda. Cuando se carga, el agente puede:

  1. Escanear skills y MCPs instalados contra bases de datos de vulnerabilidades
  2. Analizar coherencia — verificar que las acciones de cada skill corresponden con su proposito declarado
  3. Generar reportes de seguridad en .security/reports/
  4. Mantener una base de datos local de amenazas en .security/mcp-sentinel-threats.json

Flujo combinado OpenSpec + Security Agent

CODE
+----------------+    +----------------+    +--------------------+
|  Editar post   |--->|  Pre-commit    |--->|  Security Agent    |
|  o config      |    |  OpenSpec      |    |  (runtime plugin)  |
+----------------+    |  validates     |    |  blocks dangerous  |
                      |  frontmatter   |    |  tool calls        |
                      +-------+--------+    +---------+----------+
                              |                       |
                      +-------v--------+    +---------v----------+
                      |  Pre-deploy    |    |  Static scan       |
                      |  validate:     |    |  (skill invoked    |
                      |  deploy        |    |   on demand)       |
                      +----------------+    +--------------------+

OpenSpec valida la estructura y contenido (schemas, catalogos, headers). El Security Agent protege el runtime (que comandos se ejecutan, que ficheros se acceden, que conexiones se hacen).

Son capas complementarias:

CapaQue protegeCuando
OpenSpec schemasFrontmatter de posts, estructuraPre-commit, pre-deploy
OpenSpec policiesHeaders HTTP, CSP, SEOPre-deploy
Security Agent (runtime)Llamadas a herramientas del agente IACada interaccion
Security Agent (scan)Skills/MCPs instaladosBajo demanda

Configuracion en opencode.json

Para proyectos que ya usan OpenSpec, no necesitas cambiar nada en opencode.json. El plugin se carga automaticamente desde el directorio de plugins y el skill se descubre desde el directorio de skills.

Si necesitas configurar permisos especificos para el skill:

JSON
{
  "$schema": "https://opencode.ai/config.json",
  "permission": {
    "skill": {
      "security-agent": "allow"
    }
  }
}

Gestion de falsos positivos

El plugin puede bloquear acciones legitimas. Por ejemplo, durante la redaccion de este mismo post, el Security Agent bloqueo la escritura del fichero porque el contenido del articulo mencionaba rutas sensibles y patrones de exfiltracion como ejemplos educativos. Este es exactamente el tipo de situacion donde necesitas el sistema de allowlist — y donde el agente debe pedir autorizacion al humano.

Regla fundamental: el agente no puede editar el allowlist

El allowlist esta protegido por la auto-proteccion del plugin. Ningun tool del agente (write, edit, bash) puede modificar el fichero sentinel-allowlist.json. Cuando el agente encuentra un bloqueo que considera un falso positivo, debe:

  1. Informar al humano del bloqueo y la razon
  2. Sugerir la excepcion concreta que el humano debe anadir
  3. Esperar a que el humano edite el fichero manualmente fuera de OpenCode
  4. Reintentar la operacion

Este diseno garantiza que el humano siempre esta en el loop de las decisiones de seguridad.

Como funciona el allowlist

Existen dos niveles de allowlist, por proyecto y global:

  • Por proyecto: .security/sentinel-allowlist.json en la raiz del repositorio
  • Global: fichero sentinel-allowlist.json en el directorio de configuracion de OpenCode (~/.config/opencode/)

Estructura del allowlist

El fichero JSON acepta tres campos:

JSON
{
  "paths": [],
  "domains": [],
  "commands": []
}
CampoTipoDescripcion
pathsarray de stringsRutas absolutas de ficheros que el agente puede leer/escribir sin restriccion
domainsarray de stringsDominios que el agente puede contactar (ej: APIs internas de tu empresa)
commandsarray de stringsComandos especificos permitidos que de otro modo serian bloqueados

Ejemplo: permitir escritura de un fichero de contenido

Si el plugin bloquea la escritura de un post porque su contenido menciona rutas sensibles como ejemplo educativo, anade el path del fichero al allowlist:

JSON
{
  "paths": [
    "/ruta/absoluta/al/proyecto/content/mi-post.md"
  ],
  "domains": [],
  "commands": []
}

Ejemplo: permitir acceso a una API interna

Si tu flujo de trabajo necesita conectar con un servicio interno que el plugin bloquea:

JSON
{
  "paths": [],
  "domains": [
    "api.miempresa.internal",
    "registry.miempresa.internal"
  ],
  "commands": []
}

Ejemplo: allowlist combinado

Un allowlist tipico para un proyecto de desarrollo:

JSON
{
  "paths": [
    "/home/dev/proyecto/content/post-sobre-seguridad.md",
    "/home/dev/proyecto/docs/guia-de-seguridad.md"
  ],
  "domains": [
    "api.miempresa.internal"
  ],
  "commands": [
    "make deploy-staging"
  ]
}

Regla critica sobre dominios maliciosos

Los dominios que estan en la lista de IOCs confirmados de incidentes reales nunca pueden ser overrideados mediante allowlist. Esto es una decision de diseno deliberada: si un dominio ha sido verificado como malicioso en un incidente real, no hay excepcion posible. Esto previene que un skill comprometido manipule al agente para que anade el dominio a la allowlist.

Buenas practicas para allowlists

  1. Se especifico: usa rutas absolutas completas, no patrones amplios
  2. Documenta el motivo: anade un comentario en el commit explicando por que se anadio la excepcion
  3. Revisa periodicamente: elimina entradas que ya no sean necesarias
  4. No commits al repo (opcional): puedes anadir .security/sentinel-allowlist.json a .gitignore si prefieres que cada desarrollador gestione su propia allowlist
  5. Prefiere project-level: usa el allowlist por proyecto antes que el global para limitar el alcance
  6. Edita siempre fuera de OpenCode: usa tu editor de texto, vim, nano o cualquier herramienta externa. El agente no puede ni debe modificar este fichero

Desinstalacion

Si necesitas desinstalar el plugin:

BASH
# Global
bash scripts/uninstall.sh --user

# Por proyecto
bash scripts/uninstall.sh --project

El desinstalador elimina el plugin, el script Python, los IOCs y el skill de los directorios correspondientes.

Cuando usar cada capa

NecesidadSolucion
Proteger tu maquina de skills maliciosos en tiempo realPlugin runtime (v2)
Auditar skills antes de instalarlosSkill de analisis estatico (v1)
Investigar un skill sospechosoModo de investigacion del skill
Validar que tus headers HTTP cumplen la specOpenSpec validate:deploy
Detectar secretos en el repositorioGitleaks (capa DevSecOps)
Todo lo anterior a la vezLas tres capas combinadas

Vulnerabilidad descubierta y resuelta: el agente podia saltarse sus propios filtros

Durante la redaccion de este mismo articulo descubrimos una vulnerabilidad real en el diseno inicial del plugin. El Security Agent bloqueaba la escritura del post porque el contenido mencionaba rutas sensibles, variables de entorno y patrones de shell como ejemplos educativos. Para poder escribir el fichero, el agente IA (OpenCode) creo un fichero .security/sentinel-allowlist.json con el path del post como excepcion y reintento la escritura — que esta vez fue permitida.

El problema era evidente: el agente pudo crear el allowlist y saltarse la proteccion sin ninguna intervencion humana.

Escenario de ataque

Un skill malicioso podria reproducir exactamente esta secuencia:

  1. Intentar una accion bloqueada (ej: leer claves SSH)
  2. Detectar el bloqueo del Security Agent
  3. Escribir .security/sentinel-allowlist.json con la excepcion necesaria
  4. Reintentar la accion — ahora pasa sin bloqueo
  5. Eliminar el allowlist para no dejar rastro

Esto anulaba completamente la capa de proteccion runtime. El allowlist, pensado como mecanismo de gestion de falsos positivos, se convertia en un vector de bypass.

Solucion implementada: doble capa de auto-proteccion

La vulnerabilidad fue corregida implementando auto-proteccion en ambas capas del plugin:

Capa 1 — TypeScript (security-agent.ts): Antes de pasar la llamada al motor Python, el plugin inspecciona si la herramienta (write, edit, bash) intenta escribir en ficheros protegidos. Si detecta un match, bloquea inmediatamente sin invocar a Python.

Capa 2 — Python (sentinel_preflight.py): Como segunda linea de defensa, el motor Python tambien verifica escrituras a ficheros protegidos antes de ejecutar cualquier otro check.

Los ficheros protegidos son:

  • sentinel-allowlist.json — el allowlist de excepciones
  • Cualquier .json dentro de .security/ — la base de datos de amenazas
  • iocs.json — la libreria de indicadores de compromiso

Nuevo flujo cuando hay un bloqueo

Cuando el agente necesita una excepcion en el allowlist, ya no puede resolverlo solo. El mensaje de bloqueo guia al humano con la excepcion concreta que debe anadir:

CODE
OpenCode Security Agent blocked a bash call.
Reason: [CRITICAL] sensitive path: ~/.ssh/
If this is a false positive, ask the human to manually add
an exception to .security/sentinel-allowlist.json
(this file cannot be edited by the agent).
Suggested exception (for the human to add manually):
  Add to "paths": ["~/.ssh/id_rsa"]
  Then re-run the operation.

El flujo queda:

  1. El agente intenta una accion → BLOCKED
  2. El agente comunica al humano: "necesito que agregues X al allowlist"
  3. El humano evalua si es legitimo y edita el fichero fuera de OpenCode
  4. El agente reintenta → ALLOWED

El humano siempre esta en el loop de decisiones de seguridad.

Verificacion en produccion

Durante la actualizacion de este mismo post comprobamos que la proteccion funciona. El agente no pudo escribir el allowlist cuando lo necesito — tuvo que pedir al humano que lo hiciera. Ironicamente, la mejor prueba de que el fix funciona es que este articulo necesito intervencion humana para poder ser escrito.

Estado actual

La vulnerabilidad fue reportada como issue #1 y corregida en la misma sesion. Los tests cubren 55 escenarios incluyendo 7 tests especificos de auto-proteccion.

Conclusion

La seguridad en el ecosistema de agentes IA no es opcional. El incidente de Postmark demostro que incluso skills de confianza pueden convertirse en vectores de ataque. OpenCode Security Agent anade una capa de proteccion que:

  • No requiere confianza ciega en los skills instalados
  • Funciona sin coste de tokens LLM
  • Bloquea antes de ejecutar, no despues de detectar
  • Se protege a si mismo — el agente no puede desactivar sus propias protecciones
  • Mantiene al humano en el loop — las excepciones requieren intervencion humana explicita
  • Se integra nativamente con el sistema de plugins de OpenCode
  • Complementa OpenSpec para una proteccion completa del flujo de desarrollo

El desarrollo de este plugin, incluyendo el descubrimiento y resolucion de la vulnerabilidad de auto-bypass, fue realizado integramente en una sesion de pair-programming con OpenCode. El propio agente descubrio el fallo, lo reporto como issue, implemento el fix con doble capa de proteccion, escribio 55 tests y actualizo este post — pidiendo permiso al humano cada vez que necesito una excepcion en el allowlist.

El codigo es open-source y esta disponible en GitHub. Contribuciones, reportes de bugs y nuevos IOCs son bienvenidos.

Comentarios