Summary
The OTLP log gRPC exporter loads TLS settings from environment variables but does not apply them when creating gRPC transport credentials. Operators who rely on OTELEXPORTEROTLPLOGSCERTIFICATE, OTELEXPORTEROTLPCERTIFICATE, or related client certificate variables for CA pinning or mTLS get a connection that falls back to system roots and omits the env-supplied client certificate. A network attacker who can intercept or spoof the collector connection with a system-trusted certificate can read or alter log telemetry.
Introduced in commit: d99c76f
Details
The affected code is in exporters/otlp/otlplog/otlploggrpc.
newConfig resolves env-based TLS configuration into cfg.tlsCfg at exporters/otlp/otlplog/otlploggrpc/config.go:106-116. The finding also identifies loadEnvTLS at config.go:451-492 as the code that builds a tls.Config containing RootCAs and client certificates from OTELEXPORTEROTLP[LOGS]CERTIFICATE and OTELEXPORTEROTLP[LOGS]CLIENTCERTIFICATE/KEY.
However, newGRPCDialOptions in exporters/otlp/otlplog/otlploggrpc/client.go:83-92 only checks cfg.gRPCCredentials and cfg.insecure. When neither is set, which is the normal env-only TLS configuration path, it uses credentials.NewTLS(nil). That default trusts the host system root CAs and contains no env-supplied client certificate. The finding evidence reports no other tlsCfg use in the package, so env-based CA pinning and mTLS settings are loaded but not enforced.
PoC
validation-artifact.zip
The validation artifact contains a ready-to-run test at validation-artifact.zip:./pocenvtlsignoredtest.go and brief instructions at validation-artifact.zip:./README.md.
From a checkout of pellared/opentelemetry-go at commit d99c76f, with Go module dependencies available:
sh FINDINGDIR=/path/to/02-e6e2897a969c8191b260f243fbc99ebd-log-grpc-exporter-ignores-env-tls-certs-bypassing-mtls-pinning cd /path/to/opentelemetry-go git checkout d99c76f tar -xOf validation-artifact.tar ./pocenvtlsignoredtest.go > exporters/otlp/otlplog/otlploggrpc/pocenvtlsignoredtest.go cd exporters/otlp/otlplog/otlploggrpc GO111MODULE=on go test -v -run TestEnvTLSIgnored -count=1
The test generates a private CA and a TLS gRPC logs server certificate signed by that CA. It sets:
sh OTELEXPORTEROTLPLOGSENDPOINT=https://127.0.0.1:<test-port> OTELEXPORTEROTLPLOGSCERTIFICATE=<temp-dir>/ca.pem
Expected output includes an unknown authority failure for the first export call even though the env certificate points to the server CA, followed by a passing test after the same cfg.tlsCfg is explicitly wired through WithTLSCredentials:
text === RUN TestEnvTLSIgnored pocenvtlsignoredtest.go:...: export error (expected due to ignored tlsCfg): ... x509: certificate signed by unknown authority --- PASS: TestEnvTLSIgnored PASS
This demonstrates that the env CA is parsed into cfg.tlsCfg but ignored by the default gRPC dial path.
Impact
This is improper TLS certificate validation and endpoint authentication caused by ignoring configured trust material. Users of the OTLP log gRPC exporter who configure TLS, CA pinning, or mTLS through environment variables are impacted when they do not also supply explicit WithTLSCredentials. TLS still occurs with system roots, but the intended private CA pinning and client certificate authentication are bypassed. An attacker with a suitable network position and a system-trusted certificate for the collector endpoint can intercept or tamper with log telemetry that operators expected to be protected by the configured CA or mTLS policy.
Summary
A BatchingProcessor in go.opentelemetry.io/otel/sdk/log can enter a tight CPU loop when the asynchronous export buffer is full. Under exporter backpressure, attacker-driven high-volume log emission can keep the queue at or above the batch size, causing repeated immediate export retries and a denial of service through CPU exhaustion.
Introduced in commit: 4af9c20
Details
NewBatchingProcessor wraps the exporter with newBufferExporter(exporter, 1) (sdk/log/batch.go:116-122), so the asynchronous export input can fill quickly when the downstream exporter blocks. The poll goroutine dequeues a batch with b.q.TryDequeue, calls b.exporter.EnqueueExport(r), and then immediately sends on b.pollTrigger whenever qLen >= b.batchSize (sdk/log/batch.go:129-165).
bufferExporter.EnqueueExport is non-blocking: it sends to e.input if possible and returns false in the default case when the channel is full (sdk/log/exporter.go:221-248). TryDequeue leaves q.len unchanged when the write callback returns false (sdk/log/batch.go:289-314). Therefore, while the exporter is backpressured, EnqueueExport fails, the queue remains at or above one full batch, and the poll loop continuously retriggers itself without waiting for the ticker.
PoC
validation-artifact.zip
The validation artifact contains a PoC bundle:
- validation-artifact.tar:main.go: PoC source. - validation-artifact.tar:README.md: build/run notes. - validation-artifact.tar:buildfailed.log: captured build failure from the validation environment.
The PoC configures a blocking exporter and a BatchingProcessor with WithExportMaxBatchSize(1), WithExportInterval(5time.Second), and WithMaxQueueSize(2048). It emits 1000 records, records a CPU profile for 750 ms while the exporter is blocked, then writes /workspace/validationartifacts/busyloop.pprof.
Reproduction steps from an affected checkout at commit 4af9c20:
sh cd /workspace/opentelemetry-go git checkout 4af9c20
mkdir -p validationpoc/busyloop /workspace/validationartifacts /tmp/batchingprocessor-busyloop-poc tar -xf /path/to/this/finding/validation-artifact.tar -C /tmp/batchingprocessor-busyloop-poc cp /tmp/batchingprocessor-busyloop-poc/main.go validationpoc/busyloop/main.go
go build -o validationpoc/busyloop/busyloop ./validationpoc/busyloop ./validationpoc/busyloop/busyloop go tool pprof -top /workspace/validationartifacts/busyloop.pprof
Expected program output:
text cpu profile written to /workspace/validationartifacts/busyloop.pprof
Expected profile evidence: hot functions should include (BatchingProcessor).poll, (queue).TryDequeue, and (bufferExporter).EnqueueExport, showing repeated export attempts while the exporter is blocked.
Impact
This is an availability vulnerability: uncontrolled CPU consumption caused by a busy-spin retry loop. Applications using sdk/log BatchingProcessor are impacted when an attacker can cause sustained log emission and the configured exporter or downstream collector is slow, blocked, or otherwise backpressured. The impact is limited to the embedding process but can degrade or deny service for that application.
Summary
The OpenTelemetry Go SDK trace package can fail to enforce AttributeValueLengthLimit for string attributes containing the valid Unicode replacement character U+FFFD. An oversized attacker-controlled attribute value that includes U+FFFD is returned untruncated, bypassing the configured memory/DoS protection and allowing increased per-span memory usage. The finding is low severity because it requires a deployment with attribute value length limits enabled and attacker-controlled data being recorded into span attributes.
Introduced in commit 49a6536
Details
String and string-slice span attributes are truncated through safeTruncate when AttributeValueLengthLimit is non-negative. The finding evidence identifies this enforcement path in sdk/trace/span.go:303-331, with string attributes passed to safeTruncate at sdk/trace/span.go:309-310 and string-slice entries passed to safeTruncate in the loop beginning at sdk/trace/span.go:312.
safeTruncate first calls safeTruncateValidUTF8; if that returns ok=false, it calls strings.ToValidUTF8(input, "") and retries. The relevant code is identified in sdk/trace/span.go:337-355. safeTruncateValidUTF8 treats any utf8.RuneError from utf8.DecodeRuneInString as invalid UTF-8 and immediately returns the original input with ok=false. However, Go also returns utf8.RuneError for a valid encoded U+FFFD rune. The validation artifact confirms this behavior with output r=U+FFFD size=3 runeError=true.
For an input such as "AAAA" + U+FFFD + strings.Repeat("B", 20) and a limit of 5, the first truncation attempt sees U+FFFD as utf8.RuneError and returns the full input with ok=false. strings.ToValidUTF8 does not remove the valid U+FFFD rune, so the second attempt returns the same full input. As a result, the span attribute value remains 27 bytes long even though the configured limit is 5.
PoC
validation-artifact.zip
The validation artifact contains a package-level Go test at validation-artifact.tar:safetruncatebypass/safetruncatepoctest.go and supporting output at validation-artifact.tar:safetruncatebypass/runecheckoutput.txt.
Reproduction configuration: - Repository: pellared/opentelemetry-go - Commit: 49a6536 from September 12, 2022 - Package/module path: sdk/trace under the sdk module - Attribute value length limit used by the PoC: limit := 5 - Dependencies must be available through the network or a local module cache/vendor directory.
Commands: sh cd /path/to/opentelemetry-go git checkout 49a6536 tar -xOf /path/to/validation-artifact.tar safetruncatebypass/safetruncatepoctest.go > sdk/trace/safetruncatepoctest.go cd sdk go test ./trace -run TestSafeTruncateBypass -count=1 -v
Expected vulnerable output includes a failing test showing that the returned value exceeds the configured limit: text === RUN TestSafeTruncateBypass safetruncatepoctest.go:14: inputlen=27 gotlen=27 input="AAAA�BBBBBBBBBBBBBBBBBBBB" got="AAAA�BBBBBBBBBBBBBBBBBBBB" safetruncatepoctest.go:16: bypass: gotlen 27 > limit 5 --- FAIL: TestSafeTruncateBypass
The artifact also records the standalone UTF-8 behavior needed for the bypass: sh tar -xOf /path/to/validation-artifact.tar safetruncatebypass/runecheckoutput.txt
Expected output: text r=U+FFFD size=3 runeError=true
Impact
This is a Unicode handling and resource-limit bypass that weakens span attribute memory controls. Applications that enable AttributeValueLengthLimit to bound memory usage can still store oversized attacker-controlled attribute values if those values contain U+FFFD. The practical impact is increased memory use and reduced denial-of-service protection in the instrumented process; the finding does not show confidentiality or integrity impact.
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:
go 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:
text 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.
OpenTelemetry-Go is the Go implementation of OpenTelemetry. Prior to 1.43.0, the otlp HTTP exporters (traces/metrics/logs) read the full HTTP response body into an in-memory bytes.Buffer without a size cap. This is exploitable for memory exhaustion when the configured collector endpoint is attacker-controlled (or a network attacker can mitm the exporter connection). This vulnerability is fixed in 1.43.0.
OpenTelemetry-Go is the Go implementation of OpenTelemetry. From 1.15.0 to 1.42.0, the fix for CVE-2026-24051 changed the Darwin ioreg command to use an absolute path but left the BSD kenv command using a bare name, allowing the same PATH hijacking attack on BSD and Solaris platforms. This vulnerability is fixed in 1.43.0.
OpenTelemetry-Go is the Go implementation of OpenTelemetry. From 1.36.0 to 1.40.0, multi-value baggage: header extraction parses each header field-value independently and aggregates members across values. This allows an attacker to amplify cpu and allocations by sending many baggage: header lines, even when each individual value is within the 8192-byte per-value parse limit. This vulnerability is fixed in 1.41.0.