joaovicdev
postartigoCVE-2026-17629

CVE-2026-17629: Rewriting a Langflow flow's SQL connection and query through run-time tweaksCVE-2026-17629: reescrevendo a conexão e a query SQL de um flow do Langflow por tweaks em tempo de execução

Langflow's run API let an API-key caller override a stored flow's node fields and only refused code fields, so the SQL component's connection string and query could be swapped for an arbitrary server-local file and arbitrary SQL without editing the flow.A API de execução do Langflow deixava um chamador com API key sobrescrever campos dos nós de um flow salvo e só recusava campos de código, então a string de conexão e a query do componente SQL podiam ser trocadas por um arquivo local qualquer do servidor e SQL arbitrário, sem editar o flow.

Disclosure: I reported this to IBM, who ship Langflow OSS, on 2026-07-09 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 Langflow and expose flows over the run API, upgrade; IBM lists no workaround.

Summary

CVE-2026-17629 is a high-severity flaw in Langflow OSS up to and including 1.10.3, which IBM files as SQL injection (CWE-89). It lets anyone holding an API key that can run a flow containing a SQL component rewrite that component's connection string and query at run time — pointing it at an arbitrary file on the server's disk and running whatever SQL they like — without ever editing the stored flow.

The mechanism is tweaks. Langflow's /api/v1/run endpoints accept a tweaks object that overrides field values on a flow's nodes for a single run, so an integration can say "this run, use temperature 0.2" without a new version of the flow. That's a deliberate feature, and Langflow knew it was dangerous: apply_tweaks() carries a guard whose stated intent is that tweaks "must never inject executable code or widen the code sandbox." The guard does exactly what it says, and only that. It refuses tweaks to code fields. A database URL is not a code field. A SQL query is not a code field. Both were written straight into the flow's template.

It's a variant of CVE-2025-68477, where an API-key caller pointed Langflow's API Request component at internal URLs: the same run-time override channel, landing on a sink nobody had put on the list.

It is worth saying up front what this is not. It is not unauthenticated, and on stock OSS it is not cross-user: the run API resolves flows by the key's owner, so the caller is overriding a flow they can already run. What it breaks is the line between running a flow and configuring one. The author decided which database the component talks to and which query it runs; the key holder gets to change both.

Affected versions

ProductAffectedFixed
Langflow OSS1.0.0 – 1.10.31.11.0

Affected on Windows, Linux and macOS alike — it's a logic flaw in the run path, not anything platform-specific.

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

Unlike its Langflow siblings there's only one score to read: the NVD and CVE.org records weren't live as I write this.

  • AV:N / AC:L / PR:L: one well-formed request to the run API, from a caller holding a valid API key. No superuser, no code-field tweak, no access to the host.
  • S:U: IBM keeps scope unchanged — the run happens inside Langflow's own authorization context, under the flow owner's identity.
  • C:H: the file-open leg. Any SQLite file the Langflow process can read is readable in full through the component.
  • I:L / A:L: the SQL leg — writes and DDL, DROP TABLE included, against whatever database the component ends up connected to.

IBM's label, CWE-89, is a fair name for the impact: the attacker's SQL runs. It's a slightly misleading name for the cause. Nothing is concatenated into a query here, and there's no string to break out of — the caller supplies the entire query and the entire connection string. The bug is that a run-time override channel was allowed to rewrite the component's configuration at all: CWE-15, External Control of System or Configuration Setting, with CWE-73 riding along for the file path. The distinction matters for the fix. Parameterising the query fixes nothing, because the attacker owns the query.

Background: tweaks, and who they're for

A Langflow flow is a graph of components, and each node carries a template: its fields and their values, as the flow author set them in the editor. When a flow is called over the API, the caller can send tweaks keyed by node id, and for that run only, the named fields take the caller's values instead of the author's:

a tweak, as intended
{
  "tweaks": {
    "OpenAIModel-k3v9q": { "temperature": 0.2 }
  }
}

That makes tweaks an authorization surface, whether or not anyone calls it one. The person who authors a flow and the thing that runs it are often not the same principal: a flow is built in the editor, then its API key goes into a frontend, a CI job, a partner integration, a teammate's script. Whoever holds the key can set fields on any node, for any run. The author's configuration is only as fixed as the tweaks guard makes it — and the guard only knew about code.

Root cause

The guard only covers code

apply_tweaks() walks each tweak and refuses it under exactly three conditions:

src/lfx/src/lfx/processing/process.py — the tweaks guard (main, before the fix)
if (
    field_type == "code"
    or tweak_name == "code"
    or (is_code_exec_component and tweak_name in CODE_EXECUTION_FIELD_NAMES)
):
    logger.warning(f"Security: refusing to override code field {tweak_name!r} via tweaks.")
    continue

The guard had already been hardened once, from blocking only a field literally named code to catching code smuggled under alternative field names on code-execution components. But every condition is still about code. Anything else falls through to a verbatim write into the node's template — and the backend's copy of the same function, in src/backend/base/langflow/processing/process.py, has the same guard and the same gap:

process.py — everything else is written as-is
else:
    key = "file_path" if field_type == "file" else "value"
    template_data[tweak_name][key] = tweak_value

SQLComponent's fields fail every condition

SQLComponent (lfx.components.data_source.sql_executor) is a data-source component, not a code-execution one, and its two dangerous inputs are ordinary text inputs:

Guard conditiondatabase_urlquery
field_type == "code"no — a MessageTextInput, serialized as "str"no — a MultilineInput, serialized as "str"
tweak_name == "code"nono
code-execution component and sandbox fieldno — SQLComponent isn't in CODE_EXECUTION_COMPONENT_TYPESno — neither name is in CODE_EXECUTION_FIELD_NAMES

Three Falses, so both values go straight into the template.

The sinks trust their inputs

From the template, the strings become the component's parameters with no further sanitisation, and the component hands them directly to the database layer:

sql_executor.py — the two sink calls (abridged)
# maybe_create_db(): the connection string becomes an engine, unchecked
SQLDatabase.from_uri(self.database_url, engine_args=...)
 
# build_component(): the query string is executed as given
self.db.run(self.query, ...)

from_uri has no scheme allowlist and no path restriction, and sqlite:////etc/passwd is a perfectly valid SQLAlchemy URL: open the file at /etc/passwd as a SQLite database. Langflow does ship database-URL sanitisers (lfx/utils/util_strings.py), but they validate the server's own database configuration and are never applied to this component field. And db.run executes whatever it is handed — unparameterised by design, since the field is the query.

Exploitation

The preconditions are short:

  • A valid API key whose owner can run a flow that contains a SQLComponent (the PR:L in the vector). In OSS the run endpoints resolve the flow by the key's owner, so a key from another account gets a 404.
  • The node id of that component, e.g. SQLComponent-abc123 — the handle tweaks are keyed by. The node's display name works too.

Then one request:

the request
POST /api/v1/run/<flow-id> HTTP/1.1
x-api-key: <caller-api-key>
Content-Type: application/json
 
{
  "output_type": "text",
  "tweaks": {
    "SQLComponent-abc123": {
      "database_url": "sqlite:////etc/passwd",
      "query": "SELECT name FROM sqlite_master"
    }
  }
}

The same tweaks work against /api/v1/run/advanced/<flow>. Three things follow:

  1. Arbitrary file open. Pointed at a file that isn't a SQLite database, the open succeeds and the header parse fails: file is not a database. A path that doesn't exist fails one step earlier, with unable to open database file. That difference is a reliable oracle for whether any path on the server exists and is readable by the Langflow process.
  2. File read. Pointed at a real SQLite file — Langflow's own langflow.db on a SQLite-backed install, or any other database file the process can read — a matching SELECT returns its rows.
  3. Arbitrary SQL. The query tweak runs as given, DDL included. Tweak only the query and it runs against the author's configured database with the author's credentials. On a networked engine — the author's Postgres or SQL Server, or another one the Langflow host can reach — the query body is that engine's whole server-side surface: pg_read_file, COPY … FROM, xp_cmdshell, ATTACH DATABASE.

I reproduced the chain in a network-isolated (--network none) container against main, running the real process_tweaks/apply_tweaks and the real SQLComponent.build_component(). There were two fixtures: secret.db, with a table holding the token SUPER_SECRET_TOKEN_XYZ, and ddl_test.db, with a table t:

reproduction output
[GAP] template["database_url"]["value"] == "sqlite:////etc/passwd"   (attacker string written)
[GAP] template["query"]["value"]        == "SELECT name FROM sqlite_master"   (attacker string written)
[GAP] apply_tweaks guard (process.py:186-190) did NOT fire for either field — PASS
[FILE-OPEN] build_component() -> "[{'token': 'SUPER_SECRET_TOKEN_XYZ'}]"  — PASS
[CTRL] /etc/passwd   -> (sqlite3.DatabaseError) file is not a database        (OPENED, header parse failed)
[CTRL] /nonexistent  -> (sqlite3.OperationalError) unable to open database file (open() failed)  — distinct → arbitrary file open PASS
[DDL] after DROP TABLE t via tweak -> sqlite_master has 0 rows for 't'  — PASS

Four independent checks: the guard didn't fire for either field; a seeded secret came back through the tweaked database_url; the /etc/passwd versus non-existent-path error differential proves an arbitrary file was opened as a database; and a DROP TABLE supplied through the query tweak took effect.

The boundary this crosses is the author/caller one — the same one CVE-2025-68477 crossed — so it's worth being explicit about when it doesn't matter. If you can edit the flow, you can already point its SQL component anywhere you like in the editor, and tweaks give you nothing new. In OSS a key only runs its owner's flows, so the caller is holding the author's own key; the point is that a key is something you hand out, and handing it out was supposed to grant "run this flow", not "reconfigure it". The stronger version — one account's key overriding another account's flow — needs an authorization plugin that enables cross-user flow fetches (supports_cross_user_fetch()), which the OSS pass-through doesn't. I didn't demonstrate that leg; on a deployment that registers such a plugin, this deserves re-rating upward.

Only run this against a deployment you own or are explicitly authorized to assess.

Fix

IBM's advisory only says to upgrade to 1.11.0; it doesn't describe the patch. What I recommended in the report was to stop keying the refusal on code-ness — refuse tweaks on sensitive non-code fields like database_url and query, or make them opt-in — and, as defence in depth, to give SQLComponent.database_url its own scheme and path allowlist so sqlite:/// can't open arbitrary local files.

Reading main as I write this, the first part is what landed, and then some. apply_tweaks() no longer decides refusals itself. It asks a single function, which applies a protected-field floor in every mode, and that floor now names fields per component:

src/lfx/src/lfx/utils/flow_validation.py on main (abridged)
PROTECTED_TWEAK_FIELDS_BY_COMPONENT = {
    "SQLComponent": {"database_url", "query"},
    # ...and a similar entry for the Bing Search component's bing_search_url
}

On top of that floor sits a deployment-level tweak policy: permissive, declared or off. Under declared, a flow marks the fields the API may set (api_editable) and everything else is refused; under off, no tweak is accepted. That second layer is the more durable half. A list of dangerous fields will always be one component behind — CVE-2025-68477 was the URL, this one was the SQL — and the fix that doesn't age is making the author declare what a caller is allowed to change.

Detection

If you want to know whether this was used against an instance before you upgraded:

  • /api/v1/run* requests whose tweaks set database_url or query on a SQLComponent node, if anything in front of Langflow records run-API bodies — especially sqlite:/// URLs with an absolute path, or queries carrying DDL (DROP, CREATE, ATTACH DATABASE) or engine file and command primitives (pg_read_file, COPY … TO/FROM, xp_cmdshell).
  • SQLite file is not a database errors from a SQL component run: someone opened a path that isn't a database as one.
  • The absence of a Security: refusing to override log line for such a tweak, which confirms it went through the gap instead of being refused.

Timeline

  1. 2026-07-09: report sent to IBM.
  2. 2026-08-05: CVE-2026-17629 published in an IBM bulletin covering Langflow secret handling and sensitive configuration access, alongside three other CVEs.
  3. 2026-08-24: IBM bulletin last modified.
  4. 1.11.0: Langflow OSS ships with the fix; this is the first fixed release.
  5. (pending): finder credit added in a later revision of the IBM advisory.
  6. 2026-10-30: this writeup.

References

Divulgação: reportei isto à IBM, que distribui o Langflow OSS, em 09/07/2026, 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 o Langflow e expõe flows pela API de execução, atualize; a IBM não lista workaround.

Resumo

A CVE-2026-17629 é uma falha de severidade alta no Langflow OSS até a 1.10.3 inclusive, que a IBM classifica como SQL injection (CWE-89). Ela permite que qualquer um com uma API key capaz de rodar um flow que contenha um componente SQL reescreva a string de conexão e a query desse componente em tempo de execução — apontando-o para um arquivo qualquer no disco do servidor e rodando o SQL que quiser — sem nunca editar o flow salvo.

O mecanismo são os tweaks. Os endpoints /api/v1/run do Langflow aceitam um objeto tweaks que sobrescreve valores de campos nos nós de um flow durante uma única execução, para que uma integração possa dizer "nesta execução, use temperature 0.2" sem criar uma nova versão do flow. É uma funcionalidade deliberada, e o Langflow sabia que era perigosa: o apply_tweaks() carrega uma guarda cuja intenção declarada é que tweaks "must never inject executable code or widen the code sandbox" (nunca devem injetar código executável nem alargar a sandbox de código). A guarda faz exatamente o que diz, e só isso. Ela recusa tweaks em campos de código. Uma URL de banco não é um campo de código. Uma query SQL não é um campo de código. As duas eram escritas direto no template do flow.

É uma variante da CVE-2025-68477, em que um chamador com API key apontava o componente API Request do Langflow para URLs internas: o mesmo canal de sobrescrita em tempo de execução, caindo num sink que ninguém tinha posto na lista.

Vale dizer logo de cara o que isto não é. Não é sem autenticação e, no OSS padrão, não é entre usuários: a API de execução resolve os flows pelo dono da key, então o chamador está sobrescrevendo um flow que ele já consegue rodar. O que se quebra é a linha entre rodar um flow e configurá-lo. O autor decidiu com qual banco o componente conversa e qual query ele roda; quem tem a key passa a poder mudar as duas coisas.

Versões afetadas

ProdutoAfetadasCorrigido
Langflow OSS1.0.0 – 1.10.31.11.0

Afeta Windows, Linux e macOS igualmente — é uma falha de lógica no caminho de execução, nada específico de plataforma.

FontePontuação baseVetor
IBM (CNA)7.6 (Alta)CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:L

Diferente das irmãs no Langflow, aqui só há uma pontuação para ler: os registros na NVD e na CVE.org ainda não estavam no ar quando escrevi isto.

  • AV:N / AC:L / PR:L: uma requisição bem formada para a API de execução, de um chamador com uma API key válida. Sem superusuário, sem tweak em campo de código, sem acesso ao host.
  • S:U: a IBM mantém o escopo inalterado — a execução acontece dentro do próprio contexto de autorização do Langflow, sob a identidade do dono do flow.
  • C:H: a perna da abertura de arquivo. Qualquer arquivo SQLite que o processo do Langflow consiga ler fica legível por inteiro pelo componente.
  • I:L / A:L: a perna do SQL — escritas e DDL, DROP TABLE incluído, contra o banco ao qual o componente acabar conectado.

O rótulo da IBM, CWE-89, é um nome justo para o impacto: o SQL do atacante roda. É um nome um pouco enganoso para a causa. Nada é concatenado numa query aqui, e não há string de onde escapar — o chamador fornece a query inteira e a string de conexão inteira. O bug é que um canal de sobrescrita em tempo de execução tinha permissão para reescrever a configuração do componente: CWE-15, External Control of System or Configuration Setting, com a CWE-73 de carona pelo caminho de arquivo. A distinção importa para a correção. Parametrizar a query não corrige nada, porque a query é do atacante.

Contexto: tweaks, e para quem eles servem

Um flow do Langflow é um grafo de componentes, e cada nó carrega um template: seus campos e os valores deles, do jeito que o autor do flow os definiu no editor. Quando um flow é chamado pela API, o chamador pode mandar tweaks indexados pelo id do nó, e só naquela execução os campos nomeados assumem os valores do chamador em vez dos do autor:

um tweak, como foi pensado
{
  "tweaks": {
    "OpenAIModel-k3v9q": { "temperature": 0.2 }
  }
}

Isso faz dos tweaks uma superfície de autorização, quer alguém chame assim ou não. Quem cria um flow e o que o roda muitas vezes não são o mesmo principal: o flow é montado no editor, depois a API key dele vai parar num frontend, num job de CI, numa integração de parceiro, no script de um colega. Quem tem a key pode definir campos em qualquer nó, em qualquer execução. A configuração do autor só é tão fixa quanto a guarda de tweaks a torna — e a guarda só conhecia código.

Causa raiz

A guarda só cobre código

O apply_tweaks() percorre cada tweak e o recusa em exatamente três condições:

src/lfx/src/lfx/processing/process.py — a guarda de tweaks (main, antes da correção)
if (
    field_type == "code"
    or tweak_name == "code"
    or (is_code_exec_component and tweak_name in CODE_EXECUTION_FIELD_NAMES)
):
    logger.warning(f"Security: refusing to override code field {tweak_name!r} via tweaks.")
    continue

A guarda já tinha sido endurecida uma vez, de bloquear só um campo chamado literalmente code para pegar código contrabandeado sob nomes de campo alternativos em componentes que executam código. Mas toda condição continua sendo sobre código. Todo o resto cai numa escrita literal no template do nó — e a cópia da mesma função no backend, em src/backend/base/langflow/processing/process.py, tem a mesma guarda e a mesma brecha:

process.py — todo o resto é escrito como veio
else:
    key = "file_path" if field_type == "file" else "value"
    template_data[tweak_name][key] = tweak_value

Os campos do SQLComponent falham em todas as condições

O SQLComponent (lfx.components.data_source.sql_executor) é um componente de fonte de dados, não de execução de código, e suas duas entradas perigosas são entradas de texto comuns:

Condição da guardadatabase_urlquery
field_type == "code"não — um MessageTextInput, serializado como "str"não — um MultilineInput, serializado como "str"
tweak_name == "code"nãonão
componente que executa código e campo de sandboxnão — o SQLComponent não está em CODE_EXECUTION_COMPONENT_TYPESnão — nenhum dos nomes está em CODE_EXECUTION_FIELD_NAMES

Três False, então os dois valores vão direto para o template.

Os sinks confiam nas entradas

Do template, as strings viram parâmetros do componente sem nenhuma sanitização adicional, e o componente as entrega direto à camada de banco:

sql_executor.py — as duas chamadas de sink (resumido)
# maybe_create_db(): the connection string becomes an engine, unchecked
SQLDatabase.from_uri(self.database_url, engine_args=...)
 
# build_component(): the query string is executed as given
self.db.run(self.query, ...)

O from_uri não tem allowlist de esquema nem restrição de caminho, e sqlite:////etc/passwd é uma URL SQLAlchemy perfeitamente válida: abra o arquivo em /etc/passwd como um banco SQLite. O Langflow tem, sim, sanitizadores de URL de banco (lfx/utils/util_strings.py), mas eles validam a configuração do banco do próprio servidor e nunca são aplicados a este campo do componente. E o db.run executa o que receber — sem parametrização por design, já que o campo é a query.

Exploração

As precondições são curtas:

  • Uma API key válida cujo dono consiga rodar um flow que contenha um SQLComponent (o PR:L do vetor). No OSS, os endpoints de execução resolvem o flow pelo dono da key, então uma key de outra conta recebe 404.
  • O id do nó desse componente, por exemplo SQLComponent-abc123 — a chave pela qual os tweaks são indexados. O display name do nó também funciona.

Depois, uma requisição:

a requisição
POST /api/v1/run/<flow-id> HTTP/1.1
x-api-key: <caller-api-key>
Content-Type: application/json
 
{
  "output_type": "text",
  "tweaks": {
    "SQLComponent-abc123": {
      "database_url": "sqlite:////etc/passwd",
      "query": "SELECT name FROM sqlite_master"
    }
  }
}

Os mesmos tweaks funcionam contra /api/v1/run/advanced/<flow>. Três coisas se seguem:

  1. Abertura de arquivo arbitrário. Apontado para um arquivo que não é um banco SQLite, o open funciona e o parse do cabeçalho falha: file is not a database. Um caminho que não existe falha um passo antes, com unable to open database file. Essa diferença é um oráculo confiável para saber se qualquer caminho no servidor existe e é legível pelo processo do Langflow.
  2. Leitura de arquivo. Apontado para um arquivo SQLite de verdade — o próprio langflow.db do Langflow numa instalação com SQLite, ou qualquer outro arquivo de banco que o processo consiga ler —, um SELECT compatível devolve as linhas dele.
  3. SQL arbitrário. O tweak de query roda como veio, DDL incluído. Mude só a query e ela roda contra o banco configurado pelo autor, com as credenciais do autor. Num engine de rede — o Postgres ou SQL Server do autor, ou outro que o host do Langflow alcance — o corpo da query é toda a superfície server-side desse engine: pg_read_file, COPY … FROM, xp_cmdshell, ATTACH DATABASE.

Reproduzi a cadeia num container isolado de rede (--network none) contra a main, rodando o process_tweaks/apply_tweaks reais e o SQLComponent.build_component() real. Havia duas fixtures: secret.db, com uma tabela guardando o token SUPER_SECRET_TOKEN_XYZ, e ddl_test.db, com uma tabela t:

saída da reprodução
[GAP] template["database_url"]["value"] == "sqlite:////etc/passwd"   (attacker string written)
[GAP] template["query"]["value"]        == "SELECT name FROM sqlite_master"   (attacker string written)
[GAP] apply_tweaks guard (process.py:186-190) did NOT fire for either field — PASS
[FILE-OPEN] build_component() -> "[{'token': 'SUPER_SECRET_TOKEN_XYZ'}]"  — PASS
[CTRL] /etc/passwd   -> (sqlite3.DatabaseError) file is not a database        (OPENED, header parse failed)
[CTRL] /nonexistent  -> (sqlite3.OperationalError) unable to open database file (open() failed)  — distinct → arbitrary file open PASS
[DDL] after DROP TABLE t via tweak -> sqlite_master has 0 rows for 't'  — PASS

Quatro verificações independentes: a guarda não disparou para nenhum dos dois campos; um segredo plantado voltou pelo database_url modificado; a diferença de erro entre /etc/passwd e um caminho inexistente prova que um arquivo arbitrário foi aberto como banco; e um DROP TABLE enviado pelo tweak de query teve efeito.

A fronteira que isto cruza é a de autor/chamador — a mesma que a CVE-2025-68477 cruzou —, então vale ser explícito sobre quando ela não importa. Se você pode editar o flow, já pode apontar o componente SQL dele para onde quiser no editor, e os tweaks não te dão nada de novo. No OSS uma key só roda os flows do próprio dono, então o chamador está segurando a key do próprio autor; a questão é que uma key é algo que se distribui, e distribuí-la deveria conceder "rodar este flow", não "reconfigurá-lo". A versão mais forte — a key de uma conta sobrescrevendo o flow de outra conta — precisa de um plugin de autorização que habilite buscas de flow entre usuários (supports_cross_user_fetch()), o que o pass-through do OSS não faz. Não demonstrei essa perna; numa implantação que registre um plugin assim, isto merece ser reavaliado para cima.

Só rode isto contra uma implantação que você possua ou esteja explicitamente autorizado a avaliar.

Correção

O advisory da IBM só diz para atualizar para a 1.11.0; não descreve o patch. O que recomendei no report foi parar de basear a recusa em ser ou não código — recusar tweaks em campos sensíveis que não são código, como database_url e query, ou torná-los opt-in — e, como defesa em profundidade, dar ao SQLComponent.database_url sua própria allowlist de esquema e caminho, para que sqlite:/// não abra arquivos locais arbitrários.

Lendo a main enquanto escrevo, a primeira parte foi o que entrou, e mais um pouco. O apply_tweaks() não decide mais as recusas sozinho. Ele pergunta a uma única função, que aplica um piso de campos protegidos em todos os modos, e esse piso agora nomeia campos por componente:

src/lfx/src/lfx/utils/flow_validation.py na main (resumido)
PROTECTED_TWEAK_FIELDS_BY_COMPONENT = {
    "SQLComponent": {"database_url", "query"},
    # ...and a similar entry for the Bing Search component's bing_search_url
}

Em cima desse piso fica uma política de tweaks no nível da implantação: permissive, declared ou off. Em declared, um flow marca os campos que a API pode definir (api_editable) e todo o resto é recusado; em off, nenhum tweak é aceito. Essa segunda camada é a metade mais durável. Uma lista de campos perigosos sempre vai estar um componente atrás — a CVE-2025-68477 foi a URL, esta foi o SQL —, e a correção que não envelhece é fazer o autor declarar o que um chamador tem permissão de mudar.

Detecção

Se você quer saber se isto foi usado contra uma instância antes de atualizar:

  • Requisições /api/v1/run* cujos tweaks definem database_url ou query num nó SQLComponent, se algo na frente do Langflow registrar os corpos da API de execução — especialmente URLs sqlite:/// com caminho absoluto, ou queries com DDL (DROP, CREATE, ATTACH DATABASE) ou primitivas de arquivo e comando do engine (pg_read_file, COPY … TO/FROM, xp_cmdshell).
  • Erros SQLite file is not a database vindos de uma execução do componente SQL: alguém abriu como banco um caminho que não é banco.
  • A ausência de uma linha de log Security: refusing to override para um tweak desses, o que confirma que ele passou pela brecha em vez de ser recusado.

Linha do tempo

  1. 09/07/2026: report enviado à IBM.
  2. 05/08/2026: CVE-2026-17629 publicada num boletim da IBM sobre tratamento de segredos e acesso a configuração sensível no Langflow, junto com outras três CVEs.
  3. 24/08/2026: última modificação do boletim da IBM.
  4. 1.11.0: o Langflow OSS sai com a correção; é a primeira release corrigida.
  5. (pendente): crédito de quem encontrou adicionado numa revisão posterior do advisory da IBM.
  6. 30/10/2026: este writeup.

Referências