FlawOpen

flawopen.com/Teardowns/cve-2026-104873-langgraph-sdk-auth-action-bypass

● CVE-2026-104873 · CVSS v4 7.6[1] · Haute
Recherche · FlawOpen

LangGraph CVE-2026-104873 : contournement d’autorisation

LangGraph SDK 0.1.45–0.4.3 ignorait les filtres d’action de trois décorateurs Python. Passez à 0.4.4.[1]

Vecteur CVSSCVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N[1]
💡 Explication en Langage Simple (ELI5)

Imaginez l’accueil d’un immeuble chargé de recevoir uniquement les livraisons, alors que le système y envoie tous les visiteurs. S’il vérifie seulement l’entrée, une personne peut atteindre des pièces qu’un autre guichet aurait fermées. Le SDK ignorait actions= lors de l’enregistrement d’un gestionnaire de ressource, qui pouvait aussi recevoir lectures, modifications ou suppressions. Le risque dépend de l’absence de contrôle des permissions dans ce gestionnaire.[1]

Concepts Clés et Termes

Décorateur par ressource
Décorateur Python qui enregistre l’autorisation d’un type de ressource.[1], [2]
Filtre d’action
actions= sélectionne les opérations du gestionnaire.[1], [2]
Repli
Gestionnaire général utilisé sans correspondance ciblée.[1], [2]
CWE-863
Autorisation incorrecte : le contrôle n’applique pas la permission prévue.[1]

Correspondances de référentiels

Analyse de Cause Racine

Dans les versions 0.1.45–0.4.3, les trois décorateurs concernés acceptaient actions=, mais enregistraient le gestionnaire sous le joker *. LangGraph pouvait le choisir avant le repli général. S’il n’autorisait que l’action prévue, il pouvait recevoir les autres sans les contrôles de repli. L’accès aux ressources d’autrui exigeait que l’application dépende de ces contrôles et que le gestionnaire ne vérifie pas lui-même action et propriété. La version 0.4.4 valide la liste et enregistre chaque action séparément ; sans actions=, le joker est conservé volontairement. L’avis attribue CWE-863 et CVSS 4.0 7.6 High. Cette faille dépend de la configuration, sans être un contournement sans authentification ni un défaut de toutes les installations LangGraph. Les décorateurs concernés sont : @auth.on.threads / @auth.on.assistants / @auth.on.crons. Limite importante du correctif : le changement du SDK limite les enregistrements aux actions déclarées, mais les chemins d’autorisation personnalisée non couverts restent autorisés si l’application ne configure pas de gestionnaire global de refus par défaut. Le changelog 0.4.4 indique que langgraph-api 0.10+ signale au démarrage les chemins non couverts ; considérez cet avertissement comme une détection, pas comme une mesure d’application.[1], [2], [5]

Déroulement de l'Attaque Étape par Étape

Étape 1

Configuration

SDK 0.1.45–0.4.3 et décorateur concerné avec actions=.[1], [4]

Étape 2

Enregistrement

Un gestionnaire peut viser une seule action.[1]

Étape 3

Joker

Le SDK associe le gestionnaire à toutes les actions de la ressource.[1], [2]

Étape 4

Contrôle

La requête atteint un gestionnaire qui peut omettre action et propriété.[1]

Étape 5

Correction

Passez à 0.4.4 et testez action, repli et propriété.[1], [2], [3], [5]

Code Source : Vulnérable vs Sécurisé

L’extrait vulnérable montre l’ancien comportement : _ResourceOn.__call__ utilisait * quelle que soit la valeur de actions=. Le correctif valide et enregistre chaque action séparément. C’est un extrait, pas le remplacement du module. Mettez à niveau et faites vérifier action et propriété par les gestionnaires. Emplacement dans le code source : libs/sdk-py/langgraph_sdk/auth/__init__.py, method _ResourceOn.__call__.[1], [2]

Extrait source: Enregistrement du gestionnaire selon l’action

IMPLÉMENTATION VULNÉRABLE
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
PATCH SÉCURISÉ ET ROBUSTE
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

Liste de Contrôle de Sécurité pour l'Ingénierie

Sources