Unescaped special characters in search queries are a direct path to SQL injection (CWE-89) and OWASP A03:2021 Injection — apostrophes, quotes, and backslashes passed raw into a query string can terminate the intended SQL clause and execute attacker-controlled statements. Beyond active exploitation, the same bug silently breaks legitimate searches: John's Café, Fish & Chips, and Rock (n roll) are common real-world listing names that will surface 500 errors if input validation is absent. Every error response leaks backend implementation details and destroys user trust at the exact moment they're trying to find something.
High because unescaped input enables SQL injection (CWE-89, OWASP A03) and crashes the search experience for ordinary user input containing apostrophes or special characters.
Switch to parameterized ORM queries and URL-encode the query string on the client. String concatenation into raw SQL is never acceptable here.
// /api/listings/search — use ORM parameterization, never string concat
const results = await db.listings.findMany({
where: {
OR: [
{ name: { contains: q, mode: 'insensitive' } },
{ description: { contains: q, mode: 'insensitive' } }
]
}
})
On the client, always encode before appending to the URL: ?q=${encodeURIComponent(query)}. Test with Bob's Diner, Fish & Chips, and <script> to confirm no 500 responses or JS exceptions.
ID: directory-search-discovery.search-input.special-chars-handling
Severity: high
What to look for: Enumerate all relevant files and Test the search input by typing special characters: quotes, apostrophes, ampersands, slashes, parentheses, and emojis. Example queries: John's Café, Fish & Chips, Rock (n roll), Bob's Diner's Roost. Monitor the browser console for JavaScript errors and check whether the search API returns an error or a 5xx status. Verify that the search results are sensible (matching relevant listings or showing no results gracefully) rather than breaking.
Pass criteria: No more than 0 violations are acceptable. Special characters in search queries do not cause JavaScript errors, API errors, or 500 errors. Results are returned (either matching results or a polite no-results message) without breaking the page. Report the count of conforming instances found even on pass.
Fail criteria: Typing special characters causes a JavaScript error, an API 5xx error, or a crash. The search breaks the page or prevents further interaction. A partial or incomplete implementation does not count as pass.
Skip (N/A) when: Search is not implemented or is disabled. Signal: no search input or API endpoint found.
Cross-reference: For comprehensive mobile UX evaluation beyond search, the Mobile Responsiveness audit covers touch targets and viewport adaptation.
Detail on fail: Specify the problematic character(s) and the error. Example: "Searching for \"Bob's Diner\" triggers a 500 error: 'Unexpected token' in the backend search handler." or "Query with apostrophe breaks the JavaScript on the client side."
Remediation: Escape or sanitize user input before passing it to the search backend. Use parameterized queries (not string concatenation) for database queries:
// API handler: /api/listings/search
export async function GET(req) {
const q = req.nextUrl.searchParams.get('q') || ''
try {
// Use parameterized query to prevent SQL injection and escaping issues
const results = await db.listings.findMany({
where: {
OR: [
{ name: { contains: q, mode: 'insensitive' } },
{ description: { contains: q, mode: 'insensitive' } }
]
}
})
return Response.json({ results })
} catch (error) {
console.error('Search error:', error)
return Response.json({ error: 'Search failed' }, { status: 500 })
}
}
On the frontend, ensure the query parameter is URL-encoded: ?q=${encodeURIComponent(query)}. Test with special characters to verify nothing breaks.