high · 8.5CVE-2026-73654Aug 13, 2026

CVE-2026-73654: @trigger.dev/core Prototype Pollution via Run Metadata Operations

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

A low-privilege API key holder can pollute Object.prototype in the shared Trigger.dev webapp process with a single HTTP request, crashing the server for all tenants and breaking other tenants' worker…

Package@trigger.dev/core
Ecosystemnpm
Affected>= 3.3.8, <= 4.5.5
Fixed in4.5.6
CVE-2026-73654: @trigger.dev/core Prototype Pollution via Run Metadata Operations

The problem

The run-metadata update endpoint (PUT /api/v1/runs/:runId/metadata) accepts a JSON array of operations. Each operation has a key field that is passed directly into new JSONHeroPath(operation.key).set(newMetadata, value) in packages/core/src/v3/runMetadata/operations.ts with no validation.

@jsonhero/path@^1.0.21 treats __proto__ as an ordinary path segment, so a key like $.__proto__.polluted writes to Object.prototype in the Node.js process. Because the webapp is multi-tenant and shared, every subsequent plain-object lookup inherits the injected property, corrupting Prisma query builders, breaking other tenants' worker authentication, and crashing the process via an uncaughtException in prom-client.

One request from any normal environment API key (tr_dev_...) is enough.

Proof of concept

A working proof-of-concept for CVE-2026-73654 in @trigger.dev/core, with the exact payload below.

bash
curl -X PUT "http://localhost:8030/api/v1/runs/run_cmqr2bsyo00013js2twwhdsfu/metadata" \
  -H "Authorization: Bearer tr_dev_<env_key>" \
  -H "Content-Type: application/json" \
  --data '{"operations":[{"type":"set","key":"$.__proto__.polluted","value":"PWNED"}]}'

The sink is in applyMetadataOperations() (operations.ts:22-23): new JSONHeroPath(operation.key).set(newMetadata, operation.value). The request schema types key as a plain string with no allowlist or blocklist, so $.__proto__.polluted reaches @jsonhero/path's segment walker unchecked.

Because @jsonhero/path@1.0.21 resolves __proto__ as a normal property name during traversal, the .set() call assigns onto Object.prototype rather than the local metadata object. Every plain object in the process then inherits the injected key, including Prisma query argument objects (causing Unknown argument validation errors) and prom-client label sets (causing an uncaughtException that crashes the process).

The endpoint returns HTTP 200 regardless (it wraps the Prisma error with ignoreError:true), so the attacker gets no error signal while the damage propagates process-wide. The fix, per PR #4316 (commit 6997aeb), adds a guard that rejects any operation key containing __proto__, constructor, or prototype segments before the path is built, and seeds the metadata accumulator with a null-prototype object so the assignment cannot reach Object.prototype even if the check is bypassed.

The fix

Upgrade @trigger.dev/core (and the self-hosted webapp image) to **v4.5.6** (ghcr.io/triggerdotdev/trigger.dev:v4.5.6). The patch (PR #4316, commit 6997aeb) adds a blocklist rejecting __proto__, constructor, and prototype in any operation key before JSONHeroPath is constructed, and initialises the metadata accumulator with Object.create(null) so prototype-chain writes are impossible.

If an immediate upgrade is not possible, reject or strip those key segments at your API gateway or reverse proxy as a stopgap.

Reporter not attributed.

References: [1][2][3][4][5]

Related research