diff --git a/email_validator/deliverability.py b/email_validator/deliverability.py index 3cddc77..0b122d7 100644 --- a/email_validator/deliverability.py +++ b/email_validator/deliverability.py @@ -8,7 +8,14 @@ import dns.exception +def _reject_bool_timeout(timeout: Optional[int]) -> None: + # bool subclasses int; timeout=True would silently become lifetime 1s + if isinstance(timeout, bool): + raise TypeError("timeout must be an int or float, not bool") + + def caching_resolver(*, timeout: Optional[int] = None, cache: Any = None, dns_resolver: Optional[dns.resolver.Resolver] = None) -> dns.resolver.Resolver: + _reject_bool_timeout(timeout) if timeout is None: from . import DEFAULT_TIMEOUT timeout = DEFAULT_TIMEOUT @@ -34,6 +41,7 @@ def validate_email_deliverability(domain: str, domain_i18n: str, timeout: Option # If no dns.resolver.Resolver was given, get dnspython's default resolver. # Override the default resolver's timeout. This may affect other uses of # dnspython in this process. + _reject_bool_timeout(timeout) if dns_resolver is None: from . import DEFAULT_TIMEOUT if timeout is None: diff --git a/email_validator/validate_email.py b/email_validator/validate_email.py index 07b398a..9cc46c7 100644 --- a/email_validator/validate_email.py +++ b/email_validator/validate_email.py @@ -58,6 +58,9 @@ def validate_email( globally_deliverable = GLOBALLY_DELIVERABLE if timeout is None and dns_resolver is None: timeout = DEFAULT_TIMEOUT + # bool subclasses int; timeout=True would silently become DNS lifetime 1s + if isinstance(timeout, bool): + raise TypeError("timeout must be an int or float, not bool") if isinstance(email, str): pass diff --git a/tests/test_deliverability.py b/tests/test_deliverability.py index e1307c2..9505cb4 100644 --- a/tests/test_deliverability.py +++ b/tests/test_deliverability.py @@ -90,3 +90,18 @@ def put(self, key: Any, value: Any) -> Any: validate_email("test@gmail.com", dns_resolver=resolver) assert len(cache.cache) == 1 + + +def test_timeout_rejects_bool() -> None: + """bool subclasses int; timeout=True must not silently become lifetime 1s.""" + import pytest + from email_validator.deliverability import caching_resolver, validate_email_deliverability + + for value in (True, False): + with pytest.raises(TypeError, match="timeout must be an int or float, not bool"): + caching_resolver(timeout=value) + with pytest.raises(TypeError, match="timeout must be an int or float, not bool"): + validate_email_deliverability("example.com", "example.com", timeout=value) + # valid int still accepted on caching_resolver + resolver = caching_resolver(timeout=5) + assert resolver.lifetime == 5