Commit 97c77e5
fix(contacts): stop edit route from reassigning QSO owner or injecting SQL (#251)
`Contact.update` built its UPDATE by interpolating *every* key of the
input object straight into the SQL as a column name (`SET ${key} = $n`),
and `PUT /api/contacts/[id]` passes the raw JSON request body in with no
filtering. Two problems followed:
- Mass assignment: a caller could move one of their own QSOs into another
operator's log by sending `{ "user_id": <someone-else> }` — only
id/created_at/updated_at were excluded, not user_id.
- SQL injection: the key is concatenated, never bound, so a crafted key
(e.g. `"user_id = 1, notes"`) becomes live SQL rather than a bad-column
error.
Fix: route the update through a fixed column allowlist. New
server-imports-free `@/lib/contact-update` exposes `UPDATABLE_CONTACT_COLUMNS`
(every editable `contacts` column except id/user_id/created_at/updated_at)
and a pure `buildContactUpdate()` that emits `col = $n` assignments only
for allowlisted keys, dropping unknown/forbidden keys and `undefined`
values while keeping placeholders in lockstep with their bound values.
Unit-tested directly like `@/lib/contact-search`. The edit form's
legitimate fields are all allowlisted, so normal edits are unchanged; it
was already re-sending user_id unchanged, which is now simply dropped.
Co-authored-by: Optio Agent <optio-agent@noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>1 parent c0d7de1 commit 97c77e5
0 file changed
0 commit comments