Quick Answer
We ran a full VibeDoctor code scan of shadcn-ui/taxonomy, one of the most-cloned Next.js app-router SaaS starters ever published. The report: 118 findings (2 critical, 17 high, 91 medium, 8 low), overall score 63/100. The headline issue is a business-logic bug no PR bot flags: the Stripe webhook reads renewal events as the wrong object type, so stripeCurrentPeriodEnd never advances and a still-paying customer is silently downgraded to the free plan about 24 hours after their period ends.
Why This Repo Matters
Taxonomy is shadcn's open-source Next.js 13 experiment. The README is honest about that - it is a demo, not a product. But it became the de facto reference for the app-router era: the auth, billing, and dashboard structure of thousands of vibe-coded SaaS apps started life as a clone of this repo, and AI assistants reproduce its patterns verbatim when you ask them for "Next.js + Stripe subscriptions."
That is exactly why it makes a good teardown target. The bugs below are not shadcn's problem - taxonomy was never meant to take real money. They are the problem of every production app that inherited this code without re-reading it.
The Scan
One VibeDoctor codebase scan, run on 2026-06-12. Every number below comes from that report - no estimates, no extrapolation.
| Metric | Result |
|---|---|
| Overall score | 63 / 100 |
| Total findings | 118 |
| Critical | 2 |
| High | 17 |
| Medium | 91 |
| Low | 8 |
| Launch verdict | Not safe to launch yet - 2 critical issues must be fixed before you ship |
Finding 1: The Renewal Bug That Downgrades Paying Customers (Critical)
Where: app/api/webhooks/stripe/route.ts, line 50.
The webhook handles checkout.session.completed and invoice.payment_succeeded with the same object. The first purchase works, because that event really does carry a Checkout Session. But renewal events carry a Stripe.Invoice, not a session - so when the renewal branch reads session.subscription off the session-typed variable, the subscription lookup fails, the stored period end never moves forward, and the app's plan check sees an expired subscription.
The customer is still paying. Stripe is still charging them. The app downgrades them anyway, silently, roughly a day after their first month ends. Nobody gets an error. The first signal is a churn email from a confused customer who is looking at a credit card statement that says otherwise.
Finding 2: Cancellations Never Reach the App (High)
Where: app/api/webhooks/stripe/route.ts, line 26.
The webhook handles payment events but has no customer.subscription.deleted or customer.subscription.updated branch. Cancelled, disputed, or refunded customers keep their paid plan until the stale stored period end happens to elapse. Combined with Finding 1, this webhook manages to fail in both directions: it downgrades people who pay and keeps people who cancel.
Finding 3: A Stripe Session on Every Request, No Rate Limit (High)
Where: app/api/users/stripe/route.ts, line 25.
An authenticated route creates a real Stripe billing session on every call, with no rate limiting anywhere in the project. Anyone with a session cookie and a for-loop can hammer your Stripe account with session creations. This is the kind of finding that costs money quietly instead of breaking loudly.
The Rest of the Report
The second critical is CVE-2023-45133 in @babel/traverse 7.21.4 - a build-toolchain dependency, worth patching but not the thing that hurts customers. The 17 highs include five known advisories in the pinned [email protected] (template version pins rot fast), an advisory in sharp 0.31.3, a missing React error boundary for the whole app, and a near-zero test ratio.
Why No Other Tool Tells You This
PR review bots are diff-scoped: they comment on the lines you changed this week. A webhook file you cloned eight months ago and never touched again is invisible to them. Dependency scanners see the CVEs but have no idea what your invoice.payment_succeeded branch does with a Checkout Session type. The renewal bug sat in the most-copied SaaS starter of its generation, in plain sight, the entire time.
This finding class - Stripe webhook correctness, missing cancellation handling, missing rate limiting on money routes - is exactly what VibeDoctor's business-logic checks were built for, and this teardown is the report they produce.
Launching your own app this month?
This teardown is the exact report a Launch Audit produces: one canonical finding total, business-logic checks no PR bot runs, and a plain-language launch verdict. One-time, per app - no subscription.
GET MY LAUNCH AUDIT →Check Your Own Webhook First
If your app started from taxonomy or was generated by an AI assistant that learned from it, open your Stripe webhook and look at the invoice.payment_succeeded branch. If it reads .subscription off something typed as a Checkout Session, your renewals are broken right now - you just have not hit the first renewal yet.