FlawOpen

flawopen.com/Teardowns/cve-2026-90970-gitlab-ai-gateway-prompt-template-sandbox-escape

● CVE-2026-90970 · CVSS 9.9[4] · Crítica
Investigación · FlawOpen

CVE-2026-90970: escape del sandbox de prompts de GitLab AI Gateway

Un flujo de Duo Agent Platform especialmente preparado podía escapar del sandbox de plantillas de prompt de AI Gateway y ejecutar comandos. Quienes administren un Gateway propio deben actualizar a la versión corregida de su rama de GitLab.[1], [4]

Vector CVSSCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H[4]
💡 Explicación en Lenguaje Sencillo (ELI5)

Imagina a una persona que rellena un informe a partir de registros de conversación. Debe copiar los campos aprobados como texto. Una plantilla preparada podía indicarle que usara la opción «restaurar» del archivo, haciendo que la aplicación reconstruyera objetos activos a partir de los datos almacenados, en vez de limitarse a copiar palabras. Así se cruza la frontera entre mostrar datos y pedir a la aplicación que los interprete. El informe del investigador documenta la ruta concreta con parse_raw() y pickle; el aviso de GitLab confirma la salida del sandbox y la ejecución de comandos, pero no describe todos los pasos.[3], [6]

Conceptos Clave y Términos

AI Gateway
Servicio de GitLab que conecta funciones de IA con proveedores de modelos; la instalación propia usa una imagen Gateway con versión independiente.[5], [1]
Prompt template
Plantilla que combina instrucciones fijas con valores de ejecución, como entradas del flujo y contexto de conversación.[3], [6]
Sandbox escape
Uso de un comportamiento de un entorno restringido para cruzar el límite que debía imponer.[3], [6]
CWE-1336
Neutralización incorrecta de elementos especiales de un motor de plantillas; el registro CVE asigna esta debilidad.[4], [1]

Mapeos de marcos

  • Referencia CWE para la debilidad descrita en esta guía: CWE-1336

Análisis de Causa Raíz

GitLab confirma que, bajo ciertas condiciones, una persona autenticada con acceso a Duo Agent Platform podía usar una configuración de flujo especial para escapar del sandbox de plantillas de prompt y ejecutar comandos arbitrarios en AI Gateway. Es CWE-1336, CVSS 9.9. El aviso no detalla todas las condiciones ni la función vulnerable. Un commit público del 17/09/2026 añade PromptSandboxedEnvironment, descrito como una clase que impide acceder a métodos de objetos enlazados, y refuerza operaciones y filtros de Jinja. Esto concuerda con limitar las acciones de las plantillas sobre objetos de ejecución, pero el commit no cita el CVE y las fuentes revisadas no lo vinculan con las imágenes corregidas. Considéralo un cambio público de refuerzo independiente; ninguna fuente pública lo vincula con este CVE ni con las imágenes corregidas. El elemento público nº 1850 de GitLab describe una ruta más concreta: una plantilla Jinja llama a parse_raw() sobre un HumanMessage de LangChain del historial de conversación y activa la deserialización pickle. El elemento atribuye esta demostración al informe del investigador y dice que ocurrió en el entorno de staging de GitLab; el aviso del proveedor no detalla todas las condiciones afectadas.[1], [3], [4], [2]

Matriz de versiones corregidas del AI Gateway autogestionado

Despliegue / rama de versiónVersión corregida de AI GatewayAcción del operador
GitLab autogestionado, rama 19.219.2.4Actualiza a la etiqueta correspondiente de la imagen autogestionada de Gateway y verifica la imagen desplegada.
GitLab autogestionado, rama 19.319.3.2Actualiza a la etiqueta correspondiente de la imagen autogestionada de Gateway y verifica la imagen desplegada.
GitLab autogestionado, rama 19.419.4.1Actualiza a la etiqueta correspondiente de la imagen autogestionada de Gateway y verifica la imagen desplegada.
AI Gateway alojado por GitLabGitLab afirma que ya estaba corregido.Confirma que el despliegue usa el Gateway alojado por GitLab; no apliques aquí las instrucciones para imágenes autogestionadas.

Flujo de Ataque Paso a Paso

Paso 1

Una persona puede configurar un flujo personalizado

El aviso exige una persona autenticada con acceso a Duo Agent Platform. No especifica un rol concreto ni todas las capacidades necesarias.[1], [3]

Paso 2

El flujo entrega una configuración de plantilla preparada

Una configuración preparada llega al sandbox de plantillas de prompt. El aviso público no ofrece una reproducción segura completa ni la expresión exacta.[1], [3]

Paso 3

La plantilla cruza el límite del sandbox

El elemento público nº 1850 de GitLab informa que una plantilla podía llamar a parse_raw() sobre un HumanMessage de LangChain del historial de conversación y activar la deserialización pickle. Esta es la ruta documentada por el investigador; no supongas que cubre todos los flujos afectados ni que representa el parche del proveedor.[3]

Paso 4

Pueden ejecutarse comandos en el Gateway

El impacto confirmado es la ejecución de comandos arbitrarios en AI Gateway, en el servidor, no solo un fallo del modelo al seguir instrucciones.[1], [3]

Código Fuente: Vulnerable vs. Seguro

El elemento 1850 de GitLab informa que el flujo vulnerable exponía a Jinja un objeto HumanMessage de LangChain, sobre el que una plantilla podía invocar parse_raw() y activar la deserialización pickle. Este ejemplo defensivo mantiene los objetos de mensaje fuera del contexto de la plantilla, pasa solo texto y deniega el acceso a atributos invocables y métodos. Ilustra el límite que debe cerrarse; no es el parche publicado por GitLab. El commit público 89b72644 es un cambio de seguridad independiente: su título trata de escrituras Jinja block-set y ninguna fuente pública lo vincula con CVE-2026-90970 ni con las imágenes corregidas. Los operadores deben seguir las versiones corregidas indicadas por el proveedor.[3], [2], [6]

Fragmento de origen: ai_gateway/prompts/base.py (formateador vulnerable descrito; diseño defensivo ilustrativo)

IMPLEMENTACIÓN VULNERABLE
# Fragmento de la ruta vulnerable descrita en el elemento de trabajo 1850 de GitLab.
from jinja2 import PackageLoader
from jinja2.sandbox import SandboxedEnvironment
from langchain_core.prompts.string import DEFAULT_FORMATTER_MAPPING
from typing import Any

jinja_loader = PackageLoader("ai_gateway.prompts", "definitions")
jinja_env = SandboxedEnvironment(loader=jinja_loader)

def jinja2_formatter(template: str, /, **kwargs: Any) -> str:
    return jinja_env.from_string(template).render(**kwargs)

DEFAULT_FORMATTER_MAPPING["jinja2"] = jinja2_formatter
PARCHE SEGURO Y ROBUSTO
# Patrón de defensa ilustrativo; no es el parche de GitLab para este CVE.
from typing import Any
from jinja2 import StrictUndefined
from jinja2.sandbox import ImmutableSandboxedEnvironment

class DataOnlyPromptEnvironment(ImmutableSandboxedEnvironment):
    def is_safe_attribute(self, obj: Any, attr: str, value: Any) -> bool:
        if callable(value):
            return False
        return super().is_safe_attribute(obj, attr, value)

    def is_safe_callable(self, obj: Any) -> bool:
        return False

def render_prompt(template: str, history: list[Any]) -> str:
    safe_history = []
    for message in history:
        content = message.content
        if not isinstance(content, str):
            raise TypeError("prompt history must contain text only")
        safe_history.append({"content": content})

    env = DataOnlyPromptEnvironment(undefined=StrictUndefined)
    return env.from_string(template).render(history=safe_history)

Lista de Verificación de Seguridad para Ingeniería

Fuentes