Skip to content

πŸ›‘οΈ Sentinel: [HIGH] Fix SSRF via DNS resolution check#92

Closed
google-labs-jules[bot] wants to merge 1 commit intomainfrom
sentinel/fix-ssrf-validation-2009760983712888116
Closed

πŸ›‘οΈ Sentinel: [HIGH] Fix SSRF via DNS resolution check#92
google-labs-jules[bot] wants to merge 1 commit intomainfrom
sentinel/fix-ssrf-validation-2009760983712888116

Conversation

@google-labs-jules
Copy link

🚨 Severity: HIGH
πŸ’‘ Vulnerability: SSRF (Server-Side Request Forgery) via folder-url parameter.
🎯 Impact: Attackers could potentially access internal services or the loopback interface by providing a domain that resolves to a private IP (e.g. local.attacker.com -> 127.0.0.1).
πŸ”§ Fix: Added DNS resolution step in URL validation. The system now resolves the hostname and verifies that none of the resulting IPs are private or loopback addresses.
βœ… Verification: Added tests/test_ssrf.py which mocks DNS responses to verify that private IPs (IPv4 and IPv6) are rejected, while public IPs are accepted.


PR created automatically by Jules for task 2009760983712888116 started by @abhimehro

Resolves domain names in `validate_folder_url` to ensure they do not point to private or loopback IP addresses. This mitigates SSRF attacks where a user-provided domain could target internal services.

- Uses `socket.getaddrinfo` to support both IPv4 and IPv6.
- Checks all resolved addresses against private/loopback ranges.
- Fails closed if DNS resolution fails.
- Adds comprehensive tests in `tests/test_ssrf.py`.
@google-labs-jules
Copy link
Author

πŸ‘‹ Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a πŸ‘€ emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@trunk-io
Copy link

trunk-io bot commented Jan 11, 2026

Merging to main in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

except ValueError:
# Not an IP literal, it's a domain.
pass
# Not an IP literal, it's a domain. Resolve it to prevent SSRF against internal services.

Check warning

Code scanning / Pylint (reported by Codacy)

Line too long (101/100) Warning

Line too long (101/100)
addr_info = socket.getaddrinfo(hostname, None)
for res in addr_info:
# res is (family, type, proto, canonname, sockaddr)
# sockaddr is (address, port) for IPv4 and (address, port, flow info, scope id) for IPv6

Check warning

Code scanning / Pylint (reported by Codacy)

Line too long (108/100) Warning

Line too long (108/100)
ip_str = res[4][0]
ip_obj = ipaddress.ip_address(ip_str)
if ip_obj.is_private or ip_obj.is_loopback:
log.warning(f"Skipping unsafe URL (domain resolves to private IP {ip_str}): {sanitize_for_log(url)}")

Check warning

Code scanning / Pylint (reported by Codacy)

Line too long (125/100) Warning

Line too long (125/100)
log.warning(f"Skipping unsafe URL (domain resolves to private IP {ip_str}): {sanitize_for_log(url)}")
return False
except Exception as e:
log.warning(f"Skipping unsafe URL (DNS resolution failed): {sanitize_for_log(url)} ({e})")

Check warning

Code scanning / Pylint (reported by Codacy)

Line too long (106/100) Warning

Line too long (106/100)
@@ -0,0 +1,70 @@
import unittest

Check warning

Code scanning / Pylint (reported by Codacy)

Missing module docstring Warning test

Missing module docstring
# Add parent directory to path to import main
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from main import validate_folder_url

Check warning

Code scanning / Pylint (reported by Codacy)

Import "from main import validate_folder_url" should be placed at the top of the module Warning test

Import "from main import validate_folder_url" should be placed at the top of the module

from main import validate_folder_url

class TestSSRF(unittest.TestCase):

Check warning

Code scanning / Pylint (reported by Codacy)

Missing class docstring Warning test

Missing class docstring
ip_str = res[4][0]
ip_obj = ipaddress.ip_address(ip_str)
if ip_obj.is_private or ip_obj.is_loopback:
log.warning(f"Skipping unsafe URL (domain resolves to private IP {ip_str}): {sanitize_for_log(url)}")

Check warning

Code scanning / Prospector (reported by Codacy)

Use lazy % formatting in logging functions (logging-fstring-interpolation) Warning

Use lazy % formatting in logging functions (logging-fstring-interpolation)
log.warning(f"Skipping unsafe URL (domain resolves to private IP {ip_str}): {sanitize_for_log(url)}")
return False
except Exception as e:
log.warning(f"Skipping unsafe URL (DNS resolution failed): {sanitize_for_log(url)} ({e})")

Check warning

Code scanning / Prospector (reported by Codacy)

Use lazy % formatting in logging functions (logging-fstring-interpolation) Warning

Use lazy % formatting in logging functions (logging-fstring-interpolation)
# Add parent directory to path to import main
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from main import validate_folder_url

Check warning

Code scanning / Prospector (reported by Codacy)

Import "from main import validate_folder_url" should be placed at the top of the module (wrong-import-position) Warning test

Import "from main import validate_folder_url" should be placed at the top of the module (wrong-import-position)

self.assertFalse(validate_folder_url(url), "Should reject domain that fails resolution")

if __name__ == '__main__':

Check warning

Code scanning / Prospector (reported by Codacy)

expected 2 blank lines after class or function definition, found 1 (E305) Warning test

expected 2 blank lines after class or function definition, found 1 (E305)
except ValueError:
# Not an IP literal, it's a domain.
pass
# Not an IP literal, it's a domain. Resolve it to prevent SSRF against internal services.

Check warning

Code scanning / Pylintpython3 (reported by Codacy)

Line too long (101/100) Warning

Line too long (101/100)
pass
# Not an IP literal, it's a domain. Resolve it to prevent SSRF against internal services.
try:
# Use getaddrinfo to support both IPv4 and IPv6 and check all resolved addresses

Check warning

Code scanning / Pylintpython3 (reported by Codacy)

Variable name "e" doesn't conform to snake_case naming style Warning

Variable name "e" doesn't conform to snake_case naming style
addr_info = socket.getaddrinfo(hostname, None)
for res in addr_info:
# res is (family, type, proto, canonname, sockaddr)
# sockaddr is (address, port) for IPv4 and (address, port, flow info, scope id) for IPv6

Check warning

Code scanning / Pylintpython3 (reported by Codacy)

Line too long (108/100) Warning

Line too long (108/100)
ip_str = res[4][0]
ip_obj = ipaddress.ip_address(ip_str)
if ip_obj.is_private or ip_obj.is_loopback:
log.warning(f"Skipping unsafe URL (domain resolves to private IP {ip_str}): {sanitize_for_log(url)}")

Check warning

Code scanning / Pylintpython3 (reported by Codacy)

Line too long (125/100) Warning

Line too long (125/100)
log.warning(f"Skipping unsafe URL (domain resolves to private IP {ip_str}): {sanitize_for_log(url)}")
return False
except Exception as e:
log.warning(f"Skipping unsafe URL (DNS resolution failed): {sanitize_for_log(url)} ({e})")

Check warning

Code scanning / Pylintpython3 (reported by Codacy)

Line too long (106/100) Warning

Line too long (106/100)
# Add parent directory to path to import main
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from main import validate_folder_url

Check warning

Code scanning / Pylintpython3 (reported by Codacy)

Import "from main import validate_folder_url" should be placed at the top of the module Warning test

Import "from main import validate_folder_url" should be placed at the top of the module

from main import validate_folder_url

class TestSSRF(unittest.TestCase):

Check warning

Code scanning / Pylintpython3 (reported by Codacy)

Missing class docstring Warning test

Missing class docstring
pass
# Not an IP literal, it's a domain. Resolve it to prevent SSRF against internal services.
try:
# Use getaddrinfo to support both IPv4 and IPv6 and check all resolved addresses

Check notice

Code scanning / Pylintpython3 (reported by Codacy)

Catching too general exception Exception Note

Catching too general exception Exception
ip_str = res[4][0]
ip_obj = ipaddress.ip_address(ip_str)
if ip_obj.is_private or ip_obj.is_loopback:
log.warning(f"Skipping unsafe URL (domain resolves to private IP {ip_str}): {sanitize_for_log(url)}")

Check notice

Code scanning / Pylintpython3 (reported by Codacy)

Use lazy % formatting in logging functions Note

Use lazy % formatting in logging functions
log.warning(f"Skipping unsafe URL (domain resolves to private IP {ip_str}): {sanitize_for_log(url)}")
return False
except Exception as e:
log.warning(f"Skipping unsafe URL (DNS resolution failed): {sanitize_for_log(url)} ({e})")

Check notice

Code scanning / Pylintpython3 (reported by Codacy)

Use lazy % formatting in logging functions Note

Use lazy % formatting in logging functions
@abhimehro
Copy link
Owner

Superseded: SSRF DNS resolution fix already in main via PR #109.

@abhimehro abhimehro closed this Jan 18, 2026
@google-labs-jules
Copy link
Author

Superseded: SSRF DNS resolution fix already in main via PR #109.

Understood. Acknowledging that this work is now obsolete as it has been addressed in PR #109. I will stop work on this task.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant