Root cause
Injection & unsafe execution introduced
fix(simplex): accept display name in SIMPLEX_ALLOWED_USERS
AI-assisted change: Claude
How AI contributed: Direct introductionPublished Jul 26, 2026 in
nousresearch/hermes-agentAll 2 findings in this repository
The AI-written change let a SIMPLEX_ALLOWED_USERS entry match a contact's display name as well as the stable numeric contact identifier. The adapter fills the session user name from remote profile metadata, and the gateway adds that name to the set of accepted identities for the platform. Because any contact can set their profile name to an allowlisted value and message the bot, a remote user impersonates an approved sender and reaches the normal agent and tool path. The project has not merged a fix.
The gateway's _is_user_authorized adds the session display name to the accepted identities for the simplex platform before comparing them with SIMPLEX_ALLOWED_USERS. Display names are attacker-controlled profile metadata rather than stable contact identifiers, so the allowlist becomes a name-matching check that any inbound contact can satisfy. Matching only the numeric contact identifier restores the intended sender boundary.
Root cause
fix(simplex): accept display name in SIMPLEX_ALLOWED_USERS
AI-assisted change: Claude
Fix status
No minimum fix commit has been established for this finding.
Fix status unresolved
Case facts
@@ -20,7 +20,11 @@ (default: ws://127.0.0.1:5225) Optional environment variables:- SIMPLEX_ALLOWED_USERS Comma-separated contact IDs (allowlist)+ SIMPLEX_ALLOWED_USERS Comma-separated allowlist. Each entry may be+ either a numeric contactId (stable across+ renames; visible via `/contacts` in the CLI)+ or a contact display name (what the SimpleX+ UI shows). Both forms are accepted. SIMPLEX_ALLOW_ALL_USERS Set 'true' to allow all contacts SIMPLEX_HOME_CHANNEL Default contact/group ID for cron delivery SIMPLEX_HOME_CHANNEL_NAME Human label for the home channel@@ -706,7 +710,7 @@ def _prompt(var: str, prompt: str, *, secret: bool = False) -> None: save_env_value(var, value) _prompt("SIMPLEX_WS_URL", "Daemon WebSocket URL (default ws://127.0.0.1:5225)")- _prompt("SIMPLEX_ALLOWED_USERS", "Allowed contact IDs (comma-separated; blank=skip)")+ _prompt("SIMPLEX_ALLOWED_USERS", "Allowed contactIds or display names (comma-separated; blank=skip)") _prompt("SIMPLEX_HOME_CHANNEL", "Home channel contact/group ID (or empty)") print("Done. Make sure the simplex-chat daemon is running before starting the gateway.") @@ -7281,6 +7281,21 @@ def _is_user_authorized(self, source: SessionSource) -> bool: if normalized_user_id: check_ids.add(normalized_user_id) + # SimpleX: SIMPLEX_ALLOWED_USERS accepts either the numeric contactId+ # or the contact's display name. The adapter sets user_id=contactId for+ # stability across renames, but the SimpleX UI never surfaces the+ # numeric id — operators only see display names, so that's what they+ # naturally put in the env var. Match both so the allowlist works+ # regardless of which form was chosen.+ # Plugin platform: compare by value since Platform.SIMPLEX is not a+ # hardcoded enum member (it's a dynamic plugin platform).+ if (+ source.platform is not None+ and source.platform.value == "simplex"+ and source.user_name+ ):+ check_ids.add(source.user_name)+ return bool(check_ids & allowed_ids) def _get_unauthorized_dm_behavior(self, platform: Optional[Platform]) -> str:@@ -52,20 +52,20 @@ SIMPLEX_HOME_CHANNEL=<contact-id> | Variable | Required | Description | |---|---|---| | `SIMPLEX_WS_URL` | Yes | WebSocket URL of the simplex-chat daemon |-| `SIMPLEX_ALLOWED_USERS` | Recommended | Comma-separated contact IDs allowed to use the agent |+| `SIMPLEX_ALLOWED_USERS` | Recommended | Comma-separated allowlist. Each entry can be a numeric `contactId` **or** a display name — both forms work. | | `SIMPLEX_ALLOW_ALL_USERS` | Optional | Set `true` to allow every contact (use carefully) | | `SIMPLEX_HOME_CHANNEL` | Optional | Default contact ID for cron job delivery | | `SIMPLEX_HOME_CHANNEL_NAME` | Optional | Human label for the home channel | -## Find your contact ID+## Find your contact ID or display name -After starting the daemon, open a conversation with your agent contact. The contact ID will appear in session logs or via `hermes send_message action=list`.+After starting the daemon, open a conversation with your agent contact. The numeric `contactId` appears in session logs or via `hermes send_message action=list`. If you'd rather use the display name shown in the SimpleX UI, that works too — `SIMPLEX_ALLOWED_USERS` accepts either form. ## Authorization By default **all contacts are denied**. You must either: -1. Set `SIMPLEX_ALLOWED_USERS` to a comma-separated list of contact IDs, or+1. Set `SIMPLEX_ALLOWED_USERS` to a comma-separated list of `contactId`s and/or display names (e.g. `SIMPLEX_ALLOWED_USERS=4,alice` matches either contactId 4 or the contact whose display name is "alice"), or 2. Use **DM pairing** — send any message to the bot and it will reply with a pairing code. Enter that code via `hermes gateway pair`. ## Using SimpleX with cron jobs