Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions gateway/platforms/webhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,9 @@ def _validate_signature(
self, request: "web.Request", body: bytes, secret: str
) -> bool:
"""Validate webhook signature (GitHub, GitLab, generic HMAC-SHA256)."""
# Reject unresolved ${VAR} env-template placeholders — these would
# otherwise be HMAC'd verbatim, accepting any caller who guesses the
# literal ``${WEBHOOK_SECRET}`` string as the secret.
if _looks_unresolved_secret(secret):
logger.warning(
"[webhook] Unresolved placeholder secret configured (e.g. ${WEBHOOK_SECRET}) — rejecting"
Expand Down
2 changes: 1 addition & 1 deletion gateway/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -5373,7 +5373,7 @@ def _is_user_authorized(self, source: SessionSource) -> bool:
user_id = source.user_id
if not user_id:
return False
team_id = (source.guild_id or "").strip() if source.platform == Platform.SLACK else ""
team_id = str(source.guild_id or "").strip() if source.platform == Platform.SLACK else ""

platform_env_map = {
Platform.TELEGRAM: "TELEGRAM_ALLOWED_USERS",
Expand Down
5 changes: 1 addition & 4 deletions gateway/session_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
platform = get_session_env("HERMES_SESSION_PLATFORM", "")
"""

import os
from contextvars import ContextVar
from typing import Any

Expand Down Expand Up @@ -151,8 +152,6 @@ def get_session_env(name: str, default: str = "") -> str:
don't use ``set_session_vars`` at all).
3. *default*
"""
import os

var = _VAR_MAP.get(name)
if var is not None:
value = var.get()
Expand Down Expand Up @@ -180,8 +179,6 @@ def get_terminal_cwd(default=None):
take precedence so concurrent gateway/cron sessions cannot clobber
each other.
"""
import os

value = _TERMINAL_CWD.get()
if value is not _UNSET:
return value
Expand Down
31 changes: 12 additions & 19 deletions tools/browser_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,10 @@ def _browser_url_security_block(url: str, *, auto_local_this_nav: bool = False)
),
}

if not _is_local_backend() and _is_always_blocked_url(url):
if _is_always_blocked_url(url):
return {"success": False, "error": "Blocked: URL targets a cloud metadata endpoint"}

if _is_camofox_mode():
if _is_always_blocked_url(url):
return {"success": False, "error": "Blocked: URL targets a cloud metadata endpoint"}
if not _is_safe_url(url):
return {"success": False, "error": "Blocked: URL targets a private or internal address"}

Expand Down Expand Up @@ -2298,23 +2296,19 @@ def browser_navigate(url: str, task_id: Optional[str] = None) -> str:
# 169.254.169.254 / metadata.google.internal / ECS task metadata
# via a browser, and routing those to a local Chromium sidecar
# on an EC2/GCP/Azure host exfiltrates IAM credentials (#16234).
if not _is_local_backend() and _is_always_blocked_url(url):
# Local backends are NOT exempt — an agent on EC2 with a local
# Chromium and allow_private_urls=true could otherwise hit IMDS.
if _is_always_blocked_url(url):
return json.dumps({
"success": False,
"error": "Blocked: URL targets a cloud metadata endpoint",
})

if _is_camofox_mode():
if _is_always_blocked_url(url):
return json.dumps({
"success": False,
"error": "Blocked: URL targets a cloud metadata endpoint",
})
if not _is_safe_url(url):
return json.dumps({
"success": False,
"error": "Blocked: URL targets a private or internal address",
})
if _is_camofox_mode() and not _is_safe_url(url):
return json.dumps({
"success": False,
"error": "Blocked: URL targets a private or internal address",
})

if (
not auto_local_this_nav
Expand Down Expand Up @@ -2378,11 +2372,10 @@ def browser_navigate(url: str, task_id: Optional[str] = None) -> str:
# and for the hybrid local sidecar (we're already on a local browser
# hitting a private URL by design).
# Always-blocked floor (cloud metadata / IMDS) is enforced even
# when auto_local_this_nav is true — see pre-nav check for
# rationale (#16234).
# when auto_local_this_nav is true and for local backends — see
# pre-nav check for rationale (#16234).
if (
not _is_local_backend()
and final_url
final_url
and final_url != url
and _is_always_blocked_url(final_url)
):
Expand Down
3 changes: 2 additions & 1 deletion tools/web_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,9 @@ def _ddgs_package_available() -> bool:
return ddgs_package_available()


# Backward-compat alias for older tests / external callers that imported
# the previous name. New code should call ``_ddgs_package_available``.
def _ddgs_package_importable() -> bool:
"""Backward-compat alias for :func:`_ddgs_package_available`."""
return _ddgs_package_available()

# ─── Firecrawl Client ────────────────────────────────────────────────────────
Expand Down
Loading