Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Arbitrary Stripe priceId accepted at three checkout endpoints → Pro entitlement bypass

Aperta
#2,222 2 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
55/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
nextjs, typescript

Direzione di ricerca

Inizia da packages/utils/src/constants/plans.ts e confronta i tre punti di ingresso del checkout: le route guest-checkout, subscribe e desktop root. Leggi la validazione esistente in apps/web/app/api/v1/[...route]/route.ts e segui userIsPro, isProSubscription e la gestione dei webhook di Stripe. Il lavoro è completo quando i piani consentiti, le quantità limitate, l’autenticazione degli ospiti e i controlli dei diritti di accesso sono coperti in modo coerente lungo questi percorsi.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

bug
Summary

priceId and quantity are taken directly from client-supplied JSON and passed into
stripe.checkout.sessions.create with no allowlist, at three separate endpoints, despite
STRIPE_PLAN_IDS (packages/utils/src/constants/plans.ts) existing for exactly this purpose:

  • apps/web/app/api/settings/billing/guest-checkout/route.ts:9,26 no auth at all.
  • apps/web/app/api/settings/billing/subscribe/route.ts:14,68 authenticated, but no allowlist.
  • apps/web/app/api/desktop/[...route]/root.ts:702-791 (POST /api/desktop/subscribe, used by
    desktop + mobile) authenticated via withAuth, but priceId is only validated as
    z.string(), no enum against STRIPE_PLAN_IDS.

For contrast, apps/web/app/api/v1/[...route]/route.ts:4402-4409 does this correctly: it
derives the price itself from STRIPE_PLAN_IDS[environment][payload.interval] and never
trusts a client-supplied price id.

Why this grants full Pro

Entitlement is checked by status, not by price:

  • userIsPro (packages/utils/src/lib/stripe/subscriptions.ts) only checks
    stripeSubscriptionStatus / thirdPartyStripeSubscriptionId.
  • isProSubscription is a deny-list it excludes only SSO and signed-BAA subscriptions.
  • In the Stripe webhook (apps/web/app/api/webhooks/stripe/route.ts:110-124,601-610),
    checkout.session.completed special-cases only SSO and signed-BAA subscriptions via
    isSsoSubscription/isSignedBaaSubscription. Every other subscription i.e. any other live
    recurring price on the account — falls into the generic path that sets
    stripeSubscriptionStatus: subscription.status and inviteQuota from the line-item quantity,
    with no price check at all.

So completing checkout with any other live recurring price in Cap's Stripe account (a
retired/legacy tier, an internal test price, anything not SSO/BAA) grants full Pro status.
allow_promotion_codes: true widens this further, and on subscribe/route.ts and
guest-checkout/route.ts an unbounded quantity flows straight into users.inviteQuota.

Existing related work (none of it closes this)
  • PR #2141 (open, "Checkout conversion...guest checkout lockdown") fixes only
    guest-checkout/route.ts adds an allowedPriceIds() check against STRIPE_PLAN_IDS and
    clamps quantity to 1-100. subscribe/route.ts and the desktop /subscribe endpoint are
    untouched by that PR, and isProSubscription remains a deny-list.
  • PR #1929 (open) adds Vercel-Firewall rate limiting to guest-checkout only doesn't prevent
    a single request from buying Pro at an arbitrary price, and fails open on self-hosted deploys
    without a configured Firewall rule.
  • No existing issue covers this.
Fix
  1. Allowlist priceId against STRIPE_PLAN_IDS[env] at all three endpoints (not just
    guest-checkout), mirroring the pattern already used in v1/route.ts.
  2. Clamp quantity at subscribe/route.ts and the desktop endpoint the same way #2141 does
    for guest-checkout.
  3. Convert isProSubscription from a deny-list to an allow-list (only prices in
    STRIPE_PLAN_IDS grant Pro), so entitlement isn't dependent on catching every non-Pro price
    individually as it's created in Stripe.
  4. Require auth on guest-checkout or otherwise bound its blast radius (currently zero auth).
Caveat

Actual historical exposure depends on which legacy/test prices are live (non-archived) in Cap's
Stripe account right now someone with Stripe dashboard access should check before sizing
impact. The code-level flaw is independent of that and reproducible today with any second live
recurring price.

Lingua principale
Rust
Stelle
22.5k
Fork
1.9k
Merge medio
7h 43m
PR unite (30g)
80

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di CapSoftware/Cap

Tutte le issue di CapSoftware/Cap

Issue simili

Altre issue su Rust

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.