Skip to content

fix: restrict OTC bridge CORS origins#5061

Closed
galpetame wants to merge 1 commit into
Scottcjn:mainfrom
galpetame:codex-fix-otc-cors-5054
Closed

fix: restrict OTC bridge CORS origins#5061
galpetame wants to merge 1 commit into
Scottcjn:mainfrom
galpetame:codex-fix-otc-cors-5054

Conversation

@galpetame

@galpetame galpetame commented May 13, 2026

Copy link
Copy Markdown
Contributor

Fixes #5054.

Summary

  • Replaces the wildcard CORS(app) setup in the OTC bridge with an explicit allow-list.
  • Adds OTC_CORS_ORIGINS so production/deployment origins can be configured without code changes.
  • Defaults to the restricted public origins https://bottube.ai and https://rustchain.org.
  • Ignores * if it is accidentally included and falls back to the restricted public origins.
  • Adds regression tests for rejected untrusted origins and accepted configured origins.
  • Adds parser-level regression tests for restricted defaults and wildcard env rejection.
  • Isolates the OTC bridge test DB per test so CORS/rate-limit tests do not leak state across cases.

Validation

  • python -m pytest .\otc-bridge\test_otc_bridge.py .\tests\test_otc_bridge_query_validation.py -q -> 32 passed
  • python -m py_compile .\otc-bridge\otc_bridge.py .\otc-bridge\test_otc_bridge.py .\tests\test_otc_bridge_query_validation.py
  • git diff --check
  • python .\tools\bcos_spdx_check.py --base-ref origin/main -> OK

Bounty contact

@galpetame
RTC wallet address: RTCe4fbe4c9085b8b2ed3f1228504de66799025f6ce

@508704820

Copy link
Copy Markdown
Contributor

Code Review: Restrict OTC bridge CORS origins

Summary

Same fix as #5054 (our bug report) — restricts CORS from wildcard * to configurable origins.

Both #5061 and #5063 fix the same issue. Either one works.

Review quality: Standard review

@galpetame galpetame force-pushed the codex-fix-otc-cors-5054 branch from 06b76ae to d177096 Compare May 13, 2026 09:45
@github-actions github-actions Bot added tests Test suite changes size/M PR: 51-200 lines and removed size/S PR: 11-50 lines labels May 13, 2026
@galpetame

Copy link
Copy Markdown
Contributor Author

Updated this PR to make the CORS fix stricter and easier to review:

  • Default origins are now only https://bottube.ai and https://rustchain.org.
  • Localhost/dev origins must be explicitly configured through OTC_CORS_ORIGINS.
  • * is still ignored if accidentally supplied.
  • Added focused parser-level regressions in tests/test_otc_bridge_query_validation.py.
  • Combined validation now passes:
    • python -m pytest .\otc-bridge\test_otc_bridge.py .\tests\test_otc_bridge_query_validation.py -q -> 32 passed
    • python -m py_compile .\otc-bridge\otc_bridge.py .\otc-bridge\test_otc_bridge.py .\tests\test_otc_bridge_query_validation.py
    • git diff --check
    • python .\tools\bcos_spdx_check.py --base-ref origin/main -> OK

@galpetame
RTC wallet address: RTCe4fbe4c9085b8b2ed3f1228504de66799025f6ce

@loganoe loganoe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The general CORS allowlist approach is good, and these checks passed:

  • git diff --check origin/main...HEAD
  • python3 -m py_compile otc-bridge/otc_bridge.py otc-bridge/test_otc_bridge.py tests/test_otc_bridge_query_validation.py
  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 /tmp/review-5058-venv/bin/python -m pytest tests/test_otc_bridge_query_validation.py otc-bridge/test_otc_bridge.py -q (32 passed)

Requesting changes because parse_cors_origins() only removes one wildcard. With OTC_CORS_ORIGINS="*, *, https://trusted.example", the parsed value is still ["*", "https://trusted.example"]; loading the real app with Flask-CORS then reflects an untrusted origin: a request from https://evil.example receives Access-Control-Allow-Origin: https://evil.example.

Please filter all wildcard entries while parsing, e.g. build the origins list with origin.strip() != "*", and add a regression test for duplicate wildcards so a malformed env var cannot silently restore permissive CORS.

@galpetame galpetame force-pushed the codex-fix-otc-cors-5054 branch from d177096 to 26e6476 Compare May 13, 2026 10:13
@galpetame

Copy link
Copy Markdown
Contributor Author

Fixed the duplicate-wildcard CORS parser blocker raised by @loganoe.

Root cause:

  • The previous parser removed only one * entry from OTC_CORS_ORIGINS.
  • A malformed env value such as *, *, https://trusted.example could leave one wildcard in the parsed origin list.
  • With Flask-CORS, that could still reflect an untrusted origin.

Fix:

  • parse_cors_origins() now filters all wildcard entries before passing the allow-list to Flask-CORS.
  • It logs when any wildcard entries are removed.
  • If the resulting list is empty, it falls back to the restricted public defaults.
  • Added a regression for duplicate wildcards: OTC_CORS_ORIGINS="*, *, https://trusted.example" now parses to only ["https://trusted.example"].

Validation:

  • python -m pytest .\otc-bridge\test_otc_bridge.py .\tests\test_otc_bridge_query_validation.py -q -> 33 passed
  • python -m py_compile .\otc-bridge\otc_bridge.py .\otc-bridge\test_otc_bridge.py .\tests\test_otc_bridge_query_validation.py
  • git diff --check
  • python .\tools\bcos_spdx_check.py --base-ref origin/main -> OK

@galpetame
RTC wallet address: RTCe4fbe4c9085b8b2ed3f1228504de66799025f6ce

@loganoe loganoe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed current head 26e647637981dd49ad68177819d72e3f1f6cf91f.

The implementation itself looks sound: it replaces wildcard CORS(app) with an explicit origin list, drops * from OTC_CORS_ORIGINS, falls back to the restricted public defaults when needed, and adds coverage for the parser and actual CORS response behavior. The test DB isolation change in otc-bridge/test_otc_bridge.py also fixes the cross-test state leak that would otherwise make the suite order-sensitive.

Validation run locally:

  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 timeout 180 uv run --no-project --with pytest --with flask --with flask-cors --with requests python -m pytest otc-bridge/test_otc_bridge.py tests/test_otc_bridge_query_validation.py -q -> 33 passed
  • python3 -m py_compile otc-bridge/otc_bridge.py otc-bridge/test_otc_bridge.py tests/test_otc_bridge_query_validation.py -> passed
  • git diff --check origin/main...HEAD -- otc-bridge/otc_bridge.py otc-bridge/test_otc_bridge.py tests/test_otc_bridge_query_validation.py -> clean
  • python3 tools/bcos_spdx_check.py --base-ref origin/main -> OK

No live bridge, wallet, private key, token transfer, or destructive traffic was used.

One process note: this overlaps the later PR #5104 for the same #5054 wildcard-CORS issue. I did not find a code-level blocker in this branch, but maintainers should merge only one canonical CORS fix path to avoid duplicate history and test churn.

@himanalot himanalot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the OTC bridge CORS restriction.

The PR replaces wildcard CORS with an explicit allowlist, defaults to the expected public origins, filters wildcard entries from OTC_CORS_ORIGINS, and passes the resulting list to Flask-CORS. The parser-level tests cover default and wildcard env cases, and the legacy OTC test additions verify an untrusted Origin is not echoed while https://rustchain.org is allowed. The per-test DB isolation also looks consistent with the existing module-level DB path usage.

I do not see a blocking issue in this PR.

@TJCurnutte TJCurnutte left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good from my pass.

The important part here is that CORS(app) is no longer an implicit allow-all policy. The new default is a narrow public-origin list, OTC_CORS_ORIGINS can extend it, and wildcard entries are stripped instead of accepted. That is the right fail-closed shape for this bridge surface.

Validation I ran:

git diff --check origin/main...HEAD -- otc-bridge/otc_bridge.py otc-bridge/test_otc_bridge.py tests/test_otc_bridge_query_validation.py
/tmp/rustchain-review-venv/bin/python -B -m py_compile otc-bridge/otc_bridge.py otc-bridge/test_otc_bridge.py tests/test_otc_bridge_query_validation.py
PYTHONPATH=otc-bridge /tmp/rustchain-review-venv/bin/python -B -m pytest \
  otc-bridge/test_otc_bridge.py::OTCBridgeTestCase::test_cors_rejects_untrusted_origin \
  otc-bridge/test_otc_bridge.py::OTCBridgeTestCase::test_cors_allows_configured_origin \
  tests/test_otc_bridge_query_validation.py::test_cors_defaults_to_restricted_public_origins \
  tests/test_otc_bridge_query_validation.py::test_cors_env_ignores_wildcard_origin \
  tests/test_otc_bridge_query_validation.py::test_cors_env_ignores_all_wildcard_origins -q

Result: 5 passed in 0.07s.

Focused Flask-client probe on the checked-out branch:

{
  'configured': ['https://bottube.ai', 'https://rustchain.org'],
  'evil_allow_origin': None,
  'trusted_allow_origin': 'https://rustchain.org',
  'evil_status': 200,
  'trusted_status': 200,
}

That proves an untrusted origin is not reflected while the configured RustChain origin still receives Access-Control-Allow-Origin. No blocker found.

@guangningsun

Copy link
Copy Markdown
Contributor

PR Review — Standard Quality ✓

PR: #5061 — Fix: restrict OTC bridge CORS origins

What I reviewed

  • otc-bridge/otc_bridge.py
  • otc-bridge/test_otc_bridge.py
  • tests/test_otc_bridge_query_validation.py (new test file)

Observations

  1. Adding explicit CORS origin restrictions instead of CORS(app) (wildcard) is a security improvement. Wildcard CORS allows any website to make requests to the OTC bridge API on behalf of users, which is dangerous for an API that handles financial transactions. Restricting to specific origins (DEFAULT_OTC_CORS_ORIGINS) ensures only approved frontends can access the API.

  2. The fix adds sqlite3 import and likely adds database query validation in the test file — this suggests the CORS change is part of a broader security hardening of the OTC bridge.

  3. New test file test_otc_bridge_query_validation.py covers input validation for OTC bridge queries, which is important for preventing SQL injection and other query-based attacks.

LGTM.

Bounty: #2782
Disclosure: I received RTC compensation for this review.

@Scottcjn

Copy link
Copy Markdown
Owner

Wave-5 cluster pay sent (4 RTC/PR). Please rebase against current main to clear conflicts — multiple wave-5 PRs touch overlapping files.

@jaxint jaxint left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review Summary

LGTM ✅ — Solid contribution to the RustChain ecosystem.

Code Quality

  • Implementation follows project conventions
  • Security considerations adequately addressed
  • Error handling appears robust

Testing

  • Test coverage is appropriate for the changes
  • Edge cases covered

Approved. Ready to merge. 🚀

@jaxint jaxint left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks for contributing. Approved.

@jaxint jaxint left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review

Approved

  • Code is correct
  • No obvious issues
  • Good contribution

Thanks! 🙏

Reviewed by jaxint

@Scottcjn

Copy link
Copy Markdown
Owner

Codex audit review (would award medium-tier (25 RTC) once fixed):

Issue: Winning OTC CORS fix: strict allowlist, wildcard stripping, and broader test coverage than #5063.

Address the specific point and ping for re-review.

@HCIE2054 HCIE2054 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed PR 5061. Standard review.

@HCIE2054 HCIE2054 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks for contributing!

@HCIE2054 HCIE2054 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@508704820 508704820 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Restrict OTC bridge CORS origins. Verify: (1) No wildcard (*) origin in production. (2) Allowed origins are explicitly listed. (3) Credentials mode is not enabled with wildcard origins. - Xeophon (security review, CORS)

@jaxint jaxint left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Great work on this PR. 🚀

@Scottcjn

Copy link
Copy Markdown
Owner

Closing as stale branch — would cause destructive deletions if merged.

Your branch was filed roughly 966 commits behind current main. Since then, many overlapping fixes from other contributors have landed via parallel PRs. GitHub's mergeable=clean indicator only catches text-level conflicts — it doesn't detect "this branch is so old that merging would silently delete code that landed after it was filed."

Bounty credit acknowledged

If your fix addressed a real bug, the canonical version has very likely already shipped via a parallel contributor's PR over the past two weeks. Specific cases covered by today's audit:

If you want fresh review

Rebase against current main and verify your diff shows roughly the size of the changes you originally made:

git fetch upstream main
git rebase upstream/main
git diff main..HEAD --shortstat

If the deletion count is significantly higher than what you added, the branch is still picking up stale assumptions — recreate from a fresh main.

Thanks for the contribution work this week.

@Scottcjn Scottcjn closed this May 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) size/M PR: 51-200 lines tests Test suite changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: OTC bridge uses wildcard CORS — any website can call trading API

9 participants