Problem
PR #450 at 87e14c8478d580e6e144c2642fa027dc1a58156f scopes queue persistence by canonical workspace and fingerprints each queued item, but enqueue authorization is not bound to the target session/run owner.
Any authenticated user can provide their own valid session_owner with another principal's known session_id in the same workspace. agents_chat_run_enqueue_permission() accepts the self-owned claim, and WP_Agent_Chat_Run_Control::queue_message() appends the item without verifying that the referenced run/session belongs to that owner.
The legitimate owner's next claim_queued_messages() call then filters out the foreign item and returns an empty array when the filtered count differs from the full queue. It does not remove either item, so the foreign item can indefinitely block continuation claims for that session.
Reproduction
Against PR #450 head:
- Create workspace run/session
network-session for owner user:123.
- As WordPress user 456, queue a message with the same workspace and
session_id, but principal/session owner user:456.
- Permission and enqueue both succeed.
- Claim the queue as owner
user:123.
- The claim returns no messages.
Observed result:
{"foreign_allowed":true,"foreign_queued":true,"owner_claim_count":0}
Relevant code
src/Channels/register-agents-chat-run-control-abilities.php:281-307 and :334-341: enqueue permission verifies that the caller matches the supplied owner claim, not that the target session/run does.
src/Runtime/class-wp-agent-chat-run-control.php:270-294: queue insertion does not verify the referenced run/session owner.
src/Runtime/class-wp-agent-chat-run-control.php:313-323: one foreign-owned item makes the legitimate owner's claim fail closed without clearing the poison item.
tests/chat-run-control-multisite-smoke.php: covers wrong-principal run reads and forged cancellation, but not wrong-principal enqueue/claim isolation.
Required contract
- Reject enqueue when the target run/session is not owned by the resolved principal.
- Ensure one principal cannot block another principal's queue claims.
- Add multisite workspace coverage for wrong-principal enqueue and mixed-owner queue state.
- Preserve omitted-workspace compatibility.
This blocks treating #450 as a complete foundation for Extra-Chill/extrachill-roadie#86 because #449 explicitly requires principal/session ownership across queue/continuation paths.
Problem
PR #450 at
87e14c8478d580e6e144c2642fa027dc1a58156fscopes queue persistence by canonical workspace and fingerprints each queued item, but enqueue authorization is not bound to the target session/run owner.Any authenticated user can provide their own valid
session_ownerwith another principal's knownsession_idin the same workspace.agents_chat_run_enqueue_permission()accepts the self-owned claim, andWP_Agent_Chat_Run_Control::queue_message()appends the item without verifying that the referenced run/session belongs to that owner.The legitimate owner's next
claim_queued_messages()call then filters out the foreign item and returns an empty array when the filtered count differs from the full queue. It does not remove either item, so the foreign item can indefinitely block continuation claims for that session.Reproduction
Against PR #450 head:
network-sessionfor owneruser:123.session_id, but principal/session owneruser:456.user:123.Observed result:
{"foreign_allowed":true,"foreign_queued":true,"owner_claim_count":0}Relevant code
src/Channels/register-agents-chat-run-control-abilities.php:281-307and:334-341: enqueue permission verifies that the caller matches the supplied owner claim, not that the target session/run does.src/Runtime/class-wp-agent-chat-run-control.php:270-294: queue insertion does not verify the referenced run/session owner.src/Runtime/class-wp-agent-chat-run-control.php:313-323: one foreign-owned item makes the legitimate owner's claim fail closed without clearing the poison item.tests/chat-run-control-multisite-smoke.php: covers wrong-principal run reads and forged cancellation, but not wrong-principal enqueue/claim isolation.Required contract
This blocks treating #450 as a complete foundation for Extra-Chill/extrachill-roadie#86 because #449 explicitly requires principal/session ownership across queue/continuation paths.