7befc1d2dd
Under gateway.multiplex_profiles, secondary profiles are constructed inside _profile_runtime_scope and their .env lives in the profile's secret scope - gateway/run.py explicitly does NOT mutate os.environ with it. Four adapters still read their AUTHORIZATION config via raw os.getenv, so every secondary profile either (a) silently missed its own env-only allowlists/policies (fail-closed: all DMs dropped at intake) or (b) inherited the default profile's GATEWAY_ALLOW_ALL_USERS=true / allowlists from the shared process env (fail-open admissions): - weixin.py: WEIXIN_DM_POLICY / WEIXIN_ALLOWED_USERS / WEIXIN_GROUP_ALLOWED_USERS / WEIXIN_ALLOW_ALL_USERS + GATEWAY_ALLOW_ALL_USERS in _open_dm_opted_in - yuanbao.py: YUANBAO_DM_POLICY / DM_ALLOW_FROM / GROUP_POLICY / GROUP_ALLOW_FROM / ALLOW_ALL_USERS (new _yb_secret helper; AccessPolicy hard-gates intake) - signal.py: SIGNAL_GROUP_ALLOWED_USERS / SIGNAL_ALLOWED_USERS (new _sig_secret helper; empty scoped group list previously meant "drop all groups" silently) - wecom/adapter.py: WECOM_DM_POLICY / WECOM_ALLOWED_USERS / WECOM_GROUP_POLICY / WECOM_ALLOW_ALL_USERS + GATEWAY_ALLOW_ALL_USERS - while credentials one line above already used _get_scoped_secret - gateway/run.py::_own_policy_open_startup_violation: the open-policy startup guard validated GATEWAY_ALLOW_ALL_USERS via raw os.getenv even though its sibling dm/group reads already used the scoped _getenv All reads now go through the canonical fail-closed scoped shape QQ's _resolve_qq_secret already used (scope hit wins; unscoped single-profile callers keep legacy os.environ behavior). Regression suite drives the real scope contextvar across all four helpers plus the admission gates and the startup guard, asserting both directions: profile values are visible under multiplex, default-profile values never leak. Fixes #93522