joaovicdev
postartigoCVE-2026-17624

CVE-2026-17624: Arbitrary code execution through unvalidated module imports in LangflowCVE-2026-17624: execução de código arbitrário por imports de módulo não validados no Langflow

Langflow screened custom-component code against a module allowlist but executed it during validation, and the import check missed enough of Python's import surface that an authenticated user could pull in anything and run code in-process.O Langflow filtrava o código de componentes customizados contra uma allowlist de módulos, mas o executava durante a validação — e a checagem de import deixava passar superfície suficiente para um usuário autenticado importar qualquer coisa e rodar código no processo.

Disclosure: I reported this to IBM, who ship Langflow OSS, as a coordinated disclosure with their security team; it is fixed in 1.11.0 and the CVE is public as of the 2026-08-05 advisory. The finder credit lands in a later revision of the advisory — this writeup goes up ahead of that. If you run a multi-user or hosted Langflow instance you should upgrade, there is no configuration-only workaround.

NVD detail page for CVE-2026-17624, showing the description, the CVSS 3.x panel with both the NVD 8.8 HIGH and the IBM CNA 8.5 HIGH vectors, and CWE-94 Improper Control of Generation of Code

Summary

CVE-2026-17624 is a high-severity code-execution flaw (CWE-94, Improper Control of Generation of Code) in Langflow OSS up to and including 1.10.3. It lets a low-privilege authenticated user run arbitrary Python in the Langflow backend process by way of the feature that is supposed to make custom components safe: import validation.

Langflow lets users build custom components — small Python classes you drop into a flow — and its Agentic Assistant can generate components for you. Code that arrives from either path is run through a validation step before it is trusted, and part of that step is meant to restrict which modules a component may import, so that a tenant can't simply import os and shell out.

The bug is that the import check does not cover enough of Python's import surface, and validation executes the code anyway. An authenticated user submits a component whose imports slip past the allowlist, Langflow runs it while "validating" it, and the attacker's code executes with the backend's privileges — before any human approves the component. The IBM bulletin's own title says it plainly: arbitrary code execution in component generation, validation, and custom component handling.

It is worth being precise about the precondition: this is not anonymous RCE. The caller has to be a logged-in user of the instance. On a single-user local Langflow that is nearly everyone who can reach it; on a shared or hosted multi-tenant deployment it is any tenant, and that is where it bites — a tenant turns "I can author a component in my own flow" into "I can run code as the server."

Affected versions

ProductAffectedFixed
Langflow OSS1.0.0 – 1.10.31.11.0

The CVE carries two CVSS 3.1 scores, and the gap between them is itself worth reading:

SourceBase scoreVector
IBM (CNA)8.5 (High)CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H
NVD (primary)8.8 (High)CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

They agree on the thing that matters — C:H/I:H/A:H, full read, write, and availability impact, because arbitrary code is arbitrary code — and on PR:L, an authenticated attacker. They differ on two axes:

  • AC:H vs AC:L: IBM treats the attack as higher-complexity (you have to craft a component whose imports evade the validator); NVD scores the access condition as low, since once you know the shape, reuse is trivial.
  • S:C vs S:U: IBM models a scope change — the code escapes the component "sandbox" to act as the host process — which is the honest reading of what this bug does. NVD keeps scope unchanged. The scope call is most of the 0.3-point difference.

Either way it lands in the high-8s, and either way the fix is the same: 1.11.0.

Background: why components are validated at all

Langflow's whole value proposition is running user-authored logic. A custom component is Python, and Python that the server imports and calls is Python that runs as the server. To let users write components without handing them a shell, Langflow screens the code before it is built — and the part of that screen this CVE is about is a module allowlist: a set of modules a component is permitted to import, with everything else rejected.

An allowlist on imports is a reasonable idea and a famously leaky one. The problem is that "which modules does this code import?" is not a question you can answer by reading import statements, because an import statement is only one of the ways Python pulls a module in. Miss the others and the allowlist is a suggestion.

Root cause

The public advisory is terse — "improper validation of module imports" — so the code below is illustrative, reconstructed from the advisory and Langflow's documented component model, not verbatim source. It shows the class of mistake the CVE names, not a specific patched line.

A natural, and wrong, way to enforce a module allowlist is to walk the AST and inspect the import nodes:

component validation (illustrative — reconstructed, not verbatim source)
ALLOWED_TOP_LEVEL = {"math", "json", "datetime", "typing", "langflow"}
 
def validate_component(code: str) -> None:
    tree = ast.parse(code)
    for node in ast.walk(tree):
        if isinstance(node, (ast.Import, ast.ImportFrom)):
            name = _module_of(node)
            if name.split(".")[0] not in ALLOWED_TOP_LEVEL:
                raise ValueError(f"import not allowed: {name}")
 
    # Validation then BUILDS the component — which imports its modules and
    # runs top-level code — so anything the walk above missed executes here,
    # in-process, before a human ever approves the generated component.
    build_component(code)

Two things make this exploitable, and both are necessary:

  1. The check only sees import and from … import nodes. Python reaches modules by other paths that leave no import statement to inspect — the __import__("os") builtin is always present and is a plain function call, not an import node; the same is true of any already-reachable attribute that hands back a module. An allowlist that greps the AST for imports never sees these.
  2. Validation runs the code. The screen isn't a static gate that stands between submission and execution — building the component to validate it is execution. So a payload that the walk didn't flag doesn't have to wait to be approved and run later; it runs during the check.

Put together, an authenticated user submits a component that imports nothing "disallowed" by the letter of the walk, yet reaches a forbidden module through a path the walk can't see, and the validation step executes it:

malicious component (illustrative)
# No `import os` for the allowlist to catch — __import__ is a builtin call,
# not an import statement, so the AST walk never flags it.
class Evil(CustomComponent):
    display_name = "Totally Normal"
 
    def build(self):
        __import__("os").system("id > /tmp/pwned")
        return "ok"

The fix in 1.11.0 is to stop trusting the shape of the source and to enforce the restriction where it can actually be enforced — closing the import paths the allowlist didn't account for, and not executing untrusted component code as part of validating it. The generic lesson is the usual one for CWE-94: you cannot sandbox arbitrary Python by pattern-matching its source. An allowlist that inspects import statements is defeated by every import mechanism that isn't an import statement, and a validator that runs the thing it is validating has already lost.

Exploitation

The clean threat model is any Langflow OSS deployment where the attacker can log in and author a component:

  • The attacker is a registered, authenticated user (the PR:L precondition). On a shared or hosted multi-tenant instance, that's any tenant.
  • The attacker can create or edit a custom component, or drive the Agentic Assistant to generate one — the normal, intended workflow.

From there the shape is: craft a component whose module access evades the import allowlist, submit it, and let Langflow's own validation run it.

the request (illustrative)
POST /api/v1/validate/code HTTP/1.1
Content-Type: application/json
Cookie: <attacker's authenticated session>
 
{ "code": "<component that reaches a forbidden module via a non-import path>" }

What follows is C:H/I:H/A:H: the payload runs as the Langflow backend process, which means read and write of anything that process can touch — other tenants' flows and data, stored provider credentials and API keys, environment, the filesystem — and, from there, whatever the host allows. The scope-change reading in IBM's vector is the point: the code was supposed to be confined to a component, and it isn't.

The components above are illustrative reconstructions built from the public advisory and Langflow's documented component model, not a capture against a live instance. Only run them against a deployment you own or are explicitly authorized to assess.

Timeline

  1. 2026-08-05: CVE-2026-17624 published; IBM bulletin posted and the NVD record goes live (last modified 2026-08-06).
  2. 1.11.0: Langflow OSS ships with the import validation hardened; this is the first fixed release.
  3. (pending): finder credit added in a later revision of the IBM advisory.
  4. 2026-10-19: this writeup.

References

Divulgação: reportei isto à IBM, que distribui o Langflow OSS, como uma divulgação coordenada com o time de segurança deles; está corrigido na 1.11.0 e a CVE é pública desde o advisory de 05/08/2026. O crédito de quem encontrou entra numa revisão posterior do advisory — este writeup sai antes disso. Se você roda uma instância multiusuário ou hospedada do Langflow, atualize: não existe workaround apenas por configuração.

Página de detalhe da NVD para a CVE-2026-17624, mostrando a descrição, o painel CVSS 3.x com os vetores da NVD (8.8 HIGH) e da IBM CNA (8.5 HIGH), e a CWE-94 Improper Control of Generation of Code

Resumo

A CVE-2026-17624 é uma falha de execução de código de severidade alta (CWE-94, Improper Control of Generation of Code) no Langflow OSS até a 1.10.3 inclusive. Ela permite que um usuário autenticado de baixo privilégio rode Python arbitrário no processo de backend do Langflow justamente pela funcionalidade que deveria tornar os componentes customizados seguros: a validação de imports.

O Langflow deixa os usuários criarem componentes customizados — pequenas classes Python que você encaixa num flow — e o Agentic Assistant pode gerar componentes para você. O código que chega por qualquer um desses caminhos passa por uma etapa de validação antes de ser confiado, e parte dessa etapa deveria restringir quais módulos um componente pode importar, para que um inquilino não possa simplesmente dar import os e sair pro shell.

O bug é que a checagem de import não cobre superfície suficiente dos imports do Python, e a validação executa o código mesmo assim. Um usuário autenticado submete um componente cujos imports escapam da allowlist, o Langflow o roda enquanto "valida", e o código do atacante executa com os privilégios do backend — antes de qualquer humano aprovar o componente. O próprio título do boletim da IBM diz na lata: execução de código arbitrário em geração, validação e tratamento de componentes customizados.

Vale ser preciso sobre a precondição: isto não é RCE anônimo. O chamador precisa ser um usuário logado da instância. Num Langflow local de usuário único isso é quase todo mundo que consegue alcançá-lo; numa implantação compartilhada ou hospedada multi-inquilino é qualquer inquilino, e é aí que dói — um inquilino transforma "posso criar um componente no meu próprio flow" em "posso rodar código como o servidor".

Versões afetadas

ProdutoAfetadasCorrigido
Langflow OSS1.0.0 – 1.10.31.11.0

A CVE carrega duas pontuações CVSS 3.1, e a distância entre elas já vale a leitura:

FontePontuação baseVetor
IBM (CNA)8.5 (Alta)CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H
NVD (primária)8.8 (Alta)CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Elas concordam no que importa — C:H/I:H/A:H, impacto total de leitura, escrita e disponibilidade, porque código arbitrário é código arbitrário — e em PR:L, um atacante autenticado. Divergem em dois eixos:

  • AC:H vs AC:L: a IBM trata o ataque como de complexidade mais alta (você precisa forjar um componente cujos imports driblem o validador); a NVD pontua a condição de acesso como baixa, já que, sabida a forma, reusar é trivial.
  • S:C vs S:U: a IBM modela uma mudança de escopo — o código escapa do "sandbox" do componente para agir como o processo do host —, que é a leitura honesta do que este bug faz. A NVD mantém o escopo inalterado. A decisão de escopo responde por quase toda a diferença de 0.3 ponto.

De todo jeito fica nos 8 e pouco, e de todo jeito a correção é a mesma: 1.11.0.

Contexto: por que os componentes são validados

A proposta de valor inteira do Langflow é rodar lógica escrita pelo usuário. Um componente customizado é Python, e Python que o servidor importa e chama é Python que roda como o servidor. Para deixar os usuários escreverem componentes sem entregar a eles um shell, o Langflow filtra o código antes de construí-lo — e a parte desse filtro que esta CVE ataca é uma allowlist de módulos: um conjunto de módulos que um componente tem permissão de importar, com todo o resto rejeitado.

Uma allowlist sobre imports é uma ideia razoável e famosamente furada. O problema é que "quais módulos este código importa?" não é uma pergunta que você responde lendo instruções import, porque uma instrução import é apenas uma das formas de o Python puxar um módulo. Deixe passar as outras e a allowlist vira sugestão.

Causa raiz

O advisory público é lacônico — "improper validation of module imports" — então o código abaixo é ilustrativo, reconstruído a partir do advisory e do modelo de componentes documentado do Langflow, não é o fonte literal. Ele mostra a classe de erro que a CVE nomeia, não uma linha específica corrigida.

Um jeito natural, e errado, de aplicar uma allowlist de módulos é percorrer a AST e inspecionar os nós de import:

validação de componente (ilustrativo — reconstruído, não é o fonte literal)
ALLOWED_TOP_LEVEL = {"math", "json", "datetime", "typing", "langflow"}
 
def validate_component(code: str) -> None:
    tree = ast.parse(code)
    for node in ast.walk(tree):
        if isinstance(node, (ast.Import, ast.ImportFrom)):
            name = _module_of(node)
            if name.split(".")[0] not in ALLOWED_TOP_LEVEL:
                raise ValueError(f"import not allowed: {name}")
 
    # A validação então CONSTRÓI o componente — o que importa seus módulos e
    # roda o código de nível superior —, então qualquer coisa que o walk acima
    # tenha deixado passar executa aqui, no processo, antes de um humano aprovar
    # o componente gerado.
    build_component(code)

Duas coisas tornam isto explorável, e as duas são necessárias:

  1. A checagem só enxerga nós import e from … import. O Python alcança módulos por outros caminhos que não deixam uma instrução de import para inspecionar — o builtin __import__("os") está sempre presente e é uma simples chamada de função, não um nó de import; o mesmo vale para qualquer atributo já alcançável que devolva um módulo. Uma allowlist que faz grep de imports na AST nunca vê isso.
  2. A validação roda o código. O filtro não é um portão estático entre a submissão e a execução — construir o componente para validá-lo é executá-lo. Então um payload que o walk não marcou não precisa esperar ser aprovado e rodado depois; ele roda durante a checagem.

Juntando, um usuário autenticado submete um componente que não importa nada "proibido" pela letra do walk e ainda assim alcança um módulo vetado por um caminho que o walk não consegue ver, e a etapa de validação o executa:

componente malicioso (ilustrativo)
# Nenhum `import os` para a allowlist pegar — __import__ é uma chamada de builtin,
# não uma instrução de import, então o walk na AST nunca o marca.
class Evil(CustomComponent):
    display_name = "Totally Normal"
 
    def build(self):
        __import__("os").system("id > /tmp/pwned")
        return "ok"

A correção na 1.11.0 é parar de confiar na forma do fonte e aplicar a restrição onde ela de fato pode ser aplicada — fechando os caminhos de import que a allowlist não considerava, e não executando código de componente não confiável como parte de validá-lo. A lição genérica é a de sempre para CWE-94: não dá para colocar Python arbitrário num sandbox fazendo pattern-matching no fonte dele. Uma allowlist que inspeciona instruções import é derrotada por todo mecanismo de import que não é uma instrução import, e um validador que roda a coisa que está validando já perdeu.

Exploração

O modelo de ameaça limpo é qualquer implantação do Langflow OSS onde o atacante consiga logar e criar um componente:

  • O atacante é um usuário registrado e autenticado (a precondição PR:L). Numa instância compartilhada ou hospedada multi-inquilino, isso é qualquer inquilino.
  • O atacante consegue criar ou editar um componente customizado, ou guiar o Agentic Assistant para gerar um — o fluxo normal e pretendido.

A partir daí a forma é: forjar um componente cujo acesso a módulos escape da allowlist de import, submetê-lo, e deixar a própria validação do Langflow rodá-lo.

a requisição (ilustrativo)
POST /api/v1/validate/code HTTP/1.1
Content-Type: application/json
Cookie: <sessão autenticada do atacante>
 
{ "code": "<componente que alcança um módulo vetado por um caminho que não é import>" }

O que se segue é C:H/I:H/A:H: o payload roda como o processo de backend do Langflow, o que significa leitura e escrita de qualquer coisa que esse processo alcance — flows e dados de outros inquilinos, credenciais de provedores e chaves de API armazenadas, ambiente, sistema de arquivos — e, a partir daí, o que o host permitir. A leitura de mudança de escopo no vetor da IBM é o ponto: o código devia estar confinado a um componente, e não está.

Os componentes acima são reconstruções ilustrativas montadas a partir do advisory público e do modelo de componentes documentado do Langflow, não uma captura contra uma instância viva. Só os rode contra uma implantação que você possua ou esteja explicitamente autorizado a avaliar.

Linha do tempo

  1. 05/08/2026: CVE-2026-17624 publicada; boletim da IBM postado e o registro na NVD vai ao ar (última modificação em 06/08/2026).
  2. 1.11.0: o Langflow OSS sai com a validação de imports endurecida; é a primeira release corrigida.
  3. (pendente): crédito de quem encontrou adicionado numa revisão posterior do advisory da IBM.
  4. 19/10/2026: este writeup.

Referências