You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Escape user-controlled values before interpolating them into OQL #7
Several tools interpolate a user-controlled value directly into a quoted OQL string
literal, with no escaping: get-person-from-email (email), get-person-from-fullname
(fullname), get-person-from-telephone (telephone), and search-user-request-by-caller-status-start-date (caller_email, statuses, start_date). A quote in any of them breaks out of the literal and lets the rest of the
value alter the query.
Impact
The search tools run with the API account's own iTop permissions, not the caller's, so
this can widen a caller-scoped search (e.g. search-user-request-by-caller-status-start-date)
beyond the intended caller and return tickets belonging to someone else.
Proposal
Centralize OQL literal escaping in the shared tools base class, and apply it to every
user-controlled value interpolated into OQL. A focused PR will follow.
changed the title [-]Escape caller email before interpolating it into OQL[/-][+]Escape user-controlled values before interpolating them into OQL[/+]on Sep 1, 2026
Confirmed this is exploitable in practice, not just theoretical. On an iTop 3.2.3-1 Professional
instance, calling get-person-from-fullname with:
fullname = "test' OR '1'='1"
returns everyPerson record in the database, not just the intended single match — full
table exfiltration through one parameter of a non-admin tool call.
Two more data points that might be useful for the fix:
get-person-from-email is not exploitable the same way: the #[Schema(format: 'email')]
constraint rejects non-email-shaped payloads before the tool body runs. That's an incidental
side effect of format validation, not deliberate escaping — fullname has no such constraint
and is the practically exploitable path.
A literal backslash in fullname (e.g. back\slash) breaks the query and leaks internal
details back to the MCP client: Error: Query failed to execute: query = SELECT Person WHERE friendlyname = 'back\slash', exception_class = Exception, exception_message = .... Worth
covering explicitly since it also exposes raw OQL/exception internals to whatever's on the
other end of the MCP connection (frequently an LLM). The proposed escapeOqlLiteral() already
strips backslashes, so this should be resolved as a side effect of the same fix.
Thanks David for the feedback. Really interesting that you reproduced it, and on a Professional instance on top of that, that helps a lot.
#8 does fix your backslash case, but not the leak itself: the raw OQL comes from the error path, which #8 never touches.
templates/Anything-output.toon.twig:2 prints {{json.message}} as-is when json.objects === null
src/Tools/iTopRestTools.php:28-31 returns $t->getMessage() for every tool
Any other failure, permissions, unknown class, a Twig error, leaks the same way.
@Combodo : separate issue, or fold it into this one? I'd say a new one?
@dguembel-itomig : #8 strips quotes too, so O'Connor becomes a search for OConnor. Do you see apostrophes in real Person names? That settles whether to strip or escape.
Activity
[-]Escape caller email before interpolating it into OQL[/-][+]Escape user-controlled values before interpolating them into OQL[/+]dguembel-itomig commentedon Sep 4, 2026
Confirmed this is exploitable in practice, not just theoretical. On an iTop 3.2.3-1 Professional
instance, calling
get-person-from-fullnamewith:fullname = "test' OR '1'='1"returns every
Personrecord in the database, not just the intended single match — fulltable exfiltration through one parameter of a non-admin tool call.
Two more data points that might be useful for the fix:
get-person-from-emailis not exploitable the same way: the#[Schema(format: 'email')]constraint rejects non-email-shaped payloads before the tool body runs. That's an incidental
side effect of format validation, not deliberate escaping —
fullnamehas no such constraintand is the practically exploitable path.
fullname(e.g.back\slash) breaks the query and leaks internaldetails back to the MCP client:
Error: Query failed to execute: query = SELECT Person WHERE friendlyname = 'back\slash', exception_class = Exception, exception_message = .... Worthcovering explicitly since it also exposes raw OQL/exception internals to whatever's on the
other end of the MCP connection (frequently an LLM). The proposed
escapeOqlLiteral()alreadystrips backslashes, so this should be resolved as a side effect of the same fix.
agent-qv commentedon Sep 4, 2026
Thanks David for the feedback. Really interesting that you reproduced it, and on a Professional instance on top of that, that helps a lot.
#8 does fix your backslash case, but not the leak itself: the raw OQL comes from the error path, which #8 never touches.
templates/Anything-output.toon.twig:2 prints {{json.message}} as-is when json.objects === null
src/Tools/iTopRestTools.php:28-31 returns $t->getMessage() for every tool
Any other failure, permissions, unknown class, a Twig error, leaks the same way.
@Combodo : separate issue, or fold it into this one? I'd say a new one?
@dguembel-itomig : #8 strips quotes too, so O'Connor becomes a search for OConnor. Do you see apostrophes in real Person names? That settles whether to strip or escape.
Regards, Quentin, with the help of AI.
Issue #7 - Escape user-controlled values
dflaven commentedon Sep 17, 2026
The above mentioned commit fixes this !
Issue #7 - Escape user-controlled values
Merge upstream/master into pr/escape-oql-literals
Merge upstream/master into pr/incident-tools