Skip to content

fix: validate OTC order creation payloads#5559

Closed
kekehanshujun wants to merge 1 commit into
Scottcjn:mainfrom
kekehanshujun:codex/otc-order-ttl-validation-5525
Closed

fix: validate OTC order creation payloads#5559
kekehanshujun wants to merge 1 commit into
Scottcjn:mainfrom
kekehanshujun:codex/otc-order-ttl-validation-5525

Conversation

@kekehanshujun
Copy link
Copy Markdown

Summary

  • reject non-object JSON bodies on POST /api/orders with a stable JSON 400
  • validate ttl_seconds as an actual JSON integer before order creation
  • keep missing ttl_seconds defaulting to the configured OTC order TTL and preserve min/max clamping

Fixes #5525.

Validation

  • python -B -m pytest -q tests/test_otc_bridge_query_validation.py
  • python -B -m py_compile otc-bridge/otc_bridge.py tests/test_otc_bridge_query_validation.py
  • git diff --check -- otc-bridge/otc_bridge.py tests/test_otc_bridge_query_validation.py

Bounty

RTC wallet: RTC02811ff5e2bb4bb4b95eee44c5429cd9525496e7

@github-actions github-actions Bot added BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) tests Test suite changes size/M PR: 51-200 lines labels May 17, 2026
Copy link
Copy Markdown

@LubuSeb LubuSeb left a comment

Choose a reason for hiding this comment

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

Reviewed head 4f590c0359755ecd7d6382118c85d36a14267a66.

This is a focused fix for the order-create validation gap from #5525. The route now distinguishes an absent/malformed JSON body from non-object JSON, rejects arrays/scalars before the first data.get(...), and parses ttl_seconds through a helper that rejects strings, floats, bools, and explicit nulls before order creation. Missing ttl_seconds still defaults to ORDER_TTL_DEFAULT, and accepted integer TTLs keep the existing min/max clamp.

Validation performed on the PR checkout:

git diff --check origin/main...HEAD -- otc-bridge/otc_bridge.py tests/test_otc_bridge_query_validation.py
python -B -m py_compile otc-bridge/otc_bridge.py tests/test_otc_bridge_query_validation.py
.\.venv\Scripts\python.exe -B -m pytest -q tests/test_otc_bridge_query_validation.py --tb=short --noconftest
# 15 passed

I also checked the changed route manually against the diff: the new object guard runs before side, pair, wallet, and amount validation, and the new tests cover the two reported 500 paths plus a valid integer TTL success path. No blocker found.

Copy link
Copy Markdown

@strongkeep-debug strongkeep-debug left a comment

Choose a reason for hiding this comment

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

Approved. I re-checked current head 4f590c0 specifically against the #5525 OTC order creation regressions.

What I verified:

  • Non-object JSON for POST /api/orders now returns 400 {"error":"JSON object required"} before the route tries to read order fields.
  • ttl_seconds now rejects non-integer JSON values, including strings, floats, and booleans, instead of coercing them through int(...).
  • Integer TTLs still work: values below 3600 clamp to 1 hour, 7200 yields 2 hours, and omitted ttl_seconds keeps the configured default.
  • The change is scoped to create_order() plus focused regressions in tests/test_otc_bridge_query_validation.py.

Local validation:

git diff --check origin/main...HEAD -- otc-bridge/otc_bridge.py tests/test_otc_bridge_query_validation.py
# passed

python -B -m py_compile otc-bridge/otc_bridge.py tests/test_otc_bridge_query_validation.py
# passed

python -m pytest -q tests/test_otc_bridge_query_validation.py
# 15 passed

I also ran a direct Flask client probe for the affected endpoint covering non-object JSON, string/float/bool TTL rejection, integer TTL clamping, and default TTL preservation.

Copy link
Copy Markdown

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown

@TJCurnutte TJCurnutte left a comment

Choose a reason for hiding this comment

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

Approved. I focused this on the POST /api/orders validation path from #5525 / the recent OTC TTL regressions.

Validation performed on head 4f590c0359755ecd7d6382118c85d36a14267a66:

git diff --check origin/main...HEAD -- otc-bridge/otc_bridge.py tests/test_otc_bridge_query_validation.py
python3 -B -m py_compile otc-bridge/otc_bridge.py tests/test_otc_bridge_query_validation.py
uv run --no-project --with pytest --with flask --with flask-cors --with requests \
  python -B -m pytest -q -o addopts='' --noconftest tests/test_otc_bridge_query_validation.py

The focused pytest run passed with 15 passed in 1.45s.

I also ran a Flask test-client probe against a temp DB after init_db(). That confirmed:

  • JSON array body now returns 400 with {"error":"JSON object required"}.
  • ttl_seconds values of 'abc', 1.5, true, and null all return 400 with {"error":"ttl_seconds must be an integer"}.
  • missing ttl_seconds still creates an order with the default 168.0 hour expiry.
  • integer 7200 still creates an order with 2.0 hours.
  • integer clamping remains intact: 0 clamps to 1.0 hour and an oversized integer clamps to 720.0 hours.

The important bit is that order_ttl_seconds() now rejects bools explicitly before Python's bool-is-an-int edge case, rejects non-integer JSON numbers/strings/null before order creation, and the route rejects non-object JSON before .get() calls. That closes the coercion/500 path without weakening the existing min/max TTL behavior.

Copy link
Copy Markdown

@2balmprune 2balmprune left a comment

Choose a reason for hiding this comment

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

Reviewed head 4f590c0359755ecd7d6382118c85d36a14267a66 against the #5525 OTC order-creation bug report.

This fixes the two reported 500 paths: POST /api/orders now rejects non-object JSON before any .get(...) calls, and ttl_seconds is validated as an actual JSON integer before order creation. I also checked the Python bool edge case: booleans are rejected explicitly rather than being accepted as integers. Missing ttl_seconds still falls back to the configured default, and valid integer TTLs keep the existing min/max clamp behavior.

Validation performed:

  • git diff --check origin/main...HEAD -- otc-bridge/otc_bridge.py tests/test_otc_bridge_query_validation.py
  • python -B -m py_compile otc-bridge/otc_bridge.py tests/test_otc_bridge_query_validation.py
  • python -B -m pytest -q tests/test_otc_bridge_query_validation.py -> 15 passed
  • Direct Flask client probe for JSON array body, string ttl_seconds, and integer ttl_seconds=7200

No blocker found.

Copy link
Copy Markdown

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown
Contributor

@jaxint jaxint left a comment

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. 🚀

Copy link
Copy Markdown

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown

@iamdinhthuan iamdinhthuan left a comment

Choose a reason for hiding this comment

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

Reviewed current head 4f590c0359755ecd7d6382118c85d36a14267a66 for the #5525 OTC order creation validation fix. Approving.

The patch now rejects non-object JSON before the /api/orders handler reaches any .get(...) calls, while still returning JSON body required for a missing/malformed body. The new order_ttl_seconds() helper also closes the Python coercion edge cases: strings, floats, booleans, and explicit null values are rejected before order creation, while missing ttl_seconds keeps the configured 7-day default and valid integers still use the existing min/max clamp.

Validation run on this head:

git diff --check origin/main...HEAD -- otc-bridge/otc_bridge.py tests/test_otc_bridge_query_validation.py
python3 -B -m py_compile otc-bridge/otc_bridge.py tests/test_otc_bridge_query_validation.py
python3 tools/bcos_spdx_check.py --base-ref origin/main
# BCOS SPDX check: OK
python3 -B -m pytest -q tests/test_otc_bridge_query_validation.py --noconftest
# 15 passed

Direct Flask probe with a temp DB:

array_json -> 400 {'error': 'JSON object required'}
ttl_string -> 400 {'error': 'ttl_seconds must be an integer'}
ttl_float -> 400 {'error': 'ttl_seconds must be an integer'}
ttl_bool -> 400 {'error': 'ttl_seconds must be an integer'}
ttl_null -> 400 {'error': 'ttl_seconds must be an integer'}
ttl_missing -> 201, expires_in_hours=168.0
ttl_zero_clamped -> 201, expires_in_hours=1.0
ttl_7200 -> 201, expires_in_hours=2.0

No private keys, tokens, wallet secrets, production services, live wallet actions, withdrawals, or fund movement were used.

Copy link
Copy Markdown

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Copy Markdown

@HCIE2054 HCIE2054 left a comment

Choose a reason for hiding this comment

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

LGTM!

@Scottcjn
Copy link
Copy Markdown
Owner

Closing as duplicate of #5526 (canonical first-poster on this issue per Codex forensic audit 2026-05-18). Both PRs target the same root cause: OTC order ttl_seconds + JSON-object validation. Bounty credit goes to #5526. Thanks for the contribution — for next time, please check open PRs against the same issue before filing.

@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 order creation 500s on non-object JSON and invalid ttl_seconds

9 participants