Fail production frontend builds when VITE_E2E_MOCK_WALLET is set #327

Closed
opened 2026-06-14 22:31:59 +00:00 by PlasticDigits · 6 comments
PlasticDigits commented 2026-06-14 22:31:59 +00:00 (Migrated from gitlab.com)

Parent

GitLab #325 — Security review (audit finding F-05, approved recommendation #3).

Current codebase

Why needed

If VITE_E2E_MOCK_WALLET=1 is baked into a production static build, users may interact with a deterministic mock connector instead of a real wallet — catastrophic UX/confusion on staging or mis-deployed CDN artifacts. Parent #325 approved a build-pipeline guard.

Constraints / guardrails

  • Must not break Anvil E2E: scripts/e2e-anvil.sh and ANVIL_E2E=1 flows keep using mock wallet.
  • Rabby QA path (build-frontend-for-rabby.sh) must remain mock-free.
  • Gate applies to production builds (vite build / npm run build), not vite dev on chain 31337.
  • Do not add Co-authored-by or commit as bot identity per AGENTS.md.

Relevant files

Area Paths
Wallet config frontend/src/wagmi-config.ts, frontend/src/wagmi-config.test.ts
Build frontend/vite.config.ts, frontend/package.json
Scripts scripts/check-frontend-vite-env.sh, scripts/e2e-anvil.sh, scripts/qa/build-frontend-for-rabby.sh, scripts/verify-rabby-chain-mismatch.sh
CI .github/workflows/unit-tests.yml, .github/workflows/e2e-anvil.yml
Docs docs/testing/e2e-anvil.md, docs/testing/ci.md, AGENTS.md
  1. Build-time fail-closed: in Vite config plugin or a small scripts/check-frontend-production-env.sh invoked before vite build, exit non-zero when VITE_E2E_MOCK_WALLET=1 and build mode is production (NODE_ENV=production or vite build).
  2. Extend scripts/check-frontend-vite-env.sh with optional --production flag that rejects mock wallet (for deploy scripts).
  3. CI: ensure npm run build in unit-tests workflow runs without mock flag; add explicit step or test that VITE_E2E_MOCK_WALLET=1 npm run build fails.
  4. Vitest: assert production env guard if implemented in TS.
  5. Document in docs/testing/ci.md and frontend/.env.example (already comments mock as E2E-only).

Acceptance criteria

  • cd frontend && npm run build with VITE_E2E_MOCK_WALLET=1 in env fails with a clear error message.
  • bash scripts/e2e-anvil.sh still passes (mock allowed for E2E build path).
  • bash scripts/qa/build-frontend-for-rabby.sh still produces mock-free frontend/dist.
  • CI unit-tests job catches accidental mock-in-production regression.
  • No change to dev-mode Anvil auto-mock when extension absent (useAnvilDevMockWallet without VITE_E2E_MOCK_WALLET).

Test plan

Path Command
Frontend unit cd frontend && npm run typecheck && npm run lint && npm test
Production build gate cd frontend && VITE_E2E_MOCK_WALLET=1 npm run build → expect non-zero exit
Clean production build cd frontend with required VITE_* from stack → npm run build → success
Anvil E2E bash scripts/e2e-anvil.sh (or CI e2e-anvil.yml scope)
Rabby verify script bash scripts/verify-rabby-chain-mismatch.sh (when stack up; optional smoke)

Attack / abuse test plan

  • Simulate mis-deploy: export VITE_E2E_MOCK_WALLET=1 + valid addresses, run npm run build — build must abort before writing dist/.
  • If build somehow succeeds, grep -r VITE_E2E_MOCK_WALLET frontend/dist should not find =1 in production pipeline (Rabby script pattern).
  • Confirm E2E path still sets mock and Playwright wallet-write specs pass.

Verification criteria

  • MR checklist: Production mock-wallet gate → VITE_E2E_MOCK_WALLET=1 npm run build FAIL, e2e-anvil.sh PASS
  • Parent #325 recommendation #3 satisfied.
## Parent [GitLab #325 — Security review](https://gitlab.com/PlasticDigits/yieldomega/-/issues/325) (audit finding **F-05**, approved recommendation **#3**). ## Current codebase - [`frontend/src/wagmi-config.ts`](frontend/src/wagmi-config.ts): `VITE_E2E_MOCK_WALLET=1` enables wagmi `mock` connector with Anvil account #0 (`0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266`). Comment at L119–121 says not to ship production builds with this flag. - [`scripts/e2e-anvil.sh`](scripts/e2e-anvil.sh) sets `VITE_E2E_MOCK_WALLET=1` for Playwright Anvil E2E. - [`scripts/qa/build-frontend-for-rabby.sh`](scripts/qa/build-frontend-for-rabby.sh) intentionally **omits** mock flag for Rabby QA. - [`scripts/check-frontend-vite-env.sh`](scripts/check-frontend-vite-env.sh) validates required `VITE_*` addresses but **does not** forbid `VITE_E2E_MOCK_WALLET=1`. - [`scripts/verify-rabby-chain-mismatch.sh`](scripts/verify-rabby-chain-mismatch.sh) grep-checks built `frontend/dist` for leaked mock flag string. - CI [`.github/workflows/unit-tests.yml`](.github/workflows/unit-tests.yml) runs `npm run build` for Playwright UI smoke but has no production mock-wallet gate. - Vite inlines `import.meta.env.VITE_*` at build time; a mis-built artifact permanently embeds the flag (see `frontend/dist/` when built with mock). ## Why needed If `VITE_E2E_MOCK_WALLET=1` is baked into a production static build, users may interact with a deterministic mock connector instead of a real wallet — catastrophic UX/confusion on staging or mis-deployed CDN artifacts. Parent #325 approved a build-pipeline guard. ## Constraints / guardrails - **Must not** break Anvil E2E: `scripts/e2e-anvil.sh` and `ANVIL_E2E=1` flows keep using mock wallet. - Rabby QA path (`build-frontend-for-rabby.sh`) must remain mock-free. - Gate applies to **production** builds (`vite build` / `npm run build`), not `vite dev` on chain 31337. - Do not add `Co-authored-by` or commit as bot identity per `AGENTS.md`. ## Relevant files | Area | Paths | |------|-------| | Wallet config | `frontend/src/wagmi-config.ts`, `frontend/src/wagmi-config.test.ts` | | Build | `frontend/vite.config.ts`, `frontend/package.json` | | Scripts | `scripts/check-frontend-vite-env.sh`, `scripts/e2e-anvil.sh`, `scripts/qa/build-frontend-for-rabby.sh`, `scripts/verify-rabby-chain-mismatch.sh` | | CI | `.github/workflows/unit-tests.yml`, `.github/workflows/e2e-anvil.yml` | | Docs | `docs/testing/e2e-anvil.md`, `docs/testing/ci.md`, `AGENTS.md` | ## Recommended direction 1. **Build-time fail-closed**: in Vite config plugin or a small `scripts/check-frontend-production-env.sh` invoked before `vite build`, exit non-zero when `VITE_E2E_MOCK_WALLET=1` and build mode is production (`NODE_ENV=production` or `vite build`). 2. **Extend** `scripts/check-frontend-vite-env.sh` with optional `--production` flag that rejects mock wallet (for deploy scripts). 3. **CI**: ensure `npm run build` in unit-tests workflow runs without mock flag; add explicit step or test that `VITE_E2E_MOCK_WALLET=1 npm run build` **fails**. 4. **Vitest**: assert production env guard if implemented in TS. 5. Document in `docs/testing/ci.md` and `frontend/.env.example` (already comments mock as E2E-only). ## Acceptance criteria - [ ] `cd frontend && npm run build` with `VITE_E2E_MOCK_WALLET=1` in env **fails** with a clear error message. - [ ] `bash scripts/e2e-anvil.sh` still passes (mock allowed for E2E build path). - [ ] `bash scripts/qa/build-frontend-for-rabby.sh` still produces mock-free `frontend/dist`. - [ ] CI unit-tests job catches accidental mock-in-production regression. - [ ] No change to dev-mode Anvil auto-mock when extension absent (`useAnvilDevMockWallet` without `VITE_E2E_MOCK_WALLET`). ## Test plan | Path | Command | |------|---------| | Frontend unit | `cd frontend && npm run typecheck && npm run lint && npm test` | | Production build gate | `cd frontend && VITE_E2E_MOCK_WALLET=1 npm run build` → expect **non-zero exit** | | Clean production build | `cd frontend` with required `VITE_*` from stack → `npm run build` → **success** | | Anvil E2E | `bash scripts/e2e-anvil.sh` (or CI `e2e-anvil.yml` scope) | | Rabby verify script | `bash scripts/verify-rabby-chain-mismatch.sh` (when stack up; optional smoke) | ## Attack / abuse test plan - Simulate mis-deploy: export `VITE_E2E_MOCK_WALLET=1` + valid addresses, run `npm run build` — build must abort before writing `dist/`. - If build somehow succeeds, `grep -r VITE_E2E_MOCK_WALLET frontend/dist` should not find `=1` in production pipeline (Rabby script pattern). - Confirm E2E path still sets mock and Playwright wallet-write specs pass. ## Verification criteria - MR checklist: *Production mock-wallet gate → `VITE_E2E_MOCK_WALLET=1 npm run build` FAIL, `e2e-anvil.sh` PASS* - Parent #325 recommendation #3 satisfied.
PlasticDigits commented 2026-06-14 22:32:09 +00:00 (Migrated from gitlab.com)

mentioned in issue #325

mentioned in issue #325
PlasticDigits commented 2026-06-14 22:56:41 +00:00 (Migrated from gitlab.com)

mentioned in commit a9bb2e219e

mentioned in commit a9bb2e219e5cc3765efcb1a048ce3d2e9b5f5fa2
PlasticDigits commented 2026-06-14 22:56:54 +00:00 (Migrated from gitlab.com)

mentioned in merge request !500

mentioned in merge request !500
PlasticDigits commented 2026-06-15 01:01:26 +00:00 (Migrated from gitlab.com)

mentioned in commit ecb17aad3c

mentioned in commit ecb17aad3c87f090c49cfe7b28fcf6ba5debfebb
PlasticDigits commented 2026-06-15 01:15:55 +00:00 (Migrated from gitlab.com)

Verification — GitLab #327 (PASS)

Verified production frontend build gate for VITE_E2E_MOCK_WALLET on main (no code/docs changes required).

Acceptance criteria

Criterion Result How verified
VITE_E2E_MOCK_WALLET=1 npm run build fails with clear error PASS cd frontend && VITE_*… VITE_E2E_MOCK_WALLET=1 npm run build → exit 1; message: "Production build refused: VITE_E2E_MOCK_WALLET=1 enables the wagmi mock connector…"
bash scripts/e2e-anvil.sh still passes PASS Full run: Anvil + DeployDev + frontend build (ANVIL_E2E=1) + Playwright — 7/7 specs green
bash scripts/qa/build-frontend-for-rabby.sh mock-free dist PASS Script exit 0; grep for VITE_E2E_MOCK_WALLET:"1" and mock({ accounts in frontend/dist/assets/ — no matches
CI unit-tests catches regression PASS Replayed unit-tests.yml Production mock-wallet build gate step locally — non-zero exit as expected
Dev-mode Anvil auto-mock unchanged PASS wagmi-config.ts still uses useAnvilDevMockWallet (DEV && chainId === 31337 && !browserHasInjectedProvider()); wagmi-config.test.ts + viteProductionEnvGuard.test.ts pass

Additional checks

Check Result Command
Frontend unit suite PASS cd frontend && npm run typecheck && npm run lint && npm test — 676 tests
Clean production build PASS npm run build with required VITE_* (no mock) — exit 0
Deploy script --production guard PASS bash scripts/check-frontend-vite-env.sh --production rejects mock in .env.production.local; OK without mock
Mis-deploy aborts before ship PASS Build fails at vite.config.ts load (before dist/ write) when mock flag set without ANVIL_E2E=1
Invariant documented PASS INV-FRONTEND-327-PRODUCTION-MOCK-WALLET in docs/testing/invariants-and-business-logic.md

Implementation summary (already on main)

  • frontend/src/lib/viteProductionEnvGuard.ts + frontend/vite.config.ts — fail-closed on vite build unless ANVIL_E2E=1
  • scripts/check-frontend-vite-env.sh --production — deploy-script guard
  • .github/workflows/unit-tests.yml — explicit regression step

Parent #325 recommendation #3 satisfied.

## Verification — GitLab #327 (PASS) Verified production frontend build gate for `VITE_E2E_MOCK_WALLET` on `main` (no code/docs changes required). ### Acceptance criteria | Criterion | Result | How verified | |-----------|--------|--------------| | `VITE_E2E_MOCK_WALLET=1 npm run build` fails with clear error | **PASS** | `cd frontend && VITE_*… VITE_E2E_MOCK_WALLET=1 npm run build` → exit 1; message: *"Production build refused: VITE_E2E_MOCK_WALLET=1 enables the wagmi mock connector…"* | | `bash scripts/e2e-anvil.sh` still passes | **PASS** | Full run: Anvil + DeployDev + frontend build (`ANVIL_E2E=1`) + Playwright — **7/7** specs green | | `bash scripts/qa/build-frontend-for-rabby.sh` mock-free `dist` | **PASS** | Script exit 0; `grep` for `VITE_E2E_MOCK_WALLET:"1"` and `mock({ accounts` in `frontend/dist/assets/` — no matches | | CI unit-tests catches regression | **PASS** | Replayed `unit-tests.yml` **Production mock-wallet build gate** step locally — non-zero exit as expected | | Dev-mode Anvil auto-mock unchanged | **PASS** | `wagmi-config.ts` still uses `useAnvilDevMockWallet` (`DEV && chainId === 31337 && !browserHasInjectedProvider()`); `wagmi-config.test.ts` + `viteProductionEnvGuard.test.ts` pass | ### Additional checks | Check | Result | Command | |-------|--------|---------| | Frontend unit suite | **PASS** | `cd frontend && npm run typecheck && npm run lint && npm test` — 676 tests | | Clean production build | **PASS** | `npm run build` with required `VITE_*` (no mock) — exit 0 | | Deploy script `--production` guard | **PASS** | `bash scripts/check-frontend-vite-env.sh --production` rejects mock in `.env.production.local`; OK without mock | | Mis-deploy aborts before ship | **PASS** | Build fails at `vite.config.ts` load (before `dist/` write) when mock flag set without `ANVIL_E2E=1` | | Invariant documented | **PASS** | `INV-FRONTEND-327-PRODUCTION-MOCK-WALLET` in `docs/testing/invariants-and-business-logic.md` | ### Implementation summary (already on `main`) - `frontend/src/lib/viteProductionEnvGuard.ts` + `frontend/vite.config.ts` — fail-closed on `vite build` unless `ANVIL_E2E=1` - `scripts/check-frontend-vite-env.sh --production` — deploy-script guard - `.github/workflows/unit-tests.yml` — explicit regression step Parent #325 recommendation **#3** satisfied.
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-06-15 01:16:03 +00:00
Brouie commented 2026-06-30 19:07:19 +00:00 (Migrated from gitlab.com)

mentioned in issue #358

mentioned in issue #358
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
code/yieldomega#327
No description provided.