fix(api): enforce CORS, trusted rate limits, safe errors and production network boundaries #41

Closed
opened 2026-10-14 03:11:12 +00:00 by PlasticDigits · 2 comments
Owner

Scope and source

Priority: P1. Gap coverage: GAP-006, GAP-018, GAP-020, GAP-021. Derived from GAP_1791945837.md, reviewed code commit f8dbe93df953a78b17cb3304b717dcf84c4cd517. Current e476a68da09645c2308cbf47b90211fcaffcb7af adds only the report, so implementation findings remain applicable. Report evidence is local reproduction/source inspection unless explicitly proposed; no production exploit or current live-readiness claim.

Coverage and sequencing: gap backlog tracker. Workstream 06. Dependencies: 05.

Current codebase

PUT amendments are absent from allowed CORS methods and IP limits. Invalid configured origins are discarded. First X-Forwarded-For values are trusted by default without peer CIDRs; buckets have no cleanup and are per replica. RPC/LCD errors include credential-bearing URLs and API errors expose raw SQL text. API/ledger run-mode spellings differ; dev chain IDs and some simulated-wallet/build flags remain accepted in production.

Why this implementation is needed

Cross-origin drafting fails, spoofed headers may evade limits, errors may leak secrets, and ambiguous build/runtime modes weaken deployment separation.

Relevant files

Create a testable production application factory including middleware. Allow PUT with exact validated origins; fail startup for malformed configuration. Trust forwarded chains only from explicit proxy peers with documented edge header replacement/private ingress. Cover every mutation with bounded IP plus action/wallet controls and eviction; define multi-replica aggregate enforcement. Remove URLs from transport errors, use safe provider labels and request-correlated stable public error codes. Validate run-mode/network enums consistently and reject localterra/31337, simulated wallets, forbidden browser RPC flags and missing/insecure production API URLs across Docker, direct and custom-mode builds.

Constraints and guardrails

Preserve standalone offchain advisory voting, no new smart contracts or archive-node dependency, BSC as a core electorate, raw 18-decimal integer balances and separate Terra/BSC address identities. Keep Legal fail-closed transactional UI and the proven DEX Terra/Bridge EVM connectors; no browser BSC balance RPC, seed/key prompt or embedded admin/model credential. API must retain a restricted role separate from the ledger writer. Freeze at actual opening, never at draft creation or a client-supplied height. Agents own design, implementation, review and evidence under repository guidance; do not shift approvals/tests to the human or invent PASS evidence.

Acceptance criteria

Allowed-origin PUT preflight and actual browser amend succeed; disallowed origins/headers/methods remain rejected. Spoofing untrusted forwarded headers cannot change buckets. All mutations are limited with recoverable Retry-After behavior. Fake sentinel credentials never appear in logs/responses. Every production-equivalent mode applies the same network and build rules.

Test plan: supported paths, failure paths and concurrency

Real middleware GET/POST/PUT/OPTIONS, allowed/disallowed/null origins, malformed origin configuration and body limits. Trusted/untrusted proxy hop chains, direct access, IPv4/IPv6, bursts, expiry/cleanup and replicas. Provider timeout/HTTP/JSON and DB errors with sentinel credentials. Mode spellings, missing configuration, custom build modes, local/dev versus mainnet, HTTPS/API URL rules. Browser test against a second-origin local API.

Test plan: attack, hack and abuse vectors

XFF spoofing, huge/malformed proxy headers, per-route PUT floods, unique-IP bucket exhaustion, CORS reflection and development-chain signature replay in production; error-induced URL/query/basic-auth leakage. Inspect trusted edge behavior rather than assuming live topology; abuse tests must stay isolated.

Verification and completion criteria

Attach real preflight/browser results, spoofing/quota tests, log/response sentinel scan and production build rejection matrix. Verify deployed proxy topology with dated evidence when releasing; router-only tests are insufficient.

Record commit and pinned toolchain, commands/results and sanitized artifacts. Relevant Rust changes require cargo test --workspace --lib plus isolated Postgres integration (explicit guarded LEDGER_TEST_DATABASE_URL); frontend changes require npm test and production build plus relevant browser cases. Run secret scanning for new fixtures/config. Exercise fresh/upgrade migrations when schema/grants change; report skipped/live-only checks explicitly.

<!-- voting-gap-1791945837:06 --> ## Scope and source **Priority:** P1. **Gap coverage:** GAP-006, GAP-018, GAP-020, GAP-021. Derived from [GAP_1791945837.md](https://git.cl8y.com/code/voting/src/branch/main/gaps/GAP_1791945837.md), reviewed code commit `f8dbe93df953a78b17cb3304b717dcf84c4cd517`. Current `e476a68da09645c2308cbf47b90211fcaffcb7af` adds only the report, so implementation findings remain applicable. Report evidence is local reproduction/source inspection unless explicitly proposed; no production exploit or current live-readiness claim. Coverage and sequencing: [gap backlog tracker](https://git.cl8y.com/code/voting/issues/35). Workstream **06**. Dependencies: [05](https://git.cl8y.com/code/voting/issues/40). ## Current codebase PUT amendments are absent from allowed CORS methods and IP limits. Invalid configured origins are discarded. First X-Forwarded-For values are trusted by default without peer CIDRs; buckets have no cleanup and are per replica. RPC/LCD errors include credential-bearing URLs and API errors expose raw SQL text. API/ledger run-mode spellings differ; dev chain IDs and some simulated-wallet/build flags remain accepted in production. ## Why this implementation is needed Cross-origin drafting fails, spoofed headers may evade limits, errors may leak secrets, and ambiguous build/runtime modes weaken deployment separation. ## Relevant files - [`operator-voting/src/main.rs`](https://git.cl8y.com/code/voting/src/branch/main/operator-voting/src/main.rs) - [`operator-voting/src/config.rs`](https://git.cl8y.com/code/voting/src/branch/main/operator-voting/src/config.rs) - [`operator-voting/src/rate_limit.rs`](https://git.cl8y.com/code/voting/src/branch/main/operator-voting/src/rate_limit.rs) - [`operator-voting/src/error.rs`](https://git.cl8y.com/code/voting/src/branch/main/operator-voting/src/error.rs) - [`operator-voting/src/payload.rs`](https://git.cl8y.com/code/voting/src/branch/main/operator-voting/src/payload.rs) - [`ledger/src/config.rs`](https://git.cl8y.com/code/voting/src/branch/main/ledger/src/config.rs) - [`ledger/src/lcd.rs`](https://git.cl8y.com/code/voting/src/branch/main/ledger/src/lcd.rs) - [`ledger/src/bsc.rs`](https://git.cl8y.com/code/voting/src/branch/main/ledger/src/bsc.rs) - [`ledger/src/register.rs`](https://git.cl8y.com/code/voting/src/branch/main/ledger/src/register.rs) - [`frontend/src/utils/prodEnvGuards.ts`](https://git.cl8y.com/code/voting/src/branch/main/frontend/src/utils/prodEnvGuards.ts) - [`frontend/vite.config.ts`](https://git.cl8y.com/code/voting/src/branch/main/frontend/vite.config.ts) - [`operator-voting/tests/api_flow.rs`](https://git.cl8y.com/code/voting/src/branch/main/operator-voting/tests/api_flow.rs) ## Recommended solution Create a testable production application factory including middleware. Allow PUT with exact validated origins; fail startup for malformed configuration. Trust forwarded chains only from explicit proxy peers with documented edge header replacement/private ingress. Cover every mutation with bounded IP plus action/wallet controls and eviction; define multi-replica aggregate enforcement. Remove URLs from transport errors, use safe provider labels and request-correlated stable public error codes. Validate run-mode/network enums consistently and reject localterra/31337, simulated wallets, forbidden browser RPC flags and missing/insecure production API URLs across Docker, direct and custom-mode builds. ## Constraints and guardrails Preserve standalone offchain advisory voting, no new smart contracts or archive-node dependency, BSC as a core electorate, raw 18-decimal integer balances and separate Terra/BSC address identities. Keep Legal fail-closed transactional UI and the proven DEX Terra/Bridge EVM connectors; no browser BSC balance RPC, seed/key prompt or embedded admin/model credential. API must retain a restricted role separate from the ledger writer. Freeze at actual opening, never at draft creation or a client-supplied height. Agents own design, implementation, review and evidence under repository guidance; do not shift approvals/tests to the human or invent PASS evidence. ## Acceptance criteria Allowed-origin PUT preflight and actual browser amend succeed; disallowed origins/headers/methods remain rejected. Spoofing untrusted forwarded headers cannot change buckets. All mutations are limited with recoverable Retry-After behavior. Fake sentinel credentials never appear in logs/responses. Every production-equivalent mode applies the same network and build rules. ## Test plan: supported paths, failure paths and concurrency Real middleware GET/POST/PUT/OPTIONS, allowed/disallowed/null origins, malformed origin configuration and body limits. Trusted/untrusted proxy hop chains, direct access, IPv4/IPv6, bursts, expiry/cleanup and replicas. Provider timeout/HTTP/JSON and DB errors with sentinel credentials. Mode spellings, missing configuration, custom build modes, local/dev versus mainnet, HTTPS/API URL rules. Browser test against a second-origin local API. ## Test plan: attack, hack and abuse vectors XFF spoofing, huge/malformed proxy headers, per-route PUT floods, unique-IP bucket exhaustion, CORS reflection and development-chain signature replay in production; error-induced URL/query/basic-auth leakage. Inspect trusted edge behavior rather than assuming live topology; abuse tests must stay isolated. ## Verification and completion criteria Attach real preflight/browser results, spoofing/quota tests, log/response sentinel scan and production build rejection matrix. Verify deployed proxy topology with dated evidence when releasing; router-only tests are insufficient. Record commit and pinned toolchain, commands/results and sanitized artifacts. Relevant Rust changes require `cargo test --workspace --lib` plus isolated Postgres integration (explicit guarded LEDGER_TEST_DATABASE_URL); frontend changes require `npm test` and production build plus relevant browser cases. Run secret scanning for new fixtures/config. Exercise fresh/upgrade migrations when schema/grants change; report skipped/live-only checks explicitly.
Author
Owner

Implementation status after main merge PR #53, commit cd07f00f32f04ad02f8b760ed34588fd9645919e:

  • Strict CORS, verified proxy quotas, redacted errors and production boundaries implemented with regression tests and crosslinked invariants/agent guidance.
  • Combined Rust1.88.0, isolated PostgreSQL, frontend/build/browser, OpenAPI and secret checks passed; see evidence.
  • Live release verification is still in progress. The first Coolify attempts failed before healthy acceptance. PR #54 fixes native-RPC entrypoint wiring, frontend image mode, liveness probes and pooled schema selection, with additional process/fault/privilege tests.

Services are paused while existing storage is audited and the corrected pinned release is prepared. No production wallet transaction or hardware-wallet/Legal QA is claimed. This is an interim report; the issue remains open and the final comment will map the original criteria to actual live results and any unverified cases.

Implementation status after main merge [PR #53](https://git.cl8y.com/code/voting/pulls/53), commit `cd07f00f32f04ad02f8b760ed34588fd9645919e`: - [x] Strict CORS, verified proxy quotas, redacted errors and production boundaries implemented with regression tests and crosslinked invariants/agent guidance. - [x] Combined Rust1.88.0, isolated PostgreSQL, frontend/build/browser, OpenAPI and secret checks passed; see [evidence](https://git.cl8y.com/code/voting/src/branch/main/docs/verification/2026-10-14-issues-36-43.md). - [ ] Live release verification is still in progress. The first Coolify attempts failed before healthy acceptance. [PR #54](https://git.cl8y.com/code/voting/pulls/54) fixes native-RPC entrypoint wiring, frontend image mode, liveness probes and pooled schema selection, with additional process/fault/privilege tests. Services are paused while existing storage is audited and the corrected pinned release is prepared. No production wallet transaction or hardware-wallet/Legal QA is claimed. This is an interim report; the issue remains open and the final comment will map the original criteria to actual live results and any unverified cases.
Author
Owner

Implemented the API boundary fixes through PRs #53/#54 and subsequent followups. Accepted release: 5cd9363307dc1607de952ac3243193c1a16304c7 at 2026-10-14T14:07:21Z, after PR #59 merged with CI passing. Implementation and verification evidence and documented invariants/agent guidance.

  • Exact allowed-origin PUT preflight and an actual second-origin Chromium signed draft amendment succeed, including exact receipt retry. The production application factory covers GET/POST/PUT/OPTIONS and rejects disallowed/null origins, forbidden headers/methods, malformed origin configuration and oversized bodies.
  • Every mutation uses bounded IP controls with recoverable Retry-After. Authenticated wallet/action limits are database-backed, aggregate across API processes and run after signature verification; unsigned envelopes cannot exhaust a victim wallet's quota. Tests cover direct/trusted/untrusted IPv4/IPv6 peers, hop chains, absent connection metadata, huge/malformed/duplicate forwarding headers, spoofing, saturation and expiry/eviction. Production process-local IP enforcement is explicitly restricted to one API instance.
  • Dated deployed-edge proof: on 2026-10-14, private peer 10.0.17.2 was observed and only 10.0.17.2/32 was trusted. An isolated 2-token maintenance quota with changing spoofed XFF values produced 200, 200, 429. The rejection had a stable code, matching response/header request ID and Retry-After:29; health stayed 200 and the same action succeeded after the advertised wait. Topology and evidence.
  • Production-default INFO log and public-response sentinel tests cover actual PostgreSQL wire errors and Terra/BSC HTTP, malformed-JSON and timeout failures. Injected URL/basic-auth/query/SQL sentinels are absent; useful safe classifications remain. Extractor, route, media-type and body-limit failures return correlated stable JSON rather than raw framework/SQL text.
  • prod/production enforce mainnet network domains; genuine signed localterra/31337 and v1/unsupported-version mutation attempts fail through the real factory. Direct production/custom-release Vite builds reject 18 invalid cases. The actual Dockerfile builds valid configuration and rejects nine invalid configurations with recognizable guard reasons; pull/network/install failures do not count as rejection proof. Docker evidence.
  • Final live checks passed on the accepted commit: 13/13 smoke checks, four shared canonical fixtures, exact both-chain receipt replays including the original now-expired signed payloads, and runtime configuration validation. API, ledger and frontend Coolify deployments finished and all services are healthy; final live evidence.
  • Final deployment retains normal 60/minute, burst 20 IP limits, the observed exact /32 proxy peer and one API instance. Three private readiness windows (240+240+600 =1080 observations) all passed, with maximum Terra lag 2 and BSC lag 17. The two final public windows passed 45/45 each. Loki and direct Coolify logs show no ledger warnings after activation at 13:58:22Z; the old 13:56:21Z rpc_missing_result warning preceded activation.

Verification: Rust 1.88.0, locked dependencies and fresh guarded PostgreSQL16/SCRAM infrastructure; command CARGO_BUILD_JOBS=2 scripts/verify-release.sh. Release13 passed 74 unit/supervisor tests, 121 integration tests (66 ledger hardening), 245 schema-validated API responses and 146 frontend tests, plus production build, both nginx checks and Gitleaks. Separate Chromium evidence: 11 UI regressions, nine hostile-rendering tests and one real second-origin protocol test, using Node24.21.0, Playwright1.62.1 and Chromium151.0.7922.34.

Release12 was a failed historical run; the expected late-supervisor-exit fixture race was corrected without weakening financial assertions, then passed four fresh-database repetitions and the full release13 rerun. Earlier public sampling recorded one unexplained 503 at 14:00:58Z (44/45); the next check returned 200. It is retained in final live evidence rather than counted as a pass. The subsequent two extended public windows were fully green (90/90), alongside all 1080 private observations; acceptance is based on those final windows and does not claim transient failures are impossible.

The API retains its separate restricted database role; fresh/upgrade migrations, real restricted-login and inherited/function/column privilege regressions are included in the integration evidence. No arbitrary debug/wire-log safety, exhaustive route×status Cartesian matrix, funded-holder or hardware/mobile-wallet/real-Legal QA is claimed. The isolated browser Legal hatch does not weaken production Legal gating. All required issue criteria are checked against the evidence above; no mandatory verification item remains open.

Final evidence is published through PR #60. All mandatory body/comment criteria are satisfied by the checklist above; closing this issue.

Implemented the API boundary fixes through PRs #53/#54 and subsequent followups. Accepted release: `5cd9363307dc1607de952ac3243193c1a16304c7` at **2026-10-14T14:07:21Z**, after [PR #59](https://git.cl8y.com/code/voting/pulls/59) merged with CI passing. [Implementation and verification evidence](https://git.cl8y.com/code/voting/src/branch/main/docs/verification/2026-10-14-issues-36-43.md) and [documented invariants/agent guidance](https://git.cl8y.com/code/voting/src/branch/main/skills/AGENTS_PROTOCOL_SECURITY.md). - [x] Exact allowed-origin PUT preflight and an actual second-origin Chromium signed draft amendment succeed, including exact receipt retry. The production application factory covers GET/POST/PUT/OPTIONS and rejects disallowed/null origins, forbidden headers/methods, malformed origin configuration and oversized bodies. - [x] Every mutation uses bounded IP controls with recoverable Retry-After. Authenticated wallet/action limits are database-backed, aggregate across API processes and run after signature verification; unsigned envelopes cannot exhaust a victim wallet's quota. Tests cover direct/trusted/untrusted IPv4/IPv6 peers, hop chains, absent connection metadata, huge/malformed/duplicate forwarding headers, spoofing, saturation and expiry/eviction. Production process-local IP enforcement is explicitly restricted to one API instance. - [x] Dated deployed-edge proof: on **2026-10-14**, private peer `10.0.17.2` was observed and only `10.0.17.2/32` was trusted. An isolated 2-token maintenance quota with changing spoofed XFF values produced **200, 200, 429**. The rejection had a stable code, matching response/header request ID and **Retry-After:29**; health stayed 200 and the same action succeeded after the advertised wait. [Topology and evidence](https://git.cl8y.com/code/voting/src/branch/main/docs/verification/2026-10-14-issues-36-43.md). - [x] Production-default INFO log and public-response sentinel tests cover actual PostgreSQL wire errors and Terra/BSC HTTP, malformed-JSON and timeout failures. Injected URL/basic-auth/query/SQL sentinels are absent; useful safe classifications remain. Extractor, route, media-type and body-limit failures return correlated stable JSON rather than raw framework/SQL text. - [x] `prod`/`production` enforce mainnet network domains; genuine signed localterra/31337 and v1/unsupported-version mutation attempts fail through the real factory. Direct production/custom-release Vite builds reject 18 invalid cases. The actual Dockerfile builds valid configuration and rejects nine invalid configurations with recognizable guard reasons; pull/network/install failures do not count as rejection proof. [Docker evidence](https://git.cl8y.com/code/voting/src/branch/main/docs/verification/2026-10-14-docker-guards.json). - [x] Final live checks passed on the accepted commit: **13/13 smoke checks**, four shared canonical fixtures, exact both-chain receipt replays including the original now-expired signed payloads, and runtime configuration validation. API, ledger and frontend Coolify deployments finished and all services are healthy; [final live evidence](https://git.cl8y.com/code/voting/src/branch/main/docs/verification/2026-10-14-final-live.json). - [x] Final deployment retains normal **60/minute, burst 20** IP limits, the observed exact `/32` proxy peer and one API instance. Three private readiness windows (**240+240+600 =1080 observations**) all passed, with maximum Terra lag 2 and BSC lag 17. The two final public windows passed **45/45 each**. Loki and direct Coolify logs show no ledger warnings after activation at 13:58:22Z; the old 13:56:21Z `rpc_missing_result` warning preceded activation. Verification: Rust **1.88.0**, locked dependencies and fresh guarded PostgreSQL16/SCRAM infrastructure; command `CARGO_BUILD_JOBS=2 scripts/verify-release.sh`. [Release13](https://git.cl8y.com/code/voting/src/branch/main/docs/verification/2026-10-14-release13.json) passed **74 unit/supervisor tests, 121 integration tests (66 ledger hardening), 245 schema-validated API responses and 146 frontend tests**, plus production build, both nginx checks and Gitleaks. Separate Chromium evidence: **11 UI regressions, nine hostile-rendering tests and one real second-origin protocol test**, using Node24.21.0, Playwright1.62.1 and Chromium151.0.7922.34. Release12 was a failed historical run; the expected late-supervisor-exit fixture race was corrected without weakening financial assertions, then passed four fresh-database repetitions and the full release13 rerun. Earlier public sampling recorded **one unexplained 503 at 14:00:58Z (44/45)**; the next check returned 200. It is retained in [final live evidence](https://git.cl8y.com/code/voting/src/branch/main/docs/verification/2026-10-14-final-live.json) rather than counted as a pass. The subsequent two extended public windows were fully green (**90/90**), alongside all 1080 private observations; acceptance is based on those final windows and does not claim transient failures are impossible. The API retains its separate restricted database role; fresh/upgrade migrations, real restricted-login and inherited/function/column privilege regressions are included in the integration evidence. No arbitrary debug/wire-log safety, exhaustive route×status Cartesian matrix, funded-holder or hardware/mobile-wallet/real-Legal QA is claimed. The isolated browser Legal hatch does not weaken production Legal gating. All required issue criteria are checked against the evidence above; no mandatory verification item remains open. Final evidence is published through [PR #60](https://git.cl8y.com/code/voting/pulls/60). All mandatory body/comment criteria are satisfied by the checklist above; closing this issue.
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/voting#41
No description provided.