Medium

Trigger.dev: Cross-environment deployment cancel

  • Disclosed: 02.10.2026
  • Updated: 11.10.2026 10:22

Summary

Trigger.dev isolates each project into multiple environments (dev, staging, prod, and per-PR preview branches), each with its own secret API key — the environment is a trust boundary (a dev/preview/CI key is lower-trust than a prod key). Most API routes enforce this by scoping resource lookups to the authenticated key's environment (where: { friendlyId, runtimeEnvironmentId: auth.environment.id }).

The deployment cancel path does not. DeploymentService.getDeployment() scopes the lookup by projectId only — never environmentId — so a secret key for any environment in a project can cancel a deployment belonging to any other environment of the same project, including production. The deployment GET route, by contrast, is env-scoped — so the same key that is 404'd when trying to read a prod deployment can nonetheless cancel it. That asymmetry is the bug.

Affected

apps/webapp, HEAD 5d99457 (current main). Affects self-hosted and cloud.

Root cause

apps/webapp/app/routes/api.v1.deployments.$deploymentId.cancel.ts authenticates to an environment and calls deploymentService.cancelDeployment(authenticatedEnv, deploymentId, ...).

apps/webapp/app/v3/services/deployment.server.ts:

ts
public cancelDeployment(authenticatedEnv: Pick<AuthenticatedEnvironment,"projectId">, friendlyId, ...) {
  return this.getDeployment(authenticatedEnv.projectId, friendlyId)   // projectId only
    .andThen(validateDeployment)                                      // rejects only FINAL statuses
    .andThen(cancelDeployment);                                       // updateMany -> status CANCELED
}
private getDeployment(projectId: string, friendlyId: string) {
  return this._prisma.workerDeployment.findFirst({
    where: { friendlyId, projectId },        // <-- NO environmentId filter
  });
}

cancelDeployment accepts authenticatedEnv but its type is literally Pick<AuthenticatedEnvironment,"projectId"> — it discards the environment identity. validateDeployment blocks only FINAL_DEPLOYMENT_STATUSES, so any in-progress deployment (PENDING/INSTALLING/BUILDING/DEPLOYING) is cancellable.

Contrast — the correctly env-scoped sibling api.v1.deployments.$deploymentId.ts (GET):

ts
const deployment = await prisma.workerDeployment.findFirst({
  where: { friendlyId: deploymentId, environmentId: authenticatedEnv.id },   // env-scoped
});

Reads are env-scoped; the cancel mutation is not. The same getDeployment(authenticatedEnv.projectId, …) helper also backs the deployment progress methods (deployment.server.ts:172,329), so the projectId-only scope is a small class.

Runtime PoC (proven on the self-host stack)

Seeded one project with a prod env (key tr_prod_…) and a dev env (key tr_dev_…) and one WorkerDeployment (deployment_pocvictim, status DEPLOYING) in the prod env. As the dev key:

status BEFORE                                                  : DEPLOYING
GET  /api/v1/deployments/deployment_pocvictim   (dev key)      -> 404   (env-scoped read DENIES it)
POST /api/v1/deployments/deployment_pocvictim/cancel (dev key) -> 204
status AFTER                                                   : CANCELED   (prod deploy canceled by the dev key)
CONTROL: same cancel with a DIFFERENT project's key            -> 404   (projectId scope blocks cross-project)

The dev key cannot read the prod deployment (404) yet cancels it (204 → CANCELED); a different project's key is correctly 404'd — so the gap is precisely cross-environment within a project.

Affected software

Click a technology to see all vulnerabilities affecting it.

PlatformPackageVulnerable versionsFixed in
npm package trigger.dev < 4.5.6 4.5.6

References

CVSS score 5.4/10
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L
Weakness (CWE)
CWE-639
Platform
npm
Package
trigger.dev
Disclosed
02.10.2026
Updated
11.10.2026 10:22

Sources and licenses

OSV / GitHub Advisory Database - GHSA-4672-hwv6-gq62 - CC BY 4.0

Vulnerability history: trigger.dev

All published vulnerabilities of this package (npm package), newest first. Shows whether this is a one-off slip or a package that needs watching.

All (10) in the archive
Total entries
10
First → latest
10.2026 10.2026
Without a fix
0
Exploited in attacks
0

Severity breakdown

  • Critical 1
  • High 5
  • Medium 4

Disclosures per year

  1. 2026: 10
Severity:
  1. Trigger.dev: Cross-environment deployment cancel this entry
    Medium · 5.4 ghsa-4672-hwv6-gq62 < 4.5.6 4.5.6
  2. Critical ghsa-gg6r-gp4c-89hp < 4.5.4 4.5.4

Know which vulnerabilities affect your application

With the DockRay integration and software inventory enabled, DockRay checks reported component versions against known vulnerabilities and notifies you about confirmed matches. Coverage depends on the supported technology and available advisory data.

Connect your application
DockRay newsletter

Application security in your inbox

Important vulnerabilities, actionable guidance and practical advice for your applications. Choose your technologies and follow what matters to your project.

  • A daily digest of new vulnerabilities, critical ones included.
  • Notifications about important updates.
  • Practical advice on application security and monitoring.

Leave empty to receive updates for every technology.

Free to subscribe. Unsubscribe with one click in any email.