Full security report #376

Closed
opened 2026-06-13 06:58:25 +00:00 by PlasticDigits · 21 comments
PlasticDigits commented 2026-06-13 06:58:25 +00:00 (Migrated from gitlab.com)
No description provided.
PlasticDigits commented 2026-06-13 07:09:03 +00:00 (Migrated from gitlab.com)

CL8Y DEX — Full-Stack Security Audit Report

Issue: #376 — Full security report
Auditor: Internal Composer security audit (Cloud Agent)
Date: 2026-06-13
Scope: Full codebase — CosmWasm contracts, Terra messages, indexer API/DB, frontend dApp, CI/CD, ops scripts, dependencies, secrets handling
Method: Source review, pattern search, adversarial contract test execution, cross-reference with in-repo invariant matrix (docs/contracts-security-audit.md, docs/indexer-invariants.md)


Executive Summary

CL8Y DEX is a Terra Classic constant-product AMM with hybrid limit-order book, multi-hop router, fee-discount registry, and post-swap hooks. The codebase demonstrates mature defensive engineering: checked arithmetic, governance/factory access control, escrow accounting, slippage guards, indexer SQL parameterization, prod config gates, and extensive regression tests (including adversarial token scenarios).

No Critical permissionless on-chain exploit was identified under the documented trust model (honest governance, whitelisted CW20 code IDs). Residual risk concentrates in:

  1. Governance/ops misconfiguration (hooks, token whitelist, pause, LP-burn allowlist)
  2. Off-chain trust boundaries (indexer route integrity, deploy-time VITE_* config)
  3. CI/supply-chain gaps (no automated SCA in GitLab CI, curl-pipe bootstrap on agent VMs)
  4. Accepted design tradeoffs (300s fee-discount cache, TWAP limitations, public indexer API)
Severity Count
Critical 0
High 7
Medium 14
Low 12
Informational 4

Tests executed during audit:

  • cargo test adversarial (smartcontracts) — 7/7 passed
  • Indexer tests/security.rs — 25+ cases exist (require Postgres; not executed in this session due to compile/runtime prerequisites)

Areas Analyzed

Area Components Status
CosmWasm contracts factory, pair, router, fee-discount, tax/burn/lp-burn hooks, dex-common Reviewed + adversarial tests
Terra messages BankMsg, CW20 Send/Transfer, WasmMsg hooks, router reply chain Reviewed
Indexer Axum API, sqlx queries, LCD client, route solver, CORS, rate limits Reviewed
Database Postgres schema, migrations, dynamic ORDER BY allowlists Reviewed
Frontend React dApp, wallet (Keplr/Station/WC), indexer client, tx construction Reviewed
CI/CD .gitlab-ci.yml, reference GitHub workflows, gitleaks hooks Reviewed
Ops/scripts deploy, cloud-agent, QA artifact publish/fetch, docker-compose Reviewed
Dependencies Cargo.lock, package-lock.json, forked cosmes, elliptic transitive Reviewed
Secrets .env.example, dev mnemonics, GITLAB_TOKEN handling Reviewed

Findings

HIGH

H-01 — Fee-on-transfer CW20 can desync reserves vs on-chain balance

Field Detail
Severity High (conditional on non-standard CW20 whitelist)
Location smartcontracts/contracts/pair/src/contract.rs — execute_swap, execute_provide_liquidity; factory/src/contract.rs — execute_create_pair
Issue Swaps and liquidity use declared cw20_msg.amount, not post-transfer balance deltas. Fee-on-transfer tokens credit the pair less than recorded reserves.
Impact Internal RESERVES exceed CW20 balance. Later withdrawals fail or last LPs absorb deficit.
Attack path Governance whitelists fee-on-transfer CW20 code ID → attacker provides liquidity → repeated swaps widen gap → withdraws LP while min_assets passes.
Evidence Test fee_on_transfer_creates_reserve_imbalance in smartcontracts/tests/src/adversarial_token.rs; invariant P2 in docs/contracts-security-audit.md.
Recommendation Never whitelist fee-on-transfer templates. Document in ops runbook. Optionally reconcile reserves via balance-before/after on inbound transfers.

H-02 — Allowlisted hook returning Err atomically blocks all pair swaps

Field Detail
Severity High (governance misconfiguration)
Location pair/src/contract.rs — execute_swap (~L1174–1224); execute_update_hooks
Issue Post-swap hooks dispatched as WasmMsg::Execute (not SubMsg::reply_on_error). Hook failure reverts entire swap.
Impact Full trading DoS on affected pairs until hooks removed or fixed.
Attack path Governance registers hook (or hook upgraded via wasm admin) → hook Hook handler returns Err → every swap fails.
Evidence Invariant H1; test swap_fails_atomically_when_allowlisted_hook_reverts.
Recommendation Only register audited hooks. Consider SubMsg::reply_on_error if policy hooks must never block swaps.

H-03 — LP burn hook: allowlisted non-pair caller can spoof AfterSwap.pair

Field Detail
Severity High (hook admin misconfiguration)
Location hooks/lp-burn-hook/src/contract.rs — execute_after_swap, assert_allowed_pair
Issue Authorization is info.sender ∈ ALLOWED_PAIRS. pair field in AfterSwap is trusted for burn calculation but not verified on-chain.
Impact Pre-funded LP in hook treasury burned at inflated rates.
Attack path Hook admin adds malicious spoofer to ALLOWED_PAIRS → spoofer calls Hook with fake large output_amount → hook burns min(target_burn, balance) LP.
Evidence Test lp_burn_hook_accepts_spoofed_pair_when_spoofer_allowlisted (adversarial_token.rs).
Recommendation Restrict ALLOWED_PAIRS to verified pair addresses only. Verify pair == info.sender or query pair state before burning.

H-04 — Compromised or malicious indexer can influence signed swap routes

Field Detail
Severity High
Location frontend-dapp/src/pages/SwapPage.tsx; src/services/indexer/routeOperations.ts; src/services/indexer/client.ts
Issue Swap quotes and router_operations come from indexer. dApp validates token_in/token_out against UI selection but trusts hop structure from indexer after parsing.
Impact Malicious indexer (or MITM of VITE_INDEXER_URL) returns routes through adversarial but valid pools; user signs degraded execution.
Attack path Attacker hosts fake indexer or compromises deploy env → user swaps A→B → indexer returns matching tokens with manipulated router_operations → user signs execute_swap_operations.
Evidence Partial mitigation: enrichSwapOperationsWithHopMinReturns resolves pairs via factory getPair() — invalid pairs fail, but valid adversarial pools remain possible.
Recommendation Cross-check each hop against on-chain factory pair graph before submit; pin indexer URL + TLS; show human-readable hop summary in confirmation UI; consider client-side BFS fallback comparison.

H-05 — Dev mnemonic can ship if build mode is misconfigured

Field Detail
Severity High (conditional)
Location frontend-dapp/vite.config.ts (L52–59); src/services/terraclassic/devWallet.ts
Issue VITE_DEV_MNEMONIC blocked only for command === 'build' && mode === 'production'. Non-production build deployed publicly with VITE_DEV_MODE=true inlines mnemonic.
Impact Full key material in JS bundle; wallet drainable by anyone.
Attack path CI uses vite build --mode staging with .env.development containing mnemonic → attacker extracts from bundle.
Evidence Production guard exists (GitLab #118); devWallet.ts has no default mnemonic.
Recommendation Fail build if VITE_DEV_MNEMONIC set unless mode === 'development' or explicit VITE_ALLOW_DEV_MNEMONIC=local-only.

H-06 — No dependency / SCA automation in GitLab CI

Field Detail
Severity High (supply-chain)
Location .gitlab-ci.yml — 2 build jobs only; no cargo-audit, cargo-deny, npm audit, gitleaks
Issue Known CVEs in Rust/Node deps not scanned on merge.
Impact Vulnerable transitive deps (e.g. deprecated elliptic in cosmjs) persist undetected.
Attack path Compromised dependency → key handling / HTTP client bugs in frontend or indexer.
Recommendation Add GitLab dependency scanning or scheduled cargo audit + npm audit --audit-level=high.

H-07 — Cloud Agent VM overly permissive privilege model

Field Detail
Severity High (ops)
Location gch-cloud-setup.sh (NOPASSWD:ALL, approvalMode: "unrestricted"); scripts/lib/cloud-agent-docker.sh (chmod 666 on docker.sock)
Issue Agent user has passwordless sudo; Cursor CLI unrestricted; Docker socket world-writable as fallback.
Impact Any code execution in agent session → root + docker + GITLAB_TOKEN.
Attack path Malicious npm script during make dev → docker escape or sudo → read GITLAB_TOKEN.
Recommendation Drop NOPASSWD:ALL; use docker group only; tighten Cursor approval mode; avoid chmod 666 on docker.sock.

MEDIUM

M-01 — Fee-discount cache: up to 300s fee leakage after CL8Y balance drop

Field Detail
Severity Medium (accepted design)
Location pair/src/discount_cache.rs; dex-common/src/pair.rs — DISCOUNT_CACHE_TTL_SECONDS = 300
Issue Pair caches (effective_fee_bps, discount) for 300s without re-querying registry balance.
Impact User sells CL8Y after qualifying, keeps discounted fees until cache expires (~5 min). Protocol fee revenue loss bounded per wallet per window.
Attack path Register tier → swap (populates cache) → transfer CL8Y away → continue swapping within 300s at discounted fee.
Recommendation Accept as gas tradeoff (documented P9) or shorten TTL; monitor via indexer.

M-02 — TWAP oracle manipulable over multi-block / low-liquidity windows

Field Detail
Severity Medium (external consumers)
Location packages/dex-common/src/oracle.rs; pair/src/contract.rs — oracle_update
Issue Oracle resists single-tx manipulation but documents multi-block manipulation, low TVL, staleness risks.
Impact External protocols using short-window TWAP from low-liquidity pairs can be manipulated.
Recommendation Use ≥30 min windows; cross-check secondary feeds; minimum TVL gates for oracle consumers.

M-03 — Emergency pause freezes maker limit-order withdrawals

Field Detail
Severity Medium (operational)
Location pair/src/contract.rs — assert_not_paused on cancel/claim/clean
Issue Pause blocks CancelLimitOrder, ClaimExpiredLimitOrder, CleanLimitBook. Escrow remains in pair custody.
Impact Governance pause (or compromise) locks resting limit escrow until unpause.
Recommendation Multisig governance, timelock on pause, or allow cancel/claim while paused (product decision).

M-04 — Router SWAP_STATE serializes all multi-hop swaps

Field Detail
Severity Medium (availability)
Location router/src/contract.rs — execute_swap_operations, reply_swap_hop
Issue Single global SWAP_STATE; concurrent router swaps in same block fail with SwapInProgress.
Impact One in-flight multi-hop blocks all others on that router until completion.
Recommendation Per-sender swap state, or document as intentional. Low practical impact on Terra tx serialization.

M-05 — Rate limiting disabled in dev when env vars are zero

Field Detail
Severity Medium (misconfiguration)
Location indexer/src/config.rs; indexer/src/api/mod.rs — apply_rate_limit_layer
Issue When RATE_LIMIT_RPS=0 and RATE_LIMIT_LCD_HEAVY_RPS=0, governors skipped. Prod clamps zeros to 60/10; dev does not.
Impact Unbounded per-IP request rate → DB exhaustion, LCD abuse, CPU DoS on route solver.
Attack path Flood /api/v1/route/solve from one IP if prod runs without RUN_MODE=prod.
Recommendation Always set RUN_MODE=prod in production; startup warning when both limits are 0.

M-06 — Unbounded max_maker_fills on GET route solve (LCD path)

Field Detail
Severity Medium
Location indexer/src/api/route_solver.rs L719 — max_maker_fills.max(1) without upper cap
Issue DB-hybrid path caps at MAX_MAKER_FILLS_HARD_CAP (30); LCD global_v3 path forwards uncapped value to on-chain HybridSimulation.
Impact Single request drives heavy hybrid grid work and LCD fanout before 30s timeout.
Attack path GET /api/v1/route/solve?...&max_maker_fills=4294967295 within LCD-heavy rate limit (10 RPS prod).
Recommendation Clamp GET max_maker_fills to MAX_MAKER_FILLS_HARD_CAP (30).

M-07 — CSP allows unsafe-inline scripts and arbitrary HTTPS connections

Field Detail
Severity Medium
Location frontend-dapp/index.html L18–19
Issue script-src 'self' 'unsafe-inline' and connect-src … https: wss: are broad.
Impact Any XSS (supply-chain, compromised dep) can run inline payloads and exfiltrate to any HTTPS endpoint.
Recommendation Nonce/hash-based CSP; narrow connect-src to known LCD/RPC/indexer hosts.

M-08 — Build-time contract addresses fully trusted

Field Detail
Severity Medium
Location frontend-dapp/src/utils/constants.ts; src/services/terraclassic/router.ts
Issue VITE_ROUTER_ADDRESS, VITE_FACTORY_ADDRESS compiled into bundle with public-node fallbacks.
Impact Wrong/malicious addresses in deploy env → users send tokens to attacker contracts.
Recommendation Pin addresses per network; show router/factory in swap confirmation UI; verify factory on LCD at startup.

M-09 — Indexer-supplied token logos and symbols (display phishing)

Field Detail
Severity Medium
Location frontend-dapp/src/hooks/useTokenDisplayInfo.ts; src/components/ui/TokenLogo.tsx
Issue logo_url from indexer used as <img src>. CW20 metadata cached in localStorage without integrity checks.
Impact Malicious token shows "USDC" branding for scam CW20.
Recommendation Prefer static token registry; show full terra1… address prominently; allowlist logo hosts.

M-10 — Default WalletConnect project ID in client bundle

Field Detail
Severity Medium
Location frontend-dapp/src/services/terraclassic/wallet.ts L37
Issue VITE_WC_PROJECT_ID || '2ce7811b869be33ffad28cff05c93c15' — shared default if unset.
Impact Session routing collisions; WC phishing if combined with social engineering.
Recommendation Require project-specific VITE_WC_PROJECT_ID in production; fail build if missing.

M-11 — GitLab CI Docker-in-Docker without TLS

Field Detail
Severity Medium
Location .gitlab-ci.yml L18–20: DOCKER_TLS_CERTDIR: "", DOCKER_HOST: tcp://docker:2375
Issue Unencrypted DinD socket inside job pod.
Impact Co-process in job namespace could control Docker daemon.
Recommendation Enable DinD TLS or use Kaniko/rootless build.

M-12 — curl | bash bootstrap in agent tooling

Field Detail
Severity Medium
Location scripts/lib/cloud-agent-toolchain.sh (nvm, rustup); gch-cloud-setup.sh
Issue Remote install scripts without checksum/signature verification.
Impact CDN/upstream compromise → RCE on agent VM with GITLAB_TOKEN.
Recommendation Pin script SHAs; vendor tarballs; verify GPG/checksums.

M-13 — Gitleaks optional locally; absent from CI

Field Detail
Severity Medium
Location .githooks/pre-commit L48–56; .gitlab-ci.yml
Issue Pre-commit skips scan if gitleaks not installed; CI does not run gitleaks.
Impact Secrets can reach default branch without hook enforcement.
Recommendation Add gitleaks detect to CI pipeline; fail on findings.

M-14 — Expert mode allows swaps with extreme slippage (up to 50%)

Field Detail
Severity Medium
Location frontend-dapp/src/stores/dex.ts; src/pages/SwapPage.tsx
Issue Swaps with >30% route slippage blocked unless expert mode (localStorage). Slippage capped at 50%.
Impact Social engineering to enable expert mode → devastating execution.
Recommendation Typed confirmation for expert mode + slippage >5%. Positive: min_return still applied on submit.

LOW

L-01 — Hybrid book walk caps can defer limit fills (head-clog / expired prefix)

| Location | pair/src/orderbook.rs — MAX_SCAN_STEPS = 500, MAX_EXPIRED_PARKS_PER_SWAP = 15 |
| Impact | Suboptimal execution, not direct theft. Mitigated by book_start_hint and CleanLimitBook. |

L-02 — Factory SetPairLimitBatchMax omits pair registry check

| Location | factory/src/contract.rs — execute_set_pair_limit_batch_max |
| Impact | Wasted gas only; no privilege escalation. |

L-03 — Blacklist silently disabled against pre-1.5.0 factories

| Location | pair/src/blacklist_guard.rs — probe_factory_blacklist |
| Impact | Blacklist ineffective until factory upgraded. |

L-04 — LCD upstream details logged at WARN

| Location | indexer/src/lcd/mod.rs |
| Impact | Log aggregation may retain internal LCD hostnames and chain error text. Client responses sanitized. |

L-05 — blacklist-check uses internal_err for LCD failures

| Location | indexer/src/api/compliance.rs:86 |
| Impact | Misleading 500 vs 502 status codes; body still generic. |

L-06 — Unbounded tokens/pairs list in blacklist-check

| Location | indexer/src/api/compliance.rs |
| Impact | Large query strings → big LCD payload, memory pressure. |

L-07 — Swagger UI / OpenAPI publicly exposed

| Location | indexer/src/api/mod.rs |
| Impact | Full API surface enumeration. Disable in prod or gate at reverse proxy. |

L-08 — No explicit HTTP request body size limit on POST route solve

| Location | indexer/src/api/mod.rs |
| Impact | Oversized hybrid_by_hop JSON could consume memory. Add RequestBodyLimitLayer. |

L-09 — Indexer API path segments not URL-encoded

| Location | frontend-dapp/src/services/indexer/client.ts — getPair, getTrader |
| Impact | Odd characters in route params could produce unexpected requests. |

L-10 — Weak default Postgres credentials (local)

| Location | docker-compose.yml, .env.example |
| Impact | Low on 127.0.0.1:5432; high if Postgres bound to 0.0.0.0. |

L-11 — LocalTerra test mnemonic committed and echoed

| Location | docker/init-chain.sh |
| Impact | None on LocalTerra. Critical if reused on funded networks. |

L-12 — npm audit: 23 advisories (2 critical, 10 high — mostly dev tooling)

| Location | frontend-dapp/package-lock.json |
| Impact | Runtime wallet stack inherits transitive elliptic risk. Track cosmes/cosmjs upgrades. |


INFORMATIONAL

I-01 — No API authentication on indexer (by design)

Public read-only analytics API. Mitigate with network ACLs, reverse proxy, API_BIND=127.0.0.1.

I-02 — Tax/burn hooks spend pre-funded hook balances, not swap output

Hooks compute from return_asset.amount but transfer/burn from hook contract balance. Users receive full swap output; tax subsidized from hook treasury.

I-03 — Wasm migration authority outside contract logic

Migrate authorization is chain-level wasm admin, not in-contract. Compromised migration key can alter live bytecode.

I-04 — Route solve cache: amount bucketing + tier-shared keys

Cache buckets amounts by 1_000_000; trader address not keyed — only discount_bps. By design for LCD cost reduction (#283). Not privilege escalation.


Positive Security Controls

On-chain

  • Governance-gated factory admin (ensure_governance)
  • Factory-only pair admin (fee, hooks, pause, sweep)
  • Hook caller allowlists (assert_allowed_pair)
  • MINIMUM_LIQUIDITY (1000 LP) anti-inflation
  • k-monotonicity + ceil_div pool-favorable rounding
  • Checked arithmetic throughout (Uint128 checked ops)
  • Limit order owner checks; batch all-or-nothing withdrawals
  • Escrow separate from reserves + sweep protection
  • TWAP pre-mutation sampling
  • Router hop balance delta (no dust theft) — adversarial tests pass
  • Hybrid slippage guards (#273, #307, #334)
  • Wrong-side book hint defense (#272)
  • EOA-only self-registration for fee tiers
  • Trusted-router discount attribution (untrusted trader falls back to sender)
  • One CreatePair per block
  • CW20-only (no native) attack surface reduction
  • Comprehensive invariant matrix with 30+ automated tests

Indexer

  • Parameterized SQL throughout; dynamic ORDER BY via allowlists only
  • Sanitized client errors (internal_err, lcd_gateway_err)
  • Dual-tier rate limiting (60 RPS global + 10 RPS LCD-heavy)
  • Prod config hardening (RUN_MODE=prod requires operator LCDs, non-empty CORS, rate limit floor)
  • LCD amplification budgets; 30s timeout + compression
  • 25+ security regression tests in tests/security.rs
  • CORS allowlist; no spoofable X-Forwarded-For rate limit key
  • Route cache tier isolation via discount_bps in key (#283)
  • Default API_BIND=127.0.0.1

Frontend

  • No dangerouslySetInnerHTML / eval in application code
  • Production mnemonic build failure (GitLab #118)
  • No production source maps
  • Wallet ↔ caller address match before broadcast
  • Chain-scoped wallet map; extension signing options (preferNoSetFee)
  • Submit-time quote alignment guards
  • Factory-backed pair validation for trade deep links
  • Trading blacklist integration
  • sanitizeOpaqueErrorMessage caps error length

Ops / CI

  • Docker Compose: host ports on 127.0.0.1; images pinned by digest
  • .gitignore covers .env, credentials
  • Gitleaks custom rules (.gitleaks.toml); pre-commit when installed
  • Git hooks: commit-msg policy, pre-push history check, agent trailer stripping
  • Cloud Agent identity validation (rejects bot emails)
  • glab setup strips credentials from origin URL
  • docs/operator-secrets.md, docs/contracts-security-audit.md

Attack Paths Considered (Non-Governance)

Attack Result
Unauthorized factory/pair/hook/discount admin Blocked (Unauthorized)
LP share inflation (first depositor) Blocked by MINIMUM_LIQUIDITY
Sandwich via min_assets on withdraw Reverts (withdraw_min_assets_reverts_on_sandwich)
Router dust theft on multi-hop Blocked — hop output from balance delta
SQL injection via API params Blocked — allowlists + parameterized queries
SSRF via indexer API Not vulnerable — LCD URLs from env only
XSS in React app No sinks found; CSP is weak fallback
Replay attacks Chain-level tx sequence; not applicable
Flash loan / EVM reentrancy CosmWasm atomic execution; not applicable
Discount theft via untrusted router Falls back to sender
Cross-side book hint drain Blocked (#272)

Priority Remediation Roadmap

Immediate

  1. Add gitleaks + npm audit / cargo audit to .gitlab-ci.yml
  2. Clamp GET max_maker_fills to 30 on indexer route solve
  3. Document indexer as trusted infra; verify hops against factory before sign

Short term

  1. Never whitelist fee-on-transfer CW20 code IDs (ops policy)
  2. LP-burn hook allowlist = verified pair addresses only
  3. Require RUN_MODE=prod in deployment checklist
  4. Replace curl-pipe bootstrap with pinned artifacts on agent VMs
  5. Enable DinD TLS in GitLab CI

Ongoing

  1. Upgrade cosmes/cosmjs off deprecated elliptic
  2. Tighten CSP; require VITE_WC_PROJECT_ID in prod builds
  3. Expand GitLab CI toward reference GitHub workflow (tests, lint)
  4. Third-party formal audit before high-TVL mainnet deployment

References

  • docs/contracts-security-audit.md — invariant matrix P1–L17
  • docs/security-model.md — operational trust assumptions
  • docs/indexer-invariants.md — indexer threat model
  • docs/audits/factory-treasury-bank-send.md — bank send review (#313)
  • docs/operator-secrets.md — secrets handling guidance
  • smartcontracts/tests/src/adversarial_token.rs — adversarial test harness

This report supplements but does not replace a formal third-party smart contract audit before high-TVL deployment.

# CL8Y DEX — Full-Stack Security Audit Report **Issue:** [#376 — Full security report](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/376) **Auditor:** Internal Composer security audit (Cloud Agent) **Date:** 2026-06-13 **Scope:** Full codebase — CosmWasm contracts, Terra messages, indexer API/DB, frontend dApp, CI/CD, ops scripts, dependencies, secrets handling **Method:** Source review, pattern search, adversarial contract test execution, cross-reference with in-repo invariant matrix (`docs/contracts-security-audit.md`, `docs/indexer-invariants.md`) --- ## Executive Summary CL8Y DEX is a Terra Classic constant-product AMM with hybrid limit-order book, multi-hop router, fee-discount registry, and post-swap hooks. The codebase demonstrates **mature defensive engineering**: checked arithmetic, governance/factory access control, escrow accounting, slippage guards, indexer SQL parameterization, prod config gates, and extensive regression tests (including adversarial token scenarios). **No Critical permissionless on-chain exploit** was identified under the documented trust model (honest governance, whitelisted CW20 code IDs). Residual risk concentrates in: 1. **Governance/ops misconfiguration** (hooks, token whitelist, pause, LP-burn allowlist) 2. **Off-chain trust boundaries** (indexer route integrity, deploy-time `VITE_*` config) 3. **CI/supply-chain gaps** (no automated SCA in GitLab CI, curl-pipe bootstrap on agent VMs) 4. **Accepted design tradeoffs** (300s fee-discount cache, TWAP limitations, public indexer API) | Severity | Count | |----------|-------| | Critical | 0 | | High | 7 | | Medium | 14 | | Low | 12 | | Informational | 4 | **Tests executed during audit:** - `cargo test adversarial` (smartcontracts) — **7/7 passed** - Indexer `tests/security.rs` — 25+ cases exist (require Postgres; not executed in this session due to compile/runtime prerequisites) --- ## Areas Analyzed | Area | Components | Status | |------|------------|--------| | CosmWasm contracts | factory, pair, router, fee-discount, tax/burn/lp-burn hooks, dex-common | Reviewed + adversarial tests | | Terra messages | BankMsg, CW20 Send/Transfer, WasmMsg hooks, router reply chain | Reviewed | | Indexer | Axum API, sqlx queries, LCD client, route solver, CORS, rate limits | Reviewed | | Database | Postgres schema, migrations, dynamic ORDER BY allowlists | Reviewed | | Frontend | React dApp, wallet (Keplr/Station/WC), indexer client, tx construction | Reviewed | | CI/CD | `.gitlab-ci.yml`, reference GitHub workflows, gitleaks hooks | Reviewed | | Ops/scripts | deploy, cloud-agent, QA artifact publish/fetch, docker-compose | Reviewed | | Dependencies | `Cargo.lock`, `package-lock.json`, forked cosmes, elliptic transitive | Reviewed | | Secrets | `.env.example`, dev mnemonics, `GITLAB_TOKEN` handling | Reviewed | --- ## Findings ### HIGH #### H-01 — Fee-on-transfer CW20 can desync reserves vs on-chain balance | Field | Detail | |-------|--------| | **Severity** | High (conditional on non-standard CW20 whitelist) | | **Location** | `smartcontracts/contracts/pair/src/contract.rs` — `execute_swap`, `execute_provide_liquidity`; `factory/src/contract.rs` — `execute_create_pair` | | **Issue** | Swaps and liquidity use declared `cw20_msg.amount`, not post-transfer balance deltas. Fee-on-transfer tokens credit the pair less than recorded reserves. | | **Impact** | Internal `RESERVES` exceed CW20 balance. Later withdrawals fail or last LPs absorb deficit. | | **Attack path** | Governance whitelists fee-on-transfer CW20 code ID → attacker provides liquidity → repeated swaps widen gap → withdraws LP while `min_assets` passes. | | **Evidence** | Test `fee_on_transfer_creates_reserve_imbalance` in `smartcontracts/tests/src/adversarial_token.rs`; invariant P2 in `docs/contracts-security-audit.md`. | | **Recommendation** | Never whitelist fee-on-transfer templates. Document in ops runbook. Optionally reconcile reserves via balance-before/after on inbound transfers. | #### H-02 — Allowlisted hook returning `Err` atomically blocks all pair swaps | Field | Detail | |-------|--------| | **Severity** | High (governance misconfiguration) | | **Location** | `pair/src/contract.rs` — `execute_swap` (~L1174–1224); `execute_update_hooks` | | **Issue** | Post-swap hooks dispatched as `WasmMsg::Execute` (not `SubMsg::reply_on_error`). Hook failure reverts entire swap. | | **Impact** | Full trading DoS on affected pairs until hooks removed or fixed. | | **Attack path** | Governance registers hook (or hook upgraded via wasm admin) → hook `Hook` handler returns `Err` → every swap fails. | | **Evidence** | Invariant H1; test `swap_fails_atomically_when_allowlisted_hook_reverts`. | | **Recommendation** | Only register audited hooks. Consider `SubMsg::reply_on_error` if policy hooks must never block swaps. | #### H-03 — LP burn hook: allowlisted non-pair caller can spoof `AfterSwap.pair` | Field | Detail | |-------|--------| | **Severity** | High (hook admin misconfiguration) | | **Location** | `hooks/lp-burn-hook/src/contract.rs` — `execute_after_swap`, `assert_allowed_pair` | | **Issue** | Authorization is `info.sender ∈ ALLOWED_PAIRS`. `pair` field in `AfterSwap` is trusted for burn calculation but not verified on-chain. | | **Impact** | Pre-funded LP in hook treasury burned at inflated rates. | | **Attack path** | Hook admin adds malicious spoofer to `ALLOWED_PAIRS` → spoofer calls `Hook` with fake large `output_amount` → hook burns `min(target_burn, balance)` LP. | | **Evidence** | Test `lp_burn_hook_accepts_spoofed_pair_when_spoofer_allowlisted` (adversarial_token.rs). | | **Recommendation** | Restrict `ALLOWED_PAIRS` to verified pair addresses only. Verify `pair == info.sender` or query pair state before burning. | #### H-04 — Compromised or malicious indexer can influence signed swap routes | Field | Detail | |-------|--------| | **Severity** | High | | **Location** | `frontend-dapp/src/pages/SwapPage.tsx`; `src/services/indexer/routeOperations.ts`; `src/services/indexer/client.ts` | | **Issue** | Swap quotes and `router_operations` come from indexer. dApp validates `token_in`/`token_out` against UI selection but trusts hop structure from indexer after parsing. | | **Impact** | Malicious indexer (or MITM of `VITE_INDEXER_URL`) returns routes through adversarial but valid pools; user signs degraded execution. | | **Attack path** | Attacker hosts fake indexer or compromises deploy env → user swaps A→B → indexer returns matching tokens with manipulated `router_operations` → user signs `execute_swap_operations`. | | **Evidence** | Partial mitigation: `enrichSwapOperationsWithHopMinReturns` resolves pairs via factory `getPair()` — invalid pairs fail, but valid adversarial pools remain possible. | | **Recommendation** | Cross-check each hop against on-chain factory pair graph before submit; pin indexer URL + TLS; show human-readable hop summary in confirmation UI; consider client-side BFS fallback comparison. | #### H-05 — Dev mnemonic can ship if build mode is misconfigured | Field | Detail | |-------|--------| | **Severity** | High (conditional) | | **Location** | `frontend-dapp/vite.config.ts` (L52–59); `src/services/terraclassic/devWallet.ts` | | **Issue** | `VITE_DEV_MNEMONIC` blocked only for `command === 'build' && mode === 'production'`. Non-production build deployed publicly with `VITE_DEV_MODE=true` inlines mnemonic. | | **Impact** | Full key material in JS bundle; wallet drainable by anyone. | | **Attack path** | CI uses `vite build --mode staging` with `.env.development` containing mnemonic → attacker extracts from bundle. | | **Evidence** | Production guard exists (GitLab #118); `devWallet.ts` has no default mnemonic. | | **Recommendation** | Fail build if `VITE_DEV_MNEMONIC` set unless `mode === 'development'` or explicit `VITE_ALLOW_DEV_MNEMONIC=local-only`. | #### H-06 — No dependency / SCA automation in GitLab CI | Field | Detail | |-------|--------| | **Severity** | High (supply-chain) | | **Location** | `.gitlab-ci.yml` — 2 build jobs only; no `cargo-audit`, `cargo-deny`, `npm audit`, gitleaks | | **Issue** | Known CVEs in Rust/Node deps not scanned on merge. | | **Impact** | Vulnerable transitive deps (e.g. deprecated `elliptic` in cosmjs) persist undetected. | | **Attack path** | Compromised dependency → key handling / HTTP client bugs in frontend or indexer. | | **Recommendation** | Add GitLab dependency scanning or scheduled `cargo audit` + `npm audit --audit-level=high`. | #### H-07 — Cloud Agent VM overly permissive privilege model | Field | Detail | |-------|--------| | **Severity** | High (ops) | | **Location** | `gch-cloud-setup.sh` (`NOPASSWD:ALL`, `approvalMode: "unrestricted"`); `scripts/lib/cloud-agent-docker.sh` (`chmod 666` on docker.sock) | | **Issue** | Agent user has passwordless sudo; Cursor CLI unrestricted; Docker socket world-writable as fallback. | | **Impact** | Any code execution in agent session → root + docker + `GITLAB_TOKEN`. | | **Attack path** | Malicious npm script during `make dev` → docker escape or sudo → read `GITLAB_TOKEN`. | | **Recommendation** | Drop `NOPASSWD:ALL`; use docker group only; tighten Cursor approval mode; avoid `chmod 666` on docker.sock. | --- ### MEDIUM #### M-01 — Fee-discount cache: up to 300s fee leakage after CL8Y balance drop | Field | Detail | |-------|--------| | **Severity** | Medium (accepted design) | | **Location** | `pair/src/discount_cache.rs`; `dex-common/src/pair.rs` — `DISCOUNT_CACHE_TTL_SECONDS = 300` | | **Issue** | Pair caches `(effective_fee_bps, discount)` for 300s without re-querying registry balance. | | **Impact** | User sells CL8Y after qualifying, keeps discounted fees until cache expires (~5 min). Protocol fee revenue loss bounded per wallet per window. | | **Attack path** | Register tier → swap (populates cache) → transfer CL8Y away → continue swapping within 300s at discounted fee. | | **Recommendation** | Accept as gas tradeoff (documented P9) or shorten TTL; monitor via indexer. | #### M-02 — TWAP oracle manipulable over multi-block / low-liquidity windows | Field | Detail | |-------|--------| | **Severity** | Medium (external consumers) | | **Location** | `packages/dex-common/src/oracle.rs`; `pair/src/contract.rs` — `oracle_update` | | **Issue** | Oracle resists single-tx manipulation but documents multi-block manipulation, low TVL, staleness risks. | | **Impact** | External protocols using short-window TWAP from low-liquidity pairs can be manipulated. | | **Recommendation** | Use ≥30 min windows; cross-check secondary feeds; minimum TVL gates for oracle consumers. | #### M-03 — Emergency pause freezes maker limit-order withdrawals | Field | Detail | |-------|--------| | **Severity** | Medium (operational) | | **Location** | `pair/src/contract.rs` — `assert_not_paused` on cancel/claim/clean | | **Issue** | Pause blocks `CancelLimitOrder`, `ClaimExpiredLimitOrder`, `CleanLimitBook`. Escrow remains in pair custody. | | **Impact** | Governance pause (or compromise) locks resting limit escrow until unpause. | | **Recommendation** | Multisig governance, timelock on pause, or allow cancel/claim while paused (product decision). | #### M-04 — Router `SWAP_STATE` serializes all multi-hop swaps | Field | Detail | |-------|--------| | **Severity** | Medium (availability) | | **Location** | `router/src/contract.rs` — `execute_swap_operations`, `reply_swap_hop` | | **Issue** | Single global `SWAP_STATE`; concurrent router swaps in same block fail with `SwapInProgress`. | | **Impact** | One in-flight multi-hop blocks all others on that router until completion. | | **Recommendation** | Per-sender swap state, or document as intentional. Low practical impact on Terra tx serialization. | #### M-05 — Rate limiting disabled in dev when env vars are zero | Field | Detail | |-------|--------| | **Severity** | Medium (misconfiguration) | | **Location** | `indexer/src/config.rs`; `indexer/src/api/mod.rs` — `apply_rate_limit_layer` | | **Issue** | When `RATE_LIMIT_RPS=0` and `RATE_LIMIT_LCD_HEAVY_RPS=0`, governors skipped. Prod clamps zeros to 60/10; dev does not. | | **Impact** | Unbounded per-IP request rate → DB exhaustion, LCD abuse, CPU DoS on route solver. | | **Attack path** | Flood `/api/v1/route/solve` from one IP if prod runs without `RUN_MODE=prod`. | | **Recommendation** | Always set `RUN_MODE=prod` in production; startup warning when both limits are 0. | #### M-06 — Unbounded `max_maker_fills` on GET route solve (LCD path) | Field | Detail | |-------|--------| | **Severity** | Medium | | **Location** | `indexer/src/api/route_solver.rs` L719 — `max_maker_fills.max(1)` without upper cap | | **Issue** | DB-hybrid path caps at `MAX_MAKER_FILLS_HARD_CAP` (30); LCD `global_v3` path forwards uncapped value to on-chain `HybridSimulation`. | | **Impact** | Single request drives heavy hybrid grid work and LCD fanout before 30s timeout. | | **Attack path** | `GET /api/v1/route/solve?...&max_maker_fills=4294967295` within LCD-heavy rate limit (10 RPS prod). | | **Recommendation** | Clamp GET `max_maker_fills` to `MAX_MAKER_FILLS_HARD_CAP` (30). | #### M-07 — CSP allows `unsafe-inline` scripts and arbitrary HTTPS connections | Field | Detail | |-------|--------| | **Severity** | Medium | | **Location** | `frontend-dapp/index.html` L18–19 | | **Issue** | `script-src 'self' 'unsafe-inline'` and `connect-src … https: wss:` are broad. | | **Impact** | Any XSS (supply-chain, compromised dep) can run inline payloads and exfiltrate to any HTTPS endpoint. | | **Recommendation** | Nonce/hash-based CSP; narrow `connect-src` to known LCD/RPC/indexer hosts. | #### M-08 — Build-time contract addresses fully trusted | Field | Detail | |-------|--------| | **Severity** | Medium | | **Location** | `frontend-dapp/src/utils/constants.ts`; `src/services/terraclassic/router.ts` | | **Issue** | `VITE_ROUTER_ADDRESS`, `VITE_FACTORY_ADDRESS` compiled into bundle with public-node fallbacks. | | **Impact** | Wrong/malicious addresses in deploy env → users send tokens to attacker contracts. | | **Recommendation** | Pin addresses per network; show router/factory in swap confirmation UI; verify factory on LCD at startup. | #### M-09 — Indexer-supplied token logos and symbols (display phishing) | Field | Detail | |-------|--------| | **Severity** | Medium | | **Location** | `frontend-dapp/src/hooks/useTokenDisplayInfo.ts`; `src/components/ui/TokenLogo.tsx` | | **Issue** | `logo_url` from indexer used as `<img src>`. CW20 metadata cached in `localStorage` without integrity checks. | | **Impact** | Malicious token shows "USDC" branding for scam CW20. | | **Recommendation** | Prefer static token registry; show full `terra1…` address prominently; allowlist logo hosts. | #### M-10 — Default WalletConnect project ID in client bundle | Field | Detail | |-------|--------| | **Severity** | Medium | | **Location** | `frontend-dapp/src/services/terraclassic/wallet.ts` L37 | | **Issue** | `VITE_WC_PROJECT_ID \|\| '2ce7811b869be33ffad28cff05c93c15'` — shared default if unset. | | **Impact** | Session routing collisions; WC phishing if combined with social engineering. | | **Recommendation** | Require project-specific `VITE_WC_PROJECT_ID` in production; fail build if missing. | #### M-11 — GitLab CI Docker-in-Docker without TLS | Field | Detail | |-------|--------| | **Severity** | Medium | | **Location** | `.gitlab-ci.yml` L18–20: `DOCKER_TLS_CERTDIR: ""`, `DOCKER_HOST: tcp://docker:2375` | | **Issue** | Unencrypted DinD socket inside job pod. | | **Impact** | Co-process in job namespace could control Docker daemon. | | **Recommendation** | Enable DinD TLS or use Kaniko/rootless build. | #### M-12 — `curl | bash` bootstrap in agent tooling | Field | Detail | |-------|--------| | **Severity** | Medium | | **Location** | `scripts/lib/cloud-agent-toolchain.sh` (nvm, rustup); `gch-cloud-setup.sh` | | **Issue** | Remote install scripts without checksum/signature verification. | | **Impact** | CDN/upstream compromise → RCE on agent VM with `GITLAB_TOKEN`. | | **Recommendation** | Pin script SHAs; vendor tarballs; verify GPG/checksums. | #### M-13 — Gitleaks optional locally; absent from CI | Field | Detail | |-------|--------| | **Severity** | Medium | | **Location** | `.githooks/pre-commit` L48–56; `.gitlab-ci.yml` | | **Issue** | Pre-commit skips scan if `gitleaks` not installed; CI does not run gitleaks. | | **Impact** | Secrets can reach default branch without hook enforcement. | | **Recommendation** | Add `gitleaks detect` to CI pipeline; fail on findings. | #### M-14 — Expert mode allows swaps with extreme slippage (up to 50%) | Field | Detail | |-------|--------| | **Severity** | Medium | | **Location** | `frontend-dapp/src/stores/dex.ts`; `src/pages/SwapPage.tsx` | | **Issue** | Swaps with >30% route slippage blocked unless expert mode (localStorage). Slippage capped at 50%. | | **Impact** | Social engineering to enable expert mode → devastating execution. | | **Recommendation** | Typed confirmation for expert mode + slippage >5%. Positive: `min_return` still applied on submit. | --- ### LOW #### L-01 — Hybrid book walk caps can defer limit fills (head-clog / expired prefix) | Location | `pair/src/orderbook.rs` — `MAX_SCAN_STEPS = 500`, `MAX_EXPIRED_PARKS_PER_SWAP = 15` | | Impact | Suboptimal execution, not direct theft. Mitigated by `book_start_hint` and `CleanLimitBook`. | #### L-02 — Factory `SetPairLimitBatchMax` omits pair registry check | Location | `factory/src/contract.rs` — `execute_set_pair_limit_batch_max` | | Impact | Wasted gas only; no privilege escalation. | #### L-03 — Blacklist silently disabled against pre-1.5.0 factories | Location | `pair/src/blacklist_guard.rs` — `probe_factory_blacklist` | | Impact | Blacklist ineffective until factory upgraded. | #### L-04 — LCD upstream details logged at WARN | Location | `indexer/src/lcd/mod.rs` | | Impact | Log aggregation may retain internal LCD hostnames and chain error text. Client responses sanitized. | #### L-05 — `blacklist-check` uses `internal_err` for LCD failures | Location | `indexer/src/api/compliance.rs:86` | | Impact | Misleading 500 vs 502 status codes; body still generic. | #### L-06 — Unbounded `tokens`/`pairs` list in blacklist-check | Location | `indexer/src/api/compliance.rs` | | Impact | Large query strings → big LCD payload, memory pressure. | #### L-07 — Swagger UI / OpenAPI publicly exposed | Location | `indexer/src/api/mod.rs` | | Impact | Full API surface enumeration. Disable in prod or gate at reverse proxy. | #### L-08 — No explicit HTTP request body size limit on POST route solve | Location | `indexer/src/api/mod.rs` | | Impact | Oversized `hybrid_by_hop` JSON could consume memory. Add `RequestBodyLimitLayer`. | #### L-09 — Indexer API path segments not URL-encoded | Location | `frontend-dapp/src/services/indexer/client.ts` — `getPair`, `getTrader` | | Impact | Odd characters in route params could produce unexpected requests. | #### L-10 — Weak default Postgres credentials (local) | Location | `docker-compose.yml`, `.env.example` | | Impact | Low on `127.0.0.1:5432`; high if Postgres bound to `0.0.0.0`. | #### L-11 — LocalTerra test mnemonic committed and echoed | Location | `docker/init-chain.sh` | | Impact | None on LocalTerra. Critical if reused on funded networks. | #### L-12 — npm audit: 23 advisories (2 critical, 10 high — mostly dev tooling) | Location | `frontend-dapp/package-lock.json` | | Impact | Runtime wallet stack inherits transitive `elliptic` risk. Track cosmes/cosmjs upgrades. | --- ### INFORMATIONAL #### I-01 — No API authentication on indexer (by design) Public read-only analytics API. Mitigate with network ACLs, reverse proxy, `API_BIND=127.0.0.1`. #### I-02 — Tax/burn hooks spend pre-funded hook balances, not swap output Hooks compute from `return_asset.amount` but transfer/burn from hook contract balance. Users receive full swap output; tax subsidized from hook treasury. #### I-03 — Wasm migration authority outside contract logic Migrate authorization is chain-level wasm admin, not in-contract. Compromised migration key can alter live bytecode. #### I-04 — Route solve cache: amount bucketing + tier-shared keys Cache buckets amounts by `1_000_000`; trader address not keyed — only `discount_bps`. By design for LCD cost reduction (#283). Not privilege escalation. --- ## Positive Security Controls ### On-chain - Governance-gated factory admin (`ensure_governance`) - Factory-only pair admin (fee, hooks, pause, sweep) - Hook caller allowlists (`assert_allowed_pair`) - MINIMUM_LIQUIDITY (1000 LP) anti-inflation - k-monotonicity + ceil_div pool-favorable rounding - Checked arithmetic throughout (`Uint128` checked ops) - Limit order owner checks; batch all-or-nothing withdrawals - Escrow separate from reserves + sweep protection - TWAP pre-mutation sampling - Router hop balance delta (no dust theft) — adversarial tests pass - Hybrid slippage guards (#273, #307, #334) - Wrong-side book hint defense (#272) - EOA-only self-registration for fee tiers - Trusted-router discount attribution (untrusted `trader` falls back to sender) - One `CreatePair` per block - CW20-only (no native) attack surface reduction - Comprehensive invariant matrix with 30+ automated tests ### Indexer - Parameterized SQL throughout; dynamic ORDER BY via allowlists only - Sanitized client errors (`internal_err`, `lcd_gateway_err`) - Dual-tier rate limiting (60 RPS global + 10 RPS LCD-heavy) - Prod config hardening (`RUN_MODE=prod` requires operator LCDs, non-empty CORS, rate limit floor) - LCD amplification budgets; 30s timeout + compression - 25+ security regression tests in `tests/security.rs` - CORS allowlist; no spoofable X-Forwarded-For rate limit key - Route cache tier isolation via `discount_bps` in key (#283) - Default `API_BIND=127.0.0.1` ### Frontend - No `dangerouslySetInnerHTML` / `eval` in application code - Production mnemonic build failure (GitLab #118) - No production source maps - Wallet ↔ caller address match before broadcast - Chain-scoped wallet map; extension signing options (`preferNoSetFee`) - Submit-time quote alignment guards - Factory-backed pair validation for trade deep links - Trading blacklist integration - `sanitizeOpaqueErrorMessage` caps error length ### Ops / CI - Docker Compose: host ports on `127.0.0.1`; images pinned by digest - `.gitignore` covers `.env`, credentials - Gitleaks custom rules (`.gitleaks.toml`); pre-commit when installed - Git hooks: commit-msg policy, pre-push history check, agent trailer stripping - Cloud Agent identity validation (rejects bot emails) - `glab` setup strips credentials from origin URL - `docs/operator-secrets.md`, `docs/contracts-security-audit.md` --- ## Attack Paths Considered (Non-Governance) | Attack | Result | |--------|--------| | Unauthorized factory/pair/hook/discount admin | Blocked (`Unauthorized`) | | LP share inflation (first depositor) | Blocked by MINIMUM_LIQUIDITY | | Sandwich via `min_assets` on withdraw | Reverts (`withdraw_min_assets_reverts_on_sandwich`) | | Router dust theft on multi-hop | Blocked — hop output from balance delta | | SQL injection via API params | Blocked — allowlists + parameterized queries | | SSRF via indexer API | Not vulnerable — LCD URLs from env only | | XSS in React app | No sinks found; CSP is weak fallback | | Replay attacks | Chain-level tx sequence; not applicable | | Flash loan / EVM reentrancy | CosmWasm atomic execution; not applicable | | Discount theft via untrusted router | Falls back to sender | | Cross-side book hint drain | Blocked (#272) | --- ## Priority Remediation Roadmap ### Immediate 1. Add gitleaks + `npm audit` / `cargo audit` to `.gitlab-ci.yml` 2. Clamp GET `max_maker_fills` to 30 on indexer route solve 3. Document indexer as trusted infra; verify hops against factory before sign ### Short term 4. Never whitelist fee-on-transfer CW20 code IDs (ops policy) 5. LP-burn hook allowlist = verified pair addresses only 6. Require `RUN_MODE=prod` in deployment checklist 7. Replace curl-pipe bootstrap with pinned artifacts on agent VMs 8. Enable DinD TLS in GitLab CI ### Ongoing 9. Upgrade cosmes/cosmjs off deprecated `elliptic` 10. Tighten CSP; require `VITE_WC_PROJECT_ID` in prod builds 11. Expand GitLab CI toward reference GitHub workflow (tests, lint) 12. Third-party formal audit before high-TVL mainnet deployment --- ## References - `docs/contracts-security-audit.md` — invariant matrix P1–L17 - `docs/security-model.md` — operational trust assumptions - `docs/indexer-invariants.md` — indexer threat model - `docs/audits/factory-treasury-bank-send.md` — bank send review (#313) - `docs/operator-secrets.md` — secrets handling guidance - `smartcontracts/tests/src/adversarial_token.rs` — adversarial test harness --- *This report supplements but does not replace a formal third-party smart contract audit before high-TVL deployment.*
PlasticDigits commented 2026-06-13 07:51:42 +00:00 (Migrated from gitlab.com)

Approve

  • H-01: Document that fee on tranfser templates are not permitted. Check that common CW20 from GDEX and TerraPort are not fee on transfer.
  • H-02: Document requirement to aduit hooks before registration. Hooks may be permitted to block swaps in some circumstances, such as hooks for AML or security incident mitigation.
  • H-03: Verify both sender and query pair state
  • H-04: Document the risks, but due to rpc/lcd rate limits cross checking cannot be done & no client side BFS fallback. Other recommendations approved.
  • H-05: Recommendation approved
  • H-06: Approved, but keep overhead low on CI
  • M-05: Startup warning
  • M-06: Benchmark instead of guessing 30 - ideally should be much higher
  • M-07: Approved
  • M-08: Approved but do not show user in UI, except in an existing settings/audit page (cognitive overload)
  • M-09: Confirm that Indexer listing process includes human review. Do not show more information to user (cognitive overload). Logohost allowlist approved
  • M-10: Approved
  • M-11: Approved, enable tls
  • M-13: Approved, gitleaks must never be optional
  • M-15: This is a small dex (~100k tvl projected) with many small (10k mcap), expect low liquidity and slippage. 30% is acceptable for standard and 50% for expert. Approved to have typed confirmation for expert mode.
  • L-04, L-05, L-08, L-09, L-12
  • I-02: Hooks that charge a feee should charge on swap input/output, not balance

Rejected

  • H-07: Cloud agents need full control (and vm is disposable/temporary, so blast radius is small)
  • M-01: This is expected behavior to reduce gas and rpc costs.
  • M-02: Responsibility of oracle consumers
  • M-03: Expected behavior, necessary in case of a security Multisig should be implemented after $1m+ tvl
  • M-04: Expected behavior
  • M-12: Cloud agents need full control (and vm is disposable/temporary, so blast radius is
  • L-01, L-02, L-03, L-06, L-07, L-10, L-11, I-01, I-03, I-04
Approve * H-01: Document that fee on tranfser templates are not permitted. Check that common CW20 from GDEX and TerraPort are not fee on transfer. * H-02: Document requirement to aduit hooks before registration. Hooks may be permitted to block swaps in some circumstances, such as hooks for AML or security incident mitigation. * H-03: Verify both sender and query pair state * H-04: Document the risks, but due to rpc/lcd rate limits cross checking cannot be done & no client side BFS fallback. Other recommendations approved. * H-05: Recommendation approved * H-06: Approved, but keep overhead low on CI * M-05: Startup warning * M-06: Benchmark instead of guessing 30 - ideally should be much higher * M-07: Approved * M-08: Approved but do not show user in UI, except in an existing settings/audit page (cognitive overload) * M-09: Confirm that Indexer listing process includes human review. Do not show more information to user (cognitive overload). Logohost allowlist approved * M-10: Approved * M-11: Approved, enable tls * M-13: Approved, gitleaks must never be optional * M-15: This is a small dex (~100k tvl projected) with many small (10k mcap), expect low liquidity and slippage. 30% is acceptable for standard and 50% for expert. Approved to have typed confirmation for expert mode. * L-04, L-05, L-08, L-09, L-12 * I-02: Hooks that charge a feee should charge on swap input/output, not balance Rejected * H-07: Cloud agents need full control (and vm is disposable/temporary, so blast radius is small) * M-01: This is expected behavior to reduce gas and rpc costs. * M-02: Responsibility of oracle consumers * M-03: Expected behavior, necessary in case of a security Multisig should be implemented after $1m+ tvl * M-04: Expected behavior * M-12: Cloud agents need full control (and vm is disposable/temporary, so blast radius is * L-01, L-02, L-03, L-06, L-07, L-10, L-11, I-01, I-03, I-04
PlasticDigits commented 2026-06-13 07:56:21 +00:00 (Migrated from gitlab.com)

mentioned in issue #377

mentioned in issue #377
PlasticDigits commented 2026-06-13 07:56:27 +00:00 (Migrated from gitlab.com)

mentioned in issue #378

mentioned in issue #378
PlasticDigits commented 2026-06-13 07:56:27 +00:00 (Migrated from gitlab.com)

mentioned in issue #379

mentioned in issue #379
PlasticDigits commented 2026-06-13 07:56:28 +00:00 (Migrated from gitlab.com)

mentioned in issue #380

mentioned in issue #380
PlasticDigits commented 2026-06-13 07:56:46 +00:00 (Migrated from gitlab.com)

Child issues opened from security audit triage

Approved items from the #376 comment (2026-06-13) were bundled into 4 actionable issues:

Issue Bundle Audit IDs
#377 CosmWasm hooks & ops policy H-01, H-02, H-03, I-02
#378 Frontend trust & build guards H-04, H-05, M-07, M-08, M-09, M-10, M-15
#379 Indexer hardening M-05, M-06, L-04, L-05, L-08, L-09
#380 CI & supply chain H-06, M-11, M-13, L-12

Bundle rationale

  • #377 — On-chain hook safety, CW20 whitelist ops, and fee-hook charging model are tightly coupled (H-03 before I-02 for lp-burn).
  • #378 — All frontend off-chain trust, Vite build guards, CSP, token display, and expert-mode UX.
  • #379 — Indexer API/ops hardening plus the related frontend URL-encoding fix in the indexer client.
  • #380 — GitLab CI security jobs (SCA, gitleaks, DinD TLS) share pipeline infrastructure.

Skipped — rejected

ID Reason
H-07 Cloud agents need full control; disposable VM, small blast radius
M-01 Expected behavior (300s fee-discount cache reduces gas/RPC cost)
M-02 Oracle consumer responsibility
M-03 Expected; multisig after $1M+ TVL
M-04 Expected behavior
M-12 Cloud agents need full control (disposable VM)
L-01 Hybrid book walk caps — acceptable tradeoff
L-02 Factory SetPairLimitBatchMax — gas waste only
L-03 Blacklist probe against pre-1.5.0 factories — known limitation
L-06 Unbounded blacklist-check query — low priority
L-07 Swagger UI public exposure — acceptable / proxy-gated
L-10 Weak default Postgres creds — local-only
L-11 LocalTerra test mnemonic — local-only
I-01 No API auth on indexer — by design
I-03 Wasm migration authority — chain-level
I-04 Route solve cache bucketing — by design (#283)

Skipped — approved with scope exclusions (no separate issue)

These were approved but folded into bundle constraints rather than standalone issues:

ID Handling
H-04 No on-chain hop cross-check or client BFS fallback — documented in #378
H-02 Hooks may block swaps for AML/incident — docs only in #377
M-08 No swap-confirm UI for addresses — protocol/audit page only in #378
M-09 No extra user-facing token detail — allowlist + listing SOP in #378

Skipped — undecided / requested

None — all audit items had explicit approve or reject in the triage comment.


Opened by agent workflow from agent:open_issues on #376.

## Child issues opened from security audit triage Approved items from the [#376](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/376) comment (2026-06-13) were bundled into **4** actionable issues: | Issue | Bundle | Audit IDs | |-------|--------|-----------| | [#377](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/377) | CosmWasm hooks & ops policy | H-01, H-02, H-03, I-02 | | [#378](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/378) | Frontend trust & build guards | H-04, H-05, M-07, M-08, M-09, M-10, M-15 | | [#379](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/379) | Indexer hardening | M-05, M-06, L-04, L-05, L-08, L-09 | | [#380](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/380) | CI & supply chain | H-06, M-11, M-13, L-12 | ### Bundle rationale - **#377** — On-chain hook safety, CW20 whitelist ops, and fee-hook charging model are tightly coupled (H-03 before I-02 for lp-burn). - **#378** — All frontend off-chain trust, Vite build guards, CSP, token display, and expert-mode UX. - **#379** — Indexer API/ops hardening plus the related frontend URL-encoding fix in the indexer client. - **#380** — GitLab CI security jobs (SCA, gitleaks, DinD TLS) share pipeline infrastructure. --- ## Skipped — rejected | ID | Reason | |----|--------| | H-07 | Cloud agents need full control; disposable VM, small blast radius | | M-01 | Expected behavior (300s fee-discount cache reduces gas/RPC cost) | | M-02 | Oracle consumer responsibility | | M-03 | Expected; multisig after $1M+ TVL | | M-04 | Expected behavior | | M-12 | Cloud agents need full control (disposable VM) | | L-01 | Hybrid book walk caps — acceptable tradeoff | | L-02 | Factory `SetPairLimitBatchMax` — gas waste only | | L-03 | Blacklist probe against pre-1.5.0 factories — known limitation | | L-06 | Unbounded blacklist-check query — low priority | | L-07 | Swagger UI public exposure — acceptable / proxy-gated | | L-10 | Weak default Postgres creds — local-only | | L-11 | LocalTerra test mnemonic — local-only | | I-01 | No API auth on indexer — by design | | I-03 | Wasm migration authority — chain-level | | I-04 | Route solve cache bucketing — by design (#283) | ## Skipped — approved with scope exclusions (no separate issue) These were approved but folded into bundle constraints rather than standalone issues: | ID | Handling | |----|----------| | H-04 | No on-chain hop cross-check or client BFS fallback — documented in #378 | | H-02 | Hooks may block swaps for AML/incident — docs only in #377 | | M-08 | No swap-confirm UI for addresses — protocol/audit page only in #378 | | M-09 | No extra user-facing token detail — allowlist + listing SOP in #378 | ## Skipped — undecided / requested None — all audit items had explicit approve or reject in the triage comment. --- *Opened by agent workflow from `agent:open_issues` on #376.*
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-06-13 09:32:05 +00:00
PlasticDigits commented 2026-06-13 09:43:04 +00:00 (Migrated from gitlab.com)

mentioned in merge request !901

mentioned in merge request !901
PlasticDigits commented 2026-06-13 09:45:19 +00:00 (Migrated from gitlab.com)

mentioned in commit b45ac0aea6

mentioned in commit b45ac0aea60c370b1097a42a4677a9c14bbb0dbb
PlasticDigits commented 2026-06-13 09:45:34 +00:00 (Migrated from gitlab.com)

mentioned in merge request !902

mentioned in merge request !902
PlasticDigits commented 2026-06-13 10:11:05 +00:00 (Migrated from gitlab.com)

mentioned in merge request !903

mentioned in merge request !903
PlasticDigits commented 2026-06-13 10:14:33 +00:00 (Migrated from gitlab.com)

mentioned in merge request !904

mentioned in merge request !904
PlasticDigits commented 2026-06-13 13:59:28 +00:00 (Migrated from gitlab.com)

mentioned in merge request !905

mentioned in merge request !905
PlasticDigits commented 2026-06-13 14:02:50 +00:00 (Migrated from gitlab.com)

mentioned in merge request !906

mentioned in merge request !906
PlasticDigits commented 2026-06-14 03:07:51 +00:00 (Migrated from gitlab.com)

mentioned in commit a776b93d50

mentioned in commit a776b93d508f8c3917c73aa020b373e936ece43b
PlasticDigits commented 2026-06-14 03:08:12 +00:00 (Migrated from gitlab.com)

mentioned in merge request !908

mentioned in merge request !908
PlasticDigits commented 2026-06-14 03:11:03 +00:00 (Migrated from gitlab.com)

mentioned in merge request !909

mentioned in merge request !909
PlasticDigits commented 2026-06-14 05:49:28 +00:00 (Migrated from gitlab.com)

mentioned in merge request !910

mentioned in merge request !910
Brouie commented 2026-06-28 23:24:44 +00:00 (Migrated from gitlab.com)

mentioned in issue #337

mentioned in issue #337
PlasticDigits commented 2026-08-17 03:45:49 +00:00 (Migrated from gitlab.com)

mentioned in issue #542

mentioned in issue #542
PlasticDigits commented 2026-08-17 03:45:50 +00:00 (Migrated from gitlab.com)

marked as related to #542

marked as related to #542
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/cl8y-dex-terraclassic#376
No description provided.