Skip to content

security: strip server-only env vars from next.config.js#311

Open
Mischa0x wants to merge 2 commits into
mainfrom
security/strip-server-only-env-vars
Open

security: strip server-only env vars from next.config.js#311
Mischa0x wants to merge 2 commits into
mainfrom
security/strip-server-only-env-vars

Conversation

@Mischa0x

Copy link
Copy Markdown
Contributor

Summary

The env: block in next.config.js inlines every listed value into the client-side JS bundle at build time. Server-only values have no business there — they were being exposed to every visitor's browser the moment a Vercel env var was set under one of these names.

This PR removes every var that is only referenced by pages/api/*, lib/** server code, or is entirely unreferenced. API routes read process.env at runtime and do not need to be inlined.

What changed

Removed from env: block:

Var Where it's read
BOT_PRIVATE_KEY pages/api/vcxTrader.ts:38 (server-only)
TEST_BOT_PRIVATE_KEY pages/bots/compoundRewards.tsx:39, pages/bots/allocation.tsx:314client-reads; see TODO #1
DUNE_API_KEY pages/api/circulatingSupply.ts, pages/api/totalSupply.ts
DISCORD_WEBHOOK lib/discord/discordBot.ts
FLASHBOTS_RPC_URL pages/api/vcxTrader.ts
MAINNET_RPC_URL pages/api/vcxTrader.ts
VCX_RECIPIENT pages/api/vcxTrader.ts
MIN_AMOUNT pages/api/vcxTrader.ts
WATCH_LIST unreferenced
ZK_FETCH_APP_ID unreferenced
ZK_FETCH_SECRET unreferenced
VAULTS_CACHE unreferenced (only in next.config.js)

Kept in env: block (client code reads them today — see TODO #2):
NEXT_PUBLIC_ALCHEMY_API_KEY, PINATA_API_SECRET, PINATA_API_KEY, IPFS_URL, ENSO_API_KEY, DEFILLAMA_API_KEY, DEBANK_API_KEY.

Verification

Scanned all client chunks currently served from vaultcraft.io and app.vaultcraft.io for 64-hex strings. Matches found (13 on app, 0 on root) are all known constants: secp256k1 curve params, UniV3 init code hash, EIP-6492 magic 0x6492…, EVM bytecode prefixes, Balancer pool IDs. No private key present in the live bundles today — meaning BOT_PRIVATE_KEY / TEST_BOT_PRIVATE_KEY are not currently populated in the Vercel project. This PR locks in that safety: even if a value is added to Vercel env in the future, it will no longer ship to the browser.

TODO (not in this PR)

  1. pages/bots/compoundRewards.tsx:39 and pages/bots/allocation.tsx:314 call process.env.TEST_BOT_PRIVATE_KEY from client code. Those flows must be rearchitected to POST to an API route that signs server-side. Their signing path is currently non-functional on the live deploy (var unset), so this PR does not cause a regression — it removes the exposure path.
  2. PINATA_API_SECRET, PINATA_API_KEY, DEBANK_API_KEY are still inlined. Pinata secrets grant upload privileges billed to the account and should be proxied through an authenticated API route.

Test plan

  • Vercel preview build succeeds
  • /api/vcxTrader, /api/circulatingSupply, /api/totalSupply still work on preview (they read process.env at runtime, unaffected)
  • Client features that rely on kept vars (IPFS upload, Enso swaps, DeFiLlama charts, DeBank price fallback) still work on preview
  • Bundle re-scan on preview confirms no new secrets appear

🤖 Generated with Claude Code

The `env:` block in next.config.js inlines every listed value into the
client-side JS bundle at build time. Server-only values have no business
there — they were being exposed to every visitor's browser the moment a
Vercel env set them.

Removed (referenced only by pages/api/* or scripts, which read process.env
at runtime and do not require inlining):

  BOT_PRIVATE_KEY        pages/api/vcxTrader.ts
  TEST_BOT_PRIVATE_KEY   pages/bots/compoundRewards.tsx, pages/bots/allocation.tsx
                         (also client-read — see TODO below)
  DUNE_API_KEY           pages/api/circulatingSupply.ts, totalSupply.ts
  DISCORD_WEBHOOK        lib/discord/discordBot.ts
  FLASHBOTS_RPC_URL      pages/api/vcxTrader.ts
  MAINNET_RPC_URL        pages/api/vcxTrader.ts
  VCX_RECIPIENT          pages/api/vcxTrader.ts
  MIN_AMOUNT             pages/api/vcxTrader.ts
  WATCH_LIST             unreferenced
  ZK_FETCH_APP_ID        unreferenced
  ZK_FETCH_SECRET        unreferenced
  VAULTS_CACHE           unreferenced (only in next.config.js)

Verified against current deploys: scanned all client chunks from
vaultcraft.io and app.vaultcraft.io — none currently contain a
private-key-shaped hex, confirming these env vars are not populated in
the Vercel project today. This change prevents accidental future
exposure the first time someone adds a real value to the Vercel env
under one of these names.

TODO (separate PRs needed, not fixed here):

1. pages/bots/compoundRewards.tsx:39 and pages/bots/allocation.tsx:314
   call `process.env.TEST_BOT_PRIVATE_KEY` from client code. That flow
   must be rearchitected to POST to an API route that signs server-side.
   Those pages' signing functionality is currently non-functional on the
   live deploy (env var unset), so this change does not cause a
   regression — it removes the exposure path.

2. PINATA_API_SECRET / PINATA_API_KEY / DEBANK_API_KEY are still inlined
   to the client because client code reads them today. Pinata secrets in
   particular grant upload privileges billed to the account and should
   be proxied through an authenticated API route.
@vercel

vercel Bot commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
vaultcraft Ready Ready Preview, Comment Apr 19, 2026 6:54pm

Request Review

The previous commit wrongly classified VAULTS_CACHE as unreferenced. It
is read by components/common/Page.tsx:509,545 — a client component that
fetches a pre-computed vault-data JSON blob on initial load for a faster
first paint, falling back to live computation if unset.

It is not a secret (a public cache URL that gets fetched from browsers
anyway), so inlining it is acceptable. Restoring to preserve the cache
performance path.
@Mischa0x

Copy link
Copy Markdown
Contributor Author

Self-correction: pushed a follow-up commit restoring VAULTS_CACHE to the env: block. It's read by components/common/Page.tsx:509,545 (client-side cache URL, not a secret) — my initial classification missed that reference. The rest of the stripped vars remain server-only and safe to remove.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant