mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-28 06:51:16 +08:00
fix(security): treat quoted false as false in browser SSRF guards
This commit is contained in:
@@ -235,3 +235,21 @@ class TestPostRedirectSsrf:
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["url"] == final
|
||||
|
||||
|
||||
class TestAllowPrivateUrlsConfig:
|
||||
@pytest.fixture(autouse=True)
|
||||
def _reset_cache(self):
|
||||
browser_tool._allow_private_urls_resolved = False
|
||||
browser_tool._cached_allow_private_urls = None
|
||||
yield
|
||||
browser_tool._allow_private_urls_resolved = False
|
||||
browser_tool._cached_allow_private_urls = None
|
||||
|
||||
def test_browser_config_string_false_stays_disabled(self, monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.config.read_raw_config",
|
||||
lambda: {"browser": {"allow_private_urls": "false"}},
|
||||
)
|
||||
|
||||
assert browser_tool._allow_private_urls() is False
|
||||
|
||||
@@ -259,6 +259,20 @@ class TestGlobalAllowPrivateUrls:
|
||||
with patch("hermes_cli.config.read_raw_config", return_value=cfg):
|
||||
assert _global_allow_private_urls() is True
|
||||
|
||||
def test_config_security_string_false_stays_disabled(self, monkeypatch):
|
||||
"""Quoted false must not opt out of SSRF protection."""
|
||||
monkeypatch.delenv("HERMES_ALLOW_PRIVATE_URLS", raising=False)
|
||||
cfg = {"security": {"allow_private_urls": "false"}}
|
||||
with patch("hermes_cli.config.read_raw_config", return_value=cfg):
|
||||
assert _global_allow_private_urls() is False
|
||||
|
||||
def test_config_browser_string_false_stays_disabled(self, monkeypatch):
|
||||
"""Legacy browser.allow_private_urls also normalises quoted false."""
|
||||
monkeypatch.delenv("HERMES_ALLOW_PRIVATE_URLS", raising=False)
|
||||
cfg = {"browser": {"allow_private_urls": "false"}}
|
||||
with patch("hermes_cli.config.read_raw_config", return_value=cfg):
|
||||
assert _global_allow_private_urls() is False
|
||||
|
||||
def test_config_security_takes_precedence_over_browser(self, monkeypatch):
|
||||
"""security section is checked before browser section."""
|
||||
monkeypatch.delenv("HERMES_ALLOW_PRIVATE_URLS", raising=False)
|
||||
|
||||
@@ -67,6 +67,7 @@ from typing import Dict, Any, Optional, List, Tuple
|
||||
from pathlib import Path
|
||||
from agent.auxiliary_client import call_llm
|
||||
from hermes_constants import get_hermes_home
|
||||
from utils import is_truthy_value
|
||||
|
||||
try:
|
||||
from tools.website_policy import check_website_access
|
||||
@@ -639,7 +640,11 @@ def _allow_private_urls() -> bool:
|
||||
try:
|
||||
from hermes_cli.config import read_raw_config
|
||||
cfg = read_raw_config()
|
||||
_cached_allow_private_urls = bool(cfg.get("browser", {}).get("allow_private_urls"))
|
||||
browser_cfg = cfg.get("browser", {})
|
||||
if isinstance(browser_cfg, dict):
|
||||
_cached_allow_private_urls = is_truthy_value(
|
||||
browser_cfg.get("allow_private_urls"), default=False
|
||||
)
|
||||
except Exception as e:
|
||||
logger.debug("Could not read allow_private_urls from config: %s", e)
|
||||
return _cached_allow_private_urls
|
||||
|
||||
@@ -29,6 +29,8 @@ import os
|
||||
import socket
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from utils import is_truthy_value
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Hostnames that should always be blocked regardless of IP resolution
|
||||
@@ -107,12 +109,16 @@ def _global_allow_private_urls() -> bool:
|
||||
cfg = read_raw_config()
|
||||
# security.allow_private_urls (preferred)
|
||||
sec = cfg.get("security", {})
|
||||
if isinstance(sec, dict) and sec.get("allow_private_urls"):
|
||||
if isinstance(sec, dict) and is_truthy_value(
|
||||
sec.get("allow_private_urls"), default=False
|
||||
):
|
||||
_cached_allow_private = True
|
||||
return _cached_allow_private
|
||||
# browser.allow_private_urls (legacy fallback)
|
||||
browser = cfg.get("browser", {})
|
||||
if isinstance(browser, dict) and browser.get("allow_private_urls"):
|
||||
if isinstance(browser, dict) and is_truthy_value(
|
||||
browser.get("allow_private_urls"), default=False
|
||||
):
|
||||
_cached_allow_private = True
|
||||
return _cached_allow_private
|
||||
except Exception:
|
||||
|
||||
Reference in New Issue
Block a user