Nov 13, 2026·10 min read·3 visits
An inefficient regular expression in opentelemetry-api prior to version 1.45.2 allows unauthenticated attackers to trigger quadratic-time CPU exhaustion via crafted W3C traceparent headers containing trailing spaces and embedded newlines.
CVE-2026-126360 (GHSA-mf5v-w3w3-f6cg) is an algorithmic complexity vulnerability (Regular Expression Denial of Service) in the trace context propagation logic of the opentelemetry-api package within the OpenTelemetry Python ecosystem. In versions prior to 1.45.2, the TraceContextTextMapPropagator.extract() method utilizes a regular expression susceptible to quadratic-time backtracking when parsing malformed W3C traceparent headers containing long sequences of whitespace followed by an embedded newline character. In Python applications—particularly those using single-threaded asynchronous event loops—an unauthenticated remote attacker transmitting payloads over newline-preserving carriers can monopolize CPU execution and induce application-level Denial of Service.
OpenTelemetry Python (open-telemetry/opentelemetry-python) provides the standard API and SDK implementations for generating, collecting, and propagating distributed telemetry data in Python applications. A critical function of the opentelemetry-api package is context propagation, which enables distributed traces to correlate spans across process and network boundaries. The TraceContextTextMapPropagator class implements the W3C Trace Context specification, parsing incoming traceparent and tracestate fields from carrier objects on every instrumented incoming request.
Because context extraction occurs at the ingress point of instrumented services, TraceContextTextMapPropagator.extract() is directly exposed to untrusted external input. In versions of opentelemetry-api prior to 1.45.2, the regular expression responsible for validating and tokenizing the traceparent header string contains an algorithmic complexity flaw classified under CWE-1333 (Inefficient Regular Expression Complexity).
When evaluating a specially structured traceparent string that starts with a valid prefix followed by a hyphen, a sequence of whitespace characters, an embedded newline (\n), and at least one trailing character, the Python re engine enters a catastrophic backtracking loop. The evaluation complexity scales quadratically ($O(N^2)$) with the number of whitespace characters in the payload.
In Python runtime environments, CPU-bound regular expression evaluation holds the Global Interpreter Lock (GIL) and blocks the executing OS thread. For applications built on asynchronous frameworks such as FastAPI, Starlette, aiohttp, Quart, or Tornado, context propagation runs synchronously on the main event loop thread. Consequently, processing a single malicious carrier payload blocks the entire event loop, starving all concurrent requests and causing an application-wide Denial of Service (DoS).
The root cause of CVE-2026-126360 lies in the construction of _TRACEPARENT_HEADER_FORMAT within opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py. The W3C Trace Context specification defines traceparent as four hyphen-separated hexadecimal fields: version (2 characters), trace-id (32 characters), parent-id (16 characters), and trace-flags (2 characters). For forward compatibility with higher specification versions, the parser permits optional trailing fields prefixed by a hyphen (-.*), as well as optional leading and trailing whitespace ([ \t]*).
In vulnerable versions, the regular expression combined this optional suffix group directly with the trailing whitespace pattern before the end-of-string anchor: (-.*)?[ \t]*$. This juxtaposition creates two adjacent subpatterns containing unbounded quantifiers (.* and [ \t]*) that can both match horizontal whitespace characters ( and \t).
When re.search() evaluates an input string containing a valid 55-character prefix followed by "-" + (" " * N) + "\nx", the initial four groups match the prefix and the optional group (-.*)? is entered upon encountering the fifth hyphen. Because the . metacharacter in Python's re module matches any character except a newline (\n) by default, the greedy .* quantifier consumes all $N$ space characters up to the embedded \n.
Next, the engine attempts to satisfy [ \t]*$ against the remaining input substring "\nx". While [ \t]* matches zero characters, the $ anchor only matches the end of the string or immediately before a newline at the very end of the string. Because \n is followed by x, the $ assertion fails.
Upon failure at $, the NFA regular expression engine backtracks into (-.*)?. The .* quantifier surrenders one space character, leaving it to be matched by [ \t]*. The engine then re-evaluates $ against "\nx", which fails again. The .* quantifier then surrenders a second space, [ \t]* consumes both surrendered spaces, and $ fails once more. For an input containing $N$ spaces, the engine performs $\sum_{k=1}^{N} k = \frac{N(N + 1)}{2}$ backtracking steps, resulting in $O(N^2)$ time complexity. Furthermore, because extract() lacked any maximum length validation prior to executing re.search(), an attacker could supply arbitrarily large values of $N$.
The vulnerability resides in opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py. Prior to version 1.45.2, TraceContextTextMapPropagator compiled the vulnerable regular expression at class definition time and applied it directly to the first element extracted from the carrier without prior boundary checks:
# Vulnerable implementation (< 1.45.2)
class TraceContextTextMapPropagator(textmap.TextMapPropagator):
_TRACEPARENT_HEADER_NAME = "traceparent"
_TRACESTATE_HEADER_NAME = "tracestate"
_TRACEPARENT_HEADER_FORMAT = (
"^[ \t]*([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})"
+ "(-.*)?[ \t]*$" # BUG: Overlapping match space between '.*' and '[ \t]*'
)
_TRACEPARENT_HEADER_FORMAT_RE = re.compile(_TRACEPARENT_HEADER_FORMAT)
def extract(
self,
carrier: textmap.CarrierT,
context: typing.Optional[context.Context] = None,
getter: textmap.Getter[textmap.CarrierT] = default_getter,
) -> context.Context:
if context is None:
context = context_api.get_current()
header = getter.get(carrier, self._TRACEPARENT_HEADER_NAME)
if not header:
return context
# BUG: Unbounded input passed directly to regex engine
match = re.search(self._TRACEPARENT_HEADER_FORMAT_RE, header[0])
if not match:
return contextThe patch, introduced in commit faa72dae511ae64a7bf8ca63eff9ad4387d0b939 (PR #5818) and backported via commit 78123a2755c907c602e02352aebed4be86aa5350 (PR #5856), resolves the issue through two defense-in-depth modifications:
# Patched implementation (>= 1.45.2)
class TraceContextTextMapPropagator(textmap.TextMapPropagator):
_TRACEPARENT_HEADER_NAME = "traceparent"
_TRACESTATE_HEADER_NAME = "tracestate"
_TRACEPARENT_HEADER_FORMAT = (
"^[ \t]*([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})"
+ "(?:(-.*)|[ \t]*)$" # FIX 1: Mutually exclusive alternation eliminates backtracking
)
_TRACEPARENT_HEADER_FORMAT_RE = re.compile(_TRACEPARENT_HEADER_FORMAT)
_TRACEPARENT_MAXIMUM_LENGTH = 512 # FIX 2: Hard upper bound on input length
# pylint: disable-next=too-many-return-statements
def extract(
self,
carrier: textmap.CarrierT,
context: typing.Optional[context.Context] = None,
getter: textmap.Getter[textmap.CarrierT] = default_getter,
) -> context.Context:
if context is None:
context = context_api.get_current()
header = getter.get(carrier, self._TRACEPARENT_HEADER_NAME)
if not header:
return context
# FIX 2: Reject overly long headers before regex evaluation
if len(header[0]) > self._TRACEPARENT_MAXIMUM_LENGTH:
_logger.warning(
"traceparent header exceeded the maximum length of %d characters",
self._TRACEPARENT_MAXIMUM_LENGTH,
)
return context
match = re.search(self._TRACEPARENT_HEADER_FORMAT_RE, header[0])
if not match:
return contextFirst, the regular expression tail (-.*)?[ \t]*$ was replaced with the non-capturing alternation (?:(-.*)|[ \t]*)$. Under this structure, the engine either matches the hyphen-prefixed suffix (-.*) up to the end of the line, or—if no hyphen is present—matches trailing whitespace [ \t]*. Because the two branches are mutually exclusive and (-.*) is no longer followed by [ \t]*, characters surrendered by .* during backtracking have no subsequent subpattern to match against, reducing evaluation to linear time $O(N)$.
Second, the patch enforces a strict maximum length check (_TRACEPARENT_MAXIMUM_LENGTH = 512) prior to calling re.search(). Note that for version 00 headers, trailing characters matched by (-.*) are subsequently rejected by post-regex validation checks in extract() (if version == "00" and match.group(5): return context), whereas higher versions allow suffixes containing trailing spaces. Both the structural regex fix and the 512-character ceiling completely eliminate the quadratic complexity vector.
Exploiting CVE-2026-126360 requires delivering a traceparent value to TraceContextTextMapPropagator.extract() that satisfies three structural conditions:
^[ \t]*([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2}) followed immediately by a hyphen (-).0x20) or horizontal tab (0x09) characters.\n, 0x0A) followed by at least one non-matching character (e.g., x).A key architectural nuance of exploitation involves the transport carrier. Standard HTTP/1.1 and HTTP/2 gateways, load balancers, and WSGI/ASGI servers strictly enforce RFC 9110 (and RFC 7230) header syntax, which forbids raw newline (\n) bytes inside HTTP header values. Consequently, attempts to exploit this flaw over standard HTTP headers will normally be rejected at the HTTP parsing layer before reaching OpenTelemetry instrumentation.
However, TraceContextTextMapPropagator is transport-agnostic and extracts context from arbitrary Python dictionary mappings (CarrierT). Exploitation is directly viable across protocols and serialization formats that preserve newline characters inside string values:
\n sequences into literal newline characters in Python memory prior to calling extract().The following proof-of-concept demonstrates the quadratic execution time degradation in opentelemetry-api < 1.45.2:
import time
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
propagator = TraceContextTextMapPropagator()
# 1. Construct a valid 55-character W3C traceparent prefix
valid_prefix = "01-12345678901234567890123456789012-1234567890123456-01"
# 2. Append hyphen, 50,000 spaces, newline, and trailing character
payload_length = 50000
malformed_traceparent = valid_prefix + "-" + (" " * payload_length) + "\nx"
carrier = {
"traceparent": malformed_traceparent
}
# 3. Trigger extraction and measure execution time
start_time = time.perf_counter()
propagator.extract(carrier)
elapsed = time.perf_counter() - start_time
print(f"Extraction completed in: {elapsed:.4f} seconds")> [!NOTE]
> Doubling payload_length from 50,000 to 100,000 quadruples the CPU processing time required for a single extract() call, allowing a lightweight ~100 KB payload to pin a CPU core for several seconds.
CVE-2026-126360 is rated Medium severity with a CVSS v3.1 base score of 5.3 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L). The attack vector is Network (AV:N) with Low attack complexity (AC:L), requiring neither privileges (PR:N) nor user interaction (UI:N). Because the vulnerability is strictly an algorithmic resource exhaustion flaw, Confidentiality (C:N) and Integrity (I:N) are unaffected.
The primary operational impact is Availability degradation (A:L). In Python, the re engine executes in C without releasing the Global Interpreter Lock (GIL) during single pattern match evaluations. In multi-threaded WSGI servers (such as Gunicorn sync/threaded workers), each malicious request monopolizes one worker thread and blocks other threads within the same process from executing Python bytecode while the GIL is held.
The impact is amplified in asynchronous Python servers (such as Uvicorn running FastAPI or Starlette, or aiohttp services) and single-threaded message consumers. Because TraceContextTextMapPropagator.extract() runs synchronously inside middleware or message handler callbacks on the main asyncio event loop, blocking that thread halts all concurrent I/O multiplexing, health check responses, and request handling for the duration of the regular expression evaluation. Sustained transmission of crafted payloads at a low request rate is sufficient to render the target service unresponsive and trigger container liveness probe failures.
As of November 2026, the Exploit Prediction Scoring System (EPSS) assigns CVE-2026-126360 a score of 0.00519 (0.52% probability of exploitation within 30 days, 44.64th percentile), and the vulnerability is not listed in CISA's Known Exploited Vulnerabilities (KEV) catalog.
To remediate CVE-2026-126360, upgrade the opentelemetry-api package to version 1.45.2 or later (or 0.66b2 for prerelease instrumentation tracks). Version 1.45.2 enforces a 512-character ceiling on traceparent headers and eliminates the backtracking regular expression pattern.
Update the dependency using pip:
pip install --upgrade "opentelemetry-api>=1.45.2"Ensure that dependency lockfiles (requirements.txt, poetry.lock, Pipfile.lock, or uv.lock) are updated and container images are rebuilt to incorporate the patched package:
opentelemetry-api>=1.45.2
opentelemetry-sdk>=1.45.2For environments where an immediate dependency upgrade is restricted by version constraints, apply an application-level workaround by sanitizing carrier dictionaries or wrapping the custom Getter passed to extract() to reject traceparent strings exceeding 512 bytes or containing line break characters (\n or \r):
from opentelemetry.propagate import get_global_textmap
def safe_extract(carrier: dict):
tp = carrier.get("traceparent")
if isinstance(tp, str) and (len(tp) > 512 or "\n" in tp or "\r" in tp):
carrier = carrier.copy()
carrier.pop("traceparent", None)
elif isinstance(tp, list) and tp and (len(tp[0]) > 512 or "\n" in tp[0] or "\r" in tp[0]):
carrier = carrier.copy()
carrier.pop("traceparent", None)
return get_global_textmap().extract(carrier)Additionally, organizations exposing JSON-RPC, Model Context Protocol (MCP), or message queue consumers to untrusted producers should enforce strict schema validation at API gateways and ingestion boundaries, restricting traceparent fields to a maximum length of 512 ASCII characters and rejecting control characters (0x00–0x1F).
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
opentelemetry-api OpenTelemetry | < 1.45.2 | 1.45.2 |
| Attribute | Detail |
|---|---|
| Vulnerability Type | Regular Expression Denial of Service (ReDoS) |
| CWE Classification | CWE-1333: Inefficient Regular Expression Complexity |
| CVSS v3.1 Score | 5.3 Medium (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L) |
| EPSS Score | 0.52% (44.64th Percentile) |
| Affected Component | opentelemetry-api (< 1.45.2) TraceContextTextMapPropagator |
| Patched Version | opentelemetry-api 1.45.2 |
| Exploit Status | Proof-of-Concept (PoC) Available |
| CISA KEV Status | Not Listed |
The product uses a regular expression with an inefficient, possibly exponential worst-case computational complexity that consumes excessive CPU resources when evaluating crafted inputs.
CVE-2026-128068 (GHSA-cpgf-5mjf-73p7) is a Regular Expression Denial of Service (ReDoS) vulnerability in the rails-html-sanitizer Ruby gem affecting versions 1.0.3 through 1.7.1. The flaw resides in the Rails::HTML::PermitScrubber#scrub_attribute method, which uses the multiline regular expression /^\s*[^#\s].*/m to enforce local-only URI fragment references on SVG elements. On Ruby runtimes prior to version 3.2 (CRuby <= 3.1 and JRuby), evaluating an attribute value composed of repeated newline and whitespace characters without a hash character triggers quadratic backtracking O(n^2). Applications are only vulnerable if their sanitization allowlist is explicitly configured to permit SVG reference elements such as <use> alongside href or xlink:href attributes.
CVE-2026-126359 (GHSA-3gg2-335m-rxc6) is an improper input neutralization vulnerability (CWE-79) in the Hono web framework (`honojs/hono`). Versions from 3.12.0 prior to 4.13.16 evaluate and escape interpolated strings in the `hono/css` helper individually before concatenating template chunks and stripping CSS comments. When a `<` character and a `/style>` sequence are split across template boundaries, adjacent interpolations, or separated by a CSS comment, the sanitizer fails to escape the closing tag sequence. During Server-Side Rendering (SSR), the `<Style />` component outputs an unescaped `</style>` delimiter, permitting an attacker to break out of the CSS raw text context and execute arbitrary HTML and JavaScript in the victim's browser.
An incorrect authorization vulnerability (CWE-863) exists in the Hono web application framework (npm package `hono`) from version 4.1.4 prior to 4.13.16. Flaws in the registration-time coverage calculation and regular expression compilation within `RegExpRouter` cause wildcard middleware to be omitted under three specific routing conditions: parameter pattern equivalence mismatches, wildcard boundary collisions, and request paths containing percent-encoded line terminators. Because Hono's default `SmartRouter` selects `RegExpRouter` for most standard web applications, unauthenticated remote clients can bypass authentication, authorization, or security middleware registered on wildcard paths to directly access protected route handlers or the application's not-found handler.
CVE-2026-126357 (GHSA-4m76-xfj5-7975) is an authentication bypass vulnerability in the Hono web application framework prior to version 4.13.16. The flaw resides in the prefix-stripping and URL reconstruction logic of `app.mount()` and the `mount()` middleware. Two distinct path transformation errors—static route-pattern length slicing on dynamic prefixes and URL normalization of decoded pathnames—create a routing discrepancy between the outer Hono router and the mounted sub-application. An unauthenticated attacker can exploit these discrepancies to bypass outer authentication or authorization middleware and access protected inner routes directly.
A parser differential vulnerability exists in the Hono web application framework versions 4.5.1 prior to 4.13.16. The built-in form validation middleware (`validator('form')`) utilizes an overly restrictive regular expression to evaluate the HTTP `Content-Type` header for `multipart/form-data` payloads. When a client supplies additional valid MIME parameters (such as `charset=utf-8`) or multiple whitespace characters following a semicolon delimiter, the regular expression fails to match. Consequently, the validator passes an empty object to the validation callback instead of the parsed form fields. If the validation logic permits empty objects or only checks for disallowed values on optional properties, the request passes validation, allowing downstream route handlers calling `c.req.parseBody()` or `c.req.formData()` to process unvalidated user input.
CVE-2026-126355 (GHSA-46wx-v44x-9hh2) is a Cross-Site Request Forgery (CSRF) validation bypass in Hono, a web application framework for JavaScript runtimes. Prior to version 4.13.16, Hono's built-in csrf() middleware only evaluated origin headers for requests matching standard HTML form MIME types (application/x-www-form-urlencoded, multipart/form-data, and text/plain). By leveraging the HTML anchor ping attribute in Chromium-based browsers, an attacker can initiate an unprompted, credentialed cross-origin HTTP POST request with Content-Type: text/ping without triggering a CORS preflight request. Because text/ping was omitted from the middleware regular expression, requests bypassed Origin and Sec-Fetch-Site verification, allowing unauthorized state-changing operations.