Skip to content

fix(security): prevent SSRF bypass for IMDS endpoints in browser routing - #16250

Closed
PratikRai0101 wants to merge 1 commit into
NousResearch:mainfrom
PratikRai0101:fix/browser-ssrf-imds-guard
Closed

fix(security): prevent SSRF bypass for IMDS endpoints in browser routing#16250
PratikRai0101 wants to merge 1 commit into
NousResearch:mainfrom
PratikRai0101:fix/browser-ssrf-imds-guard

Conversation

@PratikRai0101

Copy link
Copy Markdown
Contributor

What does this PR do?

This PR patches a P0 SSRF vulnerability where the hybrid browser routing logic unintentionally short-circuits the pre-navigation safety guard.

By default, an agent could bypass the block for link-local addresses (e.g., 169.254.169.254), allowing it to reach Instance Metadata Services (IMDS) and exfiltrate cloud credentials in AWS, GCP, Azure, or Aliyun environments.

This fix decouples the routing decision (auto_local_this_nav) from the security gate, ensuring _is_safe_url() acts as an absolute gatekeeper before any routing occurs. It also includes a secondary fix for Python 3.10 compatibility regarding private IP classification.

Related Issue

Fixes #16234

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • tools/browser_tool.py (Security Guard): Moved the _is_safe_url() check before the hybrid routing decision for both pre-navigation and post-redirect logic. The auto_local_this_nav flag can no longer short-circuit the safety gate.
  • tools/browser_tool.py (Python 3.10 Compatibility): Replaced the reliance on ip.is_private with an explicit _PRIVATE_IP_RANGES tuple (RFC1918 + 169.254.0.0/16 + fd00::/8). This ensures 172.16.x.x addresses don't leak on older Python runtimes.
  • tests/tools/test_browser_ssrf_local.py: Added TestImdsBlockingWithHybridRouting covering AWS, GCP, Azure, and Aliyun metadata endpoints.

How to Test

  1. Run the targeted automated test suite:

    pytest tests/tools/test_browser_ssrf_local.py -v
    
  2. Manual Verification: Run the Hermes agent configured with a cloud browser provider (e.g., Browserbase) and instruct it to navigate to http://169.254.169.254/latest/meta-data/.

  3. Verify that the agent immediately returns a Blocked: URL targets a private or internal address error JSON rather than successfully retrieving the page.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: Arch Linux

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Screenshots / Logs

Test Execution Output:

tests/tools/test_browser_ssrf_local.py       20 passed
tests/tools/test_browser_hybrid_routing.py   20 passed  
tests/tools/test_browser_hardening.py        20 passed
tests/tools/test_browser_camofox.py          16 passed
tests/tools/test_browser_cloud_fallback.py    7 passed
─────────────────────────────────────────────────────
TOTAL                                        91 passed

@alt-glitch alt-glitch added type/security Security vulnerability or hardening P0 Critical — data loss, security, crash loop tool/browser Browser automation (CDP, Playwright) labels Apr 26, 2026
@egilewski

Copy link
Copy Markdown
Contributor

Recommendation: close as superseded rather than merging this branch as-is.

I checked current upstream/main 7f016f5f336093a684769f5d2413aa77e2510000 against PR head 7432f85ce2d17ec2c823228b2fe7f6a6a3385ae0. The linked issue #16234 is already fixed on main by 0214858ef5fb0f5577c2ff26ff8f7e3178103837 / #21228.

Validation:

  • Current main: .venv/bin/python -m pytest -o addopts='' -p no:cacheprovider tests/tools/test_browser_ssrf_local.py -q -> 20 passed.
  • PR replay on its base: same command -> 20 passed.
  • Mocked cloud+hybrid probe on current main: http://169.254.169.254/latest/meta-data/ returns Blocked: URL targets a cloud metadata endpoint, while ordinary private http://192.168.1.1/admin still succeeds through the local sidecar.
  • Same ordinary private-URL probe on this PR replay returns Blocked: URL targets a private or internal address unless allow_private_urls=True, which changes the documented/default hybrid-routing behavior for LAN and local-dashboard URLs.
  • GitHub reports mergeable=CONFLICTING, mergeStateStatus=DIRTY; the patch does not apply to current main in tools/browser_tool.py and tests/tools/test_browser_ssrf_local.py.

CodeRabbit completed on the uncommitted PR patch and only reported a minor unused importlib import. The mainline fix is narrower: it keeps an always-blocked metadata floor for IMDS/cloud metadata while preserving ordinary private URL sidecar routing.

Signed: GPT-5.5-xhigh in Codex

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for this — IMDS endpoints are already blocked ahead of the routing decision on current main (landed via #16234), so we're closing as already-implemented.

tools/browser_tool.py has an explicit always-blocked floor that runs BEFORE the auto-local/hybrid routing branch: a non-local backend with a metadata-endpoint URL is rejected ("Blocked: URL targets a cloud metadata endpoint") regardless of how routing would otherwise dispatch it. So the hybrid-routing bypass you describe is closed.

Appreciate the SSRF analysis — thanks!

@teknium1 teknium1 closed this Jun 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P0 Critical — data loss, security, crash loop tool/browser Browser automation (CDP, Playwright) type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Security: cloud browser hybrid routing bypasses pre-nav SSRF guard for IMDS endpoints

4 participants