From ab70551b3de2de49658e65476b1bdeeadc55a2f1 Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Sun, 5 Jul 2026 19:20:06 -0700 Subject: [PATCH] fix(gateway): fail-closed adapter resolution for unregistered secondary profiles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to the routing sweep: when a stamped secondary profile has no _profile_adapters entry (adapter failed to connect / was refused), return None instead of falling back to the default profile's adapter — the fallback sends replies out the wrong bot, which is the exact leak class this cluster fixes. Also restores main's deliberate fail-fast on port-binding platforms in secondary profiles (the cherry-picked commit had softened it to silent force-disable). Co-authored-by: ManniBr --- gateway/authz_mixin.py | 6 +++++- gateway/run.py | 18 ++++-------------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/gateway/authz_mixin.py b/gateway/authz_mixin.py index 86984913372..f84ca259ccb 100644 --- a/gateway/authz_mixin.py +++ b/gateway/authz_mixin.py @@ -47,10 +47,14 @@ class GatewayAuthorizationMixin: if not platform: return None profile_name = (profile or "").strip() or None - if profile_name: + if profile_name and profile_name != "default": profile_adapters = getattr(self, "_profile_adapters", None) or {} if profile_name in profile_adapters: return profile_adapters[profile_name].get(platform) + # Fail closed: a stamped secondary profile with no registry entry + # (e.g. its adapter failed to connect) must NOT fall back to the + # default profile's adapter — that sends replies out the wrong bot. + return None adapters = getattr(self, "adapters", None) or {} return adapters.get(platform) diff --git a/gateway/run.py b/gateway/run.py index fd6f9d6a42f..51e33ceb499 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -8337,25 +8337,15 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew "'open'." ) - # Secondary profiles must never bind ports — the default profile's - # listener serves every profile through /p//. Force-disable - # any port-binding platform that _apply_env_overrides may have enabled. - for pb_platform in _PORT_BINDING_PLATFORM_VALUES: - try: - plat = Platform(pb_platform) - except ValueError: - continue - if plat in profile_cfg.platforms: - profile_cfg.platforms[plat].enabled = False - profile_map = self._profile_adapters.setdefault(profile_name, {}) connected = 0 for platform, platform_config in profile_cfg.platforms.items(): if not platform_config.enabled: continue - # The default profile owns the single shared HTTP listener and - # serves every profile through the /p// URL prefix. - # A secondary profile must NOT enable a port-binding platform. + # A secondary profile must NOT enable a port-binding platform: the + # default profile's listener already serves every profile via the + # /p// prefix, so a second bind can only collide. This is a + # config error, not a transient failure — fail fast and loud. if platform.value in _PORT_BINDING_PLATFORM_VALUES: raise MultiplexConfigError( f"Profile '{profile_name}' enables the port-binding platform "