fix: use one query validator in Express and Vercel search handlers (#97) - #370
Merged
Emmy123222 merged 1 commit intoSep 3, 2026
Merged
Conversation
|
@Hollujay is attempting to deploy a commit to the Emmanuel's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
@Hollujay Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
Hollujay
force-pushed
the
fix/shared-query-validator-97
branch
from
September 1, 2026 13:30
4003dc8 to
5700124
Compare
…lers server/index.ts stripped control characters and capped query length via a local validateQuery(); api/search.ts only trimmed whitespace, so a control-character or over-length query could 400 on Express but sail through to Serper on Vercel — a production-specific validation gap. - Extract validateQuery/MAX_QUERY_LENGTH into src/lib/queryValidator.ts, a runtime-agnostic module imported by both server/index.ts and api/search.ts. - Add src/lib/queryValidator.fixtures.ts: a single shared test table, consumed by src/lib/queryValidator.test.ts (unit), and by server/payment.test.ts + api/search.test.ts (integration) to assert both handlers return identical status codes and cleaned queries for the same input. - Move server/validateQuery.test.ts's unit coverage into src/lib/queryValidator.test.ts alongside the code it tests. - Update vite.config.ts coverage ratchets: add a 100% threshold for the new src/lib/queryValidator.ts, and adjust server/index.ts's function/branch ratchet to reflect the validator logic moving out. Closes Emmy123222#97
Hollujay
force-pushed
the
fix/shared-query-validator-97
branch
from
September 2, 2026 12:51
5f4858d to
9b38c3d
Compare
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.
Summary
Closes #97.
server/index.tshad a localvalidateQuery()that capped query length at 256 chars and stripped null bytes / ASCII control characters (C0 + DEL).api/search.ts(the Vercel serverless equivalent) only did!q?.trim()andq.trim()— no length cap, no control-character stripping. That's a production-specific validation gap: a query that Express would reject with 400 (too long, or control-character-only) could sail straight through to Serper on Vercel.Changes
src/lib/queryValidator.ts(new) — extractedvalidateQuery()/MAX_QUERY_LENGTHout ofserver/index.tsinto a runtime-agnostic module (no Express or Vercel types), so both handlers call the exact same code.server/index.ts— now importsvalidateQueryfromsrc/lib/queryValidator.tsinstead of defining it locally./search,/images,/newsbehavior is unchanged.api/search.ts— replaced the ad-hoc!q?.trim()/q.trim()checks with the sharedvalidateQuery(), so it now enforces the same 256-char cap and control-character stripping as Express, and returns the same error strings.src/lib/queryValidator.fixtures.ts(new) — one shared test table (valid queries, missing/empty/whitespace-only, over-length, control-character-only, control-chars-then-trim, unicode/punctuation) used by:src/lib/queryValidator.test.ts(new) — unit tests againstvalidateQuery()directly. Replacesserver/validateQuery.test.ts(deleted; its coverage moved next to the code it tests).server/payment.test.ts— newdescribe('shared query-validator table — /search (Express)')block that drives the same table through the real Express route viasupertest.api/search.test.ts— newdescribe('shared query-validator table — /api/search (Vercel)')block that drives the same table through the real Vercel handler.Both integration blocks assert identical status codes and identical cleaned queries for every case in the table — this is the "shared test table" the issue's acceptance criteria asked for.
vite.config.ts— added a 100% coverage ratchet for the newsrc/lib/queryValidator.ts, and adjustedserver/index.ts's function/branch ratchet down slightly (55%→50%, 60%→55%) since the validator logic — and the branches/tests that exercised it — moved out of that file into the shared module.x402 payment logic (header parsing, replay protection, settlement amounts) is untouched on both runtimes — only the pre-payment query-validation gate changed.
Test plan
npm run typecheck:allpasses (frontend, server, api, mcp, scripts)npm run lintpasses (eslint . --max-warnings=0)npx vitest run— all 224 tests pass across 16 test files, including the new shared-table suites insrc/lib/queryValidator.test.ts,server/payment.test.ts, andapi/search.test.tsnpm run test:coverage:check— passes with updated ratchetsnpm run build— production build succeeds