flawopen.com/Teardowns/cve-2026-90970-gitlab-ai-gateway-prompt-template-sandbox-escape
CVE-2026-90970: escape do sandbox de prompts do GitLab AI Gateway
Um fluxo especialmente elaborado do Duo Agent Platform podia escapar do sandbox de templates de prompt do AI Gateway e executar comandos. Operadores de Gateway próprio devem atualizar para a versão corrigida da linha GitLab correspondente.[1], [4]
Imagine uma pessoa preenchendo um formulário com base em registros de conversa. Ela deve copiar os campos aprovados como texto. Um template especialmente criado podia mandar essa pessoa usar o comando “restaurar” do arquivo, fazendo o aplicativo reconstruir objetos ativos a partir dos dados armazenados, em vez de apenas copiar palavras. Assim, o limite entre exibir dados e pedir ao aplicativo que os interprete era ultrapassado. O relato do pesquisador documenta o caminho específico com parse_raw() e pickle; o aviso do GitLab confirma a saída do sandbox e a execução de comandos, mas não descreve todas as etapas.[3], [6]
Conceitos Centrais e Termos
AI Gateway- Serviço GitLab que conecta recursos de IA a provedores de modelos; instalações próprias têm uma imagem Gateway com versão separada.[5], [1]
Prompt template- Template que combina instruções fixas com valores de runtime, como entradas do fluxo e contexto da conversa.[3], [6]
Mapeamentos de frameworks
- Referência CWE para a fraqueza descrita neste guia: CWE-1336
Análise de Causa Raiz
O aviso do GitLab confirma que, em certas condições, uma pessoa autenticada com acesso ao Duo Agent Platform podia usar uma configuração especial de fluxo para escapar do sandbox de templates de prompt e executar comandos arbitrários no AI Gateway. O problema é CWE-1336, CVSS 9.9. O aviso não detalha todas as condições nem a função vulnerável. Um commit público de 17/09/2026 adiciona PromptSandboxedEnvironment, descrito como impedindo o acesso a métodos em objetos vinculados, e reforça operações e filtros Jinja. Isso é coerente com limitar o que templates fazem com objetos de runtime, mas o commit não cita o CVE e as fontes públicas consultadas não o vinculam às imagens corrigidas. Trate-o como uma alteração pública de reforço separada; nenhuma fonte pública o vincula a este CVE ou às imagens corrigidas. O item público do GitLab nº 1850 descreve um caminho mais específico: um template Jinja chama parse_raw() em um HumanMessage do LangChain presente no histórico da conversa, acionando a desserialização pickle. O item atribui essa demonstração ao relato do pesquisador e informa que ela ocorreu no ambiente de staging do GitLab; o aviso do fornecedor não detalha todas as condições afetadas.[1], [3], [4], [2]
Matriz de versões corrigidas do AI Gateway auto-hospedado
| Implantação / linha de versão | Versão corrigida do AI Gateway | Ação do operador |
|---|---|---|
| GitLab auto-hospedado, linha 19.2 | 19.2.4 | Atualize para a tag correspondente da imagem auto-hospedada do Gateway e verifique a imagem implantada. |
| GitLab auto-hospedado, linha 19.3 | 19.3.2 | Atualize para a tag correspondente da imagem auto-hospedada do Gateway e verifique a imagem implantada. |
| GitLab auto-hospedado, linha 19.4 | 19.4.1 | Atualize para a tag correspondente da imagem auto-hospedada do Gateway e verifique a imagem implantada. |
| AI Gateway hospedado pelo GitLab | O GitLab afirma que já estava corrigido. | Confirme que a implantação usa o Gateway hospedado pelo GitLab; não aplique a orientação de imagem auto-hospedada a este caso. |
Fluxo de Ataque Passo a Paso
A execução do template cruza a fronteira do sandbox
O item público do GitLab nº 1850 relata que um template podia chamar parse_raw() em um HumanMessage do LangChain no histórico da conversa, acionando a desserialização pickle. Esse é o caminho documentado pelo pesquisador; não presuma que cobre todos os fluxos afetados nem que corresponde ao patch do fornecedor.[3]
Código-Fonte: Vulnerável vs. Seguro
O item 1850 do GitLab relata que o fluxo vulnerável expunha um objeto HumanMessage do LangChain ao Jinja, onde um template podia chamar parse_raw() e acionar a desserialização pickle. Este exemplo defensivo mantém objetos de mensagem fora do contexto do template, fornece somente texto e nega acesso a atributos chamáveis e métodos. Ele ilustra o limite que deve ser fechado; não é o patch lançado pelo GitLab. O commit público 89b72644 é uma alteração de segurança separada: seu título trata de gravações Jinja block-set, e nenhuma fonte pública o vincula à CVE-2026-90970 ou às imagens corrigidas. Operadores devem seguir as versões corrigidas indicadas pelo fornecedor.[3], [2], [6]
Trecho de origem: ai_gateway/prompts/base.py (formatador vulnerável relatado; defesa ilustrativa)
# Trecho do caminho vulnerável relatado no item de trabalho 1850 do 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
# Padrão de defesa ilustrativo; não é o patch do 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 Verificação de Segurança para Engenharia
- ✓Atualize o AI Gateway próprio para 19.2.4, 19.3.2 ou 19.4.1 na linha GitLab compatível e use a tag de imagem indicada pela documentação.[1], [5]
- ✓Identifique se o Gateway é próprio ou hospedado pelo GitLab. GitLab diz que GitLab.com, GitLab Dedicated e instâncias que usam Gateway hospedado já foram corrigidos.[1]
- ✓Não trate o sandbox de templates como isolamento completo quando recebe objetos ativos da aplicação.[6]
- ✓Passe aos templates apenas dados inertes; mantenha métodos, desserializadores, handles de arquivo/processo e objetos de serviço fora do contexto.[6], [2]
- ✓Restrinja atributos e chamadas, audite filtros que invocam métodos diretamente e teste a versão e configuração Jinja de produção.[3], [2], [6]
- ✓Adicione testes que rejeitem operações inseguras em objetos e preservem renderização normal de strings; não use payloads de comandos reais.[3], [6]
- ✓Não deduza uma campanha pública de exploração nem uma cadeia completa apenas pelo aviso. O NVD registra a avaliação SSVC do CISA Coordinator como “exploitation: none” em 02/10/2026; trate isso como um status datado, não como afirmação sobre atividade posterior.[1], [4]
Fontes
- GitLab AI Gateway Critical Patch Release: 19.2.4, 19.3.2, and 19.4.1
- Commit GitLab 89b72644 — reforço separado e sem vínculo confirmado: “fix(prompts): block Jinja block-set NSRef writes on non-namespace objects”
- GitLab AI Gateway work item 1850 (reporter account; reproduction material omitted)
- NVD: CVE-2026-90970
- GitLab Self-Hosted AI Gateway installation guide
- Jinja 3.1 Sandbox documentation