fix: validate Airdrop V2 public route payloads#5565
Conversation
|
Welcome to RustChain! Thanks for your first pull request. Before we review, please make sure:
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! |
kekehanshujun
left a comment
There was a problem hiding this comment.
Reviewing this as part of the RustChain code review bounty.
This PR still has the same crash class it is trying to fix because the route handlers only check if not data, not that data is a JSON object. A truthy JSON array reaches text_field(data, ...), where data.get(...) raises AttributeError; that means /api/airdrop/eligibility, /api/airdrop/claim, and /api/bridge/lock can still return a 500 for malformed but valid JSON.
Please add an isinstance(data, dict) guard before calling text_field()/wrtc_amount_field() and add a regression for a JSON array body on at least one public route.
There is a second type issue in wrtc_amount_field: float(True) becomes 1.0, so amount_wrtc: true is accepted as a positive amount. Since booleans are not numeric user amounts, reject bool explicitly before float conversion and cover it in the invalid amount cases.
TJCurnutte
left a comment
There was a problem hiding this comment.
Reviewed PR #5565 at head 374722f04529993e5255db2277c9c60bf8c6e056.
The added string/amount field validation is moving in the right direction, and the new focused tests pass:
git diff --check origin/main...HEAD -- node/airdrop_v2.py node/test_airdrop_v2_route_validation.py
uv run --no-project --with pytest --with flask --with requests --with pynacl --with psutil python -B -m py_compile node/airdrop_v2.py node/test_airdrop_v2_route_validation.py
uv run --no-project --with pytest --with flask --with requests --with pynacl --with psutil python -B -m pytest -q -o addopts='' --noconftest node/test_airdrop_v2_route_validation.py
# 2 passed, 10 subtests passedI still found two malformed public-payload paths that should be fixed before merge:
- The three public POST routes still accept a non-object JSON body into
dataand then calldata.get(...). A non-empty array body reaches that path and returns a 500 instead of a controlled 400.
Focused Flask probe against /api/airdrop/eligibility, /api/airdrop/claim, and /api/bridge/lock with json=[1] produced:
eligibility_top_array status=500 ... AttributeError: 'list' object has no attribute 'get'
claim_top_array status=500 ... AttributeError: 'list' object has no attribute 'get'
bridge_top_array status=500 ... AttributeError: 'list' object has no attribute 'get'
Given this PR is hardening public route payload validation, I would add an explicit isinstance(data, dict) guard after request.get_json(silent=True) and cover non-empty array/string JSON bodies in the regression tests.
amount_wrtc: trueis still accepted by/api/bridge/lockbecausefloat(True) == 1.0, creating a 1 wRTC bridge lock:
bridge_bool_amount status=200 body={"lock":{"amount_uwrtc":1000000,"amount_wrtc":1.0,...}}
That boolean-as-number coercion should be rejected for the same reason the patch rejects nan, inf, objects, and arrays.
|
Addressed the route validation review in commit Added regressions for:
Validation run on the PR branch: python3 -m pytest -q node/test_airdrop_v2_route_validation.py node/test_airdrop_v2.py tests/test_airdrop_bridge_admin_auth.py
python3 -m py_compile node/airdrop_v2.py node/test_airdrop_v2_route_validation.py
git diff --checkResult: |
ZacharyZhang-NY
left a comment
There was a problem hiding this comment.
Reviewed the Airdrop V2 public route payload validation changes at head dd40d72. The patch validates JSON object shape and string fields before .strip() usage across /api/airdrop/eligibility, /api/airdrop/claim, and /api/bridge/lock, and it rejects non-finite, boolean, structured, and non-positive amount_wrtc values before bridge lock creation.\n\nValidation performed:\n- python -B -m py_compile node/airdrop_v2.py node/test_airdrop_v2_route_validation.py\n- git diff --check origin/main...HEAD -- node/airdrop_v2.py node/test_airdrop_v2_route_validation.py\n- PYTHONPATH=node python -B -m pytest -q node/test_airdrop_v2_route_validation.py --noconftest -> 3 passed\n- PYTHONPATH=node python -B -m pytest -q node/test_airdrop_v2_route_validation.py node/test_airdrop_v2.py tests/test_airdrop_bridge_admin_auth.py --noconftest -> 37 passed\n\nI did not find a blocking issue in this PR.
jaxint
left a comment
There was a problem hiding this comment.
LGTM! Great work on this PR. 🚀
PR Review — PR #5565Title: fix: align integrated node schema with reward settlement Author: iamdinhthuan What I reviewed
Observations
LGTM pending CI. The schema alignment and validation improvements are important for financial correctness. Good test coverage added. Bounty: #2782 |
|
Closing as stale branch — would cause destructive deletions if merged. Your branch was filed roughly 155 commits behind current main. Since then, many overlapping fixes from other contributors have landed via parallel PRs. GitHub's Bounty credit acknowledgedIf 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 reviewRebase against current main and verify your diff shows roughly the size of the changes you originally made: 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. |
Summary
Validates public Airdrop V2 route payloads before string/number operations so malformed unauthenticated JSON requests return controlled
400errors instead of uncaught500s.Covered routes:
POST /api/airdrop/eligibilityPOST /api/airdrop/claimPOST /api/bridge/lockThe patch rejects non-string public route fields, invalid chain/tier values, and non-finite or non-positive
amount_wrtcvalues before downstream processing.Verification
Observed locally on current upstream main:
Bounty route
Prepared for Scottcjn/rustchain-bounties#71. No private keys, tokens, credentials, wallet secrets, or account-internal data are included.