Verified by Proofpack
- Repository
- proofpack-demo/tasks-saas
- Commit
- 0000000
- Scanned
- 1 Nov 2026
- Version
- v1
- Content hash (SHA-256)
- 0aa193635309868558c0afb9c9a84dff35a08a5c9f098036abdb3776cd53fc79
Proofpack report
Technical due-diligence report
proofpack-demo/tasks-saas · main
Summary
Capped at 69: 3 critical findings still count.
- Security0 of 100
- Dependencies94 of 100
- Code health66 of 100
- Docs52 of 100
- Operability86 of 100
- Ownership80 of 100
Summary
AI-suggested. Check before you rely on it.
TaskPilot is an AI task planner for small teams, built with Next.js 14, Supabase and Stripe. Users open a dashboard, enter their goals, and the app asks OpenAI to turn them into a weekly task list that is saved in a Supabase table. Paid plans are sold with Stripe Checkout, and a webhook upgrades the team after payment. The codebase is small and easy to read, but it was built fast: a live Stripe key is written in the code, one table has no row-level security, the payment webhook accepts unsigned events and the task API does not check who the caller is. There are no tests, no CI and only a short README, so a buyer will need time to verify and run it. Most of these problems take hours, not weeks, to fix.
Strengths
- A small, conventional Next.js App Router structure that a new owner can read in an afternoon.
app/page.tsxapp/dashboard/page.tsx - The database schema is versioned as a Supabase migration.
supabase/migrations/20240901120000_init.sql - Row-level security is on for the main teams and tasks tables.
supabase/migrations/20240901120000_init.sql - All AI calls go through one helper module, so the provider is easy to change.
lib/ai.ts
Top risks for a buyer
- Live Stripe secret key in the source codeAnyone with the code can charge customers or issue refunds. The key must be rotated before the sale, and old commits still contain it.Fix: Revoke the key in the Stripe dashboard, read it from STRIPE_SECRET_KEY, and remove it from the git history.
lib/stripe.ts - The Stripe webhook accepts unsigned eventsA forged checkout.session.completed request can upgrade any team to Pro for free.Fix: Verify the Stripe-Signature header with stripe.webhooks.constructEvent on the raw request body.
app/api/webhooks/stripe/route.ts - The team_invites table has no row-level securityWith the public anon key, anyone can read every invite email and token.Fix: Enable row-level security on team_invites and add policies that allow team members only.
supabase/migrations/20240901120000_init.sql - The task API creates tasks without checking the callerAnyone can create tasks for any team ID and spend the OpenAI budget.Fix: Read the signed-in user first, check team membership, and add a rate limit.
app/api/tasks/route.ts - No tests and no CIChanges after the handover are risky, because nothing catches regressions.Fix: Add tests for checkout, the webhook and the task API, and run them in CI.
package.json
Questions a buyer will ask
- Has the Stripe key in lib/stripe.ts been rotated?Show the Stripe event that revoked the old key, and the new environment variable setup.
lib/stripe.ts - How are paid upgrades verified?Point to the webhook signature check after you add it, and to Stripe's event log.
app/api/webhooks/stripe/route.ts - Who can read the team_invites table?Share the migration that turns on row-level security and its policies.
supabase/migrations/20240901120000_init.sql - What does OpenAI cost each month?Share the OpenAI billing page for the last three months and the number of active teams.
lib/ai.ts - How do I set up and deploy the app?Add setup and deploy steps to the README, and list every environment variable in .env.example.
README.mdpackage.json
Findings (24)
All findings (24)
A stripe live secret key (sk_l…8D) is written in lib/stripe.ts, line 4. Anyone who can read this repository can use it.
lib/stripe.ts · line 4
export const stripe = new Stripe("sk_l…8D", { apiVersion: "2024-04-10" });Why buyers care
Anyone with the code can spend your money or read your data. A buyer will ask you to rotate every key before the sale.
How to fix
1. Revoke the key in the provider's dashboard and make a new one. 2. Remove it from the code and read it from an environment variable. 3. Remove it from the git history (for example with git filter-repo): old commits still contain it.
The migration creates public.team_invites, but no migration turns on row-level security for it. With the public anon key, anyone can read and change every row.
supabase/migrations/20240901120000_init.sql · line 16
create table public.team_invites (Why buyers care
Any user, or anyone with the public key, can read every row.
How to fix
1. Add a migration: alter table public.team_invites enable row level security; 2. Add policies that allow only the rows that each user may see. 3. Test with two users that one cannot read the other's rows.
GHSA-2464-fhr4-w9xw: Next.js: Unauthenticated Remote Code Execution in Image Optimization API when SVG files are used · GHSA-2xp9-vwfh-vxw4: Next.js: Unauthenticated Remote Code Execution in Image Optimization API when AVIF files are used · GHSA-36qx-fr4f-26g5: Next.js has a Middleware / Proxy bypass in Pages Router applications using i18n
package-lock.json
next@14.2.3Why buyers care
Known exploit paths that attackers can look up.
How to fix
1. Update next to a fixed version (see the advisories). 2. Run the tests. 3. Scan again to confirm.
GHSA-29mw-wpgm-hmr9: Regular Expression Denial of Service (ReDoS) in lodash · GHSA-35jh-r3h4-6jhm: Command Injection in lodash · GHSA-f23m-r3pf-42rh: lodash vulnerable to Prototype Pollution via array path bypass in `_.unset` and `_.omit`
package-lock.json
lodash@4.17.15Why buyers care
Known exploit paths that attackers can look up.
How to fix
1. Update lodash to a fixed version (see the advisories). 2. Run the tests. 3. Scan again to confirm.
app/api/webhooks/stripe/route.ts handles Stripe events but does not verify the Stripe-Signature header.
app/api/webhooks/stripe/route.ts · line 6
if (event.type === "checkout.session.completed") {Why buyers care
Anyone can send a fake "payment succeeded" event and unlock paid features.
How to fix
1. Read the raw request body. 2. Call stripe.webhooks.constructEvent(body, signature, webhookSecret) before you use the event. 3. Return HTTP 400 when the check fails.
The repository has 11 source files and no test files.
package.json
Why buyers care
Every change is risky: nothing shows when something breaks.
How to fix
1. Add a test runner (for example Vitest or pytest). 2. Write tests for the most important flows first: sign-in, payments, data access. 3. Run them in CI.
README.md has 26 words. A buyer needs at least an overview, the setup steps and how to deploy.
README.md
Why buyers care
The buyer cannot start the app without asking you.
How to fix
1. Write what the product does and its stack. 2. Add the setup commands, the environment variables and the deploy steps. 3. Add how to run the tests.
The code reads 4 environment variables (NEXT_PUBLIC_SUPABASE_ANON_KEY, NEXT_PUBLIC_SUPABASE_URL, OPENAI_API_KEY and SUPABASE_SERVICE_ROLE_KEY), but there is no .env.example that lists them.
.env.example
Why buyers care
The buyer cannot configure the app.
How to fix
1. Add .env.example with every variable name and an empty value. 2. Add a comment that says where to get each value.
LICENSE gives everyone the right to use, copy and change this code under the MIT licence.
LICENSE
MIT LicenseWhy buyers care
The buyer may not get exclusive rights: copies already have a licence.
How to fix
1. Check whether the licence came from a template, and keep that template's notice where it applies. 2. For your own code, replace the licence with a proprietary notice. 3. Ask a lawyer if the code was ever published under this licence.
No GitHub Actions, GitLab CI, CircleCI or similar configuration was found.
.github/workflows/
Why buyers care
Nothing checks changes before they reach production.
How to fix
1. Add .github/workflows/ci.yml. 2. Run install, typecheck, lint, tests and build on each push and pull request.
README.md does not show the commands to install and run the app.
README.md
Why buyers care
The handover takes longer, and the buyer may set it up wrong.
How to fix
1. Add a "Develop" section with the exact commands, for example pnpm install and pnpm dev. 2. Add the commands for tests and the database.
No deploy file (for example vercel.json, Dockerfile or fly.toml) was found, and the README does not explain hosting.
README.md
Why buyers care
The buyer does not know where and how the app runs.
How to fix
1. Add a "Deploy" section to the README: the host, the build command and the environment variables. 2. Commit the host's config file if it has one.
AI-suggested. Check before you rely on it.
The handler reads teamId from the request body and inserts tasks with the service-role client, without reading a session. (AI review of app/api/tasks/route.ts.)
app/api/tasks/route.ts
Why buyers care
An open endpoint that anyone can call.
How to fix
1. Get the signed-in user (or verify the webhook signature) before the route reads or changes data. 2. Return HTTP 401 when the check fails. 3. Add a test that calls the route without a session.
AI-suggested. Check before you rely on it.
The webhook parses the JSON body without verifying the Stripe-Signature header. (AI review of app/api/webhooks/stripe/route.ts.)
app/api/webhooks/stripe/route.ts
Why buyers care
An open endpoint that anyone can call.
How to fix
1. Get the signed-in user (or verify the webhook signature) before the route reads or changes data. 2. Return HTTP 401 when the check fails. 3. Add a test that calls the route without a session.
No Content-Security-Policy, X-Frame-Options or Strict-Transport-Security header is set in the code or config.
next.config.js
Why buyers care
Weaker browser protection against clickjacking and script injection.
How to fix
1. Add security headers in the framework config (for Next.js: headers() in next.config). 2. Start with X-Frame-Options: DENY, Referrer-Policy and a strict CSP.
The root folder has package-lock.json and yarn.lock. Different package managers can install different versions.
package-lock.json
yarn.lock
Why buyers care
Confusing builds: it is not clear which install is the real one.
How to fix
1. Choose one package manager. 2. Delete the other lockfiles. 3. Set "packageManager" in package.json.
package.json uses Git URLs, "*" or "latest" for left-pad (latest).
package.json
"left-pad": "latest"Why buyers care
Builds that change over time without a code change.
How to fix
1. Replace each one with a published version range, for example ^1.2.3. 2. Install again and commit the lockfile.
tsconfig.json does not set "strict": true, so TypeScript does not report many common bugs (for example null values).
tsconfig.json
Why buyers care
More hidden bugs that the compiler could catch.
How to fix
1. Set "strict": true in tsconfig.json. 2. Fix the errors that the compiler reports, one folder at a time.
No ESLint, Biome, Prettier, Ruff, Black or similar configuration was found.
package.json
Why buyers care
Inconsistent code that is harder to read and change.
How to fix
1. Add a linter and a formatter (for example ESLint and Prettier, or Ruff). 2. Run them in CI.
No error-tracking package (for example Sentry or Bugsnag) was found.
package.json
Why buyers care
Errors in production go unseen until a customer complains.
How to fix
1. Add an error-tracking service (for example Sentry). 2. Put its DSN in .env.example.
No /health (or similar) route was found for uptime monitors.
app/api/health/route.ts
Why buyers care
Nothing simple to monitor, so outages are found late.
How to fix
1. Add a GET /api/health route that returns HTTP 200 with {"ok": true}. 2. Point an uptime monitor at it.
The app uses OpenAI, Stripe and Supabase. Each one is an account to transfer and a cost to expect.
package.json
Why buyers care
Accounts to transfer and monthly costs to plan for.
How to fix
1. List each service in the data room with its plan and monthly cost. 2. Plan how each account moves to the buyer.
Signs of AI-assisted development: CLAUDE.md, .cursorrules and 31 commits with an AI co-author.
CLAUDE.md
.cursorrules
Why buyers care
Disclose AI use in the sale agreement. Buyers ask about it.
How to fix
1. Say in the data room which AI tools were used and how the code was reviewed. 2. Check the tools' terms for code ownership.
The repository has one contributor (46 commits).
.git
Why buyers care
Context for the buyer: the bus factor and how mature the code is.
How to fix
1. Explain the history in the data room. 2. Offer a handover call or a support period after the sale.
Dependencies
Direct dependencies (10)
next 14.2.3
MITOK
Critical37 (GHSA-2464-fhr4-w9xw, GHSA-2xp9-vwfh-vxw4, GHSA-36qx-fr4f-26g5, and 34 more)lodash 4.17.15
MITOK
High6 (GHSA-29mw-wpgm-hmr9, GHSA-35jh-r3h4-6jhm, GHSA-f23m-r3pf-42rh, and 3 more)@types/react 18.3.3 · dev
Unknown licenceOK
None knowntypescript 5.4.5 · dev
Unknown licenceOK
None knownleft-pad 1.3.0
MITOK
None knownopenai 4.47.1
MITOK
None knownstripe 15.8.0
MITOK
None known@supabase/supabase-js 2.43.4
MITOK
None knownreact-dom 18.3.1
MITOK
None knownreact 18.3.1
MITOK
None known
Architecture
- Languages
- TypeScript (9), SQL (1), JavaScript (1)
- Frameworks
- Next.js, React
- Third-party services
- OpenAI, Stripe, Supabase
- Size
- 11 source files · 115 lines · 0 test files
Architecture
AI-suggested. Check before you rely on it.
TaskPilot is one Next.js 14 App Router application. Pages render on the server and read tasks from Supabase with the anon key. Route handlers under app/api create tasks with the OpenAI API and receive Stripe webhooks. A Supabase admin client with the service-role key writes data from the server. Stripe Checkout takes payments. There are no background jobs; everything runs inside request handlers.
- Web pagesThe landing page and the task dashboard (server components).
app/page.tsxapp/dashboard/page.tsxapp/layout.tsx - Task APITurns goals into tasks with OpenAI and saves them.
app/api/tasks/route.tslib/ai.ts - Stripe webhookUpgrades a team to Pro after checkout.
app/api/webhooks/stripe/route.tslib/stripe.ts - Database accessSupabase clients: the anon key for pages, the service-role key for server writes.
lib/supabase.tslib/supabase-admin.ts - Database schemaThe teams, tasks and team_invites tables.
supabase/migrations/20240901120000_init.sql
Findings: Code health (4)
The repository has 11 source files and no test files.
package.json
Why buyers care
Every change is risky: nothing shows when something breaks.
How to fix
1. Add a test runner (for example Vitest or pytest). 2. Write tests for the most important flows first: sign-in, payments, data access. 3. Run them in CI.
No GitHub Actions, GitLab CI, CircleCI or similar configuration was found.
.github/workflows/
Why buyers care
Nothing checks changes before they reach production.
How to fix
1. Add .github/workflows/ci.yml. 2. Run install, typecheck, lint, tests and build on each push and pull request.
tsconfig.json does not set "strict": true, so TypeScript does not report many common bugs (for example null values).
tsconfig.json
Why buyers care
More hidden bugs that the compiler could catch.
How to fix
1. Set "strict": true in tsconfig.json. 2. Fix the errors that the compiler reports, one folder at a time.
No ESLint, Biome, Prettier, Ruff, Black or similar configuration was found.
package.json
Why buyers care
Inconsistent code that is harder to read and change.
How to fix
1. Add a linter and a formatter (for example ESLint and Prettier, or Ruff). 2. Run them in CI.
Operations
- Deploy config
- None found
- CI config
- None found
- Scripts
- dev, build, start
- Environment variables
- 4 used · 4 not in .env.example
Handover plan
AI-suggested. Check before you rely on it.
Set up a new copy
- Install Node.js 20 or newer.
- Run npm install.
- Make a Supabase project and run the migration in supabase/migrations.
- Put the environment variables below in .env.local.
- Run npm run dev and open http://localhost:3000.
Environment variables
NEXT_PUBLIC_SUPABASE_URL· requiredThe Supabase project URL.NEXT_PUBLIC_SUPABASE_ANON_KEY· requiredThe public key for page reads.SUPABASE_SERVICE_ROLE_KEY· requiredThe server-only key for writes in route handlers.OPENAI_API_KEY· requiredWrites task plans.
Services and costs
- SupabaseDatabase and auth. A free tier, then a monthly plan for each project.
lib/supabase.ts - OpenAITask plans. Billed per token. It grows with the number of active teams.
lib/ai.ts - StripePayments. A fee on each successful payment.
lib/stripe.ts
Runbook
- Rotate the Stripe key and move it to an environment variable before the transfer.
- Make each database change as a new migration in supabase/migrations.
- Watch OpenAI usage after each pricing change.
- Scan again with Proofpack after each fix.
Known gaps
- There are no tests and no CI.
package.json - There is no .env.example, so the configuration is not documented.
lib/supabase.tslib/ai.ts - The README has no setup or deploy steps.
README.md
Ownership
- Commits
- 46
- Contributors
- 1
- First commit
- 2 Sept 2026
- Last commit
- 28 Oct 2026
- Commits with an AI co-author
- 31 of 46
Licences to review (0)
All direct dependencies use permissive licences.
Data room
AI-suggested. Check before you rely on it.
TaskPilot
TaskPilot is an AI task planner for small teams. A team lead enters the goals for the week, and TaskPilot turns them into a list of tasks that the whole team can see.
Users
- Small teams of 3 to 15 people who plan work every week.
- A free plan, and a Pro plan sold through Stripe Checkout.
Stack
- Next.js 14 with the App Router (
app/). - Supabase Postgres for data (
supabase/migrations/20240901120000_init.sql). - OpenAI for the task plans (
lib/ai.ts). - Stripe for payments (
lib/stripe.ts).
AI-suggested. Check before you rely on it.
Components
- Pages render on the server and read tasks with the Supabase anon key (
app/dashboard/page.tsx). - Task API turns goals into tasks with OpenAI and saves them with the service-role key (
app/api/tasks/route.ts). - Stripe webhook upgrades a team after checkout (
app/api/webhooks/stripe/route.ts).
Data flow
- A user submits goals.
- The task API asks OpenAI for a plan.
- The tasks are saved in Supabase.
- A payment through Stripe Checkout calls the webhook, which sets the plan to Pro.
AI-suggested. Check before you rely on it.
Requirements
- Node.js 20 or newer
- A Supabase project
- An OpenAI API key and a Stripe account
Steps
- Run
npm install. - Run the migration in
supabase/migrations. - Put the environment variables in
.env.local(see the environment variables document). - Run
npm run dev.
AI-suggested. Check before you rely on it.
Services
| Service | What it does | How it is billed |
|---|---|---|
| Supabase | Database and auth | Free tier, then a monthly plan |
| OpenAI | Writes task plans | Per token |
| Stripe | Payments | A fee on each payment |
OpenAI is the cost that grows with use. See lib/ai.ts.
Environment variables
Names only. Values are never stored or shown.
| Name | Read in code | In .env.example | Purpose (AI-suggested) |
|---|---|---|---|
NEXT_PUBLIC_SUPABASE_ANON_KEY | yes | no | The public key for page reads. |
NEXT_PUBLIC_SUPABASE_URL | yes | no | The Supabase project URL. |
OPENAI_API_KEY | yes | no | Writes task plans. |
SUPABASE_SERVICE_ROLE_KEY | yes | no | The server-only key for writes in route handlers. |
Dependencies & licences
10 direct dependencies.
Known vulnerabilities
next@14.2.3: 37 advisorieslodash@4.17.15: 6 advisories
Licences to review
All direct dependencies use permissive licences.
AI-suggested. Check before you rely on it.
Before the transfer
- Rotate the Stripe key that is written in
lib/stripe.ts, and move it to an environment variable. - Turn on row-level security for
team_invites. - Add a signature check to the Stripe webhook.
Accounts to transfer
- Supabase project
- Stripe account
- OpenAI organization
Known gaps
- No tests or CI.
- No
.env.example.
Disclosures
Facts a buyer may ask the seller to state in the sale agreement.
History
- 46 commits by 1 contributor
- First commit 2026-09-02, last commit 2026-10-28
- 31 commits name an AI co-author
Ownership & provenance
- High: The product carries an open-source licence (MIT)
- Info: Built with AI coding tools
- Info: One contributor
Proofpack is an automated screening tool. It reads your repository and reports patterns that buyers often ask about. It is not a security audit, a legal opinion, an IP opinion or a valuation. Check important findings with a qualified professional.
Proofpack is an automated screening tool. It reads your repository and reports patterns that buyers often ask about. It is not a security audit, a legal opinion, an IP opinion or a valuation. Check important findings with a qualified professional.