fix(gateway): make Discord/Telegram allow/deny gates per-profile under multiplex - #75970
Conversation
…tiplex_profiles Under gateway.multiplex_profiles, Discord and Telegram authorization gates (allowed/ignored channels, allowed users/roles, allow-all flags) were read from process-global os.environ, populated first-writer-wins by the YAML->env bridge in each adapter's _apply_yaml_config. The first profile to initialize pinned its allow/deny lists — and its ALLOW_ALL flags — for every other profile in the process (issue #72348, incl. the Telegram mirror reported in the thread). Fix (per-adapter-instance gate reads, whole class): - gateway/authz_mixin.py: new _platform_gate_env — scope-authoritative gate read: under an installed profile secret scope with multiplex active, a missing key returns the default instead of falling through to os.environ (which may hold another profile's value). Single-profile behavior is byte-identical to os.getenv. - Discord adapter: - connect() snapshots all gate env vars (_GATE_ENV_KEYS) inside the owning profile's runtime scope into a per-adapter dict; new accessors (_get_allowed_channels/_get_ignored_channels/_get_allowed_users/ _get_allowed_roles/_get_no_thread_channels/_discord_allow_all_users/ _gateway_allow_all_users/_get_allow_bots) resolve snapshot -> config.extra -> scope-aware env, replacing every raw os.getenv gate read: on_message channel gates, _is_allowed_user allow-all flags, slash authorization, fail-closed diagnostics, missed-message backfill, bot-message gating, and _component_check_auth (component buttons). - _apply_yaml_config always seeds gate values into PlatformConfig.extra (incl. new allowed_roles / allow_all_users keys) and SKIPS the process-global env writes when loading a profile-scoped config under multiplex; the legacy first-writer env bridge is preserved verbatim for single-profile deployments. - _resolve_allowed_usernames no longer unconditionally rewrites os.environ[DISCORD_ALLOWED_USERS] — under multiplex the resolved IDs stay adapter-local (snapshot refresh); single-profile keeps the env rewrite. - Telegram adapter (mirror of the same class): intake prefilter and callback-auth fallbacks, _telegram_auth_env_configured, and the allowed/ignored chats-topics-threads getters now read via the scoped gate reader; _apply_yaml_config skips authorization env writes for profile-scoped loads and seeds free_response_chats/ignored_threads extras. Regression tests (tests/plugins/platforms/test_discord_gate_isolation.py): two adapter instances with different allow-lists enforce their OWN lists order-independently across message, slash, and component gates; negative allow-all case proves profile A's open-access flag cannot authorize profile B; username-resolution env-clobber; YAML-bridge seeding/skip matrix; and the Telegram scoped-reader matrix. Sabotage-verified: reverting either the Discord snapshot accessors or the Telegram scoped reader fails 12/2 tests respectively. Credit: builds on the per-adapter accessor direction of PR #72427 (@JonthanaHanh) and the scope-aware-reader approach validated live on v0.19.0 by @yournetworkplug-ctrl for the Telegram mirror; scope corrections from jackjin1997's and cal88's analysis in the issue thread (allow-all flags, unguarded username-resolution env write, per-site channel reads). Fixes #72348
8a99cfa to
54d8b3a
Compare
CI's plugin-test slice runs without the discord optional extra; the raw import failed with ModuleNotFoundError while every other test in the file uses injected mock modules.
૮ >ﻌ< ა ci reviewran on f35cca3 ℹ️ InfoDesktop E2E visual evidence · View test artifacts · View job2 visual diffs. inline evidence upload failed. Failed to upload diff-1508682a2ae8-boot-ready-diff.png with gh image (exit code 1): Error uploading /home/runner/work/_temp/e2e-evidence/diff-1508682a2ae8-boot-ready-diff.png: step 0 (get upload token): uploadToken not found on repo page — do you have write access to NousResearch/hermes-agent? (or, if NousResearch enforces SAML SSO, authorize at https://github.com/orgs/NousResearch/sso) |
|
suggesting changes The adapter-local Discord/Telegram gates are substantially improved, but the gateway's authoritative authorization decision still reads the process-global first-profile allowlist. A focused two-profile probe reproduces the same cross-profile authorization failure on both current main and the PR head: profile B authorizes profile A's user and rejects its own configured user. Because this remaining path is the final inbound gateway gate before agent execution, the PR does not yet close the reported security boundary.
Security evidence:
Uncertainty: The leased environment lacks pytest, so the repository's focused unit tests were not executed.; No live Discord or Telegram network session was exercised; validation used the real local authorization code with deterministic profile and sender inputs. Signed: GPT-5.6-sol-xhigh in Codex |
Summary
Under
gateway.multiplex_profiles, Discord/Telegram authorization gates read process-global env populated first-writer-wins — profile A's allow/deny lists and allow-all flags governed profile B. All gate reads are now per-adapter-instance.Changes
_platform_gate_env— under multiplex with a profile scope installed, a scope miss returns the default instead of falling through to another profile's env value; single-profile behavior identical.DISCORD_ALLOWED_USERSenv clobber (multiplex only).Deferred (non-security, listed for follow-up)
Non-auth Discord env bridges (auto_thread, reactions, history_backfill, require_mention, reply_to_mode); Slack/Signal/WhatsApp sibling bridges; gateway
_auth_envtightening (already scope-preferring via #65629/#65700).Validation
21 new order-independent regression tests (two-adapter own-list enforcement, negative allow-all isolation, slash-gate isolation, env-clobber, bridge skip/seed matrix, Telegram scoped-reader matrix); sabotage-verified both platforms; ~1,100 related gateway tests pass (4 pre-existing flakes reproduce on clean main); ruff clean.
Credits: direction from #72427 (@JonthanaHanh), @yournetworkplug-ctrl's live-verified Telegram approach, thread analysis by @jackjin1997 / @cal88.
Fixes #72348
Infographic