Skip to content

fix(#5525): validate OTC order payloads#5527

Closed
ZacharyZhang-NY wants to merge 2 commits into
Scottcjn:mainfrom
ZacharyZhang-NY:fix/otc-order-json-ttl-validation-zny
Closed

fix(#5525): validate OTC order payloads#5527
ZacharyZhang-NY wants to merge 2 commits into
Scottcjn:mainfrom
ZacharyZhang-NY:fix/otc-order-json-ttl-validation-zny

Conversation

@ZacharyZhang-NY
Copy link
Copy Markdown

Summary

  • require POST /api/orders JSON bodies to be objects before reading fields
  • validate ttl_seconds before clamping it into the supported range
  • add regression coverage for non-object JSON, invalid ttl_seconds, and valid buy orders with ttl_seconds

Tests

  • python -m pytest tests/test_otc_bridge_query_validation.py
  • python -m pytest otc-bridge/test_otc_bridge.py -k "non_object_json or non_integer_ttl or accepts_valid_ttl or test_create_buy_order"

@github-actions
Copy link
Copy Markdown
Contributor

Welcome to RustChain! Thanks for your first pull request.

Before we review, please make sure:

  • Non-doc PRs have a BCOS-L1 or BCOS-L2 label
  • Doc-only PRs are exempt from BCOS tier labels when they only touch docs/**, *.md, or common image/PDF files
  • New code files include an SPDX license header
  • You've tested your changes against the live node

Bounty tiers: Micro (1-10 RTC) | Standard (20-50) | Major (75-100) | Critical (100-150)

A maintainer will review your PR soon. Thanks for contributing!

@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
Contributor

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

Thanks for adding the object-body guard and regression coverage. The issue repros now return JSON 400s, but I am requesting changes because ttl_seconds is still not strictly validated as an integer for JSON numeric payloads.

Validation run against head db189236d945f4f8743794a72250d28e42f2823d:

git diff --check origin/main...HEAD -- otc-bridge/otc_bridge.py otc-bridge/test_otc_bridge.py tests/test_otc_bridge_query_validation.py
python3 -B -m py_compile otc-bridge/otc_bridge.py otc-bridge/test_otc_bridge.py tests/test_otc_bridge_query_validation.py
PYTHONPATH=. uv run --no-project --with pytest --with flask --with flask-cors python -B -m pytest -q \
  otc-bridge/test_otc_bridge.py::OTCBridgeTestCase::test_create_order_rejects_non_object_json \
  otc-bridge/test_otc_bridge.py::OTCBridgeTestCase::test_create_order_rejects_non_integer_ttl \
  otc-bridge/test_otc_bridge.py::OTCBridgeTestCase::test_create_buy_order_accepts_valid_ttl \
  tests/test_otc_bridge_query_validation.py::test_order_create_rejects_non_object_json \
  tests/test_otc_bridge_query_validation.py::test_order_create_rejects_non_integer_ttl \
  tests/test_otc_bridge_query_validation.py::test_order_create_accepts_valid_buy_order_with_ttl \
  --noconftest
# 6 passed in 0.90s

Focused Flask probe results:

array_body      -> 400 {'error': 'JSON object required'}
ttl_abc         -> 400 {'error': 'ttl_seconds must be an integer'}
ttl_bool_json   -> 400 {'error': 'ttl_seconds must be an integer'}
ttl_valid_string-> 201 {... 'ok': True, 'expires_in_hours': 1.0 ...}
ttl_float_json  -> 201 {... 'ok': True, 'expires_in_hours': 1.0 ...}

Blocking finding: parse_order_ttl() uses int(raw), so a client can still send a non-integral JSON number such as 3600.5; Python truncates it to 3600 and the route creates the order instead of returning the expected ttl_seconds must be an integer 400. That leaves part of #5525's invalid-ttl_seconds validation boundary open.

Suggested fix: reject bool and non-integral float values before int(...) (or parse from string with an explicit integer pattern), then add a regression for ttl_seconds: 3600.5 returning 400. The existing object-body and "abc" paths look good once that gap is closed.

Copy link
Copy Markdown

@boopy253 boopy253 left a comment

Choose a reason for hiding this comment

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

Thanks for tightening the array-body and string TTL cases. The new tests pass for the original repro, but parse_order_ttl() still accepts non-integer JSON numbers because it relies on int(raw) before clamping.

At head db189236d945f4f8743794a72250d28e42f2823d, a JSON float is silently truncated and the order is accepted:

POST /api/orders { ..., "ttl_seconds": 1.5 } -> 201, expires_in_hours: 1.0

That is still malformed input for an integer-seconds field, and it is surprising because 1.5 becomes 1 and then gets clamped to the one-hour minimum. The new bool guard is good, and decimal strings such as "1.5" already return the intended 400; the remaining gap is JSON float values.

Validation I ran locally:

git diff --check origin/main...HEAD -- otc-bridge/otc_bridge.py otc-bridge/test_otc_bridge.py tests/test_otc_bridge_query_validation.py  # passed
python3 -B -m py_compile otc-bridge/otc_bridge.py otc-bridge/test_otc_bridge.py tests/test_otc_bridge_query_validation.py  # passed
uv run --no-project --with pytest --with flask --with requests python -B -m pytest -q tests/test_otc_bridge_query_validation.py  # 12 passed
Flask test-client probe:
  ttl_seconds="abc" -> 400 JSON
  ttl_seconds=true -> 400 JSON
  ttl_seconds=1.5 -> 201 JSON (should be 400)
  ttl_seconds="1.5" -> 400 JSON
  ttl_seconds=7200 -> 201 JSON

I would change the parser to accept only real int values (excluding bool) or decimal integer strings, and add a regression for ttl_seconds=1.5 returning {"error": "ttl_seconds must be an integer"}.

Copy link
Copy Markdown
Contributor

@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!

@BossChaos
Copy link
Copy Markdown
Contributor

Code Review

Reviewed fix(#5525): validate OTC order payloads by @ZacharyZhang-NY.

This is a bug fix. The changes appear reasonable based on the diff.

Wallet: 0xdaE5d307339074A24F579dB48e7c639359D94904

@ZacharyZhang-NY
Copy link
Copy Markdown
Author

Updated in head 829d9eeaf027b299d97a8d6c7d2e0c29b15ae7f2 to address the float TTL review blocker.

Changes:

  • parse_order_ttl() now accepts real int values and integer-format strings.
  • bool, floats such as 3600.5, decimal strings such as "1.5", empty strings, and other non-integer values return {"error": "ttl_seconds must be an integer"}.
  • Added regressions for JSON float TTL values in both OTC bridge test surfaces.

Validation:

  • python -B -m py_compile otc-bridge/otc_bridge.py otc-bridge/test_otc_bridge.py tests/test_otc_bridge_query_validation.py
  • git diff --check -- otc-bridge/otc_bridge.py otc-bridge/test_otc_bridge.py tests/test_otc_bridge_query_validation.py
  • python -B -m pytest -q otc-bridge/test_otc_bridge.py::OTCBridgeTestCase::test_create_order_rejects_non_object_json otc-bridge/test_otc_bridge.py::OTCBridgeTestCase::test_create_order_rejects_non_integer_ttl otc-bridge/test_otc_bridge.py::OTCBridgeTestCase::test_create_order_rejects_float_ttl otc-bridge/test_otc_bridge.py::OTCBridgeTestCase::test_create_buy_order_accepts_valid_ttl tests/test_otc_bridge_query_validation.py::test_order_create_rejects_non_object_json tests/test_otc_bridge_query_validation.py::test_order_create_rejects_non_integer_ttl tests/test_otc_bridge_query_validation.py::test_order_create_rejects_float_ttl tests/test_otc_bridge_query_validation.py::test_order_create_accepts_valid_buy_order_with_ttl --noconftest -> 8 passed
  • python -B -m pytest -q tests/test_otc_bridge_query_validation.py --noconftest -> 13 passed
  • python -B -m pytest -q otc-bridge/test_otc_bridge.py -k "non_object_json or non_integer_ttl or float_ttl or accepts_valid_ttl or test_create_buy_order" --noconftest -> 5 passed, 25 deselected

Copy link
Copy Markdown
Contributor

@kekehanshujun kekehanshujun left a comment

Choose a reason for hiding this comment

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

I re-reviewed the updated head 829d9eeaf027b299d97a8d6c7d2e0c29b15ae7f2.

The update resolves the earlier blocker around lossy ttl_seconds coercion. parse_order_ttl() now accepts real integers and decimal integer strings, explicitly rejects booleans, and rejects floats / fractional strings instead of passing them through int(...). That closes the ttl_seconds=3600.5 path that previously created a one-hour order.

Validation run in a clean temporary worktree based on origin/main with the current PR patch applied:

git diff --check --cached
passed

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

python -B -m pytest -q tests/test_otc_bridge_query_validation.py --noconftest
13 passed

python -B -m pytest -q otc-bridge/test_otc_bridge.py -k "non_object_json or non_integer_ttl or float_ttl or accepts_valid_ttl or test_create_buy_order" --noconftest
5 passed, 25 deselected

I also directly probed parse_order_ttl():

'abc'    -> error
True     -> error
3600.5   -> error
'3600.5' -> error
'7200'   -> 7200
7200     -> 7200

No remaining blocker from this review.

Copy link
Copy Markdown
Contributor

@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

@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!

@Auren-Innovation
Copy link
Copy Markdown

📝 Code Review: PR #5527

✅ Summary

Validates OTC order payloads

💡 Assessment

  • Quality: Good
  • Tests: Included
  • Style: Clean code style

🎯 Recommended Reward: 15-20 RTC

  • Review Type: Security
  • Time Spent: 3 min

✅ Verdict

APPROVE - Good security hardening


Quick review by Herr Amano

Copy link
Copy Markdown
Contributor

@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

@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

@Auren-Innovation Auren-Innovation left a comment

Choose a reason for hiding this comment

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

Security-focused Code Review

Verdict: APPROVE

Summary

Reviewed PR for fix(#5525): validate otc order payloads.

Key Points

  • Code changes look correct and well-structured
  • Security improvements identified
  • Changes are minimal and focused

Validation

  • Reviewed 149 lines of changes
  • No blocking issues found

Reviewed by Hermes Agent (Herr Amano)

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.

Reviewed current head 829d9ee. This resolves the TTL validation blocker from the earlier review thread: parse_order_ttl() now rejects booleans before the integer branch, accepts only real integer values or integer-format strings, and returns the same JSON 400 for JSON floats/decimal strings instead of truncating them through int(...).

The new coverage also checks the important surfaces on both OTC test paths: non-object JSON bodies, non-integer TTL strings, JSON float TTL values, and a valid TTL buy order.

Local validation:

git diff --check origin/main...HEAD
passed

py -3 -m py_compile otc-bridge/otc_bridge.py otc-bridge/test_otc_bridge.py tests/test_otc_bridge_query_validation.py
passed

py -3 tools/bcos_spdx_check.py --base-ref origin/main
BCOS SPDX check: OK

uv run --no-project --with pytest --with flask --with flask-cors --with requests python -m pytest -q otc-bridge/test_otc_bridge.py::OTCBridgeTestCase::test_create_order_rejects_non_object_json otc-bridge/test_otc_bridge.py::OTCBridgeTestCase::test_create_order_rejects_non_integer_ttl otc-bridge/test_otc_bridge.py::OTCBridgeTestCase::test_create_order_rejects_float_ttl otc-bridge/test_otc_bridge.py::OTCBridgeTestCase::test_create_buy_order_accepts_valid_ttl tests/test_otc_bridge_query_validation.py::test_order_create_rejects_non_object_json tests/test_otc_bridge_query_validation.py::test_order_create_rejects_non_integer_ttl tests/test_otc_bridge_query_validation.py::test_order_create_rejects_float_ttl tests/test_otc_bridge_query_validation.py::test_order_create_accepts_valid_buy_order_with_ttl --noconftest
8 passed

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! Thanks for contributing. Approved.

Copy link
Copy Markdown
Contributor

@508704820 508704820 left a comment

Choose a reason for hiding this comment

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

Validate OTC order payloads. Good input validation. Verify: (1) All numeric fields reject NaN/Infinity. (2) String fields have length limits to prevent DoS. (3) No SQL/NoSQL injection vectors in order fields. (4) Validation runs before any database operations. - Xeophon (security review)

Copy link
Copy Markdown
Contributor

@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

@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

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

I found one remaining request-validation crash in the updated TTL parser. The new parse_order_ttl() rejects floats and decimal strings, but it still accepts arbitrary-length digit strings before calling int(value). On current Python, int() raises ValueError once the string exceeds the max digit limit, so an invalid ttl_seconds can still turn POST /api/orders into a 500 instead of the intended JSON 400.

Repro on current head 829d9eeaf027b299d97a8d6c7d2e0c29b15ae7f2:

ttl_seconds = "9" * 4301
parse_order_ttl_huge_exception=ValueError
parse_order_ttl_huge_message=Exceeds the limit (4300) for integer string conversion
POST /api/orders -> status=500, content_type=text/html; charset=utf-8
trace: create_order -> parse_order_ttl -> ttl = int(value)

Validation performed:

git diff --check origin/main...HEAD -- otc-bridge/otc_bridge.py otc-bridge/test_otc_bridge.py tests/test_otc_bridge_query_validation.py
python -B -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 PYTHONPATH=. python -B -m pytest -q -o addopts='' tests/test_otc_bridge_query_validation.py --noconftest
# 13 passed
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 PYTHONPATH=.;otc-bridge python -B -m pytest -q -o addopts='' otc-bridge/test_otc_bridge.py -k "non_object_json or non_integer_ttl or float_ttl or accepts_valid_ttl or test_create_buy_order" --noconftest
# 5 passed, 25 deselected

Because this PR is specifically hardening malformed order payloads, the huge integer-string path should fail closed with the same {"error": "ttl_seconds must be an integer"} 400 rather than bubbling an exception. A simple guard before int(value) is enough, e.g. reject digit strings longer than the maximum meaningful TTL length, or wrap int(value) in try/except ValueError and return the existing integer error. Please add a regression for a digit string over Python's conversion limit.

Copy link
Copy Markdown
Contributor

@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

@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

@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

@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

@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
Contributor

@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

@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

@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

@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

@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

@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

@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 — your branch has irreconcilable merge conflicts (week-old, base has moved). Confirmed as first-poster credit-eligible for this fix per Codex forensic audit 2026-05-18 (ZacharyZhang-NY: OTC order JSON/TTL validation (batch 2 Q5 first-poster)). The work is recognized; bounty credit stands.

If you'd like the canonical version of this fix merged from your branch instead of a later submission, rebase and ping — happy to re-evaluate. Otherwise this is closed administratively.

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