trigger.dev: Cross-tenant object-store access via path traversal in the packet presign routes
An account with any project-scoped API key uses a .. sequence in a presign request file path to read and overwrite another organization's offloaded task payloads.
Advisory ID: TP-2026-044
Product: trigger.dev (open-source background-task orchestration platform; the vulnerability is in the self-hosted webapp)
Vulnerability type: Path traversal to cross-tenant object-store access (CWE-22)
CVE: CVE-2026-73659
CVSS 3.1: 8.1 (High) · CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
Vendor advisory: GHSA-m3mf-37q7-8928
Affected versions: trigger.dev >= 4.4.2, <= 4.4.6
Fixed in: trigger.dev 4.5.0
Reported: 29 May 2026
Summary
trigger.dev is an open-source background-task orchestration platform. For large task payloads the self-hosted webapp offloads the data into an S3-compatible object store and serves it through presigned URLs whose object key the presign route builds from the client-supplied file path. The path is placed into the key packets/<projectRef>/<env>/<path> without any check for .. sequences, so a traversal sequence leaves the caller's own organization prefix. The SigV4 signature normalizes the path separators before signing, so the presigned URL stays valid for the foreign key, and no check ties the key to the calling organization. An account with any project-scoped API key thereby reads another organization's offloaded payloads and overwrites them. turingpoint verified the chain live and reported it to the vendor, who fixed it in trigger.dev 4.5.0.
Root cause
The presign route builds the object key as packets/<projectRef>/<env>/<path> and passes the client-supplied path segment unchanged into generatePresignedRequest (apps/webapp/app/v3/objectStore.server.ts:256), where resolveStoreProtocolForPacketPresign returns the filename with no check for .. (:199-213). Both the GET loader (apps/webapp/app/routes/api.v1.packets.$.ts:52) and the PUT action (:26) take the splat parameter raw from the URL, so a ..%2f sequence reaches the key builder unfiltered. objectStoreClient.server.ts:60-69 assigns that path to the URL and signs it with SigV4, which normalizes the separators before signing, so the presigned URL stays valid for the escaped, foreign key. The loader's authorization is a constant (findResource: async () => 1, api.v1.packets.$.ts:47-49) and never checks that the requested key belongs to the caller's project, so any project-scoped API key is accepted. Because the offloaded payloads of every organization live under neighbouring packets/<projectRef>/... prefixes in the same bucket, ..%2f..%2f<victimProjectRef>%2fdev%2f<runId>%2fpayload.json reads or overwrites the inputs and outputs of another organization's task run.
Proof of Concept
# Any project-scoped API key. Read another organization's payload:
GET /api/v1/packets/..%2f..%2f<victimProjectRef>%2fdev%2f<runId>%2fpayload.json
Authorization: Bearer tr_dev_<attacker_project_key>
# The response is a presigned URL for the foreign object; GET it to read,
# or request the same path as a PUT presign to overwrite the object.
The ..%2f sequences leave the caller's packets/<projectRef>/<env>/ prefix; SigV4 normalizes the separators, so the signed URL resolves to the foreign key. turingpoint verified this end to end: reading a 600 KB payload of another organization and overwriting it successfully across organizations.
Impact
- Cross-tenant read of another organization's offloaded task payloads (run inputs and outputs, frequently secrets and PII).
- Cross-tenant overwrite of those payloads via a PUT presign, poisoning the inputs of another organization's task runs.
- Reachable with any project-scoped API key, without an administrator role.
- Affects self-hosted instances with multiple organizations; single-tenant deployments are unaffected.
References
Is Something Like This in Your Software?
Our team found this vulnerability in the course of its work. Have your applications reviewed by the same specialists, with a penetration test from turingpoint.
