Skip to content

fix(deps): bump @opentelemetry/sdk-node to >=0.217.0 for high severity CVE#458

Closed
sergioestebance wants to merge 2 commits into
mainfrom
fix/deps-bump-opentelemetry-sdk-node-to-0.217.0
Closed

fix(deps): bump @opentelemetry/sdk-node to >=0.217.0 for high severity CVE#458
sergioestebance wants to merge 2 commits into
mainfrom
fix/deps-bump-opentelemetry-sdk-node-to-0.217.0

Conversation

@sergioestebance

@sergioestebance sergioestebance commented May 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Bumps @opentelemetry/sdk-node from 0.212.0 to 0.217.0 to address high severity CVE:

  • GHSA-q7rr-3cgh-j5r3: Prometheus exporter process crash via malformed HTTP request (@opentelemetry/exporter-prometheus is a transitive dependency of @opentelemetry/sdk-node)

Changes

  • package.json (root): ^0.212.0 -> ^0.217.0
  • javascript/package.json: 0.212.0 -> 0.217.0
  • javascript/examples/custom-observability/package.json: bumped @opentelemetry/sdk-trace-base and @opentelemetry/sdk-trace-node from ^1.30.0 to ^2.7.1 to match the new sdk-node transitive dependency versions
  • javascript/pnpm-lock.yaml: regenerated

Compatibility

  • @opentelemetry/api@1.9.1 remains compatible (sdk-node 0.217.0 requires >=1.3.0 <1.10.0)
  • @opentelemetry/exporter-prometheus resolved to 0.217.0 (patched)

Closes Dependabot alerts #353, #355, #356, and #357.

Linked to #400

rogeriochaves
rogeriochaves previously approved these changes May 12, 2026
@sergioestebance

Copy link
Copy Markdown
Contributor Author

Review Assessment

Risk: Medium — Run JS tests before merging

Diff Analysis

  • package.json (root): ^0.212.0^0.217.0
  • javascript/package.json: 0.212.00.217.0
  • javascript/examples/custom-observability/package.json: sdk-trace-base and sdk-trace-node bumped ^1.30.0^2.7.1 ⚠️
  • javascript/pnpm-lock.yaml: regenerated via pnpm install
  • langwatch resolution hash changed (81f561df...4c2f69e8...) — expected, pnpm recalculates when peer dep graph changes ✅

Usage

This is a production dependency. The codebase imports from OpenTelemetry packages extensively:

Import Used in Location
SimpleSpanProcessor Production src/tracing/setup.ts
ReadableSpan, SpanProcessor Production src/agents/judge/*.ts
trace, context Production src/execution/scenario-execution.ts
NodeTracerProvider Tests only src/execution/__tests__/*.ts

Concerns

  1. Major version jump in transitive dep used by production code: @opentelemetry/sdk-trace-base goes from 1.30.1 → 2.7.1 (major semver break). While sdk-trace-base is not a direct dependency of javascript/package.json, production code imports SimpleSpanProcessor, ReadableSpan, and SpanProcessor from it. The OpenTelemetry JS team shipped this as a coordinated release and these APIs are stable, but the major version boundary warrants validation.

  2. Example code bumped across major version: javascript/examples/custom-observability/package.json jumps sdk-trace-base and sdk-trace-node from ^1.30.0 to ^2.7.1. This is shipped to users as reference code — correct to update, but notable.

  3. Root package.json uses ^ range: Consumers installing @langwatch/scenario will pull sdk-node 0.217+. This is fine since 0.x has no semver guarantees anyway.

Recommendation

Run cd javascript && pnpm test to validate that SimpleSpanProcessor, ReadableSpan, and SpanProcessor APIs are compatible with sdk-trace-base 2.x before merging.

Verdict

Likely safe, but the implicit sdk-trace-base 1.x → 2.x major jump means tests should pass before merging. No changes to application logic — purely dependency version bumps.

…DK v2

In @opentelemetry/sdk-trace-base 2.x, `parentSpanId` was removed from the
`ReadableSpan` interface. The canonical accessor is now
`parentSpanContext?.spanId`. Updated all three `getParentSpanId()` helpers
to use `parentSpanContext` as the primary path while keeping a runtime
fallback to the legacy `parentSpanId` property for backward compatibility.
@github-actions

Copy link
Copy Markdown
Contributor

Automated low-risk assessment

This PR was evaluated against the repository's Low-Risk Pull Requests procedure and does not qualify as low risk.

The PR updates @opentelemetry/sdk-node and many transitive OpenTelemetry packages (lockfile regenerated) and also changes runtime code that extracts parent span IDs. These are changes to a third‑party integration and modify application behavior (not just docs/tests/formatting), so it does not meet the low‑risk criteria.

This PR requires a manual review before merging.

@sergioestebance

Copy link
Copy Markdown
Contributor Author

Security Assessment: Dependabot Alerts #353#357 — Prometheus Exporter DoS (CVE in @opentelemetry/exporter-prometheus < 0.217.0)

Verdict: We are NOT exploitable. The alerts can be safely dismissed.


Executive Summary

The vulnerability allows a single malformed HTTP request to crash any Node.js process running the OpenTelemetry Prometheus exporter's built-in HTTP server. However, after exhaustive code-path tracing through three layers of the dependency chain, this project never starts the Prometheus exporter HTTP server, and there is no configuration path — explicit or implicit — that would cause it to start.

The production code changes in this PR are not security fixes — they are compatibility adjustments required by the OTel SDK v2 API breaking changes (parentSpanIdparentSpanContext?.spanId). These changes carry regression risk and are only necessary if we upgrade.


Detailed Analysis

1. The Vulnerability (confirmed in installed code)

File: node_modules/@opentelemetry/exporter-prometheus@0.212.0/build/src/PrometheusExporter.js:164-172

_requestHandler = (request, response) => {
    if (request.url != null &&
        new url_1.URL(request.url, this._baseUrl).pathname === this._endpoint) {
        this._exportMetrics(response);
    } else {
        this._notFound(response);
    }
};

No try-catch. Sending GET http:// HTTP/1.1 causes new URL('http://', ...) to throw an uncaught TypeError, crashing the process. The fix in 0.217.0 wraps this in try-catch and returns HTTP 400.

2. How the Prometheus server starts (confirmed in installed code)

File: node_modules/@opentelemetry/exporter-prometheus@0.212.0/build/src/PrometheusExporter.js:84-93

The HTTP server auto-starts in the constructor unless preventServerStart: true is passed:

this._server = createServer(this._requestHandler).unref();
// ...
if (config.preventServerStart !== true) {
    this.startServer().then(callback, err => { ... });
}

Default: binds to 0.0.0.0:9464, unauthenticated.

3. When does PrometheusExporter get instantiated? (confirmed in installed code)

File: node_modules/@opentelemetry/sdk-node@0.212.0/build/src/sdk.js:38-66

NodeSDK calls getMetricReadersFromEnv() which instantiates PrometheusExporter only if OTEL_METRICS_EXPORTER env var contains "prometheus":

function getMetricReadersFromEnv() {
    const enabledExporters = Array.from(new Set(getStringListFromEnv('OTEL_METRICS_EXPORTER') ?? []));
    if (enabledExporters.length === 0) {
        enabledExporters.push('otlp');  // DEFAULT IS OTLP, NOT PROMETHEUS
    }
    // ...
    if (exporter === 'prometheus') {
        metricReaders.push(new PrometheusExporter());  // Only if explicitly set
    }
}

When OTEL_METRICS_EXPORTER is unset, it defaults to 'otlp' — Prometheus is never instantiated.

4. Does this project ever trigger that code path? NO.

Path 1 — Direct: Zero files in javascript/src/, javascript/examples/, python/, or any config file import, reference, or configure PrometheusExporter, exporter-prometheus, or OTEL_METRICS_EXPORTER.

Path 2 — Via langwatch SDK: setupObservability() in langwatch@0.16.1 (chunk-C2X4KWDK.js:239-254) creates a NodeSDK and passes metricReader: options.metricReader. The scenario project's initializeFullSetup() (javascript/src/tracing/setup.ts:170-178) never passes metricReader, so it arrives as undefined.

Critical logic in sdk.js:170-188:

if (configuration.metricReaders) {       // undefined → skip
    // use explicit readers
} else if (configuration.metricReader) { // undefined → skip
    // use deprecated single reader
} else {
    // FALLS THROUGH TO getMetricReadersFromEnv()
}

Since neither metricReaders nor metricReader is passed, it does call getMetricReadersFromEnv(). But as shown in step 3, this only creates a Prometheus exporter if OTEL_METRICS_EXPORTER=prometheus is set.

Path 3 — Environment: Exhaustive search of all .env, .env.example, CI workflows (.github/workflows/*.yml), Makefiles, and config files: zero references to OTEL_METRICS_EXPORTER or OTEL_EXPORTER_PROMETHEUS_* anywhere in the project.

Path 4 — End users of @langwatch/scenario: The published package.json declares "@opentelemetry/sdk-node": "^0.212.0" as a dependency. End users would need to explicitly set OTEL_METRICS_EXPORTER=prometheus in their environment to trigger the vulnerability. This is not documented, not suggested in examples, and not a supported configuration.

5. Production code changes in this PR are OTel v2 compatibility fixes, not security fixes

The PR modifies three source files:

  • judge-span-collector.ts — changes getParentSpanId() helper
  • judge-span-digest-formatter.ts — same helper change
  • span-utils.ts — same helper change

These adapt the parentSpanId accessor from the OTel SDK v1 API (span.parentSpanId) to the v2 API (span.parentSpanContext?.spanId). This is required because @opentelemetry/sdk-node@0.217.0 pulls in @opentelemetry/sdk-trace-base@2.7.1 which removed parentSpanId from the ReadableSpan interface.

These changes are necessary only if we upgrade. They are not security fixes — they are breaking-change adaptations.


Risk Assessment

Factor Status
Prometheus exporter imported in source code No
Prometheus exporter configured programmatically No
OTEL_METRICS_EXPORTER=prometheus set anywhere No
Prometheus HTTP server starts at runtime No
Port 9464 referenced anywhere No
Default env behavior triggers Prometheus No (defaults to otlp)
End-user docs suggest Prometheus config No
Production code changes required by upgrade Yes — 3 files, regression risk

Recommendation

Dismiss the Dependabot alerts. The vulnerability requires the Prometheus exporter's HTTP server to be running, which never happens in this project. The @opentelemetry/exporter-prometheus package is a transitive dependency of @opentelemetry/sdk-node that is never activated.

If we want to be extra cautious, the correct fix would be to pass metricReader: 'none' or metricReaders: [] to the NodeSDK constructor in the langwatch SDK to prevent getMetricReadersFromEnv() from ever being called — eliminating the theoretical env-var attack vector entirely, without requiring a major version bump.

Upgrading @opentelemetry/sdk-node from 0.212.0 to 0.217.0 is a semver-minor bump that includes breaking changes (OTel experimental packages don't follow semver strictly). It forces production code changes to adapt to the v2 ReadableSpan interface, introduces regression risk, and solves a vulnerability we are not exposed to.


Files examined: node_modules/@opentelemetry/sdk-node@0.212.0/build/src/sdk.js, node_modules/@opentelemetry/exporter-prometheus@0.212.0/build/src/PrometheusExporter.js, node_modules/langwatch@0.16.1/dist/chunk-C2X4KWDK.js, javascript/src/tracing/setup.ts, javascript/src/agents/judge/judge-span-collector.ts, javascript/src/agents/judge/judge-span-digest-formatter.ts, javascript/src/agents/judge/span-utils.ts, all .env* files, all .github/workflows/*.yml, package.json (root + javascript), pnpm-lock.yaml.

@sergioestebance

Copy link
Copy Markdown
Contributor Author

Independent Security Assessment — Prometheus Exporter DoS (GHSA-q7rr-3cgh-j5r3)

Verdict: Confirmed — we are NOT exploitable. The alerts can be safely dismissed.


Methodology

I independently traced the full activation path for @opentelemetry/exporter-prometheus@0.212.0 through four layers: the vulnerable code itself, the @opentelemetry/sdk-node constructor, the langwatch SDK's setupObservability(), and this project's setup.ts.

Findings

1. Vulnerability confirmed in installed code

PrometheusExporter._requestHandler passes request.url to new URL() without try-catch. A malformed request (e.g. GET http:// HTTP/1.1) throws an uncaught TypeError that crashes the Node.js process. The HTTP server auto-starts in the constructor unless preventServerStart: true is passed.

2. Activation requires explicit opt-in via OTEL_METRICS_EXPORTER=prometheus

In @opentelemetry/sdk-node@0.212.0/build/src/sdk.js, the constructor checks:

  1. If configuration.metricReaders is set → use those (skip env)
  2. Else if configuration.metricReader is set → use that (skip env)
  3. Else → call getMetricReadersFromEnv() which reads OTEL_METRICS_EXPORTER

getMetricReadersFromEnv() defaults to 'otlp' when the env var is unset. PrometheusExporter is only instantiated if the env var explicitly contains "prometheus".

3. This project never passes metricReader — but the default is safe

  • setup.ts:initializeFullSetup() calls the langwatch SDK's setupObservability() with spanProcessors but no metricReader/metricReaders.
  • The langwatch SDK (chunk-C2X4KWDK.js) passes metricReader: options.metricReader to NodeSDK — which is undefined.
  • This means NodeSDK falls through to getMetricReadersFromEnv() — but since OTEL_METRICS_EXPORTER is unset everywhere in this project (checked all .env*, CI workflows, Dockerfiles, Makefiles), it defaults to 'otlp'.

4. Zero references to Prometheus anywhere in source or config

Searched all .ts, .js, .env*, .yml, and config files in the project — zero hits for PrometheusExporter, exporter-prometheus, OTEL_METRICS_EXPORTER, OTEL_EXPORTER_PROMETHEUS, or port 9464.

Risk Summary

Check Result
Prometheus exporter imported in source No
Prometheus configured programmatically No
OTEL_METRICS_EXPORTER=prometheus set anywhere No
Default env behavior triggers Prometheus No (defaults to otlp)
Prometheus HTTP server starts at runtime No
Port 9464 referenced anywhere No

Theoretical edge case

If an attacker could inject OTEL_METRICS_EXPORTER=prometheus into the runtime environment, the exporter would start. However, this requires a prior environment compromise — which is a much more severe vulnerability on its own. To eliminate even this theoretical vector, the langwatch SDK could pass metricReaders: [] explicitly.

Recommendation

Dismiss Dependabot alerts #353#357. The upgrade to @opentelemetry/sdk-node@0.217.0 introduces a major semver break in transitive dependencies (sdk-trace-base 1.x → 2.x) that requires production code changes for a vulnerability we are not exposed to. The risk/reward does not justify the upgrade.


Independent assessment — traced code paths through installed node_modules without relying on the prior review.

Related: #400

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.

2 participants