fix(automations): refuse the automations cron on Vercel preview deployments - #64
polylane[bot] wants to merge 2 commits into
Conversation
…yments Co-authored-by: polylane[bot] <277585245+polylane[bot]@users.noreply.github.com>
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Tip Expected to resolve the linked issue. Linked issue: iss_0ba833886001zmax1m730uno — "cache-app preview deployments run with production secrets, database and cron credential": a preview build could claim due runs and start production automation workflows The guard fires only when Also considered · 2 refuted
Analysed against 1 Project and 1 repository
Polylane analysed Rate this review: 👍 helpful · 👎 not helpful · 😕 confusing Previous verdicts (1)
|
| if (!isProductionDeployment(serverEnv.VERCEL_ENV)) { | ||
| return Response.json( | ||
| { error: "Not found" }, | ||
| { headers: NO_STORE_HEADERS, status: 404 } | ||
| ); | ||
| } |
There was a problem hiding this comment.
The refusal path is silent today, so a preview deployment attempting to claim production automation runs leaves no queryable trace and the containment cannot be confirmed in production.
| if (!isProductionDeployment(serverEnv.VERCEL_ENV)) { | |
| return Response.json( | |
| { error: "Not found" }, | |
| { headers: NO_STORE_HEADERS, status: 404 } | |
| ); | |
| } | |
| if (!isProductionDeployment(serverEnv.VERCEL_ENV)) { | |
| log.warn("Refused automations cron outside production", { | |
| deploymentTarget: serverEnv.VERCEL_ENV, | |
| }); | |
| return Response.json( | |
| { error: "Not found" }, | |
| { headers: NO_STORE_HEADERS, status: 404 } | |
| ); | |
| } |
…yments Co-authored-by: polylane[bot] <277585245+polylane[bot]@users.noreply.github.com>
Fixes: cache-app preview deployments run with production secrets, database and cron credential
A Vercel preview deployment of cache-app builds the same way as production and inherits every production secret, including the cron credential. The automations endpoint decided it was production from the runtime mode that a preview build also reports, so an unreviewed branch build could claim due runs and start production automation workflows. Requests that arrive on a preview deployment are now refused.
flowchart LR P["Vercel preview deployment (unreviewed branch)"] --> R["GET /api/cron/automations"] R --> G{"deployment target"} G -- "preview" --> X["404, before any work"] G -- "production or self-hosted" --> A["bearer CRON_SECRET check"] A --> W["claim due runs and start production workflows"]What caused this
Affected:
acc_0b563dc42001x121b0ek7bgcWhy this fix
This endpoint is reachable by anyone who holds the cron credential, and it starts durable production workflows. Its authorization keyed on the runtime mode, which every Vercel preview build also reports as production, so the shared credential alone was enough for a branch deployment to claim due runs.
A preview deployment is the only untrusted deploy target Vercel gives this project, and Vercel reports it directly:
serverEnv.VERCEL_ENVis"preview"there, and that is the value the handler now compares. Off Vercel the variable is unset, so self-hosted installs keep their current behavior and still require the bearer secret. Production is untouched.I did not put the comparison on the runtime mode, because it cannot distinguish a preview. A throwaway test that imported this route with the runtime mode set to production and the deployment target set to preview returned 401: the request passed the deployment check and reached the token check. The same request against this guard returns 404. That reproduces the mechanism the finding rested on.
The guard is the first statement in the handler, so a refused preview request performs no database read and starts no workflow. Nothing legitimate is lost: no in-repository or Vercel scheduler drives this route (
crons.definitionsis empty), so its only caller is the operator's own scheduler against production.Remaining work, tracked on the issue: preview deployments still receive the production database, Redis, Stripe and cron credentials, so a preview route or server action can still read or write production rows. Re-scoping those project variables, or pointing preview at a separate database, and issuing a per-environment cron credential is owner action on a read-only account. See the follow-up entry on the issue timeline.
Out of scope
DATABASE_URL,REDIS_URL,STRIPE_SECRET_KEYandSTRIPE_WEBHOOK_SECRET: owner action on the read-only Vercel account, tracked on the issue timeline.CRON_SECRET: same account, same owner.This change is a declared stopgap: it contains the symptom while the producer's fix is tracked as a follow-up on the issue timeline.
1 file changed (+7/-0)
app/api/cron/automations/route.ts: modified, +7/-0Generated by Polylane. You can ask follow-ups by mentioning @polylane in a comment.