Skip to content

ci: tighten credential and build-cache hygiene - #346

Merged
EliMoshkovich merged 2 commits into
mainfrom
eli/per-16640-ci-cache-hardening
Sep 30, 2026
Merged

EliMoshkovich merged 2 commits into
mainfrom
eli/per-16640-ci-cache-hardening

Conversation

@EliMoshkovich

@EliMoshkovich EliMoshkovich commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Follow-up to #345: general CI hygiene.

  • pdp-tester checkout: persist-credentials: false, and pinned to the same actions/checkout SHA (v6.0.3) as the other token-bearing checkouts. Nothing after the clone uses git, and pdp-tester has no git-sourced dependencies.
  • .dockerignore: also exclude nested .git directories (**/.git), not just the root one.
  • GHA build cache: only rust_chef is cached, in its own rust-chef scope, by a dedicated Cache the rust_chef stage step in tests.yml.
    • rust_chef reads nothing from the build context: base image, apk add, cargo install and rustup only. The Dockerfile now says it must stay free of COPY/ADD.
    • The image builds in tests.yml and release.yml read that scope and export nothing. Every layer of the final image comes from main, which no-cache-filters: main never serves from cache, so exporting it only filled the cache.
    • The arm64 opa_build step no longer reads the cache.
    • Comments that described the old mode=max behavior are updated.

Cost: about 2 seconds per build, for opa_build's pre-COPY layers (the go floor check) no longer coming from the cache. rust_chef (about 4 minutes when cold) stays cached. The rust_planner/rust_builder stages already re-ran on almost every build.

Tracked internally as PER-16640.

🤖 Generated with Claude Code

- persist-credentials: false on the pdp-tester checkout, like the permit-opa ones
- .dockerignore: exclude nested .git directories from the build context
- export only the final image's layers to the GHA cache (mode=min) in tests.yml
  and release.yml

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

🔍 Vulnerabilities of permitio/pdp-v2:next

📦 Image Reference permitio/pdp-v2:next
digestsha256:29e30d69bb483319cb54f5aef771016653f95aa487263b8fd79b1f88edd3667f
vulnerabilitiescritical: 0 high: 3 medium: 4 low: 5 unspecified: 1
platformlinux/amd64
size140 MB
packages247
📦 Base Image oisupport/staging-amd64:026d26881d2e1ebad06e4f309b0fc5f03c0471c5230d325d7b571be802586ee6
also known as
  • 3.13-alpine3.23
  • 3.13.15-alpine3.23
digestsha256:f282f385cfce21b0a644094330930704424a48d59afe8f08052e0f8e0b7a35c6
vulnerabilitiescritical: 0 high: 3 medium: 2 low: 0
critical: 0 high: 2 medium: 2 low: 1 starlette 0.50.0 (pypi)

pkg:pypi/starlette@0.50.0

high 7.5: CVE--2026--54283 Allocation of Resources Without Limits or Throttling

Affected range>=0.4.1
<1.3.1
Fixed version1.3.1
CVSS Score7.5
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
EPSS Score0.481%
EPSS Percentile39th percentile
Description

Summary

request.form() accepts max_fields and max_part_size to bound resource consumption while parsing form data. These limits are enforced for multipart/form-data, but silently ignored for application/x-www-form-urlencoded. An unauthenticated attacker can therefore send a urlencoded body with an arbitrarily large number of fields or an arbitrarily large field, even when the application configured limits it believed would apply.

Details

request.form() dispatches to a different parser depending on the Content-Type. For multipart/form-data the max_files, max_fields, and max_part_size limits are forwarded to the parser, but for application/x-www-form-urlencoded the parser is constructed without them. It has no max_fields or max_part_size parameter to receive them, and it appends every field with no count check and accumulates each field's name and value with no size check. The configured limits are therefore both unreachable and unenforced for url-encoded bodies.

Because the url-encoded parser does its work synchronously between stream reads, the two attack shapes have different effects:

  • Field count drives CPU and event-loop blocking. A body of ~1,000,000 fields (a sub-10MB payload such as f0=v&f1=v&...) blocks the worker's event loop for several seconds while parsing, during which the worker serves no other request.
  • Field size drives memory. A single large field value (e.g. a 50MB value) is buffered in full to build the FormData, forcing memory allocation proportional to the request body.

The equivalent multipart/form-data request is correctly rejected with 400 Too many fields / 400 Field exceeded maximum size.

Impact

This Denial of service (DoS) vulnerability affects all applications built with Starlette (or FastAPI) that call request.form() on application/x-www-form-urlencoded requests. A single request with a very large number of fields blocks the event loop for several seconds, and a single request with a very large field forces unbounded memory allocation; in either case, parallel requests can render the service unusable. A reverse proxy that enforces a request body size limit reduces but does not eliminate the exposure, since a sub-10MB body is already enough to block the event loop.

Mitigation

Upgrade to a patched version, which forwards max_fields and max_part_size to the url-encoded parser and enforces them while parsing, raising before the oversized field or excess fields are accumulated. The defaults match multipart/form-data (max_fields=1000, max_part_size=1MB) and can be customized via request.form(max_fields=..., max_part_size=...).

high 7.5: CVE--2026--48818 Server-Side Request Forgery (SSRF)

Affected range<1.1.0
Fixed version1.1.0
CVSS Score7.5
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
EPSS Score0.648%
EPSS Percentile49th percentile
Description

Summary

When serving static files on Windows, StaticFiles resolves the requested path with os.path.realpath. If a UNC path (such as \\attacker.com\share) reaches the resolver, realpath causes the process to open a connection to the remote host over SMB (port 445). This is a server-side request forgery (SSRF) that leaks the service account's NTLMv2 credentials to the attacker-controlled host, which can then be cracked offline or relayed to other hosts.

Details

StaticFiles.lookup_path() joins the requested path onto the served directory and calls os.path.realpath on the result before checking containment with os.path.commonpath. On Windows, a UNC path is absolute, so os.path.join discards the served directory and realpath resolves the bare UNC path, triggering the outbound SMB connection and NTLM authentication before the containment check rejects the path. The HTTP response is a benign 404, but the credential disclosure has already happened. POSIX systems are not affected.

This only affects the default configuration (follow_symlink=False), which uses os.path.realpath. The follow_symlink=True branch uses os.path.abspath, which performs no I/O.

Impact

Applications running on Windows that serve files with StaticFiles (directly, or via a framework built on Starlette such as FastAPI) in the default configuration are affected. StaticFiles is typically unauthenticated, so any client can trigger the SMB connection and leak the service account's NTLMv2 hash. A secondary impact is discovering internal hosts reachable over SMB by timing responses for valid versus invalid addresses.

Mitigation

Applications not running on Windows are not affected. On Windows, serving static files through a dedicated web server (such as nginx or IIS) instead of StaticFiles avoids the issue. Blocking outbound SMB (port 445) from the application host prevents the credential disclosure even if a UNC path is resolved.

medium 6.5: CVE--2026--48710 Improper Validation of Unsafe Equivalence in Input

Affected range<=1.0.0
Fixed version1.0.1
CVSS Score6.5
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N
EPSS Score7.056%
EPSS Percentile94th percentile
Description

Summary

In affected versions, the HTTP Host request header was not validated before being used to reconstruct request.url. Because the routing algorithm relies on the raw HTTP path while request.url is rebuilt from the Host header, a malformed header could make request.url.path differ from the path that was actually requested. Middleware and endpoints that apply security restrictions based on request.url (rather than the raw scope path) could therefore be bypassed.

Details

When a client requests http://example.com/foo, it sends:

GET /foo HTTP/1.1
Host: example.com

Affected versions reconstructed the URL by concatenating http://{host}{path} and re-parsing the result. The Host value is only valid as a uri-host [ ":" port ] per RFC 9112 §3.2, where uri-host follows the restricted host grammar of RFC 3986 §3.2.2. When it contains characters outside that grammar - notably /, ?, or # - those characters move the path/query/fragment boundaries during re-parsing, so the parsed request.url.path no longer matches the path the server actually received. For example:

GET /foo HTTP/1.1
Host: example.com/abc?bar=

reconstructs to http://example.com/abc?bar=/foo, whose parsed path is /abc - even though routing used the real path /foo. The router still dispatches to /foo and the endpoint executes, but any middleware or code that reads request.url.path sees /abc, so path-based authorization checks can be bypassed.

Impact

Any application running an affected version that relies on request.url (or request.url.path) for security-sensitive decisions is affected. The most common case is middleware that gates access to certain path prefixes based on request.url.path. Deployments fronted by a proxy or load balancer are mitigated only if that proxy rejects or normalizes the malformed Host header before forwarding and the application does not trust attacker-controlled host headers (e.g. X-Forwarded-Host) elsewhere.

Mitigation

Upgrade to a patched version, which validates the Host header against the grammar of RFC 9112 §3.2 / RFC 3986 §3.2.2 when constructing request.url and falls back to scope["server"] for malformed values.

medium 5.3: CVE--2026--48817 Use of Externally-Controlled Input to Select Classes or Code ('Unsafe Reflection')

Affected range<1.1.0
Fixed version1.1.0
CVSS Score5.3
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
EPSS Score0.348%
EPSS Percentile26th percentile
Description

Summary

When dispatching a request, HTTPEndpoint selects the handler by lowercasing the HTTP method and looking it up as an attribute with getattr, without restricting the lookup to a known set of HTTP verbs.

When an HTTPEndpoint subclass is registered through Route(...) without an explicit methods= argument, the route does not constrain the method and every method reaches the endpoint. If a non-standard HTTP method whose lowercased name matches an attribute on the endpoint subclass reaches the endpoint, that attribute is invoked as if it were a request handler. An attacker can use this to reach methods that were never meant to be HTTP handlers, such as internal helpers, without the authorization checks applied by the intended public handler.

Details

HTTPEndpoint uses the client-supplied method name to resolve an instance attribute, without validating it against the set of HTTP verbs the endpoint supports. A method such as _DO_DELETE therefore resolves an attribute like _do_delete and invokes it. Non-standard methods are valid RFC 9110 token methods, so an endpoint must not treat the method name as a trusted attribute selector.

Impact

An application is affected when all of the following hold:

  • It defines an HTTPEndpoint subclass and registers it via Route(...) without an explicit methods= argument.
  • The subclass defines additional methods whose names match a non-standard HTTP-method token shape and that accept a single request argument and return a response.

This also affects frameworks built on Starlette, like FastAPI.

Mitigation

Register HTTPEndpoint subclasses with an explicit methods= argument on the Route, listing only the HTTP verbs the endpoint supports. The route then rejects any other method with 405 Method Not Allowed before it reaches the endpoint, so non-standard methods cannot resolve an attribute.

low 3.7: CVE--2026--54282 Improper Input Validation

Affected range<1.3.0
Fixed version1.3.0
CVSS Score3.7
CVSS VectorCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N
EPSS Score0.273%
EPSS Percentile18th percentile
Description

Summary

In affected versions, the HTTP request path is not validated before being used to reconstruct request.url. Because request.url is rebuilt by concatenating {scheme}://{host}{path} and re-parsing the result, a path that does not begin with / (for example @<!-- -->google.com) moves the authority boundary during re-parsing, so request.url.hostname and request.url.netloc become attacker-controlled. Code that reads request.url.hostname (rather than the Host header or scope) can therefore be misled into trusting an attacker-supplied host.

Details

When a client requests a path that does not start with /:

GET @<!-- -->google.com HTTP/1.1
Host: localhost

affected versions reconstruct the URL as http://localhost@<!-- -->google.com. Per RFC 3986 §3.2.1, the substring before @ in the authority is userinfo, so re-parsing yields username = "localhost" and hostname = "google.com", with an empty path:

request.url          == "http://localhost@<!-- -->google.com"
request.url.hostname == "google.com"
request.url.path     == ""

The root cause is that the path is concatenated directly after the host without a separating /, and without validating that it begins with one. Only the Host header was validated when constructing request.url; the path was not.

This requires an ASGI server that forwards a request-target lacking a leading / into scope["path"].

Impact

Any application running an affected version that uses request.url, request.url.netloc, or request.url.hostname for a security-sensitive decision (host-based authorization, redirect/callback base, SSRF target, cache key, audit log) may be affected, when no fronting proxy or load balancer rejects the malformed request-target first.

Note that this is less exploitable than GHSA-86qp-5c8j-p5mr: there, the poison is carried in the Host header, so the real path still routes to a valid endpoint while request.url.path lies. Here, the poison must be carried in the path itself, and that path (@<!-- -->google.com) does not match any registered route, so routing returns 404 and no endpoint handler runs. The exposure is limited to code that reads request.url before routing - notably middleware - or in 404/exception handlers.

Mitigation

Upgrade to a patched version, which prevents the request path from crossing into the URL authority. The request above instead yields http://localhost/@<!-- -->google.com with request.url.hostname == "localhost".

critical: 0 high: 1 medium: 0 low: 0 ddtrace 3.19.8 (pypi)

pkg:pypi/ddtrace@3.19.8

high 7.5: CVE--2026--50271 Uncontrolled Resource Consumption

Affected range<4.8.2
Fixed version4.8.2
CVSS Score7.5
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
EPSS Score0.793%
EPSS Percentile55th percentile
Description

Impact

Datadog tracing libraries that implement W3C baggage propagation parse incoming baggage HTTP headers without enforcing item-count or byte-size limits on the extract path. The DD_TRACE_BAGGAGE_MAX_ITEMS (default 64) and DD_TRACE_BAGGAGE_MAX_BYTES (default 8192) limits were applied only to baggage injection, not extraction. A remote, unauthenticated attacker can send a request whose baggage header contains an arbitrarily large number of comma-separated key-value pairs (or a single very large value). The tracer allocates a hash-map entry for each pair on every request, causing unbounded CPU and memory consumption and enabling a remote Denial of Service against any HTTP service that has the baggage propagation style enabled.
The baggage propagation style is enabled by default in most affected tracers, so any internet-facing service that has been instrumented with an affected tracer version is exposed unless the propagation style has been explicitly narrowed.

Patches

This is resolved in version 4.8.2 and later of the dd-trace-py library

Workarounds

If users cannot upgrade immediately:

  1. Disable baggage extraction by removing baggage from DD_TRACE_PROPAGATION_STYLE (or DD_TRACE_PROPAGATION_STYLE_EXTRACT if set independently).
  2. Cap the maximum HTTP request header size at an upstream proxy or web server (for example, Apache LimitRequestFieldSize, Nginx large_client_header_buffers, Envoy max_request_headers_kb).

Resources

Related upstream advisories:
opentelemetry-go GHSA-mh2q-q3fh-2475
opentelemetry-dotnet GHSA-g94r-2vxg-569j

critical: 0 high: 0 medium: 1 low: 0 busybox 1.37.0-r30 (apk)

pkg:apk/alpine/busybox@1.37.0-r30?os_name=alpine&os_version=3.23

medium : CVE--2025--60876

Affected range<=1.37.0-r30
Fixed versionNot Fixed
EPSS Score0.291%
EPSS Percentile19th percentile
Description
critical: 0 high: 0 medium: 1 low: 0 github.com/containerd/containerd/v2 2.2.8 (golang)

pkg:golang/github.com/containerd/containerd/v2@2.2.8

medium 6.9: CVE--2026--53493 Uncontrolled Resource Consumption

Affected range>=2.1.0
<2.2.9
Fixed version2.2.9
CVSS Score6.9
CVSS VectorCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N
EPSS Score0.356%
EPSS Percentile27th percentile
Description

Impact

A vulnerability exists in containerd's image pull handlers where a crafted OCI image index containing deeply nested or heavily fanned-out descriptor graphs can cause unbounded CPU and memory consumption. During the PullImage operation, the recursive traversal and processing of child descriptors lack sufficient depth and breadth limits, and fail to adequately deduplicate identical descriptors. This unbounded traversal leads to excessive resource allocation.

Consequently, pulling a malicious image reference can result in prolonged stalls during container creation and significant resource pressure on the host system. This issue occurs entirely during the image pull phase, prior to any container execution.

Patches

This bug has been fixed in containerd 2.4.1, 2.3.6, 2.2.9, 2.0.13, and 1.7.36. Users should update to these versions to resolve the issue.

Workarounds

There are no known workarounds for this issue. Users are advised to only pull trusted images from known registries until the patch can be applied.

Credits

The containerd project would like to thank Jakub Ciolek at ElevenLabs and @jlgore who independently discovered and responsibly disclosed this issue in accordance with the containerd security policy.

For more information

If you have any questions or comments about this advisory:

  • Open an issue in containerd
  • Email us at [security@containerd.io](mailto:security@containerd.io)

To report a security issue in containerd:

critical: 0 high: 0 medium: 0 low: 1 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc 1.43.0 (golang)

pkg:golang/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc@1.43.0

low 2.0: CVE--2026--81870 Exposure of Sensitive Information to an Unauthorized Actor

Affected range>=1.5.0
<=1.44.0
Fixed version1.45.0
CVSS Score2
CVSS VectorCVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N
EPSS Score0.195%
EPSS Percentile8th percentile
Description

Summary

OpenTelemetry Go versions 1.5.0 through 1.44.0 can include trace exporter endpoint configuration in an internal diagnostic log emitted when an SDK TracerProvider is created. The default OpenTelemetry logger does not emit this event. Exposure requires an application to install a logger that enables OpenTelemetry's internal Info-level diagnostics and for someone other than the intended audience to have access to those logs.

The logged configuration can disclose the address of the trace collector and whether the OTLP/HTTP connection is configured as insecure. The Zipkin exporter logs its complete collector URL, so credentials in URL userinfo or tokens in the query string are also disclosed if an application embeds them there. OTLP authentication headers, TLS key material, and exported span data are not included in this log.

Exporter MarshalLog implementations that caused this configuration to be included in internal logs were introduced by a1fff3c.

Details

When sdk/trace.NewTracerProvider constructs a provider, it records a TracerProvider created internal Info event containing the provider configuration. In affected versions, the configuration's MarshalLog methods recursively include:

  1. the provider's span processors;
  2. each processor's span exporter; and
  3. for the OTLP trace exporter, its client configuration.

This causes the following values to be present in the event:

  • OTLP trace gRPC: the configured endpoint;
  • OTLP trace HTTP: the configured endpoint and the Insecure flag; and
  • Zipkin: the complete collector URL.

OpenTelemetry Go does not emit this event with its default logger, which only emits errors. An application must explicitly configure a sufficiently verbose logger with otel.SetLogger. The required logr verbosity is version-dependent:

  • versions 1.5.0 through 1.14.x use V(1) for this Info event; and
  • versions 1.15.0 through 1.44.0 use V(4).

OTLP header configuration is not part of the marshaled object, so credentials supplied with WithHeaders or the corresponding environment variables are not exposed. The documented OTLP WithEndpoint input is a collector address rather than a credential-bearing URL. The higher-risk case is therefore the Zipkin collector URL, which is retained and logged in full, or an application passing sensitive data in an OTLP endpoint outside the documented format.

Proof of concept

The following program demonstrates the behavior with OpenTelemetry Go 1.44.0. It deliberately places credentials and a token in the Zipkin collector URL and enables internal Info logging:

package main

import (
	"bytes"
	"context"
	"fmt"

	"github.com/go-logr/logr/funcr"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/exporters/zipkin"
	sdktrace "go.opentelemetry.io/otel/sdk/trace"
)

func main() {
	var logs bytes.Buffer
	otel.SetLogger(funcr.New(func(_, args string) {
		_, _ = logs.WriteString(args)
	}, funcr.Options{Verbosity: 4}))

	exporter, err := zipkin.New(
		"http://user:pass@<!-- -->zipkin.internal:9411/api/v2/spans?token=secret",
	)
	if err != nil {
		panic(err)
	}

	tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exporter))
	_ = tp.Shutdown(context.Background())

	fmt.Println(logs.String())
}

The TracerProvider created event contains:

http://user:pass@<!-- -->zipkin.internal:9411/api/v2/spans?token=secret

For versions before 1.15.0, set funcr.Options{Verbosity: 1} instead.

Impact

This is a conditional disclosure through application logs. Affected applications must enable verbose OpenTelemetry internal diagnostics and configure a trace exporter containing information they do not intend to expose to readers of those logs. In that configuration, a person or system with log access can learn the trace collector address and internal network topology. If credentials or tokens are embedded directly in a Zipkin collector URL, those values can also be recovered from the logs.

There is no exposure with the default OpenTelemetry logger, and the vulnerable log is generated from local application configuration rather than remotely supplied span data. OTLP authentication headers, certificate or private-key contents, and telemetry payloads are not logged by this path.

Remediation

Upgrade the affected OpenTelemetry Go modules to version 1.45.0 or later. The fix in 3a1412d stops recursively marshaling exporter and client configuration and records their types instead.

If an immediate upgrade is not possible:

  • keep OpenTelemetry internal logging below the Info verbosity described above;
  • do not embed credentials or tokens in exporter endpoint URLs; use authentication headers or another supported credential mechanism; and
  • restrict access to existing logs and rotate any credentials that may already have been recorded.
critical: 0 high: 0 medium: 0 low: 1 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp 1.43.0 (golang)

pkg:golang/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp@1.43.0

low 2.0: CVE--2026--81870 Exposure of Sensitive Information to an Unauthorized Actor

Affected range>=1.5.0
<=1.44.0
Fixed version1.45.0
CVSS Score2
CVSS VectorCVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N
EPSS Score0.195%
EPSS Percentile8th percentile
Description

Summary

OpenTelemetry Go versions 1.5.0 through 1.44.0 can include trace exporter endpoint configuration in an internal diagnostic log emitted when an SDK TracerProvider is created. The default OpenTelemetry logger does not emit this event. Exposure requires an application to install a logger that enables OpenTelemetry's internal Info-level diagnostics and for someone other than the intended audience to have access to those logs.

The logged configuration can disclose the address of the trace collector and whether the OTLP/HTTP connection is configured as insecure. The Zipkin exporter logs its complete collector URL, so credentials in URL userinfo or tokens in the query string are also disclosed if an application embeds them there. OTLP authentication headers, TLS key material, and exported span data are not included in this log.

Exporter MarshalLog implementations that caused this configuration to be included in internal logs were introduced by a1fff3c.

Details

When sdk/trace.NewTracerProvider constructs a provider, it records a TracerProvider created internal Info event containing the provider configuration. In affected versions, the configuration's MarshalLog methods recursively include:

  1. the provider's span processors;
  2. each processor's span exporter; and
  3. for the OTLP trace exporter, its client configuration.

This causes the following values to be present in the event:

  • OTLP trace gRPC: the configured endpoint;
  • OTLP trace HTTP: the configured endpoint and the Insecure flag; and
  • Zipkin: the complete collector URL.

OpenTelemetry Go does not emit this event with its default logger, which only emits errors. An application must explicitly configure a sufficiently verbose logger with otel.SetLogger. The required logr verbosity is version-dependent:

  • versions 1.5.0 through 1.14.x use V(1) for this Info event; and
  • versions 1.15.0 through 1.44.0 use V(4).

OTLP header configuration is not part of the marshaled object, so credentials supplied with WithHeaders or the corresponding environment variables are not exposed. The documented OTLP WithEndpoint input is a collector address rather than a credential-bearing URL. The higher-risk case is therefore the Zipkin collector URL, which is retained and logged in full, or an application passing sensitive data in an OTLP endpoint outside the documented format.

Proof of concept

The following program demonstrates the behavior with OpenTelemetry Go 1.44.0. It deliberately places credentials and a token in the Zipkin collector URL and enables internal Info logging:

package main

import (
	"bytes"
	"context"
	"fmt"

	"github.com/go-logr/logr/funcr"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/exporters/zipkin"
	sdktrace "go.opentelemetry.io/otel/sdk/trace"
)

func main() {
	var logs bytes.Buffer
	otel.SetLogger(funcr.New(func(_, args string) {
		_, _ = logs.WriteString(args)
	}, funcr.Options{Verbosity: 4}))

	exporter, err := zipkin.New(
		"http://user:pass@<!-- -->zipkin.internal:9411/api/v2/spans?token=secret",
	)
	if err != nil {
		panic(err)
	}

	tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exporter))
	_ = tp.Shutdown(context.Background())

	fmt.Println(logs.String())
}

The TracerProvider created event contains:

http://user:pass@<!-- -->zipkin.internal:9411/api/v2/spans?token=secret

For versions before 1.15.0, set funcr.Options{Verbosity: 1} instead.

Impact

This is a conditional disclosure through application logs. Affected applications must enable verbose OpenTelemetry internal diagnostics and configure a trace exporter containing information they do not intend to expose to readers of those logs. In that configuration, a person or system with log access can learn the trace collector address and internal network topology. If credentials or tokens are embedded directly in a Zipkin collector URL, those values can also be recovered from the logs.

There is no exposure with the default OpenTelemetry logger, and the vulnerable log is generated from local application configuration rather than remotely supplied span data. OTLP authentication headers, certificate or private-key contents, and telemetry payloads are not logged by this path.

Remediation

Upgrade the affected OpenTelemetry Go modules to version 1.45.0 or later. The fix in 3a1412d stops recursively marshaling exporter and client configuration and records their types instead.

If an immediate upgrade is not possible:

  • keep OpenTelemetry internal logging below the Info verbosity described above;
  • do not embed credentials or tokens in exporter endpoint URLs; use authentication headers or another supported credential mechanism; and
  • restrict access to existing logs and rotate any credentials that may already have been recorded.
critical: 0 high: 0 medium: 0 low: 1 go.opentelemetry.io/otel/exporters/otlp/otlptrace 1.43.0 (golang)

pkg:golang/go.opentelemetry.io/otel/exporters/otlp/otlptrace@1.43.0

low 2.0: CVE--2026--81870 Exposure of Sensitive Information to an Unauthorized Actor

Affected range>=1.5.0
<=1.44.0
Fixed version1.45.0
CVSS Score2
CVSS VectorCVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N
EPSS Score0.195%
EPSS Percentile8th percentile
Description

Summary

OpenTelemetry Go versions 1.5.0 through 1.44.0 can include trace exporter endpoint configuration in an internal diagnostic log emitted when an SDK TracerProvider is created. The default OpenTelemetry logger does not emit this event. Exposure requires an application to install a logger that enables OpenTelemetry's internal Info-level diagnostics and for someone other than the intended audience to have access to those logs.

The logged configuration can disclose the address of the trace collector and whether the OTLP/HTTP connection is configured as insecure. The Zipkin exporter logs its complete collector URL, so credentials in URL userinfo or tokens in the query string are also disclosed if an application embeds them there. OTLP authentication headers, TLS key material, and exported span data are not included in this log.

Exporter MarshalLog implementations that caused this configuration to be included in internal logs were introduced by a1fff3c.

Details

When sdk/trace.NewTracerProvider constructs a provider, it records a TracerProvider created internal Info event containing the provider configuration. In affected versions, the configuration's MarshalLog methods recursively include:

  1. the provider's span processors;
  2. each processor's span exporter; and
  3. for the OTLP trace exporter, its client configuration.

This causes the following values to be present in the event:

  • OTLP trace gRPC: the configured endpoint;
  • OTLP trace HTTP: the configured endpoint and the Insecure flag; and
  • Zipkin: the complete collector URL.

OpenTelemetry Go does not emit this event with its default logger, which only emits errors. An application must explicitly configure a sufficiently verbose logger with otel.SetLogger. The required logr verbosity is version-dependent:

  • versions 1.5.0 through 1.14.x use V(1) for this Info event; and
  • versions 1.15.0 through 1.44.0 use V(4).

OTLP header configuration is not part of the marshaled object, so credentials supplied with WithHeaders or the corresponding environment variables are not exposed. The documented OTLP WithEndpoint input is a collector address rather than a credential-bearing URL. The higher-risk case is therefore the Zipkin collector URL, which is retained and logged in full, or an application passing sensitive data in an OTLP endpoint outside the documented format.

Proof of concept

The following program demonstrates the behavior with OpenTelemetry Go 1.44.0. It deliberately places credentials and a token in the Zipkin collector URL and enables internal Info logging:

package main

import (
	"bytes"
	"context"
	"fmt"

	"github.com/go-logr/logr/funcr"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/exporters/zipkin"
	sdktrace "go.opentelemetry.io/otel/sdk/trace"
)

func main() {
	var logs bytes.Buffer
	otel.SetLogger(funcr.New(func(_, args string) {
		_, _ = logs.WriteString(args)
	}, funcr.Options{Verbosity: 4}))

	exporter, err := zipkin.New(
		"http://user:pass@<!-- -->zipkin.internal:9411/api/v2/spans?token=secret",
	)
	if err != nil {
		panic(err)
	}

	tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exporter))
	_ = tp.Shutdown(context.Background())

	fmt.Println(logs.String())
}

The TracerProvider created event contains:

http://user:pass@<!-- -->zipkin.internal:9411/api/v2/spans?token=secret

For versions before 1.15.0, set funcr.Options{Verbosity: 1} instead.

Impact

This is a conditional disclosure through application logs. Affected applications must enable verbose OpenTelemetry internal diagnostics and configure a trace exporter containing information they do not intend to expose to readers of those logs. In that configuration, a person or system with log access can learn the trace collector address and internal network topology. If credentials or tokens are embedded directly in a Zipkin collector URL, those values can also be recovered from the logs.

There is no exposure with the default OpenTelemetry logger, and the vulnerable log is generated from local application configuration rather than remotely supplied span data. OTLP authentication headers, certificate or private-key contents, and telemetry payloads are not logged by this path.

Remediation

Upgrade the affected OpenTelemetry Go modules to version 1.45.0 or later. The fix in 3a1412d stops recursively marshaling exporter and client configuration and records their types instead.

If an immediate upgrade is not possible:

  • keep OpenTelemetry internal logging below the Info verbosity described above;
  • do not embed credentials or tokens in exporter endpoint URLs; use authentication headers or another supported credential mechanism; and
  • restrict access to existing logs and rotate any credentials that may already have been recorded.
critical: 0 high: 0 medium: 0 low: 1 go.opentelemetry.io/otel/sdk 1.44.0 (golang)

pkg:golang/go.opentelemetry.io/otel/sdk@1.44.0

low 2.0: CVE--2026--81870 Exposure of Sensitive Information to an Unauthorized Actor

Affected range>=1.5.0
<=1.44.0
Fixed version1.45.0
CVSS Score2
CVSS VectorCVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N
EPSS Score0.195%
EPSS Percentile8th percentile
Description

Summary

OpenTelemetry Go versions 1.5.0 through 1.44.0 can include trace exporter endpoint configuration in an internal diagnostic log emitted when an SDK TracerProvider is created. The default OpenTelemetry logger does not emit this event. Exposure requires an application to install a logger that enables OpenTelemetry's internal Info-level diagnostics and for someone other than the intended audience to have access to those logs.

The logged configuration can disclose the address of the trace collector and whether the OTLP/HTTP connection is configured as insecure. The Zipkin exporter logs its complete collector URL, so credentials in URL userinfo or tokens in the query string are also disclosed if an application embeds them there. OTLP authentication headers, TLS key material, and exported span data are not included in this log.

Exporter MarshalLog implementations that caused this configuration to be included in internal logs were introduced by a1fff3c.

Details

When sdk/trace.NewTracerProvider constructs a provider, it records a TracerProvider created internal Info event containing the provider configuration. In affected versions, the configuration's MarshalLog methods recursively include:

  1. the provider's span processors;
  2. each processor's span exporter; and
  3. for the OTLP trace exporter, its client configuration.

This causes the following values to be present in the event:

  • OTLP trace gRPC: the configured endpoint;
  • OTLP trace HTTP: the configured endpoint and the Insecure flag; and
  • Zipkin: the complete collector URL.

OpenTelemetry Go does not emit this event with its default logger, which only emits errors. An application must explicitly configure a sufficiently verbose logger with otel.SetLogger. The required logr verbosity is version-dependent:

  • versions 1.5.0 through 1.14.x use V(1) for this Info event; and
  • versions 1.15.0 through 1.44.0 use V(4).

OTLP header configuration is not part of the marshaled object, so credentials supplied with WithHeaders or the corresponding environment variables are not exposed. The documented OTLP WithEndpoint input is a collector address rather than a credential-bearing URL. The higher-risk case is therefore the Zipkin collector URL, which is retained and logged in full, or an application passing sensitive data in an OTLP endpoint outside the documented format.

Proof of concept

The following program demonstrates the behavior with OpenTelemetry Go 1.44.0. It deliberately places credentials and a token in the Zipkin collector URL and enables internal Info logging:

package main

import (
	"bytes"
	"context"
	"fmt"

	"github.com/go-logr/logr/funcr"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/exporters/zipkin"
	sdktrace "go.opentelemetry.io/otel/sdk/trace"
)

func main() {
	var logs bytes.Buffer
	otel.SetLogger(funcr.New(func(_, args string) {
		_, _ = logs.WriteString(args)
	}, funcr.Options{Verbosity: 4}))

	exporter, err := zipkin.New(
		"http://user:pass@<!-- -->zipkin.internal:9411/api/v2/spans?token=secret",
	)
	if err != nil {
		panic(err)
	}

	tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exporter))
	_ = tp.Shutdown(context.Background())

	fmt.Println(logs.String())
}

The TracerProvider created event contains:

http://user:pass@<!-- -->zipkin.internal:9411/api/v2/spans?token=secret

For versions before 1.15.0, set funcr.Options{Verbosity: 1} instead.

Impact

This is a conditional disclosure through application logs. Affected applications must enable verbose OpenTelemetry internal diagnostics and configure a trace exporter containing information they do not intend to expose to readers of those logs. In that configuration, a person or system with log access can learn the trace collector address and internal network topology. If credentials or tokens are embedded directly in a Zipkin collector URL, those values can also be recovered from the logs.

There is no exposure with the default OpenTelemetry logger, and the vulnerable log is generated from local application configuration rather than remotely supplied span data. OTLP authentication headers, certificate or private-key contents, and telemetry payloads are not logged by this path.

Remediation

Upgrade the affected OpenTelemetry Go modules to version 1.45.0 or later. The fix in 3a1412d stops recursively marshaling exporter and client configuration and records their types instead.

If an immediate upgrade is not possible:

  • keep OpenTelemetry internal logging below the Info verbosity described above;
  • do not embed credentials or tokens in exporter endpoint URLs; use authentication headers or another supported credential mechanism; and
  • restrict access to existing logs and rotate any credentials that may already have been recorded.
critical: 0 high: 0 medium: 0 low: 0 unspecified: 1golang.org/x/crypto 0.57.0 (golang)

pkg:golang/golang.org/x/crypto@0.57.0

unspecified : GO--2026--5932

Affected range>=0
Fixed versionNot Fixed
Description

The golang.org/x/crypto/openpgp package is unsafe by design, has numerous known security issues, is not maintained, and should not be used.

If you are required to interoperate with OpenPGP systems and need a maintained package, consider github.com/ProtonMail/go-crypto/openpgp which is a maintained fork that aims to be a drop-in replacement for this package.

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

🔍 Vulnerabilities of permitio/pdp-v2:next

📦 Image Reference permitio/pdp-v2:next
digestsha256:29e30d69bb483319cb54f5aef771016653f95aa487263b8fd79b1f88edd3667f
vulnerabilitiescritical: 0 high: 0 medium: 0 low: 0
platformlinux/amd64
size140 MB
packages247
📦 Base Image oisupport/staging-amd64:026d26881d2e1ebad06e4f309b0fc5f03c0471c5230d325d7b571be802586ee6
also known as
  • 3.13-alpine3.23
  • 3.13.15-alpine3.23
digestsha256:f282f385cfce21b0a644094330930704424a48d59afe8f08052e0f8e0b7a35c6
vulnerabilitiescritical: 0 high: 3 medium: 2 low: 0

@zeevmoney zeevmoney left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved — no CRITICAL or HIGH issues found.

Non-blocking:

  • MEDIUM .github/workflows/release.yml:73 — "Little is lost" leaves out rust_chef (~4 min per build); notes at 86-93, 112-119 and Dockerfile:104-105/165 go stale
  • MEDIUM .github/workflows/tests.yml:117 — with no-cache-filters: main, the mode=min export is never read back
  • LOW .github/workflows/tests.yml:116 — comment omits the pdp binary the final image takes from the Rust stages
  • LOW .github/workflows/tests.yml:167 — the token-bearing pdp-tester checkout is on a mutable tag

Details are in the inline comments on each line.

Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/tests.yml Outdated
Comment thread .github/workflows/tests.yml Outdated
Comment thread .github/workflows/tests.yml
…R-16640)

Review follow-ups on #346:
- mode=min exported only `main`'s layers, which no-cache-filters: main never
  reads back. Drop cache-to from the image builds and cache rust_chef (no
  COPY/ADD, ~4 min cold) by itself in scope `rust-chef`, read by the image builds
  in tests.yml and release.yml.
- The arm64 opa_build step no longer reads the default scope.
- Fix the comments that described mode=max behavior, and the final-image wording
  (/opa and /app/pdp).
- Pin the pdp-tester checkout to the same checkout SHA as the other
  token-bearing checkouts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings September 30, 2026 17:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@EliMoshkovich
EliMoshkovich merged commit c96697e into main Sep 30, 2026
8 of 9 checks passed
@EliMoshkovich
EliMoshkovich deleted the eli/per-16640-ci-cache-hardening branch September 30, 2026 18:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants