fix(gateway): isolate _auth_env under multiplex to prevent cross-profile allowlist leak (#80026) - #80238
Conversation
…ile allowlist leak (NousResearch#80026) _auth_env() fell back to os.getenv() when the profile secret scope did not contain the requested key. Under multiplex, process-global environ may hold another profile's first-writer-bridged allowlist value, causing profile B's authorization to consume profile A's allowlist. Apply the same multiplex guard already used by _platform_gate_env(): when a scope is installed AND multiplex is active, absent keys return default instead of falling through to os.environ. Fixes NousResearch#80026
|
suggesting changes The authorization helper still allows a shared process-environment allowlist or allow-all value to influence an active multiplexed lookup when profile-scope resolution fails. In multiplexed operation, every scope-resolution failure must return the supplied default; only the legacy non-multiplexed path may consult the process environment. Until that fail-closed behavior is enforced, one profile can still authorize a sender using another profile’s bridged value. Security evidence:
Not checked:
Signed: GPT-5.6-luna-max in Codex |
…ate is fixed The _auth_env isolation in authz_mixin.py now correctly prevents cross-profile env leakage under multiplex, so the previously-expected-fail test test_scope_does_not_inherit_environ_opt_in passes. Remove the xfail(strict=True) marker so CI goes green.
|
suggesting changes The new head closes the ordinary installed-scope miss and makes the previously xfailed isolation test pass, but the earlier request to fail closed for a missing or failed scope is still open. With multiplexing active and Security evidence:
Not checked:
Signed: GPT-5.6-sol-xhigh in Codex |
|
suggesting changes The proposed scoped-miss fix prevents a profile scope from inheriting another profile's authorization values, but multiplex authorization still falls back to the process environment when no profile scope is installed. The secondary busy-session callback, startup/reconnect resume validation, adapter-side sender checks, and voice authorization invoke authorization outside the routed profile scope, so a process-global allowlist or allow-all setting can authorize an event under the wrong profile. Keep this change blocked until those flows run inside the routed profile scope or the helper fails closed when multiplexing has no active scope.
The authorization helper must use only the routed profile's scope while multiplexing. A missing scope must not read process-global values; single-profile fallback remains allowed. Apply the routed scope to busy-session authorization, startup/reconnect resume-owner validation, and adapter-side sender and voice authorization callbacks, or make the helper fail closed when no scope is active, and add regression coverage for these flows with an empty scope and conflicting process values. Security evidence:
Not checked:
Signed: GPT-5.6-luna-max in Codex |
|
Implemented on main via #93446 (2912c36): |
Fix: Isolate
_auth_envunder multiplex to prevent cross-profile allowlist leakFixes #80026
Root Cause
_auth_env()ingateway/authz_mixin.pyreads allowlist/auth environment variables used by the gateway's second authorization gate. When the profile's secret scope does not contain a requested key, it falls back toos.getenv()— the process-global environment.Under multiplexed deployments, the process env holds first-writer-bridged values from whichever profile armed its YAML→env bridge first. This causes profile B's authorization gate to consume profile A's allowlist values (e.g.
DISCORD_ALLOWED_USERS,GATEWAY_ALLOWED_USERS).Fix
Apply the same multiplex isolation guard already used by
_platform_gate_env()(lines 46-73): when a profile scope is installed ANDis_multiplex_active(), absent keys returndefaultinstead of falling through toos.environ.Single-profile deployments (no scope installed, or multiplex off) retain the legacy
os.getenv()fallback — behavior-neutral for the common case.Verification
python3 -m py_compile gateway/authz_mixin.py— syntax OK_platform_gate_env()which has been in production since the [Bug]: Discord adapter allow/deny gates are process-global, breaking per-profile isolation under multiplex_profiles #72348 fix_authorization_gate_check()and_is_user_authorized()now use the isolated path under multiplex