Indexer hardening: rate limits, max_maker_fills, logging, body limits (#379) #910
No reviewers
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!910
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "issue-379-indexer-hardening"
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?
Summary
Indexer hardening for #379 (parent #376):
tracing::warn!when bothRATE_LIMIT_RPS=0andRATE_LIMIT_LCD_HEAVY_RPS=0(anyRUN_MODE; prod clamp unchanged).MAX_MAKER_FILLS_HARD_CAP = 100inindexer/src/hybrid_limits.rs(aligned with on-chaindex-common); GET route solve clamps viaclamp_max_maker_fills; benchmark doc + harness.blacklist-checkuseslcd_gateway_err→ 502 (sanitized body).RequestBodyLimitLayer(128 KiB) onPOST /api/v1/route/solve→ 413.client.tsURL-encodes all path segments viapathSegment().Acceptance checklist
cd indexer && cargo test dev_dual_zero --lib;cargo test --test security dev_dual_zero_rate_limits -- --test-threads=1max_maker_fillsclamped to unified cap (100)docs/benchmarks/max-maker-fills-route-solve.md;cargo test clamp_rejects --lib; grephybrid_limits::MAX_MAKER_FILLS_HARD_CAPindexer/src/lcd/mod.rsblacklist-checkreturns 502/503 on LCD failure, not 500cargo test --test security blacklist_check_lcd_failure -- --test-threads=1cargo test --test security route_solve_post_oversized -- --test-threads=1npm test -- src/services/indexer/__tests__/client.test.tssecurity.rsincludes M-05, L-05, L-08 casescargo test --test security -- --test-threads=1(28/28)MAX_MAKER_FILLS_HARD_CAPsource in indexerindexer/src/hybrid_limits.rsonlycargo test --test api_route_solve -- --test-threads=1(23/23);cargo test --test security -- --test-threads=1(28/28)max_maker_fillsbenchmark harness runscripts/qa/bench-max-maker-fills-route-solve.shmake setup-cloud-localterradeploy env)Third-party verification
Attack probes (manual):
GET /api/v1/route/solve/best?...&max_maker_fills=4294967295→ completes; server uses cap 100.POST /api/v1/route/solvewith JSON pad > 128 KiB → 413.GET /api/v1/compliance/blacklist-check?wallet=terra1…with LCD down → 502, bodyUpstream LCD query failed.Docs:
docs/indexer-invariants.md,docs/operator-secrets.md,skills/AGENTS_INDEXER_API_LCD_SECURITY.md,docs/benchmarks/max-maker-fills-route-solve.md.changed the description
changed the description
Stale Security Review comment
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit
1d12feb3c2. Configure here.Route cache maker-fill collision
Medium Severity
Hybrid GET route-solve caches responses under a coarse
cache_key_maker_fillsbucket that maps every value from 17 through 100 to the same key, while each request still runs optimization with its own clampedmax_maker_fills. A later caller with a higher fill limit can receive a cached quote built with a lower limit, understating book depth andestimated_amount_out.Reviewed by Cursor Bugbot for commit
1d12feb3c2. Configure here.Security review — MR !910
Commit reviewed:
1d12feb3c2fd9612a55a06358744a5df853bbf6eScope: Indexer hardening (#379) —
max_maker_fillscap/clamp (hybrid_limits.rs), LCD WARN log redaction (lcd/mod.rs),blacklist-checksanitized 502 (compliance.rs),POST /api/v1/route/solve128 KiB body limit (api/mod.rs), dual-zero rate-limit startup warning (config.rs), frontend indexerpathSegment()URL encoding (client.ts), docs/tests/benchmark harness.Method: Diff review with attacker-input tracing to sinks (LCD fanout, error bodies, logging, POST body handling, client path construction). Cross-checked existing controls: prod rate-limit clamps,
TimeoutLayer(30s),LCD_HYBRID_SIM_BUDGET, on-chainMAX_MAKER_FILLS_HARD_CAP(100),lcd_gateway_errclient sanitization.Outcome:
FINDINGS: 0medium+Inline threads: None (no medium+ issues on this diff).
Notes (below reporting threshold)
clamp_max_maker_fills(abuse probemax_maker_fills=4294967295→ 100), and LCD-heavy routes remain rate-limited in prod.RequestBodyLimitLayeronPOST /api/v1/route/solveis appropriately scoped; GET on the same route is unaffected.blacklist-checkswitch frominternal_errtolcd_gateway_errremoves raw upstream detail from client responses.pathSegment()is a correct client-side hardening; server-side pair/trader validation is unchanged and out of diff scope.Label:
block:securitynot applied.changed this line in version 2 of the diff
added 33 commits
maind14c9cf6- Merge branch 'main' into issue-379-indexer-hardeningCompare with previous version
Resolved merge conflicts with
main(commitd14c9cf6).Conflicts resolved (8 files):
docs/indexer-invariants.md,skills/AGENTS_INDEXER_API_LCD_SECURITY.md— merged #379 hardening rows; kepthybrid_limits.rsas themax_maker_fillssourceindexer/src/config.rs— single dual-zero rate-limit startup warning (M-05)indexer/src/lcd/mod.rs— main's structured WARN logging (endpoint idx + path, no URLs)indexer/src/api/route_solver.rs,db_orderbook_sim.rs—hybrid_limits::MAX_MAKER_FILLS_HARD_CAP(100), updated cache bucketsindexer/src/api/mod.rs— route-level POST body limit only (removed duplicate router-wide layer)indexer/tests/security.rs— kept MR test names/casesfrontend-dapp/src/services/indexer/client.ts— kept MRpathSegment()encoding (incl. order ids)Dropped
indexer/src/constants.rsfrom main in favor of existinghybrid_limits.rs.Sanity:
cargo test --test security(#379 cases), frontendclient.test.ts(19/19).resolved all threads
changed the description
Cursor Bugbot has reviewed your changes using default effort and found 3 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit
d14c9cf619. Configure here.POST hybrid fills not clamped
Medium Severity
GET
max_maker_fillsis clamped to the hard cap, butPOST /api/v1/route/solvestill embeds each hop’s rawmax_maker_fillsfromhybrid_by_hopinto router operations. Clients can send extremely large values in the JSON body and drive heavier LCD hybrid simulation than the capped GET path.Reviewed by Cursor Bugbot for commit
d14c9cf619. Configure here.Unused route body limit constant
Low Severity
ROUTE_SOLVE_POST_BODY_LIMITis declared inapi/mod.rsbut the router usesconfig::ROUTE_SOLVE_POST_BODY_LIMIT_BYTESforRequestBodyLimitLayer. The duplicate constant is never referenced and can drift from the enforced limit.Reviewed by Cursor Bugbot for commit
d14c9cf619. Configure here.LCD verify skips base64 decode
Medium Severity
verifyDeployAddressesOnLcdreadsbody.data.factoryfrom the router smart-query response, but Terra LCD normally returnsdataas a base64-encoded JSON string. The check then treats the payload as missing and reports failure even when the router config is valid.Reviewed by Cursor Bugbot for commit
d14c9cf619. Configure here.Security review — MR !910
Commit reviewed:
d14c9cf619645cb86f73f6085c828ea0eea3059bScope: Re-review after merge-conflict resolution with
main. Diff covers indexer hardening (#379):hybrid_limits.rs(MAX_MAKER_FILLS_HARD_CAP= 100,clamp_max_maker_fills), coarse hybrid GET cache bucket update (route_solver.rs),POST /api/v1/route/solve128 KiBRequestBodyLimitLayer(route-scoped), dual-zero rate-limit startup warning (config.rs), frontend indexerpathSegment()URL encoding (client.ts), docs/tests/benchmark harness. Conflict resolution kept main's structured LCD WARN redaction (lcd/mod.rs);compliance.rsblacklist 502 sanitization unchanged on this diff.Method: Diff review with attacker-input tracing to sinks (LCD fanout, hybrid cache, POST body handling, client path construction). Cross-checked prod rate-limit clamps (
RUN_MODE=prod→ 60/10),TimeoutLayer(30s),LCD_HYBRID_SIM_BUDGET, on-chain cap parity, existinglcd_gateway_errsanitization.Outcome:
FINDINGS: 0medium+Inline threads: None (no medium+ issues on this diff).
Re-checked from prior threads
cache_key_maker_fillscoarse bucketing (Bugbot): Removing the30tier widens cache-key collision formax_maker_fills17–100, but this is an intentional performance tradeoff (12s TTL, 512-entry cap, LCD-heavy rate limit). On cache hit,enrich_route_slippageadjusts slippage only — it does not re-solve with the caller's fill depth. That is a quote correctness concern for integrators, not an authn/authz bypass, injection, or secret-leak vector; rated below medium for this security pass.max_maker_fills=4294967295clamps to 100; prod LCD-heavy governor remains active.RequestBodyLimitLayermoved to route-level (POST-only body path): Appropriate scoping; oversized JSON → 413 persecurity.rstest.0; prod still clamps to 60/10.Label:
block:securitynot applied.mentioned in commit
30cfd4be2e