indexer: empty FACTORY_ADDRESS + dual-zero rate-limit bind guards (#451, #458) #983

Merged
Brouie merged 1 commit from qa/451-458-indexer-config-guards into main 2026-06-30 21:58:47 +00:00
Brouie commented 2026-06-30 19:05:19 +00:00 (Migrated from gitlab.com)

Two SEC-I config findings, both in config.rs:

#451 (SEC-I02 H14): FACTORY_ADDRESS was only rejected empty under RUN_MODE=prod.
An empty factory address makes verify_factory_provenance skip the provenance
check (pair_discovery.rs), so a staging/QA indexer with FACTORY_ADDRESS="" would
silently index unverified (possibly spoofed clone) pairs. Now rejected empty in
EVERY run mode (new ConfigError::EmptyFactoryAddress), before the prod block.

#458 (SEC-I04 F01): RATE_LIMIT_RPS=0 + RATE_LIMIT_LCD_HEAVY_RPS=0 disabled all
governors with only a warn!. On a non-loopback bind that's an unthrottled public
API. Now refuses to start when both are zero on a non-loopback API_BIND in
non-prod (new ConfigError::ZeroRateLimitNonLoopbackBind), with an explicit
ALLOW_ZERO_RATE_LIMITS=1 opt-out for offline/benchmark runs. Prod already clamps
both to 60/10 so the guard never fires there; loopback (Playwright/local-dev)
binds keep working. bind_is_loopback() treats 127/8, ::1, and "localhost" as
loopback and errs toward safety on anything else.

Tests: 4 new config tests (empty-factory reject, dual-zero non-loopback reject,
opt-out allow, loopback allow, prod-clamp loads); indexer lib 167/0.

Two SEC-I config findings, both in config.rs: #451 (SEC-I02 H14): FACTORY_ADDRESS was only rejected empty under RUN_MODE=prod. An empty factory address makes verify_factory_provenance skip the provenance check (pair_discovery.rs), so a staging/QA indexer with FACTORY_ADDRESS="" would silently index unverified (possibly spoofed clone) pairs. Now rejected empty in EVERY run mode (new ConfigError::EmptyFactoryAddress), before the prod block. #458 (SEC-I04 F01): RATE_LIMIT_RPS=0 + RATE_LIMIT_LCD_HEAVY_RPS=0 disabled all governors with only a warn!. On a non-loopback bind that's an unthrottled public API. Now refuses to start when both are zero on a non-loopback API_BIND in non-prod (new ConfigError::ZeroRateLimitNonLoopbackBind), with an explicit ALLOW_ZERO_RATE_LIMITS=1 opt-out for offline/benchmark runs. Prod already clamps both to 60/10 so the guard never fires there; loopback (Playwright/local-dev) binds keep working. bind_is_loopback() treats 127/8, ::1, and "localhost" as loopback and errs toward safety on anything else. Tests: 4 new config tests (empty-factory reject, dual-zero non-loopback reject, opt-out allow, loopback allow, prod-clamp loads); indexer lib 167/0.
Brouie commented 2026-06-30 19:05:19 +00:00 (Migrated from gitlab.com)

assigned to @Brouie

assigned to @Brouie
PlasticDigits commented 2026-06-30 19:07:46 +00:00 (Migrated from gitlab.com)

Security review — MR !983

Commit reviewed: 6ae404135a3564e236c74c79a24fe4018fbc904f
Scope: indexer/src/config.rs — startup guards for empty FACTORY_ADDRESS (all RUN_MODEs) and dual-zero rate limits on non-loopback API_BIND in non-prod (ALLOW_ZERO_RATE_LIMITS opt-out).

Outcome: FINDINGS: 0 medium+

Summary: This MR closes two configuration footguns (#451 / SEC-I02 H14, #458 / SEC-I04 F01) rather than introducing new attack surface. Traced both guards against downstream sinks:

  1. Empty factory — Config::from_env() now rejects whitespace-only FACTORY_ADDRESS before mode-specific checks, so production/staging cannot reach verify_factory_provenance's empty-factory skip path (pair_discovery.rs). Attacker-controlled chain input cannot bypass this; it is enforced at process startup from operator env.

  2. Dual-zero rate limits — Non-prod startup fails when both RATE_LIMIT_RPS and RATE_LIMIT_LCD_HEAVY_RPS parse to 0, API_BIND is non-loopback, and ALLOW_ZERO_RATE_LIMITS is unset. bind_is_loopback() errs toward safety (unparsed hostnames are not treated as loopback). Prod remains protected by existing zero-to-clamp logic (60/10). The opt-out requires deploy-time env control, not a remote caller primitive.

Not reported (out of scope / pre-existing): single-zero rate limit on a public bind in dev (one governor disabled while the other remains), direct Config { ... } construction in integration tests, or governance/operator misconfiguration with ALLOW_ZERO_RATE_LIMITS=1.

Inline threads: none (no medium+ findings).

Security review: no medium+ findings on this diff.

## Security review — MR !983 **Commit reviewed:** `6ae404135a3564e236c74c79a24fe4018fbc904f` **Scope:** `indexer/src/config.rs` — startup guards for empty `FACTORY_ADDRESS` (all `RUN_MODE`s) and dual-zero rate limits on non-loopback `API_BIND` in non-prod (`ALLOW_ZERO_RATE_LIMITS` opt-out). **Outcome:** `FINDINGS: 0` medium+ **Summary:** This MR closes two configuration footguns (#451 / SEC-I02 H14, #458 / SEC-I04 F01) rather than introducing new attack surface. Traced both guards against downstream sinks: 1. **Empty factory** — `Config::from_env()` now rejects whitespace-only `FACTORY_ADDRESS` before mode-specific checks, so production/staging cannot reach `verify_factory_provenance`'s empty-factory skip path (`pair_discovery.rs`). Attacker-controlled chain input cannot bypass this; it is enforced at process startup from operator env. 2. **Dual-zero rate limits** — Non-prod startup fails when both `RATE_LIMIT_RPS` and `RATE_LIMIT_LCD_HEAVY_RPS` parse to `0`, `API_BIND` is non-loopback, and `ALLOW_ZERO_RATE_LIMITS` is unset. `bind_is_loopback()` errs toward safety (unparsed hostnames are not treated as loopback). Prod remains protected by existing zero-to-clamp logic (60/10). The opt-out requires deploy-time env control, not a remote caller primitive. **Not reported (out of scope / pre-existing):** single-zero rate limit on a public bind in dev (one governor disabled while the other remains), direct `Config { ... }` construction in integration tests, or governance/operator misconfiguration with `ALLOW_ZERO_RATE_LIMITS=1`. **Inline threads:** none (no medium+ findings). Security review: no medium+ findings on this diff.
Brouie commented 2026-06-30 19:08:39 +00:00 (Migrated from gitlab.com)

mentioned in issue #451

mentioned in issue #451
Brouie commented 2026-06-30 19:08:40 +00:00 (Migrated from gitlab.com)

mentioned in issue #458

mentioned in issue #458
PlasticDigits (Migrated from gitlab.com) merged commit d326a2e188 into main 2026-06-30 21:58:47 +00:00
PlasticDigits commented 2026-06-30 21:58:48 +00:00 (Migrated from gitlab.com)

mentioned in commit d326a2e188

mentioned in commit d326a2e18850f73f5694451b58172e3a5d965ee4
PlasticDigits commented 2026-06-30 22:19:15 +00:00 (Migrated from gitlab.com)

mentioned in merge request !992

mentioned in merge request !992
PlasticDigits commented 2026-06-30 22:21:10 +00:00 (Migrated from gitlab.com)

mentioned in merge request !996

mentioned in merge request !996
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!983
No description provided.