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
- 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. - 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. - 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.
- 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.
- 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.tsReporting 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 →