diff --git a/src/validators/domain.py b/src/validators/domain.py index 1ce4a262..5a5481a2 100644 --- a/src/validators/domain.py +++ b/src/validators/domain.py @@ -76,7 +76,16 @@ def domain( if not value: return False - if consider_tld and not _IanaTLD.check(value.rstrip(".").rsplit(".", 1)[-1].upper()): + tld = value.rstrip(".").rsplit(".", 1)[-1].upper() + try: + # IANA stores internationalized TLDs in their IDNA-encoded form + # (e.g. "XN--P1AI"), so the lookup must use the encoded TLD too + tld = tld.encode("idna").decode("utf-8").upper() + except UnicodeError: + # An unencodable TLD cannot be in the IANA list; keep the raw + # value and let the lookup below reject it + pass + if consider_tld and not _IanaTLD.check(tld): return False try: diff --git a/tests/test_domain.py b/tests/test_domain.py index d606ad79..b727a39e 100644 --- a/tests/test_domain.py +++ b/tests/test_domain.py @@ -47,6 +47,10 @@ def test_returns_true_on_valid_domain(value: str, rfc_1034: bool, rfc_2782: bool ("example_.com", True, False, True), ("somerandomexample.xn--fiqs8s", True, False, False), ("somerandomexample.onion", True, False, False), + # internationalized TLDs are matched via their IDNA-encoded form + # ref: https://github.com/python-validators/validators/issues/522 + ("пример.рф", True, False, False), + ("пример.рф.", True, True, False), ], ) def test_returns_true_on_valid_top_level_domain( @@ -100,6 +104,8 @@ def test_returns_failed_validation_on_invalid_domain(value: str, rfc_1034: bool, ("_example.#13", True, False, True), ("example_.fo-ul", True, False, True), ("somerandomexample.xn-n-fiqs8s", True, False, False), + # non-IANA IDN TLDs must still be rejected after encoding + ("пример.испытание", True, False, False), ], ) def test_returns_failed_validation_invalid_top_level_domain(