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-118759

CVE-2026-118759: Cross-Tenant Pod Log Exposure via Authorization Bypass in Kubeflow Pipelines

Alon Barad
Alon Barad
Software Engineer

Oct 31, 2026·10 min read·4 visits

Executive Summary (TL;DR)

Kubeflow Pipelines prior to 2.18.0 fails to authorize requests to the run log streaming endpoint (`GET /apis/v1alpha1/runs/{run_id}/nodes/{node_id}/log`) and does not verify pod-to-run ownership, allowing unauthorized callers to stream execution logs from arbitrary pods across tenant namespaces using the API server's service account permissions.

Prior to version 2.18.0, the Kubeflow Pipelines API server contains a missing authorization and confused deputy vulnerability in its run log streaming endpoint. The HTTP handler for `GET /apis/v1alpha1/runs/{run_id}/nodes/{node_id}/log` fails to propagate incoming request headers to gRPC metadata and omits caller authorization checks against the requested run. Furthermore, the backend accepts the caller-supplied `node_id` parameter directly as a Kubernetes pod name without verifying that the pod belongs to the specified pipeline run. An attacker with network access to the API server who knows a valid run identifier can read live or archived execution logs from any pod containing a `main` container within the run's Kubernetes namespace.

Vulnerability Overview

Kubeflow Pipelines (KFP) is a platform for building and deploying portable, scalable machine learning workflows on Kubernetes. The KFP backend API server (ml-pipeline) exposes both gRPC and RESTful HTTP interfaces to manage pipeline definitions, experiments, runs, and execution artifacts. In multi-user deployments, pipeline runs are isolated within specific Kubernetes namespaces assigned to individual users or organizational tenants, with access governed by Kubernetes Role-Based Access Control (RBAC) and SubjectAccessReview (SAR) checks.

CVE-2026-118759 (GHSA-wg5q-x4j9-qm54) is a Missing Authorization (CWE-862) and Exposure of Sensitive Information (CWE-200) vulnerability in the KFP API server affecting all versions prior to 2.18.0. Specifically, the HTTP endpoint responsible for streaming execution logs from pipeline workflow steps—GET /apis/v1alpha1/runs/{run_id}/nodes/{node_id}/log—retrieves container logs without authenticating or authorizing the caller against the target run.

In addition to missing caller authorization, the endpoint acts as a confused deputy when querying the Kubernetes API for live container logs. The server uses the target run_id solely to resolve the target Kubernetes namespace, and then passes the caller-controlled node_id path parameter directly to the Kubernetes core client as the target pod name. Because the server does not verify whether the specified pod was actually created by the given pipeline run, an attacker who knows any valid run_id in a victim namespace can read the standard output and standard error streams of the main container of any arbitrary pod within that namespace.

Root Cause Analysis

The vulnerability stems from two distinct architectural and implementation flaws in how the KFP API server handles HTTP log streaming requests compared to standard gRPC-backed REST endpoints.

First, most RESTful endpoints in the KFP API server are generated via grpc-gateway, a reverse-proxy layer that translates incoming HTTP requests into gRPC requests. During this translation, grpc-gateway automatically extracts HTTP authentication and identity headers (such as Google Identity-Aware Proxy headers, bearer tokens, or Istio authentication headers) and populates them into the incoming gRPC metadata.MD context. Downstream service methods then extract this metadata from the context.Context object to perform Kubernetes SubjectAccessReview evaluations.

However, to support chunked HTTP log streaming directly to clients, the ReadRunLogV1 handler in backend/src/apiserver/server/run_log_server.go was registered directly onto the underlying Gorilla mux HTTP router, bypassing grpc-gateway entirely. As a consequence, r.Context() inside ReadRunLogV1 contained an empty gRPC metadata structure. Furthermore, RunLogServer did not invoke canAccessRun or any equivalent authorization check before processing the request; instead, it immediately wrote an HTTP 200 OK status header (w.WriteHeader(http.StatusOK)) and initiated log retrieval.

Second, a secondary logic flaw existed in the RBAC verb model for multi-user deployments configured with shared read access (MultiUserModeSharedReadAccess). When shared read access was enabled, the authorization engine automatically approved standard get and list verbs prior to issuing a Kubernetes SubjectAccessReview. Because log retrieval did not define a dedicated RBAC verb distinct from get, restricting log access independently of basic run metadata was not supported by the policy model.

Third, inside backend/src/apiserver/resource/resource_manager.go, the ReadLog and readRunLogFromPod functions failed to validate resource ownership. Once ReadLog looked up the runId in the database and extracted its Kubernetes namespace via getPodNamespace(run), it invoked r.k8sCoreClient.PodClient(namespace).GetLogs(nodeId, &logOptions) using the API server's own privileged Kubernetes ServiceAccount. The code assumed nodeId was a valid Argo workflow pod belonging to runId, without inspecting the pod's labels (pipelines.kubeflow.org/run_id) to confirm ownership.

Code Analysis

In versions prior to 2.18.0, backend/src/apiserver/server/run_log_server.go implemented ReadRunLogV1 without embedding *BaseRunServer or validating caller permissions. Additionally, committing w.WriteHeader(http.StatusOK) prior to calling ReadLog meant that even if an internal error occurred, the HTTP status code had already been sent as 200 OK.

// Vulnerable implementation in backend/src/apiserver/server/run_log_server.go (pre-2.18.0)
func (s *RunLogServer) ReadRunLogV1(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	runId, ok := vars[RunKey]
	if !ok {
		s.writeErrorToResponse(w, http.StatusBadRequest, util.NewInvalidInputError("%s not found in request", RunKey))
		return
	}
	nodeId, ok := vars[NodeKey]
	if !ok {
		s.writeErrorToResponse(w, http.StatusBadRequest, util.NewInvalidInputError("%s not found in request", NodeKey))
		return
	}
	follow := vars[Follow] == "true"
 
	// VULNERABILITY 1: No authentication or authorization check is performed.
	// Status 200 OK is committed immediately before attempting to read logs.
	w.WriteHeader(http.StatusOK)
	w.Header().Set("Content-Type", "text/plain")
	w.Header().Set("Cache-Control", "no-cache, private")
 
	flushWriter, err := util.NewFlushWriter(w)
	if err != nil {
		s.writeErrorToResponse(w, http.StatusInternalServerError, util.Wrap(err, "Failed to create flush writer."))
		return
	}
 
	err = s.resourceManager.ReadLog(r.Context(), runId, nodeId, follow, flushWriter)
	if err != nil {
		s.writeErrorToResponse(w, s.getErrorStatus(err), util.Wrap(err, "Failed to read a run log."))
	}
}

In backend/src/apiserver/resource/resource_manager.go, the pre-patch readRunLogFromPod method directly passed the user-supplied nodeId into the Kubernetes client's GetLogs call:

// Vulnerable implementation in backend/src/apiserver/resource/resource_manager.go (pre-2.18.0)
func (r *ResourceManager) readRunLogFromPod(ctx context.Context, namespace string, nodeId string, follow bool, dst io.Writer) error {
	logOptions := corev1.PodLogOptions{
		Container:  "main",
		Timestamps: false,
		Follow:     follow,
	}
	// VULNERABILITY 2: nodeId is used directly as the Pod name without verifying
	// whether the pod actually belongs to the requested runId.
	req := r.k8sCoreClient.PodClient(namespace).GetLogs(nodeId, &logOptions)
	podLogs, err := req.Stream(ctx)
	if err != nil {
		return util.NewInternalServerError(err, "Failed to get pod log stream")
	}
	defer podLogs.Close()
 
	return copyAndFlush(dst, podLogs)
}

Commit be4723e33727c6dad71a95cba61e8f61562ab20f (PR #13684) remediates both issues. First, it introduces a dedicated RbacResourceVerbReadLog = "readLog" constant in backend/src/apiserver/common/const.go and updates the multi-user cluster roles to grant readLog on pipelines.kubeflow.org/runs. Second, RunLogServer embeds *BaseRunServer and introduces an authorize helper that copies HTTP request headers into incoming gRPC metadata and evaluates canAccessRun with the readLog verb:

// Patched implementation in backend/src/apiserver/server/run_log_server.go
func (s *RunLogServer) ReadRunLogV1(w http.ResponseWriter, r *http.Request) {
	// ... [parameter extraction omitted] ...
	follow := vars[Follow] == "true"
 
	// FIX 1: Authorize request against the target run using the 'readLog' verb
	if err := s.authorize(r, runId); err != nil {
		s.writeErrorToResponse(w, http.StatusForbidden, util.Wrap(err, "Failed to authorize the request"))
		return
	}
 
	// FIX 2: Set headers without calling w.WriteHeader(http.StatusOK) prematurely,
	// allowing downstream errors in ReadLog to return proper non-200 HTTP status codes.
	w.Header().Set("Content-Type", "text/plain")
	w.Header().Set("Cache-Control", "no-cache, private")
 
	flushWriter, err := util.NewFlushWriter(w)
	// ...
}
 
func (s *RunLogServer) authorize(r *http.Request, runID string) error {
	md := metadata.MD{}
	for key, values := range r.Header {
		md.Set(key, values...)
	}
	ctx := metadata.NewIncomingContext(r.Context(), md)
	return s.canAccessRun(ctx, runID, &authorizationv1.ResourceAttributes{Verb: common.RbacResourceVerbReadLog})
}

Third, readRunLogFromPod in backend/src/apiserver/resource/resource_manager.go now fetches the pod object from the Kubernetes API first and verifies that its pipelines.kubeflow.org/run_id label (util.LabelKeyWorkflowRunId) matches the requested runID:

// Patched implementation in backend/src/apiserver/resource/resource_manager.go
func (r *ResourceManager) readRunLogFromPod(ctx context.Context, runID string, namespace string, nodeID string, follow bool, dst io.Writer) error {
	// FIX 3: Fetch pod metadata and verify the pod was created by this specific run
	pod, err := r.k8sCoreClient.PodClient(namespace).Get(ctx, nodeID, v1.GetOptions{})
	if err != nil {
		if !apierrors.IsNotFound(err) {
			glog.Errorf("Failed to get pod %v: %v", nodeID, err)
		}
		return util.NewInternalServerError(err, "Failed to read logs from pod %v due to error fetching the pod", nodeID)
	}
	if pod == nil || pod.Labels[util.LabelKeyWorkflowRunId] != runID {
		return util.NewInvalidInputError("Pod %v does not belong to run %v", nodeID, runID)
	}
 
	logOptions := corev1.PodLogOptions{
		Container:  "main",
		Timestamps: false,
		Follow:     follow,
	}
	req := r.k8sCoreClient.PodClient(namespace).GetLogs(nodeID, &logOptions)
	// ...
}

The fix is complete with respect to both route authorization and pod ownership validation. By explicitly binding readLog to SubjectAccessReview and enforcing exact equality on pod.Labels[util.LabelKeyWorkflowRunId] == runID, callers can neither bypass tenant isolation nor pivot to unrelated pods within an authorized namespace.

Exploitation

Exploiting CVE-2026-118759 requires network connectivity to the Kubeflow Pipelines API server (ml-pipeline service, typically listening on TCP port 8888 for HTTP) and knowledge of a valid run_id associated with the target namespace, along with a target pod/node identifier. No authentication credentials are required by the handler itself, though external API gateways or service mesh policies may require the attacker to be an authenticated cluster tenant.

In a multi-tenant Kubeflow deployment where MultiUserModeSharedReadAccess is enabled, any authenticated user can list runs across namespaces to obtain valid run_id UUIDs. Even when shared read access is disabled, an attacker may obtain a run_id through shared experiment metadata, webhook notifications, or CI/CD logs.

Scenario 1: Reading Pipeline Step Logs Cross-Tenant

If the attacker knows a victim run_id (e.g., 6b6858e2-f791-4e76-8041-55bb59582236) and the corresponding Argo workflow step pod name (which typically follows deterministic naming patterns based on the workflow name, or is retrieved from archived workflow manifests), the attacker issues a standard HTTP GET request:

GET /apis/v1alpha1/runs/6b6858e2-f791-4e76-8041-55bb59582236/nodes/training-pipeline-abcde-123456789/log?follow=true HTTP/1.1
Host: ml-pipeline.kubeflow.svc.cluster.local:8888

If the pod has already terminated and its logs were archived to object storage (such as MinIO or S3), ReadLog falls back to readRunLogFromArchive, retrieving the archived log file from the storage bucket using the API server's object storage credentials and streaming it to the caller.

Scenario 2: Arbitrary Pod Log Exfiltration via Confused Deputy

Because readRunLogFromPod only uses run_id to look up the namespace (e.g., tenant-finance) and passes node_id directly as the pod name to the Kubernetes API with Container: "main", an attacker can target non-pipeline pods running in tenant-finance provided they have a container named main:

GET /apis/v1alpha1/runs/6b6858e2-f791-4e76-8041-55bb59582236/nodes/credentials-sync-worker-0/log HTTP/1.1
Host: ml-pipeline.kubeflow.svc.cluster.local:8888

Upon receiving this request, the KFP API server uses its ml-pipeline-api-server Kubernetes ServiceAccount to execute GET /api/v1/namespaces/tenant-finance/pods/credentials-sync-worker-0/log?container=main against the Kubernetes API server and pipes the output back to the attacker.

Impact Assessment

CVE-2026-118759 is rated High severity with a CVSS v3.1 base score of 7.5 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N). The vulnerability impacts confidentiality exclusively (C:H), with no direct impact on resource integrity (I:N) or service availability (A:N).

In machine learning environments, container execution logs frequently contain high-value sensitive information:

  • Credentials and Secrets: Database connection strings, cloud provider access keys, API bearer tokens, and internal service passwords printed during initialization or debugging.
  • Proprietary Data and Intellectual Property: Training dataset samples, feature engineering parameters, hyperparameter configurations, and model evaluation metrics.
  • Infrastructure Telemetry: Internal service endpoints, storage bucket paths, and debugging stack traces that facilitate lateral movement within the Kubernetes cluster.

Because the API server executes the log retrieval using its own cluster-wide ServiceAccount (ml-pipeline-api-server), the attacker bypasses Kubernetes namespace isolation boundaries. As of the latest threat intelligence assessment, CVE-2026-118759 is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog and holds an EPSS score of 0.00336 (27.01st percentile), with no publicly weaponized exploit modules tracked in ExploitDB or Metasploit.

Remediation

Official Fix and Upgrade Path

Organizations running Kubeflow Pipelines should upgrade to version 2.18.0 or later, which incorporates commit be4723e33727c6dad71a95cba61e8f61562ab20f (PR #13684).

> [!IMPORTANT] > When upgrading multi-user installations of Kubeflow Pipelines, administrators must also apply the updated Kubernetes RBAC manifests (manifests/kustomize/base/installs/multi-user/view-edit-cluster-roles.yaml). Version 2.18.0 requires the readLog verb on the pipelines.kubeflow.org/runs resource; if the ClusterRole definitions (kubeflow-pipelines-edit and kubeflow-pipelines-view) are not updated, legitimate users will receive 403 Forbidden responses when attempting to view run logs.

Workarounds and Mitigations

If an immediate upgrade to 2.18.0 is not feasible, apply the following compensating controls:

  1. Kubernetes NetworkPolicies: Restrict network ingress to the ml-pipeline service (ports 8888 and 8887) using a NetworkPolicy so that only authorized reverse proxies, Istio ingress gateways, or verified internal components can establish connections to the API server.
  2. Service Mesh / API Gateway Route Filtering: In environments using Istio or an external API gateway, configure an AuthorizationPolicy or WAF rule to block or strictly authenticate requests matching the path pattern /apis/v1alpha1/runs/*/nodes/*/log.
  3. Least-Privilege ServiceAccount Scoping: Ensure the ml-pipeline-api-server ServiceAccount is only granted pods/log read permissions in dedicated pipeline execution namespaces rather than cluster-wide (ClusterRoleBinding) across administrative or control-plane namespaces.

Official Patches

KubeflowGitHub Security Advisory GHSA-wg5q-x4j9-qm54
KubeflowFix Pull Request #13684
KubeflowPrimary Fix Commit in kubeflow/pipelines
KubeflowKubeflow Pipelines Release 2.18.0

Fix Analysis (1)

Technical Appendix

CVSS Score
7.5/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
EPSS Probability
0.34%
Top 73% most exploited

Affected Systems

Kubeflow Pipelines (kubeflow/pipelines)kfp-api-server

Affected Versions Detail

Product
Affected Versions
Fixed Version
Kubeflow Pipelines
Kubeflow
< 2.18.02.18.0
AttributeDetail
Vulnerability TypeMissing Authorization (CWE-862) / Exposure of Sensitive Information (CWE-200)
Attack VectorNetwork (HTTP GET to /apis/v1alpha1/runs/{run_id}/nodes/{node_id}/log)
CVSS v3.1 Score7.5 High (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)
EPSS Score0.34% (27.01st percentile)
Security ImpactCross-tenant read access to live and archived Kubernetes pod execution logs
Exploit StatusNo known public exploits in the wild
CISA KEV StatusNot Listed
Fixed VersionKubeflow Pipelines 2.18.0

MITRE ATT&CK Mapping

T1068Exploitation for Privilege Escalation
Privilege Escalation
T1005Data from Local System
Collection
T1552Unsecured Credentials
Credential Access
CWE-862
Missing Authorization

The software does not perform an authorization check when an actor attempts to access a resource or perform an action.

Vulnerability Timeline

Fix committed upstream (be4723e33727c6dad71a95cba61e8f61562ab20f) in PR #13684.
2026-09-04
GitHub Security Advisory GHSA-wg5q-x4j9-qm54 and CVE-2026-118759 published.
2026-10-29
Official NVD entry published for CVE-2026-118759.
2026-10-29
OSV and NVD records updated with affected commit ranges, CVSS v3.1 scoring, and CWE mappings.
2026-10-30

References & Sources

  • [1]GitHub Security Advisory GHSA-wg5q-x4j9-qm54
  • [2]Kubeflow Pipelines Fix Pull Request #13684
  • [3]Kubeflow Pipelines Fix Commit be4723e33727c6dad71a95cba61e8f61562ab20f
  • [4]Kubeflow Pipelines Release 2.18.0
  • [5]National Vulnerability Database (NVD) Record for CVE-2026-118759
  • [6]CVE.org Record for CVE-2026-118759
  • [7]cvelistV5 JSON Record for CVE-2026-118759
  • [8]MITRE ATT&CK Mapping
  • [9]FIRST EPSS Scoring
  • [10]CISA Known Exploited Vulnerabilities Catalog

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.

More Reports

•9 minutes ago•CVE-2026-119203
7.1

CVE-2026-119203: Infinite Loop in pypdf Standard Cross-Reference Table Parser

CVE-2026-119203 is a denial-of-service vulnerability affecting the pypdf library in versions prior to 6.21.4. The flaw occurs within the standard cross-reference (xref) table parsing routine when processing truncated or malformed PDF streams that terminate before a valid trailer dictionary is encountered. Because the parser attempts to rewind the stream pointer by a fixed offset whenever the trailer token is absent without verifying forward progress, hitting the end-of-file (EOF) or non-advancing tokens causes the parser to re-read the same byte offset indefinitely. An unauthenticated attacker can exploit this flaw by supplying a crafted PDF document to an application using pypdf, resulting in continuous CPU consumption and thread exhaustion.

Amit Schendel
Amit Schendel
1 views•9 min read
•about 2 hours ago•CVE-2026-118761
7.5

CVE-2026-118761: Pre-Authorization Decompression Bomb Denial of Service in Kubeflow Pipelines API Server

A high-severity Denial of Service (DoS) vulnerability exists in Kubeflow Pipelines prior to version 2.18.0 due to improper handling of highly compressed archive files (CWE-409). The API server decompresses uploaded ZIP and gzip-compressed TAR archives directly into memory without bounding the expanded byte stream or archive traversal metadata. Because decompression occurs before namespace-level authorization in multipart upload handlers, an unauthenticated or unauthorized attacker with network access to the API server can exhaust container heap memory and trigger Out-Of-Memory (OOM) terminations.

Amit Schendel
Amit Schendel
4 views•11 min read
•about 3 hours ago•CVE-2026-118763
7.4

CVE-2026-118763: Cross-Origin Credential Exposure in @sigstore/oci

A credential exposure vulnerability exists in the @sigstore/oci library prior to version 0.7.2. The RegistryClient implementation stores sensitive authentication headers, including Bearer tokens, Basic authentication credentials, and custom Docker configuration headers, as client-wide defaults on its shared HTTP client instance. When uploading binary blobs via the OCI distribution specification, the client extracts the target upload URI from the server-supplied Location header and dispatches an independent HTTP PUT request. If a compromised or untrusted registry returns a cross-origin URI in the Location header, the client transmits the stored authentication credentials directly to the third-party host.

Alon Barad
Alon Barad
7 views•10 min read
•about 6 hours ago•CVE-2026-119282
7.3

CVE-2026-119282: OS Command Injection via Plugin Options in Nx (@nx/maven and @nx/gradle)

An OS command injection and argument injection vulnerability exists in the Nx build system and its official Maven (@nx/maven) and Gradle (@nx/gradle) plugins. When constructing the project dependency graph, the plugins invoke underlying Java build wrappers via subshells while interpolating unsanitized plugin options directly from nx.json into the command arguments. An attacker who supplies a crafted nx.json file via a repository or pull request can execute arbitrary shell commands with the privileges of the user or CI/CD runner invoking any Nx command that computes the project graph.

Amit Schendel
Amit Schendel
6 views•10 min read
•about 7 hours ago•CVE-2026-59978
7.5

CVE-2026-59978: Persistent Denial of Service via Array Injection in AdminNeo

AdminNeo versions 4.6.2 through 5.4.1 contain an improper input validation vulnerability (CWE-20) in the version verification endpoint (?script=version). An unauthenticated remote attacker can submit a crafted HTTP POST request containing an array parameter for 'version', which is serialized into a shared server-side temporary cache file without type checking or authentication. On subsequent requests, the application deserializes this cached array and passes it to cookie() and rawurlencode(), triggering an uncaught fatal TypeError in PHP 8.x environments and causing a persistent 24-hour Denial of Service (DoS) for all users.

Amit Schendel
Amit Schendel
6 views•8 min read
•about 8 hours ago•GHSA-FGXM-5HVV-X4VH
5.3

GHSA-fgxm-5hvv-x4vh: Private Repository Enumeration via Go Module Discovery and HTTP Response Discrepancies in Soft Serve

Charmbracelet Soft Serve versions 0.5.0 through 0.12.2 contain an authorization bypass and observable response discrepancy vulnerability in the HTTP Git server. Remote attackers can determine the existence, exact names, and clone URLs of private repositories by issuing crafted Go remote import path discovery requests (?go-get=1) or by observing differential HTTP status codes across Git Smart HTTP, Dumb HTTP, and Git LFS endpoints.

Alon Barad
Alon Barad
7 views•12 min read