docs: SEC-I04 indexer/API abuse and data integrity review #453

Closed
opened 2026-06-30 17:20:44 +00:00 by totdking · 8 comments
totdking commented 2026-06-30 17:20:44 +00:00 (Migrated from gitlab.com)
No description provided.
totdking commented 2026-06-30 17:21:25 +00:00 (Migrated from gitlab.com)

assigned to @totdking

assigned to @totdking
totdking commented 2026-06-30 17:21:31 +00:00 (Migrated from gitlab.com)

unassigned @totdking

unassigned @totdking
totdking commented 2026-06-30 17:22:06 +00:00 (Migrated from gitlab.com)

Summary

Agent-assisted review of indexer/API abuse vectors and data integrity completed across two independent runs. Merged verdict: PASS overall with one medium operational gap and one low input validation gap.

What Was Done

Areas reviewed: SQL injection posture, rate limiting configuration and clamping, error sanitization, CORS origin allowlist enforcement, swap and fill deduplication, hybrid volume reconciliation, CSV formula injection, factory provenance validation, and ILIKE query safety.

Solid findings confirmed:

  • All user-controlled inputs reach the database exclusively through push_bind() or sqlx::query.bind(). The three format!() SQL segments interpolate only closed-match enum values and are not vulnerabilities.
  • CORS uses an explicit origin allowlist. RUN_MODE=prod refuses to start if CORS_ORIGINS is empty.
  • internal_err and lcd_gateway_err return sanitized messages only. No URLs or LCD endpoint paths exposed to clients.
  • Deduplication enforced via UNIQUE(tx_hash, pair_id, swap_index) on swap events and UNIQUE(tx_hash, pair_id, order_id) on limit fills. Insert uses ON CONFLICT DO NOTHING.
  • Hybrid volume aggregation uses book_return_amount + pool_return_amount only. Maker fill rows are sub-allocations, not additive.
  • CSV cells prefixed with apostrophe on =, +, -, @ to neutralize formula injection.
  • verify_factory_provenance confirms factory maps asset tuple to the exact emitting pair address before indexing.

Findings Triaged

  • I04-F01 (Medium): dev-mode zero rate limit with no non-loopback bind guard -- filed as Issue 57 (#458)
  • I04-F02 (Low): ILIKE search accepts unescaped % and _ wildcards, ?q=% matches every pair -- filed as Issue 58 (#459)
  • I04-F03 (Info): format!() SQL segments use closed-match whitelisting; not a vulnerability, no action required
Finding Severity GitLab #
I04-F01: dev-mode zero rate limit with no non-loopback bind guard Medium #458
I04-F02: ILIKE unescaped wildcards, ?q=% matches all pairs Low #459

Verification Checklist

  • SQL injection posture confirmed via parameterized binding review
  • Rate limiting config, clamping, and dual-zero behavior reviewed
  • Error sanitization confirmed at API boundary
  • CORS allowlist enforcement confirmed
  • Deduplication constraints confirmed at schema level
  • CSV formula injection neutralization confirmed
  • Factory provenance validation confirmed
  • All findings triaged and filed or documented
### Summary Agent-assisted review of indexer/API abuse vectors and data integrity completed across two independent runs. Merged verdict: PASS overall with one medium operational gap and one low input validation gap. ### What Was Done Areas reviewed: SQL injection posture, rate limiting configuration and clamping, error sanitization, CORS origin allowlist enforcement, swap and fill deduplication, hybrid volume reconciliation, CSV formula injection, factory provenance validation, and ILIKE query safety. Solid findings confirmed: - All user-controlled inputs reach the database exclusively through `push_bind()` or `sqlx::query.bind()`. The three `format!()` SQL segments interpolate only closed-match enum values and are not vulnerabilities. - CORS uses an explicit origin allowlist. `RUN_MODE=prod` refuses to start if `CORS_ORIGINS` is empty. - `internal_err` and `lcd_gateway_err` return sanitized messages only. No URLs or LCD endpoint paths exposed to clients. - Deduplication enforced via `UNIQUE(tx_hash, pair_id, swap_index)` on swap events and `UNIQUE(tx_hash, pair_id, order_id)` on limit fills. Insert uses `ON CONFLICT DO NOTHING`. - Hybrid volume aggregation uses `book_return_amount + pool_return_amount` only. Maker fill rows are sub-allocations, not additive. - CSV cells prefixed with apostrophe on `=`, `+`, `-`, `@` to neutralize formula injection. - `verify_factory_provenance` confirms factory maps asset tuple to the exact emitting pair address before indexing. ### Findings Triaged - I04-F01 (Medium): dev-mode zero rate limit with no non-loopback bind guard -- filed as Issue 57 (#458) - I04-F02 (Low): ILIKE search accepts unescaped `%` and `_` wildcards, `?q=%` matches every pair -- filed as Issue 58 (#459) - I04-F03 (Info): `format!()` SQL segments use closed-match whitelisting; not a vulnerability, no action required ### Related Issues | Finding | Severity | GitLab # | |---------|----------|----------| | I04-F01: dev-mode zero rate limit with no non-loopback bind guard | Medium | #458 | | I04-F02: ILIKE unescaped wildcards, `?q=%` matches all pairs | Low | #459 | ### Verification Checklist - [x] SQL injection posture confirmed via parameterized binding review - [x] Rate limiting config, clamping, and dual-zero behavior reviewed - [x] Error sanitization confirmed at API boundary - [x] CORS allowlist enforcement confirmed - [x] Deduplication constraints confirmed at schema level - [x] CSV formula injection neutralization confirmed - [x] Factory provenance validation confirmed - [x] All findings triaged and filed or documented
totdking commented 2026-06-30 18:37:55 +00:00 (Migrated from gitlab.com)

mentioned in issue #381

mentioned in issue #381
Brouie commented 2026-07-01 11:19:25 +00:00 (Migrated from gitlab.com)

Both code findings re-checked live on the fresh redeploy (21381a0e, indexer rebuilt).

I04-F01 / #458 — dual-zero rate-limit bind guard:

  • Live: booting the indexer on a non-loopback bind (0.0.0.0) with RATE_LIMIT_RPS=0 and RATE_LIMIT_LCD_HEAVY_RPS=0 now refuses to start ("disables all rate protection on a public listener ... ALLOW_ZERO_RATE_LIMITS=1 to override"). Setting ALLOW_ZERO_RATE_LIMITS=1 lets it through. Loopback binds still allow dual-zero. 6 config unit tests cover the matrix; prod-mode clamps zeros up to defaults instead.

I04-F02 / #459 — ILIKE wildcard escape:

  • Live on /api/v1/pairs: q=% -> 0, q=_ -> 0, q=EMB% -> 0 (all treated literally), while q=EMBER -> 6 and q=MBE -> 9 confirm the search is actually alive and filtering. So % no longer matches every pair. 4 escape unit tests pass.
  • Minor heads-up, out of scope for this finding: there is one remaining raw LIKE at seed_qa.rs:126, but it is QA-seed cleanup on an internally-controlled tx_hash, not the user-facing search — flagging for awareness, no action needed.

Children merged + live-verified, checklist done — good to close from my side. @PlasticDigits

Both code findings re-checked live on the fresh redeploy (21381a0e, indexer rebuilt). I04-F01 / #458 — dual-zero rate-limit bind guard: - Live: booting the indexer on a non-loopback bind (0.0.0.0) with RATE_LIMIT_RPS=0 and RATE_LIMIT_LCD_HEAVY_RPS=0 now refuses to start ("disables all rate protection on a public listener ... ALLOW_ZERO_RATE_LIMITS=1 to override"). Setting ALLOW_ZERO_RATE_LIMITS=1 lets it through. Loopback binds still allow dual-zero. 6 config unit tests cover the matrix; prod-mode clamps zeros up to defaults instead. I04-F02 / #459 — ILIKE wildcard escape: - Live on /api/v1/pairs: q=% -> 0, q=_ -> 0, q=EMB% -> 0 (all treated literally), while q=EMBER -> 6 and q=MBE -> 9 confirm the search is actually alive and filtering. So % no longer matches every pair. 4 escape unit tests pass. - Minor heads-up, out of scope for this finding: there is one remaining raw LIKE at seed_qa.rs:126, but it is QA-seed cleanup on an internally-controlled tx_hash, not the user-facing search — flagging for awareness, no action needed. Children merged + live-verified, checklist done — good to close from my side. @PlasticDigits
totdking commented 2026-07-01 18:04:05 +00:00 (Migrated from gitlab.com)

mentioned in issue #472

mentioned in issue #472
totdking commented 2026-07-01 18:45:59 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1006

mentioned in merge request !1006
totdking commented 2026-07-02 16:21:47 +00:00 (Migrated from gitlab.com)

Good to close on this end as well .

All pushed changes pass checklist and verification parameters

Good to close on this end as well . All pushed changes pass checklist and verification parameters
totdking (Migrated from gitlab.com) closed this issue 2026-07-03 08:45:44 +00:00
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#453
No description provided.