Skip to content

Commit 61eef5e

Browse files
committed
fix: validate search query input
1 parent 0242de9 commit 61eef5e

2 files changed

Lines changed: 63 additions & 2 deletions

File tree

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
1-
import { ok } from "../utils/response.js";
1+
import { fail, ok } from "../utils/response.js";
22
import { globalSearch } from "../services/searchService.js";
33

4+
const MAX_SEARCH_QUERY_LENGTH = 200;
5+
46
export async function search(req, res) {
5-
return ok(res, await globalSearch(req.query.q ?? ""));
7+
const rawQuery = req.query.q ?? "";
8+
9+
if (Array.isArray(rawQuery) || typeof rawQuery !== "string") {
10+
return fail(res, "Search query must be a string");
11+
}
12+
13+
const query = rawQuery.trim();
14+
15+
if (query.length > MAX_SEARCH_QUERY_LENGTH) {
16+
return fail(res, "Search query must be 200 characters or fewer");
17+
}
18+
19+
return ok(res, await globalSearch(query));
620
}

apps/api/src/tests/search.test.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import test from "node:test";
2+
import assert from "node:assert/strict";
3+
import { createApp } from "../app.js";
4+
5+
async function withServer(assertions) {
6+
const app = createApp();
7+
const server = app.listen(0);
8+
9+
await new Promise((resolve, reject) => {
10+
server.once("listening", resolve);
11+
server.once("error", reject);
12+
});
13+
14+
try {
15+
const { port } = server.address();
16+
await assertions(`http://127.0.0.1:${port}`);
17+
} finally {
18+
await new Promise((resolve, reject) => {
19+
server.close((error) => (error ? reject(error) : resolve()));
20+
});
21+
}
22+
}
23+
24+
test("GET /api/search trims a valid search query", async () => {
25+
await withServer(async (baseUrl) => {
26+
const response = await fetch(`${baseUrl}/api/search?q=%20%20engineer%20%20`);
27+
const payload = await response.json();
28+
29+
assert.equal(response.status, 200);
30+
assert.equal(payload.success, true);
31+
assert.equal(payload.data.query, "engineer");
32+
});
33+
});
34+
35+
test("GET /api/search rejects search queries longer than 200 characters", async () => {
36+
await withServer(async (baseUrl) => {
37+
const longQuery = "a".repeat(201);
38+
const response = await fetch(`${baseUrl}/api/search?q=${longQuery}`);
39+
const payload = await response.json();
40+
41+
assert.equal(response.status, 400);
42+
assert.deepEqual(payload, {
43+
success: false,
44+
message: "Search query must be 200 characters or fewer"
45+
});
46+
});
47+
});

0 commit comments

Comments
 (0)