[security] fix(qqbot): bind approval buttons to initiating user#24437
Closed
Hinotoi-agent wants to merge 1 commit into
Closed
[security] fix(qqbot): bind approval buttons to initiating user#24437Hinotoi-agent wants to merge 1 commit into
Hinotoi-agent wants to merge 1 commit into
Conversation
Contributor
Author
|
CI note after opening:
Patch-specific validation remains:
|
599924a to
d395ee7
Compare
7b09608 to
524e7f3
Compare
Contributor
|
Automated hermes-sweeper review: this QQBot approval-button authorization issue is already fixed on current Evidence:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR hardens QQBot dangerous-command approval buttons so a group-visible inline keyboard cannot be used by a different group member to approve another user's pending command.
source.user_idfrom the gateway runner into the adapter's approval metadata.Security issues covered
allow-once,allow-always, ordenyfor another user's pending commandBefore this PR
approve:<session_key>:<decision>in the button payload._default_interaction_dispatch()did not verify the clicker'soperator_openidbefore resolving the approval.resolve_gateway_approval(session_key, choice)solely from button data.After this PR
send_exec_approval()records an in-memory approval context containing:Why this matters
Dangerous-command approval is a human-in-the-loop security boundary. In QQ group chats, the approval prompt may be visible to multiple people, but the pending command belongs to the user/session that triggered it.
Without checking the clicker identity, the boundary becomes "anyone who sees the group button can approve." The highest-impact case is a bystander clicking
allow-always, which maps to the approval subsystem's persistentalwaysdecision.How this differs from #18180
#18180 hardened Telegram inline approval callbacks by enforcing Telegram callback authorization through gateway user policy.
This PR fixes the same trust-boundary class for a different platform and code path:
gateway/platforms/qqbot/adapter.pyINTERACTION_CREATEgroup_member_openid/operator_openidThe Telegram fix does not cover QQBot's inline keyboard dispatcher.
Attack flow
Affected code
gateway/platforms/qqbot/adapter.py,gateway/run.pytests/gateway/test_qqbot_approval_auth.pyRoot cause
QQBot group approval button authorization gap:
InteractionEvent.operator_openidwas parsed but not enforced before callingresolve_gateway_approval().send_exec_approval()metadata, so the QQ adapter had no durable expected-operator binding for group prompts.CVSS assessment
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:NRationale:
Safe reproduction steps
session_key = agent:main:qqbot:group:group-1tovictim-openid._default_interaction_dispatch()a QQ groupINTERACTION_CREATEevent with:group_openid = group-1group_member_openid = attacker-openidbutton_data = approve:agent:main:qqbot:group:group-1:allow-alwaysresolve_gateway_approval().group_member_openid = victim-openidand observe that the dispatcher resolves the approval asalways.The new regression tests implement this flow without sending live QQ traffic.
Expected vulnerable behavior
On vulnerable code, the attacker event resolves the approval:
{ "parsed_operator_openid": "attacker-openid", "approval_calls": [ ["agent:main:qqbot:group:group-1", "always"] ] }That should not be allowed for a group-visible dangerous-command approval prompt.
Changes in this PR
_approval_contextto QQBot adapter instances for pending button-based dangerous-command approvals._is_approval_click_authorized()to enforce expected operator, scene, chat id, and timeout before resolving approval button clicks.send_exec_approval()fail safely when no expected operator is available, allowing the gateway fallback text approval flow instead of sending an unbound group keyboard.source.user_idandsource.chat_typefromgateway/run.pyinto adaptersend_exec_approval()metadata.Files changed
gateway/platforms/qqbot/adapter.pygateway/run.pytests/gateway/test_qqbot_approval_auth.pyMaintainer impact
/approvefallback remains available.Fix rationale
The authorization check belongs at the adapter dispatch boundary because QQ reports the clicker identity on each
INTERACTION_CREATEevent. The button payload identifies which approval to resolve, but it should not by itself authorize the clicker to resolve that approval.Passing the initiating user id from the gateway runner keeps the fix narrow and avoids changing the shared approval subsystem or adding platform-specific data to the approval session key.
Type of change
Test plan
python3.11 -m pytest tests/gateway/test_qqbot.py tests/gateway/test_qqbot_approval_auth.py -q148 passed, 1 warningRuntimeWarning: coroutine 'QQAdapter._send_identify' was never awaited.python3.11 -m ruff check gateway/platforms/qqbot/adapter.py gateway/run.py tests/gateway/test_qqbot_approval_auth.pyAll checks passed!git diff --check --cachedpython3.11 -m py_compile gateway/platforms/qqbot/adapter.py gateway/run.py tests/gateway/test_qqbot_approval_auth.pypython3.11 -m ruff format --check tests/gateway/test_qqbot_approval_auth.py1 file already formatted.python3.11 -m ruff format --check gateway/platforms/qqbot/adapter.py gateway/run.pyDisclosure notes