Pre-launch: dev mnemonic literal in frontend source ships to prod bundle #118
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:implement
agent:implement
agent:open_issues
agent:ready
agent:research
agent:security_audit
agent:verify
architecture
backend
blocker:hybrid
blocker:launch
blocker:limit-orders
blocker:v2
block:log_only
block:security
bug
ci
contracts
correctness
deploy
dev
devops
docs
documentation
duplicate
e2e
enhancement
epic
feature
frontend
functional-completion
gas
good first issue
governance
help wanted
high-risk
hooks
hybrid
indexer
infra
infrastructure
integrators
invalid
launch-blocker
limit-orders
localnet
localterra
low priority
missing-implementation
needs-design
ops
performance
priority
high
priority
medium
product
qa
QA
question
ready
ready
research
scripts
security
security-hardening
smartcontracts
tech-debt
testing
ux
UX
v2
verification
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
code/cl8y-dex-terraclassic#118
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found during DEX security checklist gap-fill on Sunday 2026-04-26.
Repro
frontend-dapp/src/services/terraclassic/devWallet.tsL4-5:L20:
const mnemonic = import.meta.env.VITE_DEV_MNEMONIC || DEFAULT_DEV_MNEMONICThe runtime use is gated by
if (!DEV_MODE) throwat L13-15 (DEV_MODE = import.meta.env.VITE_DEV_MODE === 'true'per utils/constants.ts L29) — so prod users can't actually callcreateDevTerraWallet(). But:DEFAULT_DEV_MNEMONICis a constant in source. Vite bundles it into proddist/regardless of the runtime guard.VITE_*env var (includingVITE_DEV_MNEMONIC) is inlined by Vite at build time. If a developer ever hasVITE_DEV_MNEMONICset in their environment when runningnpm run build, that real mnemonic ships to all users of that build.On-chain check
Address
terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38vderived from the default mnemonic is a known dev wallet. Current balance via terra-classic-lcd:6 uidr+3 umnt(dust, no real funds). So the immediate-loss risk is zero — this is a pre-launch hygiene issue, not an active vulnerability.Why it still matters pre-launch
!DEV_MODEthrow) does not strip the string from the bundle — it only blocks the wallet-construction codepath.VITE_DEV_MNEMONICenv-var fallback chains a second exposure: anyone running a prod build with that env set ships their real mnemonic to all users.Fix options
Option A (simplest, recommended): split dev wallet into a separate module that's only imported in dev/test builds.
Plus:
Vite tree-shakes the dynamic import out of prod builds when
import.meta.env.DEVisfalseat build time.Option B (minimal change): keep the file, but move the literal mnemonic to a
.env.development-only var with no fallback. Throw if missing in non-prod, refuse to build if present in prod.Plus a build-time check that fails CI if
VITE_DEV_MNEMONICis set in any prod build context.Severity
LOW — current dev wallet is a dust address, no real funds, runtime guard blocks usage. But worth fixing pre-mainnet to match the security maturity of bridge + YO frontends, neither of which ship dev mnemonics in their bundles.
Acceptance
grep -F 'notice oak worry' dist/assets/*.js→ empty)cc @PlasticDigits
mentioned in commit
1c2d1bb72dFix implemented (option B) — @brouie please verify
Merged to
main:1c2d1bb(dev mnemonic removed from client source, production build guard, gitleaks rule, docs +skills/AGENTS_BUNDLE_DEV_WALLET.md).What changed
devWallet.ts: no default BIP39 literal.VITE_DEV_MNEMONICis required (trimmed) whenVITE_DEV_MODE=trueand the Simulated Wallet is used. Address shown in the UI isdevWallet.address(no hardcoded bech32).vite buildthrows ifVITE_DEV_MNEMONICis set in the mergedloadEnv('production', …)(covers.env,.env.local,.env.production, and shell).deploy-dex-local.sh: writes the LocalTerra test phrase tofrontend-dapp/.env.development(same value asTEST_MNEMONICindocker/init-chain.sh) so it is not loaded for default production builds.webServer.envsetsVITE_DEV_MNEMONICfromdocker/init-chain.shviae2e/localterra-mnemonic.ts(no duplicate insrc/).bip39-like-phrase-frontend-src(12+ quoted lowercase 3–8 letter words) scoped tofrontend-dapp/src/*.ts(x)with*.test|specallowlist. Pre-commit:gitleaks protect --staged -c .gitleaks.toml. Why it did not block before: default gitleaks rules do not model BIP39 word lists;gitleaks detecton full history can still list old commits — usegitleaks detect --no-gitor pre-commit for current sources.Ecosystem: acceptance asked for
cl8y-ecosystem-qaDEX-Security-Checklist-DRAFT.mdrow 1.5 — that file lives in another project; I did not open an MR there from this repo.Issue left open for your sign-off.
Checklist for verification
cd frontend-dapp && npm run buildsucceeds on a clean tree withoutVITE_DEV_MNEMONICin env or.env*(production).VITE_DEV_MNEMONIC=x npm run buildinfrontend-dappfails with the GitLab #118 error.grep -rF 'notice oak worry' dist/assets/*.jsis empty afternpm run build.VITE_DEV_MODE=true+VITE_DEV_MNEMONICin.env.development(or fromdeploy-dex-local.shoutput): Simulated Wallet connects and shows the expected address for that mnemonic.gitleaks protect --staged -c .gitleaks.tomlclean when staging a normaltschange; re-staging a 12+ word BIP39-like string insrc/(non-test) should be flagged.test:e2ein your environment with LocalTerra if you rely on Simulated Wallet in E2E.Leaving open as requested; tagging for verification.
mentioned in issue #117
Verified on
1c2d1bb. 5/6 PASS (item 4 browser deferred; item 5 done at source-review level since gitleaks binary is not on the QA server). Leaving open for browser walkthrough.Acceptance items
VITE_DEV_MNEMONIC—npm run buildsucceeded in 13.07s on a clean tree (.env.localhadVITE_DEV_MODE=trueonly, noVITE_DEV_MNEMONIC; shell env clean). Build output complete with all expected chunks.VITE_DEV_MNEMONIC=x npm run buildfails with #118 error — guard fires correctly: Build aborts at config-load time, before any bundle is written. Guard iscommand === 'build' && mode === 'production'scoped, usesloadEnv(mode, ..., 'VITE_')so it covers.env,.env.local,.env.production, and shell.grep -F 'notice oak worry' dist/assets/*.js— 0 hits on fresh prod build. Source filefrontend-dapp/src/services/terraclassic/devWallet.tsno longer contains the literal either (grep -Fconfirms REMOVED)..env.developmentmnemonic — DEFERRED, needs browser session. Will run with a stack-up session..gitleaks.tomlaudited:id = "bip39-like-phrase-frontend-src"(matches issue text)regex = '''['\"]([a-z]{3,8}(?:\s[a-z]{3,8}){11,})['\"]'''— 12+ lowercase words of length 3-8, BIP39 word-length range correct.path = '''(?i)(frontend-dapp|frontend)/src/.*\.(ts|tsx)$'''— scoped to app source.*.test.ts(x),*.spec.ts(x)— fixtures don't trip the rule (correct, dev-wallet tests need mnemonic strings).terra1[a-z0-9]{38,}andSecp256k1PubKey— sensible exclusions.echo "'notice oak worry ... genius'" | grep -E "..."→ MATCHES. Rule would have caught the original leak.Out-of-repo task
f2fe6a7oncl8y-ecosystem-qamain:specs(dex-security): mark row 1.5 secret leakage OK (DEX #118 verified). Row now readsNO — no secrets ship in prod | Fixed by #118 (commit 1c2d1bb)... | OK.Good layered fix — source clean + build guard + dist clean + gitleaks rule. Will close after item 4 browser walkthrough.
cc @PlasticDigits
@Brouie pending 4 browser walkthru to close
mentioned in issue #133
mentioned in issue #121
@PlasticDigits @totdking — last open item is the browser walk on Simulated Wallet (item 4 from acceptance). since totdking is now driving DEX visual QA, tagging him for the on-stack walkthrough since he likely has the LocalTerra + frontend running.
totdking — quick check when convenient: with
VITE_DEV_MODE=trueand a validVITE_DEV_MNEMONICinfrontend-dapp/.env.development(or viadeploy-dex-local.sh), confirm Simulated Wallet connects in the dapp and shows the expected address derived from that mnemonic. screenshot of the wallet panel is enough.Wallet from front end
Terminal derivation from mnemonic
Works as expected @Brouie
@totdking Do not upload screenshots including partial mnemonics (this is a dev mnemonic, but establishing best practtice & habits). please remove from your note. However, issue is closed.
mentioned in issue #337
mentioned in issue #372
mentioned in issue #376
mentioned in merge request !904
mentioned in issue #421
mentioned in issue #695
marked as related to #695
mentioned in merge request !1192
mentioned in issue #706