Skip to content

[Security] Webhook INSERT failure leaks raw Supabase error fields (code, details, hint) in the HTTP 500 response body #218

@anshul23102

Description

@anshul23102

Summary

When the GitHub webhook handler in src/app/api/webhooks/github/route.ts fails to persist an event, it serialises the raw Supabase PostgrestError object directly into the JSON response. This exposes internal database schema details to any caller that can reach the endpoint.

Affected File

src/app/api/webhooks/github/route.ts

Root Cause

On INSERT failure the handler returns:

return NextResponse.json(
  {
    error: 'persist failed',
    code: insertErr.code,
    message: insertErr.message,
    details: insertErr.details,
    hint: insertErr.hint,
  },
  { status: 500 },
);

A PostgrestError from Supabase populates all four fields with values sourced directly from PostgreSQL:

Field Example value
code 23505 (unique_violation)
message duplicate key value violates unique constraint "github_events_pkey"
details Key (id)=(gh_evt_abc123) already exists.
hint Change the conflicting key or use ON CONFLICT.

Together these reveal table names, column names, constraint names, and primary-key formats to any party that can trigger a webhook delivery failure (including GitHub itself, or an attacker who can send crafted payloads to the endpoint).

Impact

  • Schema enumeration: Table and column names are visible without any database credentials.
  • Constraint discovery: Index names disclose the primary-key scheme and uniqueness strategy of the events table.
  • Replay-attack facilitation: Knowing the PK format (gh_evt_<id>) makes it easier to craft collision payloads.
  • Compliance risk: Leaking internal infrastructure details in API responses is flagged by most security audits (OWASP API Security Top 10, item API3:2023 - Broken Object Property Level Authorization).

Steps to Reproduce

  1. Send a webhook payload that causes a unique-constraint violation on the github_events table (e.g., a duplicate delivery ID).
  2. Inspect the HTTP 500 response body.
  3. Observe that code, message, details, and hint contain raw PostgreSQL error text.

Expected Behaviour

Log the full error server-side for debugging, and return only a generic message to the caller:

console.error('[webhook] persist failed', insertErr);
return NextResponse.json(
  { error: 'internal error' },
  { status: 500 },
);

If structured error codes are needed for GitHub's retry logic, expose only a stable, application-defined code that does not map 1:1 to Postgres error codes.

Severity

High - Information disclosure in a production-facing endpoint. No authentication is required to receive the 500 body; GitHub (and any network observer) can read it on every failed delivery.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions