Skip to content

Fix SQL injection in search by using parameterized queries - #41

Merged
G4brym merged 2 commits into
mainfrom
fix/sql-injection-parameterized-queries
Mar 10, 2026
Merged

G4brym merged 2 commits into
mainfrom
fix/sql-injection-parameterized-queries

Conversation

@G4brym

@G4brym G4brym commented Mar 9, 2026

Copy link
Copy Markdown
Owner

Summary

  • Replaces SQL string interpolation in the research list search endpoint (GET /) with proper parameterized queries via workers-qb's built-in parameter binding
  • The previous implementation manually escaped single quotes and interpolated search terms directly into SQL LIKE clauses, which is vulnerable to SQL injection edge cases
  • Extracts a reusable buildSearchFilters() helper function that returns parameterized conditions and params arrays

What was changed

src/utils.ts — Added buildSearchFilters(q?, status?) that builds parameterized WHERE conditions:

  • Search queries use ? placeholders instead of string interpolation
  • Status values are parsed as integers and passed as parameters
  • Invalid status values are rejected before reaching SQL

src/index.tsx — Replaced the inline condition-building code with the new helper, passing conditions and params to workers-qb's .where(conditions, params) method.

tests/unit/utils.test.ts — Added 12 unit tests covering:

  • Empty/whitespace queries
  • Valid search and status filters
  • Combined filters
  • SQL injection attack scenarios (both in query and status parameters)
  • Special SQL characters in search terms

Why this is beneficial

The manual replace(/'/g, "''") escaping approach is fragile and doesn't protect against all SQL injection vectors. Using parameterized queries is the industry-standard defense and leverages workers-qb's existing parameter binding support (already used elsewhere in the codebase, e.g., where("research_id = ?", id)).

Test plan

  • All 159 tests pass (12 new + 147 existing)
  • Biome linter passes with no issues
  • No breaking changes — the search behavior is identical, only the SQL generation method changes

🤖 Generated with Claude Code

@G4brym G4brym left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Code Review — APPROVED ✅

Review Scores: 5/5 reviewers approved
CI Status: All checks passed ✅

Summary

Clean, well-implemented security fix that replaces fragile SQL string interpolation with proper parameterized queries. The extracted buildSearchFilters() helper is well-typed, well-tested (12 new tests covering normal usage, edge cases, and injection scenarios), and follows existing codebase conventions.

Review Perspectives

  1. Correctness: ✅ Parameterized conditions and params are correctly structured; empty/whitespace/invalid inputs handled properly; both main query and count query use consistent filters
  2. Security: ✅ Replaces fragile replace(/'/g, "''") escaping with industry-standard ? parameter binding; status gets defense-in-depth (whitelist + parameterization)
  3. Performance: ✅ Functionally equivalent SQL queries with negligible overhead from helper function
  4. Code Quality: ✅ Clean extraction as pure function; net-negative diff in route handler (-15/+5 lines); follows existing patterns in utils.ts
  5. Testing: ✅ 12 comprehensive unit tests covering normal cases, edge cases, SQL injection scenarios, and special characters; all 159 tests pass

No major, medium, or minor issues found. This is a textbook example of fixing SQL injection properly.

🤖 Automated review by prodboard

The research list search endpoint used string interpolation to build SQL
WHERE clauses, which is vulnerable to SQL injection even with manual
quote escaping. This replaces the string interpolation with proper
parameterized queries via workers-qb's built-in parameter binding.

Extracts a `buildSearchFilters` helper that returns parameterized
conditions and params, and adds comprehensive unit tests including
SQL injection attack scenarios.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@G4brym
G4brym force-pushed the fix/sql-injection-parameterized-queries branch from 48db601 to faf9d78 Compare March 10, 2026 18:03
@G4brym

G4brym commented Mar 10, 2026

Copy link
Copy Markdown
Owner Author

Automated Code Review — Changes Requested 🔄

Review Scores: 5/5 reviewers approved
CI Status: 1 check failed ❌


CI Failures

❌ Changeset Check

Link: https://github.com/G4brym/workers-research/actions/runs/22917017745/job/66505050624

The Changeset Check is failing. This project uses Changesets for versioning and changelog generation. PRs are required to include a changeset file describing the change.

To fix, run one of the following and commit the generated .changeset/*.md file:

npx changeset
# or
bunx changeset

Select the appropriate bump type:

  • patch — recommended here, as this is a non-breaking bug/security fix

Alternatively, create a .changeset/<unique-name>.md file manually:

---
"workers-research": patch
---

Fix SQL injection in research list search by replacing string interpolation with parameterized queries

Code Quality Assessment

The code change itself is excellent — all 5 review perspectives approved:

  1. Correctness: ✅ Correct parameterization, proper ? placeholder count matches params, status allowlist validation preserved
  2. Security: ✅ Eliminates SQL injection vulnerability; parameterized queries are the industry-standard defense
  3. Performance: ✅ No regressions; parameterized queries may improve D1/SQLite query plan caching
  4. Code Quality: ✅ Clean helper extraction with explicit return type and JSDoc; no dead code
  5. Testing: ✅ 12 well-structured unit tests covering attack scenarios, edge cases, and combined filters

The only blocker is the missing changeset file for CI. Once that's added and CI goes green, this PR is ready to merge.

🤖 Automated review by prodboard

Adds patch changeset required by CI for the parameterized query fix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@G4brym
G4brym merged commit 209444e into main Mar 10, 2026
3 checks passed
@G4brym
G4brym deleted the fix/sql-injection-parameterized-queries branch March 10, 2026 18:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant