fix(customer/search): escape SQL LIKE metacharacters in the user-supplied substring#158
Merged
Merged
Conversation
…lied substring
`GET /v1/customer/search?q=…` constructs an ILIKE pattern as
`%${q}%` and hands it to Sequelize's `Op.iLike`. Sequelize does
NOT auto-escape SQL LIKE metacharacters for that operator — it
treats the pattern as verbatim user-controlled SQL. So a request
like `q=%` produces `%%%` which matches **every** customer in
scope; `q=__@` matches anything-2-chars-then-@; `q=\\` lands a
raw backslash in the pattern.
Same security boundary (still company-scoped — non-master keys
can't reach beyond their own company), but the substring contract
silently breaks: a user typing `100%` into a search-as-you-type
input gets all customers back, not customers whose name contains
"100%".
Add a small `escapeLikeWildcards(s)` helper that prefixes the
three LIKE metacharacters (`%`, `_`, `\`) with `\`. Use it on `q`
before constructing the pattern. Helper is exposed via
`exports._helpers` for unit testing and so future siblings
(invoice number search, etc.) can reuse it.
Four new tests cover: the three metacharacters individually,
ordinary text passthrough, multi-meta strings (e.g. `100%_done`,
`path\\to\\file`), and defensive non-string coercion.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Closes #157.
Summary
GET /v1/customer/search?q=…builds an ILIKE pattern as%${q}%and hands it to Sequelize'sOp.iLike. Sequelize does NOT auto-escape SQL LIKE metacharacters for that operator — it treats the pattern as verbatim user-controlled SQL. Soq=%produces%%%and matches every customer in scope;q=__@matches anything-2-chars-then-@;q=\\lands a raw backslash in the pattern.Same security boundary still holds (non-master keys still scoped to their own company), but the substring contract silently breaks. A user typing
100%into a search-as-you-type input gets all customers back, not just those whose name contains100%.Add a small
escapeLikeWildcards(s)helper that prefixes the three metacharacters (%,_,\) with\. Use it onqbefore constructing the pattern. Postgres ILIKE then treats them as literals. Helper exposed viaexports._helpersso future sibling endpoints can reuse it.Test plan
npm run lint— cleannpm test— 612 passed (was 608 + 4 new helper tests), 15 skippedProudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/