Skip to content

Security boundary

What Upgrade Mode reads — and what it will never touch

When you connect your repo and hosting to plan v2 of an existing app, you're trusting us with a live system. The credible signal that we'll respect that trust isn't a marketing line — it's the code that decides what we read, published openly, with tests that prove the boundary holds.

What we read

GitHub

package.json, prisma/schema.prisma (model names only), tsconfig.json, framework config files (next.config.*, vite.config.*, etc.), tailwind.config.*, README.md (word count only), top-level folder names, commit timestamps.

Vercel

Project name, framework, primary domain + domain count, build command, output directory, Node version, edge/serverless flags, production deployment timestamps.

What we never read

  • Environment files

    .env in any form (.env, .env.local, .env.production, .env.preview, .env.development, env.production.local, etc.)

  • Crypto material

    *.pem, *.key, *.p12, *.crt, *.pfx, id_rsa, id_ed25519, .ssh/* — refused at the file matcher level.

  • Vendor secrets

    .aws/credentials, .gcloud, .kube, .npmrc, .netrc, stripe-*.json, firebase-*.json, service-account.json.

  • Logs

    *.log, logs/, build outputs, .next/ (except the safe standalone manifest), .vercel/ — common secret-leak vectors.

  • Database dumps

    *.sql, *.dump, *.bak, *.sqlite — may contain user data and password hashes.

  • Vercel env vars

    We never call /v9/projects/{id}/env. Neither names nor values.

  • Vercel logs

    We never call /v3/deployments/{id}/events or any runtime/build log endpoint.

  • Source code

    We do not read .ts/.tsx/.js/.jsx/.py/.go/.rs/.swift/.kt files. Manifests only.

  • Issues, PRs, comments

    GitHub issue bodies, PR bodies, comment threads — never accessed.

How the boundary is enforced

  1. Hardcoded path denylist — every file the scanner asks for is matched against a list of secret patterns (.env*, *.pem, *.key, *secret*, …). Blocked paths are skipped and logged as "skip" with the rule that fired.
  2. Field-level redactor — every API response is walked recursively. Any value whose key matches a secret-shaped name is replaced with the literal string [REDACTED] before being read. The fields we always redact: password, passwd, secret, token, api_key, access_key, private_key, client_secret, authorization, auth, bearer, session_id, cookie, credential, salt, hash, signature, jwt, pwd, pin.
  3. Defence in depth at the adapter layer — even if the scanner module asked the GitHub adapter for a denied path, the adapter refuses again. The boundary is not a single check.
  4. Per-scan audit log — every read, skip, redact, and error is recorded with timestamp + provider + target + reason. You can inspect the log live during a scan and afterward from your project's Upgrade page.
  5. Disconnect & purge — when you disconnect a provider, the stored credential blob is overwritten in the same transaction that flips the connection to disconnected. Audit history is retained as your right to inspect; the credential is unrecoverable.

Auth & storage

  • GitHub: a GitHub App (not OAuth User-to-Server token). You install the app on chosen repos only — we never receive a token tied to your GitHub account, and we cannot access any repo you didn't pick.
  • Vercel: standard OAuth with read-only scopes (read:project, read:deployment, read:domain). We never request env or log scopes.
  • Encryption at rest: tokens are encrypted with AES-256-GCM using a master key separate from any other credential class. Decryption happens only inside the scan route at execution time.
  • Error sanitisation: upstream API error messages are stripped of bearer tokens and query strings before being persisted, so even a leaky 4xx response can't bleed credentials into our logs.

How to verify yourself

The scanner module ships with a unit-test suite that enforces every rule on this page. Any commit that loosens the denylist fails CI before it deploys. If you self-host or audit our codebase, run:

npx tsx --test src/lib/upgrade-scanner/__tests__/*.test.ts

Reporting a boundary issue

If you find a way to coerce the scanner into reading something it shouldn't — even theoretically — email support@planmysaas.com (subject “Security”). We treat boundary bugs as ship-blockers and respond within one business day.

Ready to plan your v2?

Connect your repo + hosting from any project's Upgrade page. The full scan log is yours to inspect before, during, and after each run.

Open my project →