Problem
Tools/ChatParser.php getPending/setPending/clearPending key only on the request-supplied session_id:
getPending queries oc_sessions with WHERE id = ? (no user_id)
setPending INSERTs with user_id = NULL explicitly
clearPending clears by id only
Frogman.class.php::handleChatRequest() reads session_id from the POST body verbatim and falls back to the literal string 'chat-default' when absent.
Two consequences:
chat-default shared bucket — two ad-hoc API callers that both omit session_id collide on the same pending state. Last writer wins on confirm/cancel. Footgun.
- No user_id binding on pending rows — even with explicit session_ids, the row that holds the queued tool + params is keyed solely on session_id.
Why this is LOW (not GHSA)
The realistic attack chain ends up as self-confused-deputy at most:
- The caller doing the "yes" must already be authenticated.
runTool() uses the calling user's authContext, so the permission check is against the would-be attacker, not the original queuer — the attacker can only execute tools they're already authorized for.
- The audit row attributes to the attacker; no identity laundering.
So the "gain" is "execute under your own audit identity an action you could already execute under your own audit identity." Not an escalation. The genuine concern is the shared-bucket footgun.
Fix
Two changes, either alone closes the footgun:
- Fail closed on missing
session_id — return 400 if the body doesn't supply one, instead of defaulting to chat-default.
- Bind pending rows to
user_id — store the calling user's id in oc_sessions.user_id on setPending, and add AND user_id = ? to getPending/clearPending.
Files
Tools/ChatParser.php:19-48 (getPending/setPending/clearPending)
Frogman.class.php:230 (the 'chat-default' fallback)
Discovered
Internal security audit, 2026-05-18.
Problem
Tools/ChatParser.phpgetPending/setPending/clearPendingkey only on the request-suppliedsession_id:getPendingqueriesoc_sessionswithWHERE id = ?(no user_id)setPendingINSERTs withuser_id = NULLexplicitlyclearPendingclears by id onlyFrogman.class.php::handleChatRequest()readssession_idfrom the POST body verbatim and falls back to the literal string'chat-default'when absent.Two consequences:
chat-defaultshared bucket — two ad-hoc API callers that both omitsession_idcollide on the same pending state. Last writer wins on confirm/cancel. Footgun.Why this is LOW (not GHSA)
The realistic attack chain ends up as self-confused-deputy at most:
runTool()uses the calling user'sauthContext, so the permission check is against the would-be attacker, not the original queuer — the attacker can only execute tools they're already authorized for.So the "gain" is "execute under your own audit identity an action you could already execute under your own audit identity." Not an escalation. The genuine concern is the shared-bucket footgun.
Fix
Two changes, either alone closes the footgun:
session_id— return 400 if the body doesn't supply one, instead of defaulting tochat-default.user_id— store the calling user's id inoc_sessions.user_idonsetPending, and addAND user_id = ?togetPending/clearPending.Files
Tools/ChatParser.php:19-48(getPending/setPending/clearPending)Frogman.class.php:230(the'chat-default'fallback)Discovered
Internal security audit, 2026-05-18.