From e17dea1dc3856ab118e784e60fb8bb51d2090882 Mon Sep 17 00:00:00 2001 From: Triskell Studio Date: Wed, 24 Jun 2026 12:06:37 +0200 Subject: [PATCH] fix(crypto_addresses): correct testnet segwit prefix and reject non-hex ETH Two correctness bugs in the crypto address validators: btc_address: the segwit branch is taken when the value starts with "bc" or "tb", but the regexp only matched "bc" or "tc". Every valid testnet bech32 address (tb1...) was therefore rejected. Correct the prefix to "tb". eth_address: _validate_eth_checksum_address only checked the length of the stripped address, not that its characters were hexadecimal. Inputs made of non-hex, caseless characters (e.g. "0x" + "*" * 40) passed the checksum loop vacuously and were accepted as valid. Require 40 hex digits before running the checksum. Existing fixtures are unchanged; added testnet bech32 addresses and a non-hex input as regression tests. --- src/validators/crypto_addresses/btc_address.py | 2 +- src/validators/crypto_addresses/eth_address.py | 5 +++-- tests/crypto_addresses/test_btc_address.py | 3 +++ tests/crypto_addresses/test_eth_address.py | 2 ++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/validators/crypto_addresses/btc_address.py b/src/validators/crypto_addresses/btc_address.py index ff401114..a23a071d 100644 --- a/src/validators/crypto_addresses/btc_address.py +++ b/src/validators/crypto_addresses/btc_address.py @@ -50,7 +50,7 @@ def btc_address(value: str, /): return ( # segwit pattern - re.compile(r"^(bc|tc)[0-3][02-9ac-hj-np-z]{14,74}$").match(value) + re.compile(r"^(bc|tb)[0-3][02-9ac-hj-np-z]{14,74}$").match(value) if value[:2] in ("bc", "tb") else _validate_old_btc_address(value) ) diff --git a/src/validators/crypto_addresses/eth_address.py b/src/validators/crypto_addresses/eth_address.py index 84861861..6e9561c5 100644 --- a/src/validators/crypto_addresses/eth_address.py +++ b/src/validators/crypto_addresses/eth_address.py @@ -17,11 +17,12 @@ def _validate_eth_checksum_address(addr: str): """Validate ETH type checksum address.""" addr = addr.replace("0x", "") - addr_hash = keccak.new(addr.lower().encode("ascii")).digest().hex() # type: ignore - if len(addr) != 40: + if not re.fullmatch(r"[0-9a-fA-F]{40}", addr): return False + addr_hash = keccak.new(addr.lower().encode("ascii")).digest().hex() # type: ignore + for i in range(0, 40): if (int(addr_hash[i], 16) > 7 and addr[i].upper() != addr[i]) or ( int(addr_hash[i], 16) <= 7 and addr[i].lower() != addr[i] diff --git a/tests/crypto_addresses/test_btc_address.py b/tests/crypto_addresses/test_btc_address.py index f9c4482e..96647eab 100644 --- a/tests/crypto_addresses/test_btc_address.py +++ b/tests/crypto_addresses/test_btc_address.py @@ -17,6 +17,9 @@ # Bech32/segwit type "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq", "bc1qc7slrfxkknqcq2jevvvkdgvrt8080852dfjewde450xdlk4ugp7szw5tk9", + # Bech32/segwit testnet (tb prefix) + "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx", + "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3", ], ) def test_returns_true_on_valid_btc_address(value: str): diff --git a/tests/crypto_addresses/test_eth_address.py b/tests/crypto_addresses/test_eth_address.py index 2baaad5c..de2fd313 100644 --- a/tests/crypto_addresses/test_eth_address.py +++ b/tests/crypto_addresses/test_eth_address.py @@ -37,6 +37,8 @@ def test_returns_true_on_valid_eth_address(value: str): "0x7c8EE9977c6f96b6b9774b3e8e4Cc9B93B12b2c", "0x7Fb21a171205f3B8d8E4d88A2d2f8A56E45DdB5c", "validators.eth", + # non-hex characters (the checksum path used to accept these) + "0x" + "*" * 40, ], ) def test_returns_failed_validation_on_invalid_eth_address(value: str):