Skip to content

fix: harden the Vercel pairing flow before release - #14

Merged
enderdev01 merged 9 commits into
mainfrom
fix/vercel-pairing-hardening
Nov 4, 2026
Merged

enderdev01 merged 9 commits into
mainfrom
fix/vercel-pairing-hardening

Conversation

@enderdev01

Copy link
Copy Markdown
Owner

Linked issue

Closes #13

What and why

The Vercel integration is blocked less by code than by three things: an unreviewed pairing flow, a
build that can never be configured, and a privacy promise that contradicts the feature. This PR
closes what can be closed without your accounts.

  • Review. docs/vercel-security-review.md walks the pairing model and lists ten findings with
    severity and status. Two are fixed here; the rest are documented residual risk, including the
    review not being independent.
  • Two fixes. A Vercel 401 used to become a generic 502, so an expired or revoked token
    looked like a network failure and never asked to reconnect; it now maps to 401, which the client
    already treats as expired. The callback response also gained nosniff.
  • Packaging. make app now fills VercelWorkerURL and VercelIntegrationSlug from
    VERCEL_WORKER_URL / VERCEL_INTEGRATION_SLUG. Nothing is committed, and without the variables
    the keys stay absent, so a dev build is inert exactly as before.
  • Privacy wording. docs/vercel-privacy.md has the exact README and CONTRIBUTING changes the
    feature needs: the current "never sends anything anywhere" becomes "never sends your agent
    activity", with one opt-in exception. It is drafted, not applied, because it is your promise.
  • Runbook. The Worker README now lists the steps only you can do (Cloudflare secrets and deploy,
    Vercel integration registration, building a release that reaches it) plus an end-to-end checklist.

Changes

File Change
integrations/vercel-worker/index.mjs 401 from Vercel maps to 401; nosniff on the callback
integrations/vercel-worker/index.test.mjs Covers reconnection mapping and the callback header
Makefile Optional VERCEL_WORKER_URL / VERCEL_INTEGRATION_SLUG injection before signing
scripts/release.sh Documents the two new variables
docs/vercel-security-review.md Pairing model, ten findings, residual risk
docs/vercel-privacy.md Exact README and CONTRIBUTING wording, in one place
integrations/vercel-worker/README.md Owner runbook and end-to-end checklist
docs/vercel-deployment-monitoring.md Status updated

How I tested it

  • node --test integrations/vercel-worker/index.test.mjs passes (6 tests)
  • bash -n scripts/release.sh passes
  • make app with VERCEL_WORKER_URL writes both keys into the packaged Info.plist; without them the keys are absent (verified by extracting both ways)
  • No secrets, personal paths or network calls added

No Swift sources changed, so the Swift build and tests are unaffected; CI will still run them.

What still needs you

  1. Deploy the Worker and set the three secrets.
  2. Register the Vercel integration with deployment-read permissions and the matching redirect URL.
  3. Run the end-to-end checklist in integrations/vercel-worker/README.md.
  4. Decide on F1 (revoking the token at Vercel on disconnect) and approve the privacy wording.

A Vercel 401 used to become a generic 502, so an expired or revoked
token looked like a network failure and never asked for reconnection.
Map it to 401, which the client already treats as expired, and add
nosniff to the callback response.
The app reads VercelWorkerURL and VercelIntegrationSlug from its
Info.plist, but nothing supplied them, so every build was inert. make
app now fills them from VERCEL_WORKER_URL and VERCEL_INTEGRATION_SLUG;
without them the keys are absent and the Vercel UI stays hidden.
Record the pairing model, ten findings with severity and status (two
fixed here), the residual risk before a public release, the exact
README and CONTRIBUTING changes needed for the opt-in network exception,
and the deployment steps that need the owner's accounts.
@enderdev01 enderdev01 added the enhancement New feature or request label Nov 4, 2026
The runbook asked for wrangler secret put before the Worker existed,
and never said that Vercel assigns the client ID and secret when the
integration is created. Deploy first, then create the integration with
the redirect URL, then store the three values.
A cd failure sends wrangler deploy to a directory with no wrangler.toml,
where it looks for a Pages project and fails; record that and add a
401 smoke test for the deployed Worker.
Vercel answers the token exchange with team_id null for a personal
account; the Worker required a string and rejected a successful
authorization with 502. Store an empty team and omit teamId in that
case. Also move to /v6/deployments, the endpoint in Vercel's current
reference, and cover both with tests.
An exception during the token exchange surfaced as an opaque Error 1101
page, hiding the cause. The callback now catches it, returns a short
JSON reason, and logs the exchange status plus whether each secret
binding is present, without printing any value. Add tests for a network
failure and a non-JSON authorization response.
Cloudflare Workers rejects fetch redirect: 'error' at runtime, even
though Node accepts it, so the callback threw an unhandled exception and
showed Error 1101 after a successful authorization. Use redirect:
'manual' and refuse any 3xx explicitly, in both the token exchange and
the deployments call. Record the finding and cover the redirect case.
The flow completed against production on 2026-11-04 (team install:
start, Vercel authorization, callback, connected). Record what that
does and does not prove, and keep the checklist for the next change.
@enderdev01
enderdev01 merged commit df93ad3 into main Nov 4, 2026
1 check passed
@enderdev01
enderdev01 deleted the fix/vercel-pairing-hardening branch November 4, 2026 05:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Harden the Vercel pairing flow before release

1 participant