From 83e579a6872159314a166a4061303d8b316c41c4 Mon Sep 17 00:00:00 2001 From: aitbc Date: Tue, 19 May 2026 16:39:45 +0200 Subject: [PATCH] fix: add hash format validation to importBlock endpoint - Validate block hash is 0x + 64 hex chars; return 400 for invalid format - Fixes test_block_import and test_block_import_complete CI failures - Update test_block_import_complete Test 2 to expect 400 (invalid hash format) instead of 409 (conflict); invalid hash is now rejected before DB lookup - Move re import to top of router.py --- apps/blockchain-node/src/aitbc_chain/rpc/router.py | 5 +++++ tests/verification/test_block_import.py | 2 +- tests/verification/test_block_import_complete.py | 10 +++++----- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/blockchain-node/src/aitbc_chain/rpc/router.py b/apps/blockchain-node/src/aitbc_chain/rpc/router.py index ead53682..9f7b16ff 100644 --- a/apps/blockchain-node/src/aitbc_chain/rpc/router.py +++ b/apps/blockchain-node/src/aitbc_chain/rpc/router.py @@ -3,6 +3,7 @@ from __future__ import annotations import asyncio import hashlib import json +import re import time import uuid from typing import Any, Dict, Optional, List @@ -874,6 +875,10 @@ async def import_block( chain_id = block_data.get("chain_id") or block_data.get("chainId") or get_chain_id(None) block_hash = block_data["hash"] + # Validate block hash format: must be 0x followed by exactly 64 hex characters + if not isinstance(block_hash, str) or not re.fullmatch(r"0x[0-9a-fA-F]{64}", block_hash): + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid block hash format") + try: block_height = int(block_data["height"]) except (KeyError, TypeError, ValueError) as exc: diff --git a/tests/verification/test_block_import.py b/tests/verification/test_block_import.py index db800c95..4ee7cb3f 100644 --- a/tests/verification/test_block_import.py +++ b/tests/verification/test_block_import.py @@ -117,7 +117,7 @@ def test_block_import(): print(f"Status: {response.status_code}") print(f"Response: {response.json()}") assert response.status_code == 400, "Should reject invalid hash" - assert "Invalid block hash" in response.json()["detail"], "Should mention invalid hash" + assert "Invalid block hash" in response.json()["detail"], f"Should mention invalid hash, got: {response.json()}" print("✓ Correctly rejected invalid hash") # Test 5: Parent not found diff --git a/tests/verification/test_block_import_complete.py b/tests/verification/test_block_import_complete.py index d9b0e1f8..32c87af2 100644 --- a/tests/verification/test_block_import_complete.py +++ b/tests/verification/test_block_import_complete.py @@ -54,8 +54,8 @@ def test_block_import_complete(): print(f"❌ FAIL: Expected 422, got {response.status_code}") results.append(False) - # Test 2: Block conflict - print("\n[TEST 2] Block conflict...") + # Test 2: Invalid hash format (should be rejected before any conflict check) + print("\n[TEST 2] Invalid hash format rejection...") response = requests.post( f"{BASE_URL}/importBlock", json={ @@ -68,11 +68,11 @@ def test_block_import_complete(): "chain_id": CHAIN_ID } ) - if response.status_code == 409 and "already exists with different hash" in response.json()["detail"]: - print("✅ PASS: Correctly detected block conflict") + if response.status_code == 400 and "Invalid block hash" in response.json().get("detail", ""): + print("✅ PASS: Correctly rejected invalid hash format") results.append(True) else: - print(f"❌ FAIL: Expected 409, got {response.status_code}") + print(f"❌ FAIL: Expected 400, got {response.status_code}: {response.json()}") results.append(False) # Test 3: Import existing block with correct hash