Verify #378: frontend trust boundaries, build guards, expert mode #909

Merged
PlasticDigits merged 3 commits from verify/issue-378-frontend-trust-boundaries into main 2026-06-14 07:08:54 +00:00
PlasticDigits commented 2026-06-14 03:10:59 +00:00 (Migrated from gitlab.com)

Summary

Implements the #376 remediation bundle tracked by #378:

  • H-04: Off-chain indexer trust documented in docs/security-model.md; HTTPS VITE_INDEXER_URL in launch checklist.
  • H-05: vite build rejects VITE_DEV_MNEMONIC outside development unless VITE_ALLOW_DEV_MNEMONIC=local-only.
  • M-07: Production CSP narrows connect-src to env LCD/RPC/indexer + WalletConnect relay (viteCsp.ts); dev index.html keeps broad policy.
  • M-08: Factory/router on /protocol only (protocol-contract-addresses).
  • M-09: Token logo host allowlist + human-review doc in CG_CMC_COMPLIANCE.md.
  • M-10: Production build requires VITE_WC_PROJECT_ID; removed shared WC default from wallet.ts.
  • M-15: Expert mode typed confirmation (ENABLE EXPERT MODE); 30%/50% thresholds unchanged.

Acceptance checklist

Criterion Command / result
Off-chain indexer trust + HTTPS deploy checklist docs/security-model.md § Off-chain trust; docs/runbooks/launch-checklist.md Phase 4
Dev mnemonic build guard (staging) VITE_DEV_MNEMONIC=test npx vite build --mode staging → exit 1
WC project ID production guard npx vite build --mode production (no WC) → exit 1
CSP tightened in production VITE_WC_PROJECT_ID=… npx vite build --mode production → dist/index.html connect-src has no bare https:
Factory/router on protocol only npx vitest run src/pages/ProtocolPage.test.tsx PASS
Logo allowlist npx vitest run src/utils/__tests__/tokenLogoAllowlist.test.ts src/components/ui/__tests__/TokenLogo.test.tsx PASS
Expert typed confirm npx vitest run src/components/swap/__tests__/ExpertModeModal.test.tsx PASS; SwapPage.test.tsx updated
All frontend unit tests make test-frontend → 996 passed
Lint make lint-frontend → 0 errors
Production artifacts rg dist for default WC ID / test mnemonic → not found
Manual make dev route/protocol/expert SKIP — LocalTerra not provisioned on this VM (make has-localterra exit 2)

Test plan

make test-frontend
make lint-frontend
cd frontend-dapp && npx vitest run src/viteConfig.build.test.ts

Follow-ups

  • Add operator-specific LCD/RPC/indexer hosts to render.yaml CSP headers at deploy time (static header cannot read Vite env).
  • Optional startup factory LCD sanity check (documented; not implemented in this MR).

Note

Medium Risk
Touches production CSP, WalletConnect configuration, and documented indexer trust assumptions—misconfigured deploy env could break wallets or leave overly permissive dev CSP if production build is skipped.

Overview
Delivers the #378 remediation bundle: documents off-chain trust (indexer, build env, logos, LCD/RPC) in docs/security-model.md, launch checklist, operator secrets, and token listing review in CG_CMC_COMPLIANCE.md.

Build & deploy: vite.config.ts now fails non-development builds when VITE_DEV_MNEMONIC is set unless VITE_ALLOW_DEV_MNEMONIC=local-only, and fails production builds without VITE_WC_PROJECT_ID. wallet.ts drops the shared WalletConnect default. viteCsp.ts injects a production CSP meta tag (scoped connect-src / logo img-src); render.yaml removes the static CSP header so policy is env-aware at build time.

UX & safety: Factory/router addresses appear on /protocol only. TokenLogo uses an HTTPS host allowlist with blockie fallback. Expert mode requires typing ENABLE EXPERT MODE (ack version bumped); E2E and unit tests updated accordingly.

Reviewed by Cursor Bugbot for commit 06d36616d1. Bugbot is set up for automated code reviews on this repo. Configure here.

## Summary Implements the #376 remediation bundle tracked by #378: - **H-04:** Off-chain indexer trust documented in `docs/security-model.md`; HTTPS `VITE_INDEXER_URL` in launch checklist. - **H-05:** `vite build` rejects `VITE_DEV_MNEMONIC` outside `development` unless `VITE_ALLOW_DEV_MNEMONIC=local-only`. - **M-07:** Production CSP narrows `connect-src` to env LCD/RPC/indexer + WalletConnect relay (`viteCsp.ts`); dev `index.html` keeps broad policy. - **M-08:** Factory/router on `/protocol` only (`protocol-contract-addresses`). - **M-09:** Token logo host allowlist + human-review doc in `CG_CMC_COMPLIANCE.md`. - **M-10:** Production build requires `VITE_WC_PROJECT_ID`; removed shared WC default from `wallet.ts`. - **M-15:** Expert mode typed confirmation (`ENABLE EXPERT MODE`); 30%/50% thresholds unchanged. ## Acceptance checklist | Criterion | Command / result | |-----------|-------------------| | Off-chain indexer trust + HTTPS deploy checklist | `docs/security-model.md` § Off-chain trust; `docs/runbooks/launch-checklist.md` Phase 4 | | Dev mnemonic build guard (staging) | `VITE_DEV_MNEMONIC=test npx vite build --mode staging` → exit 1 | | WC project ID production guard | `npx vite build --mode production` (no WC) → exit 1 | | CSP tightened in production | `VITE_WC_PROJECT_ID=… npx vite build --mode production` → `dist/index.html` connect-src has no bare `https:` | | Factory/router on protocol only | `npx vitest run src/pages/ProtocolPage.test.tsx` PASS | | Logo allowlist | `npx vitest run src/utils/__tests__/tokenLogoAllowlist.test.ts src/components/ui/__tests__/TokenLogo.test.tsx` PASS | | Expert typed confirm | `npx vitest run src/components/swap/__tests__/ExpertModeModal.test.tsx` PASS; `SwapPage.test.tsx` updated | | All frontend unit tests | `make test-frontend` → 996 passed | | Lint | `make lint-frontend` → 0 errors | | Production artifacts | `rg` dist for default WC ID / test mnemonic → not found | | Manual `make dev` route/protocol/expert | **SKIP** — LocalTerra not provisioned on this VM (`make has-localterra` exit 2) | ## Test plan ```bash make test-frontend make lint-frontend cd frontend-dapp && npx vitest run src/viteConfig.build.test.ts ``` ## Follow-ups - Add operator-specific LCD/RPC/indexer hosts to `render.yaml` CSP headers at deploy time (static header cannot read Vite env). - Optional startup factory LCD sanity check (documented; not implemented in this MR). <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches production CSP, WalletConnect configuration, and documented indexer trust assumptions—misconfigured deploy env could break wallets or leave overly permissive dev CSP if production build is skipped. > > **Overview** > Delivers the **#378** remediation bundle: documents off-chain trust (indexer, build env, logos, LCD/RPC) in **`docs/security-model.md`**, launch checklist, operator secrets, and token listing review in **`CG_CMC_COMPLIANCE.md`**. > > **Build & deploy:** **`vite.config.ts`** now fails non-`development` builds when **`VITE_DEV_MNEMONIC`** is set unless **`VITE_ALLOW_DEV_MNEMONIC=local-only`**, and fails **production** builds without **`VITE_WC_PROJECT_ID`**. **`wallet.ts`** drops the shared WalletConnect default. **`viteCsp.ts`** injects a production CSP meta tag (scoped **`connect-src`** / logo **`img-src`**); **`render.yaml`** removes the static CSP header so policy is env-aware at build time. > > **UX & safety:** Factory/router addresses appear on **`/protocol`** only. **`TokenLogo`** uses an HTTPS host allowlist with blockie fallback. **Expert mode** requires typing **`ENABLE EXPERT MODE`** (ack version bumped); E2E and unit tests updated accordingly. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 06d36616d1e1fb03327da9d9d6a063e07b707c40. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
ghost1 commented 2026-06-14 03:11:13 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
ghost1 commented 2026-06-14 03:11:21 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
ghost1 commented 2026-06-14 03:12:07 +00:00 (Migrated from gitlab.com)

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit eaecea2462. Configure here.

<!-- BUGBOT_REVIEW --> Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues. <!-- BUGBOT_FIX_ALL --> <a href="https://cursor.com/open?link=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9BTExfSU5fQ1VSU09SIiwiZGF0YSI6eyJyZWRpc0tleSI6ImJ1Z2JvdC1tdWx0aTowYTYxZGU5Ny0zMjY4LTQ5ODAtODMzOC00MTY1Y2Y1MjZiYzIiLCJlbmNyeXB0aW9uS2V5IjoicnJZREN2OV94RlRtQzFyMzZsNmNOU3BOdWZ5NmEyeGhfbUJ6OWxGeFJxYyIsImJyYW5jaCI6InZlcmlmeS9pc3N1ZS0zNzgtZnJvbnRlbmQtdHJ1c3QtYm91bmRhcmllcyIsInJlcG9Pd25lciI6IlBsYXN0aWNEaWdpdHMiLCJyZXBvTmFtZSI6ImNsOHktZGV4LXRlcnJhY2xhc3NpYyJ9fQ" target="_blank" rel="noopener noreferrer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/fix-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/fix-in-cursor-light.png"><img alt="Fix All in Cursor" width="115" height="28" src="https://cursor.com/assets/images/fix-in-cursor-dark.png"></picture></a> <!-- /BUGBOT_FIX_ALL --> <!-- BUGBOT_AUTOFIX_REVIEW_FOOTNOTE_BEGIN --> <sup>❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the [Cursor dashboard](https://www.cursor.com/dashboard/bugbot).</sup> <!-- BUGBOT_AUTOFIX_REVIEW_FOOTNOTE_END --> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit eaecea24627cac200be7dd72ae8197b760d38e04. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
ghost1 commented 2026-06-14 03:12:08 +00:00 (Migrated from gitlab.com)

CSP omits runtime LCD fallbacks

Medium Severity

Production connect-src is built only from VITE_TERRA_LCD_URL, VITE_TERRA_RPC_URL, and VITE_INDEXER_URL. The app still falls back to default public LCD/RPC URLs in constants.ts when those vars are unset, so a production bundle can call hosts that CSP never whitelists and the browser blocks them.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit eaecea2462. Configure here.

### CSP omits runtime LCD fallbacks **Medium Severity** <!-- DESCRIPTION START --> Production `connect-src` is built only from `VITE_TERRA_LCD_URL`, `VITE_TERRA_RPC_URL`, and `VITE_INDEXER_URL`. The app still falls back to default public LCD/RPC URLs in `constants.ts` when those vars are unset, so a production bundle can call hosts that CSP never whitelists and the browser blocks them. <!-- DESCRIPTION END --> <!-- BUGBOT_BUG_ID: 54b46cbb-33b2-44e3-a3b9-d9f580a7f9a8 --> <!-- LOCATIONS START frontend-dapp/viteCsp.ts#L26-L34 LOCATIONS END --> <div><a href="https://cursor.com/open?link=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9DVVJTT1IiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OjdjZGFhN2M3LWVhOWMtNGU0Zi05ZGFiLTBiZjBmMGU5MmUzYiIsImVuY3J5cHRpb25LZXkiOiIwQk5KUTBndHVidmx5N3puU1BrVWxhUzVScjIxQm1mdzVzTERPRUVnVDB3IiwiYnJhbmNoIjoidmVyaWZ5L2lzc3VlLTM3OC1mcm9udGVuZC10cnVzdC1ib3VuZGFyaWVzIiwicmVwb093bmVyIjoiUGxhc3RpY0RpZ2l0cyIsInJlcG9OYW1lIjoiY2w4eS1kZXgtdGVycmFjbGFzc2ljIn19" target="_blank" rel="noopener noreferrer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/fix-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/fix-in-cursor-light.png"><img alt="Fix in Cursor" width="115" height="28" src="https://cursor.com/assets/images/fix-in-cursor-dark.png"></picture></a>&nbsp;<a href="https://cursor.com/agents?link=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9XRUIiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OjdjZGFhN2M3LWVhOWMtNGU0Zi05ZGFiLTBiZjBmMGU5MmUzYiIsImVuY3J5cHRpb25LZXkiOiIwQk5KUTBndHVidmx5N3puU1BrVWxhUzVScjIxQm1mdzVzTERPRUVnVDB3IiwiYnJhbmNoIjoidmVyaWZ5L2lzc3VlLTM3OC1mcm9udGVuZC10cnVzdC1ib3VuZGFyaWVzIiwicmVwb093bmVyIjoiUGxhc3RpY0RpZ2l0cyIsInJlcG9OYW1lIjoiY2w4eS1kZXgtdGVycmFjbGFzc2ljIiwicHJOdW1iZXIiOjE5MSwiY29tbWl0U2hhIjoiZWFlY2VhMjQ2MjdjYWMyMDBiZTdkZDcyYWU4MTk3Yjc2MGQzOGUwNCIsInByb3ZpZGVyIjoiZ2l0bGFiIiwiaG9zdG5hbWUiOiJnaXRsYWIuY29tIn19" target="_blank" rel="noopener noreferrer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/fix-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/fix-in-web-light.png"><img alt="Fix in Web" width="99" height="28" src="https://cursor.com/assets/images/fix-in-web-dark.png"></picture></a></div> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit eaecea24627cac200be7dd72ae8197b760d38e04. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
ghost1 commented 2026-06-14 03:12:08 +00:00 (Migrated from gitlab.com)

Render CSP blocks API calls

High Severity

The static Content-Security-Policy header’s connect-src lists only 'self' and WalletConnect hosts, not the Terra LCD, RPC, or indexer origins the dApp fetches at runtime. Browsers enforce every CSP policy together, so this header intersects with the production meta policy and blocks those requests on Render.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit eaecea2462. Configure here.

### Render CSP blocks API calls **High Severity** <!-- DESCRIPTION START --> The static `Content-Security-Policy` header’s `connect-src` lists only `'self'` and WalletConnect hosts, not the Terra LCD, RPC, or indexer origins the dApp fetches at runtime. Browsers enforce every CSP policy together, so this header intersects with the production meta policy and blocks those requests on Render. <!-- DESCRIPTION END --> <!-- BUGBOT_BUG_ID: 528900f7-5129-41ee-9ab4-6d2d60300fb5 --> <!-- LOCATIONS START render.yaml#L10-L11 LOCATIONS END --> <div><a href="https://cursor.com/open?link=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9DVVJTT1IiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OjAxODM4MzA2LWU4MTQtNGFlMy04MjY4LWFmNTQ2MWQyNDRmYyIsImVuY3J5cHRpb25LZXkiOiIxcTdFNko3X3RkVjRta3pqWVNDOTMyVXozT19IN2hRYUw5dHY1cC1LQ1RFIiwiYnJhbmNoIjoidmVyaWZ5L2lzc3VlLTM3OC1mcm9udGVuZC10cnVzdC1ib3VuZGFyaWVzIiwicmVwb093bmVyIjoiUGxhc3RpY0RpZ2l0cyIsInJlcG9OYW1lIjoiY2w4eS1kZXgtdGVycmFjbGFzc2ljIn19" target="_blank" rel="noopener noreferrer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/fix-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/fix-in-cursor-light.png"><img alt="Fix in Cursor" width="115" height="28" src="https://cursor.com/assets/images/fix-in-cursor-dark.png"></picture></a>&nbsp;<a href="https://cursor.com/agents?link=eyJ2ZXJzaW9uIjoxLCJ0eXBlIjoiQlVHQk9UX0ZJWF9JTl9XRUIiLCJkYXRhIjp7InJlZGlzS2V5IjoiYnVnYm90OjAxODM4MzA2LWU4MTQtNGFlMy04MjY4LWFmNTQ2MWQyNDRmYyIsImVuY3J5cHRpb25LZXkiOiIxcTdFNko3X3RkVjRta3pqWVNDOTMyVXozT19IN2hRYUw5dHY1cC1LQ1RFIiwiYnJhbmNoIjoidmVyaWZ5L2lzc3VlLTM3OC1mcm9udGVuZC10cnVzdC1ib3VuZGFyaWVzIiwicmVwb093bmVyIjoiUGxhc3RpY0RpZ2l0cyIsInJlcG9OYW1lIjoiY2w4eS1kZXgtdGVycmFjbGFzc2ljIiwicHJOdW1iZXIiOjE5MSwiY29tbWl0U2hhIjoiZWFlY2VhMjQ2MjdjYWMyMDBiZTdkZDcyYWU4MTk3Yjc2MGQzOGUwNCIsInByb3ZpZGVyIjoiZ2l0bGFiIiwiaG9zdG5hbWUiOiJnaXRsYWIuY29tIn19" target="_blank" rel="noopener noreferrer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/fix-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/fix-in-web-light.png"><img alt="Fix in Web" width="99" height="28" src="https://cursor.com/assets/images/fix-in-web-dark.png"></picture></a></div> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit eaecea24627cac200be7dd72ae8197b760d38e04. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
PlasticDigits commented 2026-06-14 03:13:33 +00:00 (Migrated from gitlab.com)

Security review — MR !909

Commit reviewed: eaecea24627cac200be7dd72ae8197b760d38e04

Scope: #378 frontend trust-boundary bundle — build env guards (VITE_DEV_MNEMONIC, VITE_WC_PROJECT_ID), production CSP (viteCsp.ts, render.yaml), token logo host allowlist (TokenLogo / tokenLogoAllowlist.ts), protocol-page contract audit surface, expert-mode typed confirmation, and related docs/tests.

Outcome: FINDINGS: 0 medium+

Summary

This MR is a net hardening pass. Traced attacker-controlled inputs (indexer logo_url, build-time VITE_*, expert-mode UI, CSP env URLs) to their sinks:

Area Assessment
Build guards Non-development builds reject inlined VITE_DEV_MNEMONIC unless VITE_ALLOW_DEV_MNEMONIC=local-only; production requires VITE_WC_PROJECT_ID. Removes shared WC default from the bundle. No bypass for external attackers — only CI/operator env.
Token logos resolveAllowedTokenLogoUri enforces https: + hostname allowlist before <img src>; disallowed URIs fall back to blockies. Narrows prior blanket https: img-src. Residual phishing via indexer-curated URLs on allowlisted CDNs (e.g. gitlab.com raw) is documented off-chain trust, not a new client-side sink.
Production CSP buildProductionCspMetaContent derives connect-src/img-src from env origins + fixed WC hosts; originFromEnvUrl normalizes via URL() (no HTML/CSP attribute breakout). unsafe-inline script/style pre-exists.
Expert mode Typed phrase gate is UX friction only; slippage block still enforced in SwapPage via readExpertMode(). localStorage tampering is same-origin self-risk only.
Protocol addresses Factory/router moved to /protocol audit surface; on-chain execution still uses env-inlined addresses (pre-existing deploy trust).

Prior threads re-checked

Re-evaluated Bugbot inline notes on render.yaml and viteCsp.ts:

  • Render header ∩ meta CSP — Intersecting policies is more restrictive; missing LCD/RPC/indexer in the static Render header breaks connectivity (availability / deploy config), not an attacker-controlled bypass or widened fetch surface.
  • CSP vs constants.ts LCD fallbacks — Unset VITE_TERRA_* yields runtime publicnode.com calls that production CSP would block; again misconfiguration/DoS, not a plausible fund-theft or injection path.

Neither rises to medium+ security under an exploitability standard.

Inline finding threads: none (no medium+ issues on this diff).

## Security review — MR !909 **Commit reviewed:** `eaecea24627cac200be7dd72ae8197b760d38e04` **Scope:** #378 frontend trust-boundary bundle — build env guards (`VITE_DEV_MNEMONIC`, `VITE_WC_PROJECT_ID`), production CSP (`viteCsp.ts`, `render.yaml`), token logo host allowlist (`TokenLogo` / `tokenLogoAllowlist.ts`), protocol-page contract audit surface, expert-mode typed confirmation, and related docs/tests. **Outcome:** `FINDINGS: 0` medium+ ### Summary This MR is a net hardening pass. Traced attacker-controlled inputs (indexer `logo_url`, build-time `VITE_*`, expert-mode UI, CSP env URLs) to their sinks: | Area | Assessment | |------|------------| | **Build guards** | Non-`development` builds reject inlined `VITE_DEV_MNEMONIC` unless `VITE_ALLOW_DEV_MNEMONIC=local-only`; production requires `VITE_WC_PROJECT_ID`. Removes shared WC default from the bundle. No bypass for external attackers — only CI/operator env. | | **Token logos** | `resolveAllowedTokenLogoUri` enforces `https:` + hostname allowlist before `<img src>`; disallowed URIs fall back to blockies. Narrows prior blanket `https:` `img-src`. Residual phishing via indexer-curated URLs on allowlisted CDNs (e.g. `gitlab.com` raw) is documented off-chain trust, not a new client-side sink. | | **Production CSP** | `buildProductionCspMetaContent` derives `connect-src`/`img-src` from env origins + fixed WC hosts; `originFromEnvUrl` normalizes via `URL()` (no HTML/CSP attribute breakout). `unsafe-inline` script/style pre-exists. | | **Expert mode** | Typed phrase gate is UX friction only; slippage block still enforced in `SwapPage` via `readExpertMode()`. localStorage tampering is same-origin self-risk only. | | **Protocol addresses** | Factory/router moved to `/protocol` audit surface; on-chain execution still uses env-inlined addresses (pre-existing deploy trust). | ### Prior threads re-checked Re-evaluated Bugbot inline notes on `render.yaml` and `viteCsp.ts`: - **Render header ∩ meta CSP** — Intersecting policies is *more* restrictive; missing LCD/RPC/indexer in the static Render header breaks connectivity (availability / deploy config), not an attacker-controlled bypass or widened fetch surface. - **CSP vs `constants.ts` LCD fallbacks** — Unset `VITE_TERRA_*` yields runtime `publicnode.com` calls that production CSP would block; again misconfiguration/DoS, not a plausible fund-theft or injection path. Neither rises to medium+ security under an exploitability standard. **Inline finding threads:** none (no medium+ issues on this diff).
PlasticDigits commented 2026-06-14 05:45:44 +00:00 (Migrated from gitlab.com)

changed this line in version 2 of the diff

changed this line in [version 2 of the diff](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/191/diffs?diff_id=1850596494&start_sha=eaecea24627cac200be7dd72ae8197b760d38e04#1b8a9c728ebe242b14ab7e5c41c6324017da50ee_11_12)
PlasticDigits commented 2026-06-14 05:45:45 +00:00 (Migrated from gitlab.com)

added 1 commit

  • 06d36616 - Fix production CSP fallback hosts and Render header intersection.

Compare with previous version

added 1 commit <ul><li>06d36616 - Fix production CSP fallback hosts and Render header intersection.</li></ul> [Compare with previous version](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/191/diffs?diff_id=1850596494&start_sha=eaecea24627cac200be7dd72ae8197b760d38e04)
PlasticDigits commented 2026-06-14 05:45:47 +00:00 (Migrated from gitlab.com)

resolved all threads

resolved all threads
PlasticDigits commented 2026-06-14 05:45:52 +00:00 (Migrated from gitlab.com)

Bugfix follow-up (06d36616)

Thread Fix
CSP omits runtime LCD fallbacks buildProductionConnectSrc now includes PRODUCTION_TERRA_LCD_FALLBACK / PRODUCTION_TERRA_RPC_FALLBACK (same hosts as constants.ts) when VITE_TERRA_* env URLs are unset. Regression: viteCsp.test.ts empty-env case.
Render CSP blocks API calls Removed the static Content-Security-Policy header from render.yaml. Browsers intersect header + meta policies; the static header could not mirror operator VITE_* origins (LCD/RPC/indexer) and blocked fetches on Render. Production CSP remains env-aware via vite build → dist/index.html meta (viteCsp.ts). Other security headers (X-Content-Type-Options, X-Frame-Options, Referrer-Policy) unchanged.

Tests: npx vitest run src/utils/__tests__/viteCsp.test.ts src/viteConfig.build.test.ts — 9 passed.

## Bugfix follow-up (`06d36616`) | Thread | Fix | |--------|-----| | **CSP omits runtime LCD fallbacks** | `buildProductionConnectSrc` now includes `PRODUCTION_TERRA_LCD_FALLBACK` / `PRODUCTION_TERRA_RPC_FALLBACK` (same hosts as `constants.ts`) when `VITE_TERRA_*` env URLs are unset. Regression: `viteCsp.test.ts` empty-env case. | | **Render CSP blocks API calls** | Removed the static `Content-Security-Policy` header from `render.yaml`. Browsers intersect header + meta policies; the static header could not mirror operator `VITE_*` origins (LCD/RPC/indexer) and blocked fetches on Render. Production CSP remains env-aware via `vite build` → `dist/index.html` meta (`viteCsp.ts`). Other security headers (`X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy`) unchanged. | Tests: `npx vitest run src/utils/__tests__/viteCsp.test.ts src/viteConfig.build.test.ts` — 9 passed.
ghost1 commented 2026-06-14 05:45:57 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
PlasticDigits commented 2026-06-14 05:48:24 +00:00 (Migrated from gitlab.com)

Security review — MR !909

Commit reviewed: 06d36616d1e1fb03327da9d9d6a063e07b707c40

Scope: #378 frontend trust-boundary bundle — vite.config.ts build guards (VITE_DEV_MNEMONIC, VITE_WC_PROJECT_ID), production CSP (viteCsp.ts, render.yaml), token logo host allowlist (tokenLogoAllowlist.ts / TokenLogo), /protocol contract audit surface, expert-mode typed confirmation, and related docs/tests.

Outcome: FINDINGS: 0 medium+

Summary

Re-reviewed after bugfix commit 06d36616 (LCD/RPC CSP fallbacks + removal of static Render Content-Security-Policy header). Traced attacker-controlled inputs to sinks:

Area Assessment
Build guards Non-development builds reject inlined VITE_DEV_MNEMONIC unless VITE_ALLOW_DEV_MNEMONIC=local-only; production requires VITE_WC_PROJECT_ID. Shared WC default removed from wallet.ts. Controls are build/CI-operator scoped — no external bypass.
Production CSP buildProductionCspMetaContent derives origins via URL() (originFromEnvUrl); includes constants.ts LCD/RPC fallbacks when env unset. Static Render CSP header removed so header ∩ meta no longer blocks legitimate fetches. unsafe-inline script/style is pre-existing.
Token logos Indexer logo_url → resolveAllowedTokenLogoUri enforces https: + hostname allowlist before <img src>; disallowed URIs fall back to blockies. Production img-src mirrors allowlist (narrower than prior blanket https:). Residual phishing via operator-curated allowlisted hosts is documented off-chain trust, not a new client sink.
Expert mode Typed phrase is UX friction; slippage block still enforced in SwapPage via readExpertMode() / SWAP_EXPERT_MODE_SLIPPAGE_BLOCK_PCT. localStorage tampering is same-origin self-risk only.
Protocol addresses Factory/router on /protocol for audit; on-chain execution still uses env-inlined addresses (deploy trust, unchanged).

Prior threads re-checked

  • Bugbot / earlier review — Render header ∩ meta CSP — Resolved in 06d36616 by removing static CSP header; intersecting policies was availability/config, not attacker-widened fetch surface.
  • Bugbot — CSP vs LCD fallbacks — Resolved: buildProductionConnectSrc now includes PRODUCTION_TERRA_LCD_FALLBACK / PRODUCTION_TERRA_RPC_FALLBACK.
  • Indexer MITM / malicious routes — Documented in docs/security-model.md; out of scope for client-only hardening in this diff.

Inline finding threads: none (no medium+ issues on this diff).

Security review: no medium+ findings on this diff.

## Security review — MR !909 **Commit reviewed:** `06d36616d1e1fb03327da9d9d6a063e07b707c40` **Scope:** #378 frontend trust-boundary bundle — `vite.config.ts` build guards (`VITE_DEV_MNEMONIC`, `VITE_WC_PROJECT_ID`), production CSP (`viteCsp.ts`, `render.yaml`), token logo host allowlist (`tokenLogoAllowlist.ts` / `TokenLogo`), `/protocol` contract audit surface, expert-mode typed confirmation, and related docs/tests. **Outcome:** `FINDINGS: 0` medium+ ### Summary Re-reviewed after bugfix commit `06d36616` (LCD/RPC CSP fallbacks + removal of static Render `Content-Security-Policy` header). Traced attacker-controlled inputs to sinks: | Area | Assessment | |------|------------| | **Build guards** | Non-`development` builds reject inlined `VITE_DEV_MNEMONIC` unless `VITE_ALLOW_DEV_MNEMONIC=local-only`; production requires `VITE_WC_PROJECT_ID`. Shared WC default removed from `wallet.ts`. Controls are build/CI-operator scoped — no external bypass. | | **Production CSP** | `buildProductionCspMetaContent` derives origins via `URL()` (`originFromEnvUrl`); includes `constants.ts` LCD/RPC fallbacks when env unset. Static Render CSP header removed so header ∩ meta no longer blocks legitimate fetches. `unsafe-inline` script/style is pre-existing. | | **Token logos** | Indexer `logo_url` → `resolveAllowedTokenLogoUri` enforces `https:` + hostname allowlist before `<img src>`; disallowed URIs fall back to blockies. Production `img-src` mirrors allowlist (narrower than prior blanket `https:`). Residual phishing via operator-curated allowlisted hosts is documented off-chain trust, not a new client sink. | | **Expert mode** | Typed phrase is UX friction; slippage block still enforced in `SwapPage` via `readExpertMode()` / `SWAP_EXPERT_MODE_SLIPPAGE_BLOCK_PCT`. localStorage tampering is same-origin self-risk only. | | **Protocol addresses** | Factory/router on `/protocol` for audit; on-chain execution still uses env-inlined addresses (deploy trust, unchanged). | ### Prior threads re-checked - **Bugbot / earlier review — Render header ∩ meta CSP** — Resolved in `06d36616` by removing static CSP header; intersecting policies was availability/config, not attacker-widened fetch surface. - **Bugbot — CSP vs LCD fallbacks** — Resolved: `buildProductionConnectSrc` now includes `PRODUCTION_TERRA_LCD_FALLBACK` / `PRODUCTION_TERRA_RPC_FALLBACK`. - **Indexer MITM / malicious routes** — Documented in `docs/security-model.md`; out of scope for client-only hardening in this diff. **Inline finding threads:** none (no medium+ issues on this diff). Security review: no medium+ findings on this diff.
PlasticDigits commented 2026-06-14 06:57:51 +00:00 (Migrated from gitlab.com)

added 33 commits

  • 06d36616...a7f2691e - 32 commits from branch main
  • 01b1b664 - Merge branch 'main' into verify/issue-378-frontend-trust-boundaries

Compare with previous version

added 33 commits <ul><li>06d36616...a7f2691e - 32 commits from branch <code>main</code></li><li>01b1b664 - Merge branch &#39;main&#39; into verify/issue-378-frontend-trust-boundaries</li></ul> [Compare with previous version](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/191/diffs?diff_id=1850611921&start_sha=06d36616d1e1fb03327da9d9d6a063e07b707c40)
PlasticDigits commented 2026-06-14 06:57:53 +00:00 (Migrated from gitlab.com)

Resolved merge conflicts with main (commit 01b1b664).

Kept from MR branch: viteCsp.ts production CSP module, unified assertBuildEnvGuards, no shared WalletConnect default in wallet.ts, protocol-contract-addresses audit section with AddressRow.

Integrated from main: LCD/RPC rows on /protocol, VITE_VERIFY_DEPLOY_ADDRESSES in .env.example, merged token-logo host allowlist (CoinGecko/CMC + IPFS gateways) with backward-compat aliases, and combined launch-checklist / compliance doc wording.

Targeted vitest: viteConfig.build.test.ts, viteCsp.test.ts, tokenLogoAllowlist, ExpertModeModal, ProtocolPage — all pass.

Resolved merge conflicts with `main` (commit 01b1b664). **Kept from MR branch:** `viteCsp.ts` production CSP module, unified `assertBuildEnvGuards`, no shared WalletConnect default in `wallet.ts`, `protocol-contract-addresses` audit section with `AddressRow`. **Integrated from main:** LCD/RPC rows on `/protocol`, `VITE_VERIFY_DEPLOY_ADDRESSES` in `.env.example`, merged token-logo host allowlist (CoinGecko/CMC + IPFS gateways) with backward-compat aliases, and combined launch-checklist / compliance doc wording. Targeted vitest: `viteConfig.build.test.ts`, `viteCsp.test.ts`, `tokenLogoAllowlist`, `ExpertModeModal`, `ProtocolPage` — all pass.
PlasticDigits commented 2026-06-14 07:01:14 +00:00 (Migrated from gitlab.com)

Security review — MR !909

Commit reviewed: 01b1b664e241ea36be95f003a5f08ddb90db69e7

Scope: #378 frontend trust-boundary bundle after merge with main — vite.config.ts build guards (VITE_DEV_MNEMONIC, VITE_WC_PROJECT_ID), production CSP (viteCsp.ts, render.yaml), token logo host allowlist (tokenLogoAllowlist.ts / TokenLogo), /protocol contract audit surface + deployAddressVerification.ts, expert-mode typed confirmation (EXPERT_MODE_ACK_VERSION 2), bootstrap script extraction (public/bootstrap/*), and related docs/tests.

Outcome: FINDINGS: 0 medium+

Summary

Re-reviewed current MR head (includes merge 01b1b664 resolving main landing conflicts). Traced attacker-controlled inputs to sinks:

Area Assessment
Build guards Non-development builds reject VITE_DEV_MNEMONIC unless VITE_ALLOW_DEV_MNEMONIC=local-only; production requires VITE_WC_PROJECT_ID. wallet.ts drops shared WC default. viteConfig.build.test.ts asserts production mnemonic rejection. Controls are build/CI-operator scoped — no unauthenticated bypass.
Production CSP buildProductionCspMetaContent derives origins via URL(); includes constants.ts LCD/RPC fallbacks when env unset. Static Render CSP header removed (fixes header ∩ meta blocking). unsafe-inline script/style matches pre-merge production template needs after bootstrap externalization.
Token logos Indexer logo_url → resolveAllowedTokenLogoUri enforces https: + hostname allowlist before <img src>; disallowed URIs fall back to blockies. Production img-src mirrors allowlist. Residual phishing via operator-curated allowlisted hosts is documented off-chain trust, not a new client sink.
Expert mode Typed phrase is UX friction; slippage block still enforced in SwapPage via readExpertMode() / SWAP_EXPERT_MODE_SLIPPAGE_BLOCK_PCT. localStorage tampering is same-origin self-risk only.
Protocol / deploy verify Factory/router on /protocol for audit; optional verifyDeployAddressesOnLcd logs warnings only (no authz change). On-chain execution still uses env-inlined addresses (deploy trust, unchanged).
Bootstrap scripts theme.js / trade-shell.js use static DOM writes; trade-shell.js innerHTML is constant markup (no user/input interpolation).

Prior threads re-checked

  • CSP omits runtime LCD fallbacks — Fixed in 06d36616 (PRODUCTION_TERRA_LCD_FALLBACK / PRODUCTION_TERRA_RPC_FALLBACK).
  • Render CSP blocks API calls — Fixed in 06d36616 (static Content-Security-Policy header removed from render.yaml).

Security review: no medium+ findings on this diff.

## Security review — MR !909 **Commit reviewed:** `01b1b664e241ea36be95f003a5f08ddb90db69e7` **Scope:** #378 frontend trust-boundary bundle after merge with `main` — `vite.config.ts` build guards (`VITE_DEV_MNEMONIC`, `VITE_WC_PROJECT_ID`), production CSP (`viteCsp.ts`, `render.yaml`), token logo host allowlist (`tokenLogoAllowlist.ts` / `TokenLogo`), `/protocol` contract audit surface + `deployAddressVerification.ts`, expert-mode typed confirmation (`EXPERT_MODE_ACK_VERSION` 2), bootstrap script extraction (`public/bootstrap/*`), and related docs/tests. **Outcome:** `FINDINGS: 0` medium+ ### Summary Re-reviewed current MR head (includes merge `01b1b664` resolving `main` landing conflicts). Traced attacker-controlled inputs to sinks: | Area | Assessment | |------|------------| | **Build guards** | Non-`development` builds reject `VITE_DEV_MNEMONIC` unless `VITE_ALLOW_DEV_MNEMONIC=local-only`; production requires `VITE_WC_PROJECT_ID`. `wallet.ts` drops shared WC default. `viteConfig.build.test.ts` asserts production mnemonic rejection. Controls are build/CI-operator scoped — no unauthenticated bypass. | | **Production CSP** | `buildProductionCspMetaContent` derives origins via `URL()`; includes `constants.ts` LCD/RPC fallbacks when env unset. Static Render CSP header removed (fixes header ∩ meta blocking). `unsafe-inline` script/style matches pre-merge production template needs after bootstrap externalization. | | **Token logos** | Indexer `logo_url` → `resolveAllowedTokenLogoUri` enforces `https:` + hostname allowlist before `<img src>`; disallowed URIs fall back to blockies. Production `img-src` mirrors allowlist. Residual phishing via operator-curated allowlisted hosts is documented off-chain trust, not a new client sink. | | **Expert mode** | Typed phrase is UX friction; slippage block still enforced in `SwapPage` via `readExpertMode()` / `SWAP_EXPERT_MODE_SLIPPAGE_BLOCK_PCT`. `localStorage` tampering is same-origin self-risk only. | | **Protocol / deploy verify** | Factory/router on `/protocol` for audit; optional `verifyDeployAddressesOnLcd` logs warnings only (no authz change). On-chain execution still uses env-inlined addresses (deploy trust, unchanged). | | **Bootstrap scripts** | `theme.js` / `trade-shell.js` use static DOM writes; `trade-shell.js` `innerHTML` is constant markup (no user/input interpolation). | ### Prior threads re-checked - **CSP omits runtime LCD fallbacks** — Fixed in `06d36616` (`PRODUCTION_TERRA_LCD_FALLBACK` / `PRODUCTION_TERRA_RPC_FALLBACK`). - **Render CSP blocks API calls** — Fixed in `06d36616` (static `Content-Security-Policy` header removed from `render.yaml`). **Security review: no medium+ findings on this diff.**
PlasticDigits commented 2026-06-14 07:08:55 +00:00 (Migrated from gitlab.com)

mentioned in commit d7aa33e895

mentioned in commit d7aa33e895a916a5a84af4b95421abf42c4488ea
PlasticDigits (Migrated from gitlab.com) merged commit d7aa33e895 into main 2026-06-14 07:08:55 +00:00
Sign in to join this conversation.
No reviewers
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/cl8y-dex-terraclassic!909
No description provided.