Skip to content

Escape user-controlled values before interpolating them into OQL - #8

Open
agent-qv wants to merge 3 commits into
Combodo:masterfrom
agent-qv:pr/escape-oql-literals
Open

agent-qv wants to merge 3 commits into
Combodo:masterfrom
agent-qv:pr/escape-oql-literals

Conversation

@agent-qv

@agent-qv agent-qv commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Closes #7

Problem

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 beyond the intended caller and return tickets
belonging to someone else.

Fix

Adds escapeOqlLiteral() on the shared iTopRestTools base class - it strips quotes,
the escape character and control characters, which is enough for exact-match values
(equality, IN lists). It intentionally leaves % and _ alone, since those are valid
characters in an email or a name; a LIKE pattern would need its own wildcard-aware
sanitization instead.

Applied to all four affected tools.

caller_email, telephone, email, fullname, statuses and start_date were all
interpolated as-is into a quoted OQL string literal. A quote in any of them
breaks out of the literal and lets the rest of the value alter the query -
for instance widening a caller-scoped ticket search to other callers, since
the search runs with the API account's own permissions rather than the
caller's.

Adds escapeOqlLiteral() on the shared iTopRestTools base class - it strips
quotes, the escape character and control characters, which is enough for
exact-match values (equality, IN lists). It intentionally leaves % and _
alone, since those are valid characters in an email or a name.
Copilot AI balanced review requested due to automatic review settings September 1, 2026 10:55

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds centralized OQL literal sanitization to prevent user-controlled query injection.

Changes:

  • Adds escapeOqlLiteral().
  • Applies sanitization across four person/request search tools.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/Tools/iTopRestTools.php Adds shared OQL sanitization.
src/Tools/core/get/iTopGetTools.php Sanitizes interpolated search parameters.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/Tools/iTopRestTools.php Outdated
Comment on lines +48 to +50
$value = str_replace(["\\", "'", '"'], '', $value);
$value = preg_replace('/[\x00-\x1F\x7F]/u', '', $value);
return trim($value);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @agent-qv, it seems that indeed, your proposition may have unwanted behaviors in some cases.
Could you please correct it, and add unit tests so it's easier to test and avoid future regression ? :)

agent-qv added a commit to agent-qv/itop-mcp that referenced this pull request Sep 2, 2026
searchIncidentByCallerStatusStartDate interpolates caller_email/statuses/start_date
into an OQL query string, the same pattern PR Combodo#8 fixes for the pre-existing
UserRequest/Person tools. Combodo#8 can't cover this site (it's based on master before
Incident tools exist), so this PR escapes it locally rather than shipping a new
instance of the same class of issue.

escapeOqlLiteral() is duplicated from Combodo#8 rather than shared, since these are two
independent, isolated proposals - intentional and temporary, to be collapsed into
one definition once both land.
@github-project-automation github-project-automation Bot moved this to Pending review in Combodo PRs dashboard Sep 3, 2026
48347d5 fixed Combodo#7 upstream with quoteString(), which supersedes the
escapeOqlLiteral() helper this branch introduced: the conflicts are
resolved by taking upstream's code, so the helper and its four call
sites are gone.

What 48347d5 left out is kept: in
search-user-request-by-caller-status-start-date, statuses and
start_date are still interpolated raw. They now go through
quoteString() too, like caller_email.
Requested in the review of this PR: a unit test, so the behaviour is
easy to check and a regression would show. The test feeds a status and
a start date carrying an injection attempt to
search-user-request-by-caller-status-start-date and checks the OQL the
tool sends, once the JSON is decoded as iTop does: each value stays one
quoted literal.
@agent-qv

Copy link
Copy Markdown
Contributor Author

@jf-cbd Sorry for the late answer. 48347d5 has since fixed most of #7, so I reworked this PR on top of it. The description above refers to the original version; the PR now only covers what 48347d5 left out:

  • statuses and start_date in search-user-request-by-caller-status-start-date were still interpolated raw. Both now go through quoteString(), like caller_email.
  • The unit test you asked for feeds an injection attempt in a status and in the start date, and checks that each value stays one quoted literal in the OQL sent to iTop. It fails without the fix.

Status codes such as escalated_tto contain an underscore, which the current quoteString() turns into \_, rejected by iTop's OQL lexer: this PR is best merged together with #26, which fixes that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Pending review

Development

Successfully merging this pull request may close these issues.

Escape user-controlled values before interpolating them into OQL

3 participants