GHSA-m2cx-gpqf-qf74 Tekton Pipelines: HTTP Resolver Unbounded Response Body Read Enables Denial of Service via Memory Exhaustion
SummaryThe HTTP resolver's `FetchHttpResource` function calls `io.ReadAll(resp.Body)` with no response body size limit. Any tenant with permission to create TaskRuns or PipelineRuns that reference the HTTP resolver can point it at an attacker-controlled HTTP server that returns a very large response body within the 1-minute timeout window, causing the `tekton-pipelines-resolvers` pod to be OOM-killed by Kubernetes. Because all resolver types (Git, Hub, Bundle, Cluster, HTTP) run in the same pod, crashing this pod denies resolution service to the entire cluster. Repeated exploitation causes a sustained crash loop. The same vulnerable code path is reached by both the deprecated `pkg/resolution/resolver/http` and the current `pkg/remoteresolution/resolver/http` implementations.Details`pkg/resolution/resolver/http/resolver.go:279–307`:`makeHttpClient` sets `http.Client{Timeout: timeout}` where `timeout` defaults to 1 minute and is configurable via `fetch-timeout` in the `http-resolver-config` ConfigMap. The timeout bounds the duration of the entire request (including body read), which limits slow-drip attacks. However, it does not limit the total number of bytes allocated. A fast HTTP server can deliver multi-gigabyte responses well within the 1-minute window.The resolver deployment (`config/core/deployments/resolvers-deployment.yaml`) sets a 4 GiB memory limit on the `controller` container. A response of 4 GiB or larger delivered at wire speed will cause `io.ReadAll` to allocate 4 GiB, triggering an OOM-kill. With the default timeout of 60 seconds, a server delivering at 100 MB/s can supply 6 GB — well above the 4 GiB limit — before the timeout fires.The `remoteresolution` HTTP resolver (`pkg/remoteresolution/resolver/http/resolver.go:90`) delegates directly to the same `FetchHttpResource` function and is equally affected.PoC**Note:** On clusters where operators have set a higher `fetch-timeout` (e.g., `10m`), the attacker has more time to deliver a larger body, and the attack is more reliable. On clusters with tight memory limits on the resolver pod, a smaller payload suffices.Impact**Denial of Service**: OOM-kill of the `tekton-pipelines-resolvers` pod denies all resolution services cluster-wide until Kubernetes restarts the pod.**Crash loop amplification**: A tenant can submit multiple concurrent TaskRuns pointing to the attack server. Each in-flight resolution request accumulates memory independently in the same pod, reducing the payload size needed to reach the OOM threshold.**Blast radius**: Because all resolver types share a single pod, disrupting the HTTP resolver also disrupts unrelated users of the Git, Bundle, Cluster, and Hub resolvers. This is a cluster-wide availability impact achievable by a single namespace-level user.Recommended FixWrap `resp.Body` with `io.LimitReader` before passing to `io.ReadAll`. Add a configurable `max-body-size` option to the `http-resolver-config` ConfigMap with a sensible default (e.g., 50 MiB, which exceeds the size of any realistic pipeline YAML file):This fix must be applied to `FetchHttpResource` in `pkg/resolution/resolver/http/resolver.go`, which is shared by both the deprecated and current HTTP resolver implementations.
CVSS v3.1 score: 6.5