Skip to content

[S1] Unauthenticated /v1/revoke/cascade destroys any contributor's data #260

Description

@DsThakurRawat

Labels: security, bug, infra, priority:critical
File: infra/contribution-worker/worker.js:68 (route), :139/:175 (body-trusted contributor_id), initiateRevoke/executeR2Purge (~:456/:607), lazy-progress GET :70

Description. The router wraps /admin/* routes in adminAuth (lines 55, 57, 59, 76) but POST /v1/revoke/cascade (line 68) has no auth and reads contributor_id straight from the request body. Anyone on the internet can revoke and purge any contributor.

Why it matters. POST /v1/revoke/cascade {"contributor_id":"<victim>","scope":"all"} sets the victim's revoked_at (permanent upload lockout) and enqueues a purge that deletes every R2 object under {free,pro,enterprise}-contributors/<victim>/. The cascade "lazily progresses" on unauthenticated GET /v1/revoke/cascade-status/<id>, so the attacker drives deletion to completion without waiting for cron. initiateRevoke will INSERT a non-existent contributor row, so IDs can be pre-revoked before a real user onboards. Irreversible, unauthenticated destruction of other users' data.

Tasks.

  • Require proof of contributor identity (planned Ed25519 challenge-response / signed consent receipt) before any destructive revoke.
  • Never trust body-supplied contributor_id for destructive actions; scope to an authenticated principal.
  • Add a worker test that an unauthenticated cascade POST returns 401/403.

Dedup. No open issue/PR touches contribution-worker revoke auth. (PR #236 only renames workers + adds a version field; #233 removes outreach/launch.)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    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