Skip to content

Validate bridge API field types#5689

Open
dazer1234 wants to merge 1 commit into
Scottcjn:mainfrom
dazer1234:codex-bridge-api-type-validation
Open

Validate bridge API field types#5689
dazer1234 wants to merge 1 commit into
Scottcjn:mainfrom
dazer1234:codex-bridge-api-type-validation

Conversation

@dazer1234
Copy link
Copy Markdown
Contributor

@dazer1234 dazer1234 commented May 18, 2026

Summary

  • Rejects non-object bridge requests, non-string text fields, and non-finite amount values with structured validation details.

Validation

  • py_compile and git diff --check passed; direct validator smoke check passed; pytest blocked locally by missing Flask in conftest.

Bounty reference: Scottcjn/rustchain-bounties#71 (Ongoing Bug Bounty Program)
Bounty fit: Input-validation bug: bridge API accepted invalid field types and non-finite amount values.
RTC payout wallet: RTC0a1c0ce2204390bc49ecf9780fe894da9dc3d92c

Implemented with OpenAI Codex assistance.

@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) node Node server related tests Test suite changes size/M PR: 51-200 lines labels May 18, 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!

@guangningsun
Copy link
Copy Markdown
Contributor

PR Review — PR #5689

What I reviewed:

  • node/bridge_api.py_text_payload_field(), _numeric_payload_field(), applied to bridge lock/ledger endpoints
  • tests/test_bridge_lock_ledger.pytest_rejects_non_object_payload(), test_rejects_non_string_route_fields()

Observations:

  1. _text_payload_field() returns a Tuple[Optional[str], Optional[str]] — the error string is the second element, following the same convention as other validators in this series
  2. The Decimal import now includes InvalidOperation — used implicitly by Decimal(str_value) when given non-numeric input, which raises the exception caught by the validator
  3. test_rejects_non_object_payload correctly tests ["not", "object"] (a list) as the JSON body — this is the key regression test for the _text_payload_field guard
  4. The validation pattern (if not isinstance(data, dict): return error) is applied at the top of each handler, before any field extraction — this is the correct order (structural validation before field validation)
  5. Tests properly use pytest-style assert result.ok is False — consistent with existing test file conventions

LGTM pending CI. Good regression coverage for the JSON type checking pattern.

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

@Auren-Innovation
Copy link
Copy Markdown

Code Review: PR #5689

Title: Validate bridge API field types
Review Type: Security-ADJ (Input Validation)
Recommended Reward: 15 RTC


Summary

Replaces raw data.get() and float() coercion in validate_bridge_request() with _text_payload_field() for string fields and Decimal-based parsing for amount_rtc. Adds bool-isinstance guard, finite-number check, and string-type validation for bridge_type and optional memo. Refactors initiate_bridge() to read validated details dict instead of raw data. Test coverage for non-object payloads, non-string route fields, non-finite amounts, and non-string optional fields.

Critical

None

Warning

  • amount_rtc validation (lines ~187-195): After the Decimal conversion and is_finite() check, the value is cast back to float(amount_decimal) before the positive check and storage. This re-introduces floating-point precision loss for large or high-precision amounts. The Decimal was used specifically to catch NaN/Infinity but the float cast discards the precision benefit. Consider keeping the value as Decimal through the validation and only converting to float at the storage boundary, or documenting the precision tradeoff.

  • _text_payload_field() rejects empty strings with "Missing required field", but the existing post-validation checks (e.g., len(source_address) < 10) also reject short-but-non-empty strings. The redundant short-circuit on empty string is fine functionally but the error message "Missing required field: source_address" for an empty-but-present field is misleading. The field was present, just invalid. Consider distinguishing "missing" from "empty" in error messages for debugging clarity.

  • bridge_type validation (lines ~203-206): bridge_type is fetched from data.get("bridge_type", "bottube") at line ~755 in initiate_bridge, but in the validator it is checked from data.get("bridge_type") which returns None when absent. The validator then checks isinstance(bridge_type, str) -- None fails this check. However, bridge_type is optional with a default. The validator should handle the absent case: if bridge_type key is missing from data, it should not raise a type error but should apply the default. Verify the validator's handling of this path is correct by checking whether data.get("bridge_type") returns None and the isinstance check fails, rejecting requests that omit bridge_type entirely.

  • The isinstance(data.get("amount_rtc"), bool) guard (line ~189) catches True/False but not bool subclasses. This is an extremely narrow edge case and the guard is correct for practical purposes, but note that isinstance(True, int) is True in Python, so the bare except (TypeError, ValueError, InvalidOperation) after the bool guard still needs to handle the case where Decimal(str(True)) yields Decimal('1') which is finite and positive -- meaning the bool guard is the only thing preventing True from being accepted as amount 1.0. This is correctly implemented but fragile; consider checking isinstance(value, (int, float)) and not isinstance(value, bool) as the positive type check instead of a negative exclusion.

Suggestion

  • The _text_payload_field helper returns a tuple (value, error) rather than raising an exception, which is inconsistent with the pattern in the other PRs (PR Validate OTC action request payloads #5691, Validate airdrop route JSON fields #5688, Validate OTC order request payloads #5687) that use raise ValueError. For consistency across the codebase, consider aligning on one pattern. The exception pattern is more concise for the try/except usage in route handlers.

  • The refactored initiate_bridge() now reads from validation.details instead of data, which is a good structural improvement. However, the bridge_type key in details may have different default behavior than the old data.get("bridge_type", "bottube") call. Confirm that ValidationResult.details includes the default "bottube" when the field is absent from the request.

Verdict

Approve - The validation improvements are substantive and well-structured. The bridge_type absent-key handling and the float precision concern should be verified before merge but do not block approval.


Review by Herr Amano | 2026-05-19
Wallet: 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

@crystal-tensor crystal-tensor left a comment

Choose a reason for hiding this comment

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

Solid validation hardening for the bridge API. The bool guard on amount_rtc and the finite number check are good security improvements. Left 2 inline comments — both non-blocking.

One suggestion: consider unifying the validation helpers with the OTC bridge's json_object_body/text_field pattern from PR #5691 to keep the codebase consistent.

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.

Reviewed head 8e528274228f9f7b335409957d586585e6fe3994.

What I checked:

git diff --check origin/main...HEAD -- node/bridge_api.py tests/test_bridge_lock_ledger.py
python3 -B -m py_compile node/bridge_api.py tests/test_bridge_lock_ledger.py
PYTHONPATH=. python3 -B -m pytest -q tests/test_bridge_lock_ledger.py --tb=short

Result: 45 passed, 1 warning in 1.94s; diff whitespace check and py_compile both passed.

The patch closes real validation gaps in the bridge lock path: non-object request bodies no longer fall through, required route fields are enforced as strings and normalized before downstream use, bool/NaN/non-finite amounts are rejected before float conversion, and optional bridge_type/memo values now get type checks. I also checked that initiate_bridge() consumes the normalized validation.details fields instead of the raw request values, so the route and validation layer stay aligned.

I’m leaving this as a review comment rather than an approval because GitHub currently reports the PR as CONFLICTING/REST mergeable_state=dirty; it needs a rebase/conflict resolution before it is merge-ready. I did not find a functional blocker in the tested head.

Copy link
Copy Markdown

@kongzi123 kongzi123 left a comment

Choose a reason for hiding this comment

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

代码审查:PR #5689 - Validate bridge API field types

作者: dazer1234 | 文件: 2个(+103/-20)| 状态: Open


🔍 根本原因分析

原始 validate_bridge_request() 函数存在类型宽松漏洞

  1. 入口类型检查不足if not data: 仅检查 falsy 值(如 None{}[]),但 data 可能为非字典类型(列表、字符串等),后续 data.get() 调用将抛出 AttributeError
  2. 字段类型信任过深:字符串字段(directionsource_chain 等)直接调用 .lower(),若收到 dict/list/bool 将触发 AttributeError
  3. 金额解析过于宽松float(data.get("amount_rtc", 0)) 可接受 "NaN""inf""-inf" 及布尔值(float(True)==1.0
  4. 使用未验证数据initiate_bridge()data["direction"] 直接取原始输入而非 validation.details

✅ 修复质量评估 — 优秀

改进项 评价
新增 _text_payload_field() 辅助函数 ✅ 抽取公共逻辑,代码复用好
入口 isinstance(data, dict) 检查 ✅ 正确拦截非字典载荷
布尔值前置拦截 (isinstance(..., bool)) ✅ 防止 float(True) 绕过
Decimal + is_finite() 检查 ✅ 拒绝 NaN/Inf,保证精度
可选字段类型检查 (bridge_typememo) ✅ 防御完整
validation.details 替代原始 data ✅ 修复数据源不一致问题

🔒 安全评估

整体安全加固效果:显著提升

  • ✅ 阻止类型混淆攻击(传递 {"direction": {"op": "deposit"}} 等)
  • ✅ 阻止 NaN/Inf 金额(可能导致除零/数值溢出)
  • ✅ 阻止布尔值作为金额(True 曾被解析为 1.0
  • ⚠️ 轻微问题:memo=Noneif memo and len(memo) > 256 跳过检查,但后续 memo=details.get("memo") 可能传 None 给数据库字段,建议显式 bridge_type = details.get("bridge_type") or "bottube" 更明确

🧪 测试覆盖评估

覆盖率:良好,新增4个边界用例:

测试用例 覆盖场景
test_rejects_non_object_payload 列表/非字典入口
test_rejects_non_string_route_fields 字段类型混淆
test_rejects_non_finite_amount NaN/Inf 金额
test_rejects_non_string_optional_fields 可选字段类型

建议补充:

  • amount_rtc=True/False 的显式测试
  • bridge_type=123(数值类型)测试
  • source_chain=""(空字符串)测试

💬 技术问题

问题: Decimal(str(data.get("amount_rtc", 0))) 的处理方式存在精度丢失风险。当用户传入科学计数法字符串(如 "1e18")时,str(1e18) 会先被 Python 转换为 "1e+18",而 Decimal("1e+18") 是合法且精确的。

但是,若传入 {"amount_rtc": 1.0000000000000001}(浮点),则 str() 会丢失精度。这是否是预期行为?还是应直接用 Decimal(data.get("amount_rtc", 0))


📝 总结

推荐:Approve

该 PR 有效填补了桥接 API 的类型验证漏洞,修复方案设计合理、测试覆盖到位。建议修复上述轻微建议后合并。

@wybbb123123
Copy link
Copy Markdown

The new Decimal validation closes the obvious NaN / boolean cases, but it still accepts finite Decimal inputs that overflow when converted back to float.

Current path in validate_bridge_request():

amount_decimal = Decimal(str(data.get("amount_rtc", 0)))
...
if not amount_decimal.is_finite():
    return ValidationResult(ok=False, error="amount_rtc must be a finite number")
amount_rtc = float(amount_decimal)

Decimal("1e309") is finite, so it passes is_finite(). But float(Decimal("1e309")) becomes inf, and the function returns ok=True with details["amount_rtc"] == inf.

Minimal reproduction on this PR branch:

from node.bridge_api import validate_bridge_request

payload = {
    "direction": "withdraw",
    "source_chain": "base",
    "dest_chain": "rustchain",
    "source_address": "0x1234567890123456789012345678901234567890",
    "dest_address": "RTC1234567890",
    "amount_rtc": "1e309",
}

result = validate_bridge_request(payload)
assert result.ok is True
assert result.details["amount_rtc"] == float("inf")

That value then reaches create_bridge_transfer(), where int(Decimal(str(request.amount_rtc)) * BRIDGE_UNIT) raises OverflowError: cannot convert Infinity to integer. The exception is not caught by the sqlite3.Error handler, so a malformed but syntactically valid amount can still become a 500 instead of a 400 validation error.

Suggested fix: keep the amount as Decimal until after range checks, enforce an explicit maximum representable bridge amount before any float conversion, or convert directly to micro-units from the Decimal and reject non-finite/overflowing results. Add a regression test for a huge finite value such as "1e309" or a long digit string.

AI assistance disclosure: OpenAI Codex / GPT-5 was used to inspect the diff and prepare this review; the finding was verified with a local reproduction on the PR branch.

@JeremyZeng77
Copy link
Copy Markdown

Review notes for #5689

I ran the focused bridge lock/ledger test file on the PR head:

git fetch origin pull/5689/head:review-pr-5689-fresh
git switch review-pr-5689-fresh
python -B -m pytest -q tests/test_bridge_lock_ledger.py --tb=short -p no:cacheprovider
45 passed, 1 warning in 5.32s

The added validation coverage is useful: non-object payloads, non-string route fields, non-finite amount values, and optional string fields are all exercised. I did not find a new blocker beyond the already-noted numeric overflow edge case in the existing review thread. If that is addressed, the tested bridge-lock surface looks healthy from this pass.

Copy link
Copy Markdown

@crystal-tensor crystal-tensor left a comment

Choose a reason for hiding this comment

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

Approved: security hardening / bug fix.

Copy link
Copy Markdown
Contributor

@BossChaos BossChaos left a comment

Choose a reason for hiding this comment

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

Code Review — Validate bridge API field types

✅ Strengths

  • Type validation for bridge API
  • Consistent with the validation pattern seen in other PRs
  • 214 lines changed (105 additions)

⚠️ Issues

1. Validation Coverage
The diff validates the structure but doesn't test:

  • Edge cases (empty strings, null values, oversized payloads)
  • Error messages for invalid input

2. Test Coverage
No tests included. Should add test cases for:

  • Valid input (should pass)
  • Invalid input (should reject with clear error)
  • Boundary conditions

📋 Summary

Good contribution. Security importance: MEDIUM. Suggested: 8-10 RTC.

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) node Node server related size/M PR: 51-200 lines tests Test suite changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.