Escape prompt search filters - #392
Conversation
Greptile SummaryThis PR escapes user-supplied search terms before they are interpolated into PostgREST's
Confidence Score: 3/5The SQL LIKE wildcard escaping is correct, but the PostgREST syntax character escaping uses a mechanism that is not documented as supported — searches containing commas, dots, or parentheses could still be parsed incorrectly by PostgREST. The fix correctly handles SQL LIKE wildcards but the approach for PostgREST reserved characters relies on backslash-escaping while PostgREST's own documentation prescribes double-quoting. A search containing a comma may still cause PostgREST to misparse the filter. The unit test mocks the client entirely and cannot catch this gap. Both changed files need attention: route.ts for the escaping logic, and route.test.ts because it validates only the JS-layer string output rather than actual PostgREST parsing. Important Files Changed
Sequence DiagramsequenceDiagram
participant Client
participant Route as GET /api/prompts
participant Escape as escapePostgrestSearch()
participant Supabase as Supabase JS Client
participant PostgREST
participant Postgres as PostgreSQL ILIKE
Client->>Route: "GET /api/prompts?search=ai%25,foo"
Route->>Escape: escapePostgrestSearch("ai%,foo")
Note over Escape: % -> \% SQL wildcard OK<br/>_ -> \_ SQL wildcard OK<br/>, -> \, NOT PostgREST-documented
Escape-->>Route: "ai\%\,foo"
Route->>Supabase: .or("title.ilike.%ai\%\,foo%,...")
Supabase->>PostgREST: filter with backslash-escaped comma
Note over PostgREST: Comma may still split condition
PostgREST->>Postgres: ILIKE pattern
Postgres-->>Route: results
Route-->>Client: 200 listings
Reviews (1): Last reviewed commit: "Escape prompt search filters" | Re-trigger Greptile |
| function escapePostgrestSearch(value: string) { | ||
| return value | ||
| .replace(/\\/g, "\\\\") | ||
| .replace(/%/g, "\\%") | ||
| .replace(/_/g, "\\_") | ||
| .replace(/,/g, "\\,") | ||
| .replace(/\(/g, "\\(") | ||
| .replace(/\)/g, "\\)") | ||
| .replace(/\./g, "\\."); | ||
| } |
There was a problem hiding this comment.
Backslash-escaping PostgREST reserved characters is not the supported mechanism
PostgREST's documented approach for handling reserved characters (,, ., :, ()) in filter values is to wrap the entire value in double quotes — not backslash-escape each character. The PostgREST URL grammar docs are explicit: "If filters include PostgREST reserved characters(,, ., :, ()) you'll have to surround them in percent encoded double quotes." Backslash-escaping is documented only for escaping " within a double-quoted value.
A search for AI, ML would produce title.ilike.%AI\, ML%,... — PostgREST's parser still treats the unquoted \, as a condition separator, which will either produce a parse error or an incorrectly constructed query. The correct approach is to double-quote the pattern and escape only " and \ within that quoted value, then use title.ilike."${safeSearch}" in the .or() string.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| it("escapes PostgREST filter characters in search terms", async () => { | ||
| const query = makePromptQuery(); | ||
| mockFrom.mockReturnValue(query); | ||
|
|
||
| const response = await GET(makeGetRequest({ search: "ai%,foo_(v1)." })); | ||
|
|
||
| expect(response.status).toBe(200); | ||
| expect(query.or).toHaveBeenCalledWith( | ||
| "title.ilike.%ai\\%\\,foo\\_\\(v1\\)\\.%,description.ilike.%ai\\%\\,foo\\_\\(v1\\)\\.%,tagline.ilike.%ai\\%\\,foo\\_\\(v1\\)\\.%" | ||
| ); | ||
| }); |
There was a problem hiding this comment.
Test validates JS-layer output, not actual PostgREST behaviour
The test verifies that or() is called with a specific escaped string, but because Supabase is fully mocked it cannot detect whether PostgREST would accept or correctly interpret that string. A scenario where backslash-escaped commas silently produce wrong query results (or a 400 from PostgREST) would pass this test. An integration test or a test asserting the double-quoting format recommended by PostgREST docs would give much stronger coverage.
Fixes prompt search queries that include PostgREST pattern/control characters by escaping %, _, comma, parentheses, dot, and related characters before building the .or(...) filter.
Validation: