flawopen.com/Teardowns/cve-2026-104873-langgraph-sdk-auth-action-bypass
LangGraph CVE-2026-104873: falha de autorização
LangGraph SDK 0.1.45–0.4.3 ignorava filtros de ação em três decorators Python. Atualize para 0.4.4.[1]
Imagine uma recepção instruída a receber só entregas, mas o sistema envia todos os visitantes para lá. Se ela só verifica quem entrou no prédio, alguém pode chegar a salas que outro balcão manteria trancadas. O SDK afetado ignorava actions= ao registrar um handler de recurso, que podia receber leituras, alterações ou exclusões. O risco depende de o handler não verificar as permissões necessárias.[1]
Conceitos Centrais e Termos
CWE-863- Autorização incorreta: a verificação não aplica a permissão esperada.[1]
Mapeamentos de frameworks
- Referência CWE para a fraqueza descrita neste guia: CWE-863
- OWASP API Security Top 10 — API5:2023 Autorização Quebrada em Nível de Função
Análise de Causa Raiz
Nas versões 0.1.45–0.4.3, os três decorators afetados aceitavam actions=, mas registravam o handler no wildcard * do recurso. LangGraph podia escolhê-lo antes do fallback geral. Se o handler só autorizasse a ação prevista, podia receber outras ações sem as verificações do fallback. Acesso a recursos alheios exigia que a aplicação dependesse dessas verificações e que o handler não validasse ação e propriedade. A versão 0.4.4 valida a lista e registra cada ação separadamente; sem actions=, mantém o wildcard intencionalmente. O advisory atribui CWE-863 e CVSS 4.0 7.6 High. É uma falha condicional à configuração, não um bypass sem autenticação nem um defeito de toda implantação LangGraph. Os decorators afetados são: @auth.on.threads / @auth.on.assistants / @auth.on.crons. Limite importante da correção: a mudança do SDK restringe os registros às ações declaradas, mas caminhos de autorização personalizada sem correspondência continuam permitidos se a aplicação não configurar um handler global de negação por padrão. O changelog do 0.4.4 informa que o langgraph-api 0.10+ alerta na inicialização sobre caminhos sem cobertura; trate o alerta como detecção, não como bloqueio.[1], [2], [5]
Fluxo de Ataque Passo a Paso
Código-Fonte: Vulnerável vs. Seguro
O trecho vulnerável mostra o comportamento anterior: _ResourceOn.__call__ usava * independentemente de actions=. A correção registra cada ação separadamente após validação. É um recorte, não uma substituição do módulo. Atualize e faça handlers verificarem ação e propriedade. Local do código-fonte: libs/sdk-py/langgraph_sdk/auth/__init__.py, method _ResourceOn.__call__.[1], [2]
Trecho de origem: Registro do handler conforme a ação
def __call__(self, fn=None, *, resources=None, actions=None):
if fn is not None:
_validate_handler(fn)
return typing.cast("_ActionHandler", _register_handler(
self.auth, self.resource, "*", fn
))
def decorator(handler):
_validate_handler(handler)
return typing.cast("_ActionHandler", _register_handler(
self.auth, self.resource, "*", handler
))
return decorator
def __call__(self, fn=None, *, resources=None, actions=None):
def decorator(handler):
_validate_handler(handler)
if resources is None:
resource_list = [self.resource]
elif isinstance(resources, str):
resource_list = [resources]
elif isinstance(resources, Sequence):
resource_list = list(resources)
else:
raise TypeError("resources must be a string or sequence of strings")
if resource_list != [self.resource]:
raise ValueError("use @auth.on(...) for other resources")
if actions is None:
action_list = ["*"]
elif isinstance(actions, str):
action_list = [actions]
elif isinstance(actions, Sequence):
action_list = list(actions)
else:
raise TypeError("actions must be a string or sequence of strings")
if not action_list:
raise ValueError("actions must not be empty")
if not all(isinstance(action, str) for action in action_list):
raise TypeError("actions must be strings")
valid_actions = {
value.action for value in vars(self).values()
if isinstance(value, _ResourceActionOn)
}
invalid_actions = (
sorted(set(action_list) - valid_actions) if actions is not None else []
)
if invalid_actions:
raise ValueError("invalid action for this resource")
if len(action_list) != len(set(action_list)):
raise ValueError("actions must not contain duplicates")
for action in action_list:
if (self.resource, action) in self.auth._handlers:
raise ValueError("handler already registered for action")
for action in action_list:
_register_handler(self.auth, self.resource, action, handler)
return handler
if fn is not None:
return decorator(typing.cast("_ActionHandler", fn))
return decorator
Lista de Verificação de Segurança para Engenharia
- ✓Atualize SDK 0.1.45–0.4.3 para 0.4.4+.[1], [3]
- ✓Busque os três decorators afetados com
actions=.[1], [2] - ✓Verifique ação e acesso ao ID exato do recurso.[1]
- ✓Teste ações declaradas e não declaradas, inclusive negativas.[2]
- ✓Teste acesso entre usuários a threads, assistants e crons.[1]
- ✓Rejeite listas duplicadas, vazias ou desconhecidas na inicialização.[2]
- ✓Não confunda login com autorização explícita.
- ✓Configure um handler global de negação por padrão para todo caminho de autorização personalizada que não seja coberto por handlers limitados a ações. O langgraph-api 0.10+ alerta sobre caminhos descobertos, mas um alerta na inicialização não bloqueia solicitações.[5], [1]