Skip to content

Commit 395c698

Browse files
committed
Check IBAN length for each registered country
1 parent 22786cc commit 395c698

2 files changed

Lines changed: 124 additions & 2 deletions

File tree

‎src/validators/iban.py‎

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,100 @@
66
# local
77
from .utils import validator
88

9+
# Swift IBAN Registry, release 103 (September 2026).
10+
# https://www.swift.com/resource/iban-registry-pdf
11+
_COUNTRY_LENGTHS = {
12+
"AD": 24,
13+
"AE": 23,
14+
"AL": 28,
15+
"AT": 20,
16+
"AZ": 28,
17+
"BA": 20,
18+
"BE": 16,
19+
"BG": 22,
20+
"BH": 22,
21+
"BI": 27,
22+
"BR": 29,
23+
"BY": 28,
24+
"CH": 21,
25+
"CR": 22,
26+
"CY": 28,
27+
"CZ": 24,
28+
"DE": 22,
29+
"DJ": 27,
30+
"DK": 18,
31+
"DO": 28,
32+
"EE": 20,
33+
"EG": 29,
34+
"ES": 24,
35+
"FI": 18,
36+
"FK": 18,
37+
"FO": 18,
38+
"FR": 27,
39+
"GB": 22,
40+
"GE": 22,
41+
"GI": 23,
42+
"GL": 18,
43+
"GR": 27,
44+
"GT": 28,
45+
"HN": 28,
46+
"HR": 21,
47+
"HU": 28,
48+
"IE": 22,
49+
"IL": 23,
50+
"IQ": 23,
51+
"IS": 26,
52+
"IT": 27,
53+
"JO": 30,
54+
"KW": 30,
55+
"KZ": 20,
56+
"LB": 28,
57+
"LC": 32,
58+
"LI": 21,
59+
"LT": 20,
60+
"LU": 20,
61+
"LV": 21,
62+
"LY": 25,
63+
"MC": 27,
64+
"MD": 24,
65+
"ME": 22,
66+
"MK": 19,
67+
"MN": 20,
68+
"MR": 27,
69+
"MT": 31,
70+
"MU": 30,
71+
"NI": 28,
72+
"NL": 18,
73+
"NO": 15,
74+
"OM": 23,
75+
"PK": 24,
76+
"PL": 28,
77+
"PS": 29,
78+
"PT": 25,
79+
"QA": 29,
80+
"RO": 24,
81+
"RS": 22,
82+
"RU": 33,
83+
"SA": 24,
84+
"SC": 31,
85+
"SD": 18,
86+
"SE": 24,
87+
"SI": 19,
88+
"SK": 24,
89+
"SM": 27,
90+
"SO": 23,
91+
"ST": 25,
92+
"SV": 28,
93+
"TL": 23,
94+
"TN": 24,
95+
"TR": 26,
96+
"UA": 29,
97+
"VA": 22,
98+
"VG": 24,
99+
"XK": 20,
100+
"YE": 30,
101+
}
102+
9103

10104
def _char_value(char: str):
11105
"""A=10, B=11, ..., Z=35 (case-insensitive)."""
@@ -23,6 +117,9 @@ def _mod_check(value: str):
23117
def iban(value: str, /):
24118
"""Return whether or not given value is a valid IBAN code.
25119
120+
Check the registered country code, national length, and checksum.
121+
This does not check all national account rules or whether the account exists.
122+
26123
Examples:
27124
>>> iban('DE29100500001061045672')
28125
True
@@ -40,6 +137,7 @@ def iban(value: str, /):
40137
return (
41138
(
42139
re.match(r"^[a-z]{2}[0-9]{2}[a-z0-9]{11,30}$", value, re.IGNORECASE | re.ASCII)
140+
and len(value) == _COUNTRY_LENGTHS.get(value[:2].upper())
43141
and _mod_check(value)
44142
)
45143
if value

‎tests/test_iban.py‎

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,16 @@
77
from validators import ValidationError, iban
88

99

10-
@pytest.mark.parametrize("value", ["GB82WEST12345698765432", "NO9386011117947"])
10+
@pytest.mark.parametrize(
11+
"value",
12+
[
13+
"GB82WEST12345698765432",
14+
"NO9386011117947",
15+
"DE29100500001061045672",
16+
"LC55HEMM000100010012001200023015",
17+
"RU5600000000000000000000000000000",
18+
],
19+
)
1120
def test_returns_true_on_valid_iban(value: str):
1221
"""Test returns true on valid iban."""
1322
assert iban(value)
@@ -19,7 +28,22 @@ def test_returns_true_on_valid_lowercase_iban(value: str):
1928
assert iban(value)
2029

2130

22-
@pytest.mark.parametrize("value", ["GB81WEST12345698765432", "NO9186011117947"])
31+
@pytest.mark.parametrize(
32+
"value",
33+
[
34+
"GB81WEST12345698765432",
35+
"NO9186011117947",
36+
# These values have correct checksums but incorrect country lengths.
37+
"DE3110050000106104567",
38+
"DE631005000010610456720",
39+
"NO37860111179470",
40+
"LC67000000000000000000000000000",
41+
"RU56000000000000000000000000000000",
42+
# These prefixes have no registered IBAN format.
43+
"ZZ66000000000000000000",
44+
"US35000000000000000000",
45+
],
46+
)
2347
def test_returns_failed_validation_on_invalid_iban(value: str):
2448
"""Test returns failed validation on invalid iban."""
2549
assert isinstance(iban(value), ValidationError)

0 commit comments

Comments
 (0)