-
Notifications
You must be signed in to change notification settings - Fork 1
🛡️ Sentinel: Harden SSRF protection and add security tests #145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,7 +1,8 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import stat | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import sys | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from unittest.mock import MagicMock | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import socket | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from unittest.mock import MagicMock, patch | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import pytest | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -285,3 +286,64 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Tests the is_valid_rule function against a strict whitelist of inputs. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert is_valid_rule(rule) == expected_validity, f"Failed for rule: {rule}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Mock helpers for TestValidateFolderUrl | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def mock_getaddrinfo_ipv4(ip): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check warningCode scanning / Pylint (reported by Codacy) Missing function docstring Warning test
Missing function docstring
Check warningCode scanning / Pylint (reported by Codacy) Argument name "ip" doesn't conform to snake_case naming style Warning test
Argument name "ip" doesn't conform to snake_case naming style
Check warningCode scanning / Pylintpython3 (reported by Codacy) Argument name "ip" doesn't conform to snake_case naming style Warning test
Argument name "ip" doesn't conform to snake_case naming style
Check warningCode scanning / Pylintpython3 (reported by Codacy) Missing function or method docstring Warning test
Missing function or method docstring
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return [(socket.AF_INET, socket.SOCK_STREAM, 6, '', (ip, 443))] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def mock_getaddrinfo_ipv6(ip): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check warningCode scanning / Pylint (reported by Codacy) Missing function docstring Warning test
Missing function docstring
Check warningCode scanning / Pylint (reported by Codacy) Argument name "ip" doesn't conform to snake_case naming style Warning test
Argument name "ip" doesn't conform to snake_case naming style
Check warningCode scanning / Pylintpython3 (reported by Codacy) Missing function or method docstring Warning test
Missing function or method docstring
Check warningCode scanning / Pylintpython3 (reported by Codacy) Argument name "ip" doesn't conform to snake_case naming style Warning test
Argument name "ip" doesn't conform to snake_case naming style
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return [(socket.AF_INET6, socket.SOCK_STREAM, 6, '', (ip, 443, 0, 0))] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class TestValidateFolderUrl: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Security tests for SSRF protection in validate_folder_url.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def test_rejects_non_https(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check warningCode scanning / Pylint (reported by Codacy) Method could be a function Warning test
Method could be a function
Check warningCode scanning / Pylint (reported by Codacy) Missing method docstring Warning test
Missing method docstring
Check warningCode scanning / Pylintpython3 (reported by Codacy) Method could be a function Warning test
Method could be a function
Check warningCode scanning / Pylintpython3 (reported by Codacy) Missing function or method docstring Warning test
Missing function or method docstring
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert main.validate_folder_url("http://example.com/foo") is False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check noticeCode scanning / Bandit Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Check noticeCode scanning / Bandit (reported by Codacy) Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert main.validate_folder_url("ftp://example.com/foo") is False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check noticeCode scanning / Bandit Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Check noticeCode scanning / Bandit (reported by Codacy) Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert main.validate_folder_url("file:///etc/passwd") is False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check noticeCode scanning / Bandit Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Comment on lines
+301
to
+304
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test can be made more concise and easier to extend by using
Suggested change
Check noticeCode scanning / Bandit (reported by Codacy) Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @patch('socket.getaddrinfo') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def test_accepts_valid_public_ip(self, mock_gai): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check warningCode scanning / Pylint (reported by Codacy) Missing method docstring Warning test
Missing method docstring
Check warningCode scanning / Pylint (reported by Codacy) Method could be a function Warning test
Method could be a function
Check warningCode scanning / Pylintpython3 (reported by Codacy) Method could be a function Warning test
Method could be a function
Check warningCode scanning / Pylintpython3 (reported by Codacy) Missing function or method docstring Warning test
Missing function or method docstring
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Mock domain resolving to public IP (8.8.8.8) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mock_gai.side_effect = lambda host, *args, **kwargs: mock_getaddrinfo_ipv4("8.8.8.8") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For a mock that should always return the same value regardless of arguments, using
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert main.validate_folder_url("https://example.com/foo") is True | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check noticeCode scanning / Bandit Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Check noticeCode scanning / Bandit (reported by Codacy) Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def test_rejects_localhost_literal(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check warningCode scanning / Pylint (reported by Codacy) Method could be a function Warning test
Method could be a function
Check warningCode scanning / Pylint (reported by Codacy) Missing method docstring Warning test
Missing method docstring
Check warningCode scanning / Pylintpython3 (reported by Codacy) Missing function or method docstring Warning test
Missing function or method docstring
Check warningCode scanning / Pylintpython3 (reported by Codacy) Method could be a function Warning test
Method could be a function
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Test explicit localhost hostname check | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert main.validate_folder_url("https://localhost/foo") is False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check noticeCode scanning / Bandit Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Check noticeCode scanning / Bandit (reported by Codacy) Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @patch('socket.getaddrinfo') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def test_rejects_private_ip_resolution(self, mock_gai): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check warningCode scanning / Pylint (reported by Codacy) Missing method docstring Warning test
Missing method docstring
Check warningCode scanning / Pylint (reported by Codacy) Method could be a function Warning test
Method could be a function
Check warningCode scanning / Pylintpython3 (reported by Codacy) Method could be a function Warning test
Method could be a function
Check warningCode scanning / Pylintpython3 (reported by Codacy) Missing function or method docstring Warning test
Missing function or method docstring
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Mock domain resolving to private IP (192.168.1.1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mock_gai.side_effect = lambda host, *args, **kwargs: mock_getaddrinfo_ipv4("192.168.1.1") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert main.validate_folder_url("https://internal.corp/foo") is False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check noticeCode scanning / Bandit Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Check noticeCode scanning / Bandit (reported by Codacy) Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @patch('socket.getaddrinfo') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def test_rejects_loopback_ip_resolution(self, mock_gai): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check warningCode scanning / Pylint (reported by Codacy) Missing method docstring Warning test
Missing method docstring
Check warningCode scanning / Pylintpython3 (reported by Codacy) Method could be a function Warning test
Method could be a function
Check warningCode scanning / Pylint (reported by Codacy) Method could be a function Warning test
Method could be a function
Check warningCode scanning / Pylintpython3 (reported by Codacy) Missing function or method docstring Warning test
Missing function or method docstring
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Mock domain resolving to loopback IP (127.0.0.1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mock_gai.side_effect = lambda host, *args, **kwargs: mock_getaddrinfo_ipv4("127.0.0.1") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert main.validate_folder_url("https://evil.com/foo") is False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check noticeCode scanning / Bandit Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Check noticeCode scanning / Bandit (reported by Codacy) Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @patch('socket.getaddrinfo') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def test_rejects_ipv6_loopback_resolution(self, mock_gai): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check warningCode scanning / Pylint (reported by Codacy) Method could be a function Warning test
Method could be a function
Check warningCode scanning / Pylintpython3 (reported by Codacy) Method could be a function Warning test
Method could be a function
Check warningCode scanning / Pylint (reported by Codacy) Missing method docstring Warning test
Missing method docstring
Check warningCode scanning / Pylintpython3 (reported by Codacy) Missing function or method docstring Warning test
Missing function or method docstring
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Mock domain resolving to IPv6 loopback (::1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mock_gai.side_effect = lambda host, *args, **kwargs: mock_getaddrinfo_ipv6("::1") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert main.validate_folder_url("https://ipv6.local/foo") is False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check noticeCode scanning / Bandit Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Comment on lines
+316
to
+332
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The tests for private, loopback, and IPv6 loopback resolutions are very similar. They can be combined into a single, parametrized test. This reduces code duplication and makes the test suite easier to maintain and extend with more non-public IP cases in the future. This suggestion also simplifies the mock setup by using
Suggested change
Check noticeCode scanning / Bandit (reported by Codacy) Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def test_rejects_ip_literal_private(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check warningCode scanning / Pylint (reported by Codacy) Method could be a function Warning test
Method could be a function
Check warningCode scanning / Pylintpython3 (reported by Codacy) Method could be a function Warning test
Method could be a function
Check warningCode scanning / Pylint (reported by Codacy) Missing method docstring Warning test
Missing method docstring
Check warningCode scanning / Pylintpython3 (reported by Codacy) Missing function or method docstring Warning test
Missing function or method docstring
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # IP literals are checked directly via ipaddress module, bypassing DNS | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert main.validate_folder_url("https://192.168.1.1/foo") is False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check noticeCode scanning / Bandit Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Check noticeCode scanning / Bandit (reported by Codacy) Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert main.validate_folder_url("https://10.0.0.1/foo") is False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check noticeCode scanning / Bandit Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Check noticeCode scanning / Bandit (reported by Codacy) Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert main.validate_folder_url("https://127.0.0.1/foo") is False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check noticeCode scanning / Bandit Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Check noticeCode scanning / Bandit (reported by Codacy) Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert main.validate_folder_url("https://[::1]/foo") is False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check noticeCode scanning / Bandit Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Comment on lines
+334
to
+339
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test can be made more concise and easier to extend by using
Suggested change
Check noticeCode scanning / Bandit (reported by Codacy) Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def test_accepts_ip_literal_public(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check warningCode scanning / Pylint (reported by Codacy) Missing method docstring Warning test
Missing method docstring
Check warningCode scanning / Pylintpython3 (reported by Codacy) Method could be a function Warning test
Method could be a function
Check warningCode scanning / Pylintpython3 (reported by Codacy) Missing function or method docstring Warning test
Missing function or method docstring
Check warningCode scanning / Pylint (reported by Codacy) Method could be a function Warning test
Method could be a function
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert main.validate_folder_url("https://8.8.8.8/foo") is True | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check noticeCode scanning / Bandit Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Check noticeCode scanning / Bandit (reported by Codacy) Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert main.validate_folder_url("https://[2001:4860:4860::8888]/foo") is True | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check noticeCode scanning / Bandit Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Comment on lines
+341
to
+343
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test can be made more concise and easier to extend by using
Suggested change
Check noticeCode scanning / Bandit (reported by Codacy) Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @patch('socket.getaddrinfo') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def test_dns_resolution_failure_is_safe(self, mock_gai): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check warningCode scanning / Pylintpython3 (reported by Codacy) Method could be a function Warning test
Method could be a function
Check warningCode scanning / Pylint (reported by Codacy) Missing method docstring Warning test
Missing method docstring
Check warningCode scanning / Pylintpython3 (reported by Codacy) Missing function or method docstring Warning test
Missing function or method docstring
Check warningCode scanning / Pylint (reported by Codacy) Method could be a function Warning test
Method could be a function
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Ensure that if DNS fails, we default to False (fail closed) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mock_gai.side_effect = socket.gaierror("Name or service not known") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert main.validate_folder_url("https://nonexistent.com/foo") is False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Check noticeCode scanning / Bandit Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Check noticeCode scanning / Bandit (reported by Codacy) Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test
Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment states "Validation now happens per-fetch" but this is not entirely accurate. Looking at the
_fetch_if_validfunction below (lines 1058-1060), URLs that are already in the data cache (_cache) are returned directly without re-validation. This means validation does not happen per-fetch for cached URLs, which could still present a TOCTOU risk if DNS records change between syncs within the same process. Consider updating the comment to clarify that validation happens per-fetch for non-cached URLs, or document the accepted risk of using cached data without re-validation.