fix: escape LIKE wildcards in NodeQuery.fileFilter and nameLike#446
fix: escape LIKE wildcards in NodeQuery.fileFilter and nameLike#446carlos-alm merged 22 commits intomainfrom
Conversation
…pyramid (3.13) Introduce a Repository base class, InMemoryRepository (Maps-backed, no SQLite), and SqliteRepository (delegates to existing fn(db,...) functions). Includes fluent TestRepoBuilder fixture factory and 105 new tests (51 unit + 54 parity) verifying behavioral equivalence between both implementations. Existing code and tests remain unchanged. Impact: 139 functions changed, 6 affected
- Add kind/role validation to InMemoryRepository.findNodesForTriage matching SqliteRepository behavior (throws ConfigError) - Optimize findIntraFileCallEdges sort with pre-built lookup map instead of spreading nodes Map per comparison - Remove dead #nameIndex field from TestRepoBuilder - Add validation parity tests for invalid kind/role Impact: 5 functions changed, 0 affected
Impact: 2 functions changed, 0 affected
Impact: 2 functions changed, 0 affected
…graph into feat/testing-pyramid
Move the duplicate-name guard before repo.addNode() so the repo is not mutated when a duplicate is detected. Impact: 2 functions changed, 0 affected
findNodesByScope, findNodeByQualifiedName, findNodesWithFanIn, listFunctionNodes, and findNodesForTriage all passed user-provided strings directly into likeToRegex without escaping % and _ chars. This caused divergence from SQLite which uses escapeLike + ESCAPE '\'. Also adds parity tests for findNodesByScope, findNodeByQualifiedName, findNodesWithFanIn, findFileNodes, findNodeChildren, import queries, getCallableNodes, getFileNodesAll, getImportEdges, and hasCfgTables. Impact: 7 functions changed, 5 affected
Impact: 9 functions changed, 14 affected
The in-memory noTests filter only checked .test. and .spec. patterns, missing __test__, __tests__, and .stories. that the SQLite query-builder excludes. Add the missing patterns to both findNodesForTriage and iterateFunctionNodes. Extend parity test fixtures with __tests__ and .stories. nodes so the divergence is caught automatically. Impact: 5 functions changed, 0 affected
fileFilter() and nameLike() in query-builder.js passed user input directly into LIKE patterns without escaping, so _ and % acted as wildcards instead of literals. This caused a parity gap where the SQLite findNodesWithFanIn treated _ as a wildcard but the in-memory version (which already called escapeLike) treated it as literal. Fix: add escapeLike() to query-builder.js (exported), use it in fileFilter() and nameLike() with ESCAPE '\', and deduplicate the copy in nodes.js by importing from query-builder.js.
Greptile SummaryThis PR fixes a correctness bug where LIKE wildcards (
Confidence Score: 5/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["User input\n(e.g. '_oo.js')"] --> B["escapeLike()\nquery-builder.js\nexport"]
B --> C["'_oo.js' → '\\_oo.js'"]
C --> D1["NodeQuery.fileFilter()\n'%\\_oo.js%'"]
C --> D2["NodeQuery.nameLike()\n'%\\_oo.js%'"]
C --> D3["InMemoryRepository\nfindNodesWithFanIn()\n likeToRegex('%\\_oo.js%')"]
D1 --> E1["SQL: n.file LIKE ? ESCAPE '\\'"]
D2 --> E2["SQL: n.name LIKE ? ESCAPE '\\'"]
D3 --> E3["RegExp: /^.*_oo\\.js.*$/i\n(via likeToRegex)"]
E1 --> F["SQLite: '_' treated as literal ✓"]
E2 --> F
E3 --> G["In-memory: '_' treated as literal ✓"]
style B fill:#4caf50,color:#fff
style F fill:#2196f3,color:#fff
style G fill:#2196f3,color:#fff
Last reviewed commit: 2cf64b5 |
| * Escape LIKE special characters so they are treated as literals. | ||
| * Mirrors the `escapeLike` function in `nodes.js`. | ||
| * @param {string} s | ||
| * @returns {string} | ||
| */ | ||
| function escapeLike(s) { | ||
| return s.replace(/[%_\\]/g, '\\$&'); | ||
| } |
There was a problem hiding this comment.
Stale JSDoc and duplicate escapeLike
The JSDoc says "Mirrors the escapeLike function in nodes.js", but nodes.js no longer defines its own escapeLike — this PR moved the canonical copy to query-builder.js. The comment is now stale.
More broadly, the PR description states it "Deduplicates escapeLike" by having nodes.js import from query-builder.js, yet this file defines a third private copy. Consider importing the canonical export instead to complete the deduplication:
| * Escape LIKE special characters so they are treated as literals. | |
| * Mirrors the `escapeLike` function in `nodes.js`. | |
| * @param {string} s | |
| * @returns {string} | |
| */ | |
| function escapeLike(s) { | |
| return s.replace(/[%_\\]/g, '\\$&'); | |
| } | |
| import { escapeLike } from '../query-builder.js'; |
There was a problem hiding this comment.
Fixed — removed the private escapeLike copy and now importing it from query-builder.js. All 183 related tests pass.
There was a problem hiding this comment.
Already addressed in the current state of the PR: in-memory-repository.js imports escapeLike from the canonical ../query-builder.js (line 3) — there is no duplicate definition and no stale JSDoc referencing nodes.js. The deduplication is complete.
Summary
NodeQuery.fileFilter()andnameLike()inquery-builder.jsnow escape LIKE wildcards (_,%,\) before building the SQL pattern, matching the behavior of direct queries innodes.jsfindNodesWithFanIntreated_as a wildcard but the in-memory version treated it as literal (Greptile review comment on feat: Repository abstraction with InMemoryRepository (3.13) #444)escapeLike—nodes.jsnow imports it fromquery-builder.jsinstead of defining its own copyoptionalDependenciesto 3.1.4 (Closes bug: native binary optionalDependencies pinned to 3.1.3 after v3.1.4 release #454)Found during
Dogfooding v3.1.4 — native binary pin issue found as #454
Test plan
fileFilter()andnameLike()escaping tests inquery-builder.test.jsverify_is treated as literalfindNodesWithFanInparity test inrepository-parity.test.jsverifies both implementations reject_as wildcard