BuildNot yet confirmed elsewhere1 publisher2 min readPublished
Kestra skips authentication for any request path that ends in the string "configs"
Kestra's AuthenticationFilter skips authentication for any request path ending in "/configs", letting an unauthenticated attacker reach root remote code execution. Naming a flow "configs" is enough, because the REST API puts the flow name in the path. The bug is CVE-2026-49869.
The Engineer · Build desk

What happened
- The one exception the filter was meant to allow was the public config endpoint the setup screen reads before any login exists.
- The bypass reaches create, run and delete routes, and FlowController with ExecutionController carry the chain from a created flow to code execution.
- A credential-free PUT creates a flow whose single task runs shell commands, and Kestra returns HTTP 200 with no login attached.
- Kestra enables more than 80 script-execution plugins by default, among them shell, Python, Node, PowerShell and Go.
Why it matters
- exposure A network-reachable Kestra OSS instance on defaults can be driven by an unauthenticated caller, and the code it runs lands as root inside the worker container.
- constraint Every request passes through the one AuthenticationFilter, so a single wrong condition in it strips login from every route the filter fronts.
- capability No memory bug or second exploit is required: the auth bypass alone reaches code execution because script plugins are on and the worker is root by default.
Kestra schedules and runs workflows, and before anyone logs in it has one job to allow: let the setup screen read the server's base configuration so it knows how login and feature flags are set. [1][2] Basic Auth is the default, and every API request passes through a single filter; when the path is on the allow list the filter proceeds without authentication. [8][9] The flaw is tracked as CVE-2026-49869. [15]
A dev.to writeup traces the hole to a suffix test at line 49 of AuthenticationFilter.java. `request.getPath().endsWith("/configs")` answers whether the tail of a path matches the string `/configs`, which is exactly what it promises. [3] What the code needed to know was whether the request was the public config endpoint. [4] For the endpoint the check was written for, both questions get the same answer. For any other path with the same ending, they don't. [17]
The gap was easy to use because Kestra names resources from caller input. The route `/flows/{namespace}/{flowId}` drops the flow id straight into the path. [5] Set the id to `configs` and the flow's write routes end in `/configs` too, so `endsWith`, which cannot separate a config read from a flow write, waves them through. [6][18]
Creating the flow executes nothing on its own. In the proof-of-concept a POST to the matching executions path triggers the run, clears authentication the same way, and a worker picks the execution up. [11] FlowController and ExecutionController together carry it from a created flow to code execution. [16]
The worker container runs as `uid=0(root)` out of the box. [13] So the flow's single shell task runs as root, and one broken check becomes unauthenticated remote code execution. [14]
The two legitimate endpoints are `GET /api/v1/configs` and `GET /api/v1/{tenant}/configs`. [7] An equality check against those, path and method both, would have passed the setup screen and admitted nothing else.
What to watch
- Whether Kestra has shipped a fixed AuthenticationFilter and which released versions carry the vulnerable endsWith check.
- Whether the two-request proof-of-concept is being used against internet-exposed Kestra instances.
- Whether disabling the default root worker or the script-execution plugins gets published as an interim mitigation.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence50
- Adoption
- Insufficient
- Hype gap+12
- Incentives42
- Confidence40
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
Kestra is an open-source platform for defining, scheduling, and running workflows.
- [2]
Kestra needs exactly one endpoint reachable before login: a public configuration endpoint that the setup screen reads to learn how login and feature flags are configured.
- [3]
In AuthenticationFilter.java at line 49, the filter set isConfigEndpoint using request.getPath().endsWith("/configs").
- [4]
The code needed to answer whether a request was the public config endpoint, but instead answered whether the path ended in the string /configs.
- [5]
Kestra's REST API identifies resources by a caller-supplied name, for example the route /flows/{namespace}/{flowId}, which puts the flow id directly into the path.
- [6]
Naming a flow 'configs' makes nearly every route for that flow end in /configs, so the APIs that create, run and delete the flow all skip authentication.
- [7]
The intended exception was the config-read endpoints GET /api/v1/configs and GET /api/v1/{tenant}/configs.
- [8]
Kestra OSS defaults to Basic Auth, and every API request passes through a single filter, AuthenticationFilter, which decides whether a path is allowed through without authentication.
- [9]
When isConfigEndpoint or another allowed condition is true, the filter calls chain.proceed and authentication is skipped entirely.
- [10]
A single PUT request with no credentials creates a flow named configs whose task is of type io.kestra.plugin.scripts.shell.Commands, and the server returns HTTP 200.
- [11]
A second request, POST /api/v1/main/executions/tutorial/configs, triggers execution and bypasses authentication the same way.
- [12]
Kestra installs more than 80 script-execution plugins by default, including shell, Python, Node, PowerShell and Go.
- [13]
The Kestra worker process runs inside its container as uid=0(root) out of the box.
- [14]
One broken authentication check becomes unauthenticated remote code execution.
- [16]
The combination of FlowController and ExecutionController drives the chain all the way to remote code execution.
- [17]
The two questions agree on the one path they were meant to cover and disagree on every other path that shares the same ending.
- [18]
endsWith has no way to tell a config-read request from a flow-management request, and waves both through identically.
Sources
1 independent publisher whose own reporting we read for this story.
- dev.toCVE-2026-49869 Explained: How One String Comparison Hands Root to an Unauthenticated Attacker
1 article · October 11, 2026
Topics and entities
Follow any of these and your For You feed starts watching them — no settings page required.