trigger.dev: Cross-Tenant Data Disclosure via SQL Injection in the Realtime API `tags` Parameter
Any API key with the read:runs scope streams the TaskRun records of every organization instead of only its own, via SQL injection in the tags parameter.
Advisory ID: TP-2026-075
Product: trigger.dev (open-source background jobs platform)
Vulnerability type: SQL injection / cross-tenant data disclosure (CWE-89)
CVE: not requested
CVSS 3.1: 7.7 (High) · CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N
Affected versions: <= 4.5.5
Fixed in: 4.5.6
Vendor advisory: GHSA-p6j2-2fjh-vc8v
Reported: 29 May 2026
Summary
trigger.dev is an open-source background jobs platform. The realtime endpoint GET /realtime/v1/runs builds the ElectricSQL where clause by string-interpolating the tags parameter without escaping it. A tags value that closes the array literal and appends a boolean condition defeats the tenant guard joined next to it with AND. Any API key with the read:runs scope then streams the TaskRun records of every organization instead of only its own. turingpoint verified the flow and reported it responsibly; the vendor fixed it in 4.5.6.
Root cause
The endpoint GET /realtime/v1/runs parses the tags parameter (apps/webapp/app/routes/realtime.v1.runs.ts) and passes it to the realtime client. That client wraps each tag as '<tag>' inside "runTags" @> ARRAY[...] and interpolates it into the string with no escaping (apps/webapp/app/services/realtimeClient.server.ts:171). This tag condition is joined with AND to the tenant guard into one where clause (realtimeClient.server.ts:180) that goes to ElectricSQL as a whole. The tenant guard itself is present in the code (realtimeClient.server.ts:168) but becomes inert once the interpolated tag value closes the array literal and appends an always-true condition. Because only a project key with read:runs is required, a low-privilege but authenticated role suffices to defeat isolation between organizations.
Proof of Concept
Schematically, with a project key holding only read:runs:
GET /realtime/v1/runs?tags=<value that closes ARRAY[...] and appends OR true>
Authorization: Bearer <read:runs key>
-> the interpolated tag value ends the ARRAY[...] literal and appends an
always-true condition; the response streams TaskRun records of other
organizations rather than only the caller's own.
Because the tag value reaches the where clause unescaped, it can override the AND-joined tenant guard with an OR-linked always-true condition.
Impact
- Reading every
TaskRunrecord across all organizations with a singleread:runskey. - Break of tenant isolation between the organizations of one instance.
- Disclosure of potentially sensitive job metadata (tags, status, timestamps, identifiers) of other tenants.
References
Is Something Like This in Your Software?
Our team found this vulnerability in the course of its work. Have your applications tested by the same specialists, with a penetration test from turingpoint.
