cg/cmc orderbook endpoints bypass the lcd-heavy rate limiter #278
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:implement
agent:implement
agent:open_issues
agent:ready
agent:research
agent:security_audit
agent:verify
architecture
backend
blocker:hybrid
blocker:launch
blocker:limit-orders
blocker:v2
block:log_only
block:security
bug
ci
contracts
correctness
deploy
dev
devops
docs
documentation
duplicate
e2e
enhancement
epic
feature
frontend
functional-completion
gas
good first issue
governance
help wanted
high-risk
hooks
hybrid
indexer
infra
infrastructure
integrators
invalid
launch-blocker
limit-orders
localnet
localterra
low priority
missing-implementation
needs-design
ops
performance
priority
high
priority
medium
product
qa
QA
question
ready
ready
research
scripts
security
security-hardening
smartcontracts
tech-debt
testing
ux
UX
v2
verification
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
code/cl8y-dex-terraclassic#278
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Severity: High
Reachability: Unauthenticated HTTP. Any client.
Affected:
/cg/orderbookand/cmc/orderbook/{market_pair}route placement (indexer/src/api/mod.rs).Root cause: the CG/CMC orderbook endpoints serve the same LCD-heavy orderbook data as the native book endpoints, but they're mounted on
api_routerinstead oflcd_heavy_router, so they skip the stricter per-IP rate limit.Summary
The native book endpoints —
order-book-head,limit-book,limit-book-shallow,insert-hints— and the solver are deliberately put behindlcd_heavy_routerwithrate_limit_lcd_heavy_rps, because each request fans out a pile of synchronous LCD queries. But/cg/orderbookand/cmc/orderbook/{market_pair}build the same kind of orderbook snapshot and are registered straight onapi_router, under only the general limiter.So an attacker just hits the CG/CMC orderbook mirrors instead of the native ones to drive the same LCD amplification while bypassing the throttle that was added specifically to contain it. It's a hole in an existing control, which is why I'm putting it at High.
Current codebase
indexer/src/api/mod.rs:lcd_heavy_routerlists the native book + solve routes and getsapply_rate_limit_layer(..., rate_limit_lcd_heavy_rps).api_routerseparately registers/cg/orderbookand/cmc/orderbook/{market_pair}and only.merges the heavy router in — the cg/cmc routes are siblings, not members.Recommended direction
/cg/orderbookand/cmc/orderbook/{market_pair}intolcd_heavy_router(or apply the same per-IP heavy limit to them).Acceptance criteria
/cg/orderbookand/cmc/orderbook/*are subject to the same per-IP rate limit as the native book endpoints.Test plan (attack / abuse)
Move both routes to the lcd_heavy_router
Fixed — moved
/cg/orderbookand/cmc/orderbook/{market_pair}intolcd_heavy_router(indexer/src/api/mod.rs) so they inherit the samerate_limit_lcd_heavy_rpsper-IP throttle as the native limit-book / route-solve endpoints. Both handlers run the sameorderbook_sim::simulate_orderbook_cachedLCD fanout (resolve_fee_bps + build_orderbook_data), so they belong under the heavy limiter, not the general one. cargo check clean.Note on AC item 2 ("no LCD-fanout route reachable under only the general limiter"): that's broader than this move — the cg/cmc tickers/summary N+1 fanout is the separate #288 (TTL + pagination), so I scoped this MR to the orderbook routes per your "move both routes to the lcd_heavy_router" note and left the tickers/summary surface to #288.
Branch
qa/278-cgcmc-orderbook-lcd-heavy, MR fork→main (no closing keyword). Live per-IP throttle verification rides on the indexer running on v4, which is gated on the indexer tx-search fix (#292 / MR !738) — until that merges the indexer can't sync on the new stack. @PlasticDigitsmentioned in merge request !739
mentioned in issue #282
mentioned in commit
03e6d7c88eVerified the orderbook routes are under the heavy limiter, and I re-ran the live per-IP throttle check that was blocked last session on the v4 indexer sync — it's unblocked now, so this is end-to-end confirmed.
AC1 (cg/cmc orderbook under the same per-IP limit as the native book): both
/cg/orderbookand/cmc/orderbook/{market_pair}are insidelcd_heavy_routernow (api/mod.rs), behind the samerate_limit_lcd_heavy_rpslayer asorder-book-head/limit-book/ route-solve. Live: a burst on/cg/orderbookruns ~20x 200 then flips to 429, and the 429 carriesx-ratelimit-limit: 20(rps 10 x burst 2) — identical to the nativeorder-book-headbudget;/cmc/orderbookshares the same per-IP heavy bucket (it 429s on the same burst once the budget is drained). A general-limiter route (/api/v1/overview) under the same hammering stays 200 across the board, so the heavy budget is really what's gating these now, not the general one. Bucket refills per second.AC2 (no LCD-fanout route left on only the general limiter): closed for the orderbook surface here. The other LCD-fanout endpoints — cg/cmc tickers + summary — are the N+1 in #288 where I put the cache; I scoped this one to the orderbook routes per your "move both routes" note.
@PlasticDigits good to close.
mentioned in commit
9e47f4daf7mentioned in merge request !765
mentioned in commit
bd90d82902mentioned in issue #319
mentioned in merge request !776
Verification — issue #278 (agent:verify)
Issue: https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/278
Branch verified:
main@4f8019c/9e47f4d(fix + regression test already merged)Acceptance criteria
/cg/orderbookand/cmc/orderbook/*use the same per-IP LCD-heavy limit as native book routesindexer/src/api/mod.rs: both routes registered onlcd_heavy_routerbeforeapply_rate_limit_layer(..., rate_limit_lcd_heavy_rps). Not duplicated onapi_router(comment at merge site).orderbook_sim::simulate_orderbook_cached/ LCD. Other CG/CMC listing endpoints (/cg/tickers,/cmc/summary, etc.) are DB N+1 + 60s aggregator cache (#288), not LCD fanout — out of scope for this issue per maintainer note.Automated tests
cg_cmc_orderbook_lcd_heavy_rate_limit_returns_429hammers/cg/orderbookand/cmc/orderbook/LUNC_USTCwithrate_limit_rps=0,rate_limit_lcd_heavy_rps=5and asserts 429 — same pattern as nativeorder-book-head.Manual / live throttle
SKIP — indexer not running on this VM (
curl :3001/health→ down). Prior implementer comment documents live burst → 429 withx-ratelimit-limit: 20(10 RPS × burst 2), shared bucket with native book; not re-run here.Test plan (issue table)
/cg/orderbookpast lcd-heavy RPSsecurity.rs)/cmc/orderbook/{pair}Docs / invariants
docs/indexer-invariants.md,skills/AGENTS_INDEXER_API_LCD_SECURITY.mdalready list CG/CMC orderbook under LCD-heavy routes — no doc drift found.Outcome: Criteria met on
main; no repo changes from this verification run.mentioned in merge request !783
mentioned in issue #337
mentioned in issue #363
mentioned in issue #361
mentioned in merge request !873
mentioned in issue #694