Skip to content

company_edit_suggestions.field_name has no allowlist — approval writes an attacker-chosen column #81

Description

@TortoiseWolfe

Problem

approveEditSuggestion interpolates a user-supplied string as a column name:

// src/lib/companies/admin-moderation-service.ts:355-358
.from('shared_companies')
.update({ [suggestion.field_name]: suggestion.new_value })

company_edit_suggestions.field_name is declared VARCHAR(50) NOT NULL with no CHECK constraint and no allowlist, and the INSERT policy lets any authenticated user set it freely.

Failure scenario

A user submits an edit suggestion with field_name = 'is_verified', new_value = 'true', reason = 'fixing a typo in the name'. An admin skims the queue, sees a plausible-looking text correction, and clicks approve. The update flips the company's verified flag instead — the admin has no way to tell from the UI what column will actually be written.

Same shape works for metro_area_id or is_seed. If the named column doesn't exist, the update errors and takes out the moderation queue instead.

This needs an admin to click approve, so it is a missing-validation bug rather than a directly exploitable one — but the admin's click is not informed consent, because the moderation UI shows the reason, not the target column.

Fix

  • Allowlist the writable columns in application code (const EDITABLE = ['name','website','description', …]) and reject anything else at both submit and approve time.
  • Add a CHECK (field_name IN (...)) constraint so the database enforces it independently of the client.
  • Surface the target column in the moderation UI so the approver sees what they are approving.

Acceptance

  • Suggestion with field_name = 'is_verified' is rejected at insert
  • Approve path re-validates rather than trusting the stored row
  • Moderation card shows field_name explicitly

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingsecurity

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions