fix(telegram): a secondary profile's YAML proxy_url reaches request construction without an env bridge

545e74d0ea correctly stopped writing telegram.proxy_url into TELEGRAM_PROXY
for a multiplexed secondary, but _build_ptb_requests still resolved the proxy
only from that env var, so the secondary silently connected direct (or via the
default's proxy). #100448 had deliberately left this bridge unscoped for that
reason; this finishes the consumer migration instead.

_apply_yaml_config seeds proxy_url into extra and resolve_proxy_url gains a
`configured` rung: scoped TELEGRAM_PROXY → the profile's YAML → HTTPS_PROXY/
HTTP_PROXY/ALL_PROXY (trust_env) → macOS system proxy, with NO_PROXY semantics
unchanged.

Refs #108440 (finding 6)
This commit is contained in:
teknium1
2026-09-13 13:53:49 -07:00
committed by Teknium
parent 3dedb71f2f
commit 7c9175f48c
3 changed files with 18 additions and 9 deletions
+12 -7
View File
@@ -285,19 +285,24 @@ def should_bypass_proxy(target_hosts: str | list[str] | tuple[str, ...] | set[st
def resolve_proxy_url(
platform_env_var: str | None = None, *,
target_hosts: str | list[str] | tuple[str, ...] | set[str] | None = None) -> str | None:
"""Proxy URL: *platform_env_var* (e.g. ``DISCORD_PROXY``) first, then HTTPS_PROXY /
HTTP_PROXY / ALL_PROXY (any case), then the macOS system proxy — the latter two only when
``gateway.trust_env`` is true. None when nothing is found or NO_PROXY matches a target.
target_hosts: str | list[str] | tuple[str, ...] | set[str] | None = None,
configured: str | None = None) -> str | None:
"""Proxy URL: *platform_env_var* (e.g. ``DISCORD_PROXY``) first, then the adapter's own YAML
value *configured* (``telegram.proxy_url``), then HTTPS_PROXY / HTTP_PROXY / ALL_PROXY (any
case), then the macOS system proxy — the latter two only when ``gateway.trust_env`` is true.
None when nothing is found or NO_PROXY matches a target.
*platform_env_var* is a per-adapter, per-profile-configurable setting (each proxy URL can
embed credentials, e.g. ``http://user:pass@host``) so it is read scope-aware: under a
secondary multiplex profile it comes from that profile's own ``.env``, not the shared
process env another profile's ``TELEGRAM_PROXY``/``DISCORD_PROXY``/etc. may hold. The
generic ``HTTPS_PROXY``/``HTTP_PROXY``/``ALL_PROXY`` fallback stays a raw process-env read —
those are OS/system-level network settings, not a per-profile Hermes concept."""
process env another profile's ``TELEGRAM_PROXY``/``DISCORD_PROXY``/etc. may hold; the YAML
value is the same profile's, so a secondary keeps its configured route without any env
bridge (#108440). The generic ``HTTPS_PROXY``/``HTTP_PROXY``/``ALL_PROXY`` fallback stays a raw
process-env read — those are OS/system-level network settings, not a per-profile Hermes concept."""
from gateway.platforms._shared import get_scoped_secret as _get_scoped_proxy_var
value = (_get_scoped_proxy_var(platform_env_var, "") or "").strip() if platform_env_var else ""
if not value:
value = str(configured or "").strip()
if not value:
if not gateway_trust_env(): # only the explicit per-platform var is honored
return None
+5 -1
View File
@@ -2797,7 +2797,9 @@ class TelegramAdapter(BasePlatformAdapter):
fallback_ips = list(SEED_FALLBACK_IPS)
else:
logger.info("[%s] Auto-discovered Telegram fallback IPs: %s", self.name, ", ".join(fallback_ips))
proxy_url = resolve_proxy_url("TELEGRAM_PROXY", target_hosts=["api.telegram.org", *fallback_ips])
proxy_url = resolve_proxy_url(
"TELEGRAM_PROXY", target_hosts=["api.telegram.org", *fallback_ips],
configured=self.config.extra.get("proxy_url"))
def _pair(general_httpx: dict, updates_httpx: dict, **extra) -> tuple:
return (HTTPXRequest(**request_kwargs, **extra, httpx_kwargs=general_httpx),
@@ -6563,6 +6565,8 @@ def _apply_yaml_config(yaml_cfg: dict, telegram_cfg: dict) -> dict | None:
_bridge_gate(key, env, telegram_cfg.get(key), seed_extra=seed)
_bridge_lower("reactions", "TELEGRAM_REACTIONS")
if "proxy_url" in telegram_cfg:
# Seeded into extra so ``_build_ptb_requests`` keeps a secondary's route without the env bridge.
extras.setdefault("proxy_url", str(telegram_cfg["proxy_url"]).strip())
_set_env("TELEGRAM_PROXY", str(telegram_cfg["proxy_url"]).strip())
_telegram_extra = telegram_cfg.get("extra") if isinstance(telegram_cfg.get("extra"), dict) else {}
_telegram_rtm = telegram_cfg["reply_to_mode"] if "reply_to_mode" in telegram_cfg else _telegram_extra.get("reply_to_mode")
@@ -326,7 +326,7 @@ async def test_fallback_disabled_excludes_configured_ips_from_proxy_targets(monk
proxy_targets = []
def resolve_proxy(_env_name, *, target_hosts):
def resolve_proxy(_env_name, *, target_hosts, configured=None):
proxy_targets.append(list(target_hosts))
return "http://127.0.0.1:8080"