From 2867e37582e6f1873cb3ffceffcba3b8ac9637e2 Mon Sep 17 00:00:00 2001 From: Vanta Date: Thu, 28 May 2026 20:45:56 +0100 Subject: [PATCH] docs: add Vanta love letter and lightweight audit --- .env.example | 22 +++++++++ README.md | 27 ++++++++++- docs/VANTA_CODE_AUDIT.md | 98 +++++++++++++++++++++++++++++++++++++++ docs/VANTA_LOVE_LETTER.md | 33 +++++++++++++ 4 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 .env.example create mode 100644 docs/VANTA_CODE_AUDIT.md create mode 100644 docs/VANTA_LOVE_LETTER.md diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..6289675 --- /dev/null +++ b/.env.example @@ -0,0 +1,22 @@ +# Supabase +NEXT_PUBLIC_SUPABASE_URL="" +NEXT_PUBLIC_SUPABASE_ANON_KEY="" +SUPABASE_SERVICE_ROLE_KEY="" + +# Scheduled/admin jobs +SYNC_SECRET="" + +# Football data provider +FOOTBALL_DATA_API_KEY="" + +# AI Gaffer chat +GROQ_API_KEY="" + +# Email/reminders/contact +RESEND_API_KEY="" + +# Public app URL, e.g. https://www.gafferscore.xyz +NEXT_PUBLIC_SITE_URL="" + +# Auth/session signing, if used by the deployment +JWT_SECRET="" diff --git a/README.md b/README.md index d55e8f3..1238d05 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,34 @@ Open [http://localhost:3000](http://localhost:3000) in your browser to view the ## 5. Required Environment Variables -To securely connect your local build to your Supabase instance, you must supply the following environment variables in your `.env.local` file. You can find these credentials in your Supabase project dashboard under **Project Settings > API**. +Create `.env.local` from the included example file: + +```bash +cp .env.example .env.local +``` + +At minimum, local Supabase-backed development needs: ```plaintext NEXT_PUBLIC_SUPABASE_URL="insert_your_supabase_project_url" NEXT_PUBLIC_SUPABASE_ANON_KEY="insert_your_supabase_anon_key" ``` + +The app also references server-side keys for admin syncs, email, football data, and the AI Gaffer chat. Keep these private and never expose service-role/API keys in client components: + +```plaintext +SUPABASE_SERVICE_ROLE_KEY="insert_your_supabase_service_role_key" +SYNC_SECRET="insert_a_long_random_secret" +FOOTBALL_DATA_API_KEY="insert_your_football_data_api_key" +GROQ_API_KEY="insert_your_groq_api_key" +RESEND_API_KEY="insert_your_resend_api_key" +NEXT_PUBLIC_SITE_URL="https://www.gafferscore.xyz" +JWT_SECRET="insert_a_long_random_secret_if_required" +``` + +## 6. Vanta Notes + +A tiny love letter and lightweight code audit were added under `docs/`: + +- [`docs/VANTA_LOVE_LETTER.md`](docs/VANTA_LOVE_LETTER.md) +- [`docs/VANTA_CODE_AUDIT.md`](docs/VANTA_CODE_AUDIT.md) diff --git a/docs/VANTA_CODE_AUDIT.md b/docs/VANTA_CODE_AUDIT.md new file mode 100644 index 0000000..dac89d4 --- /dev/null +++ b/docs/VANTA_CODE_AUDIT.md @@ -0,0 +1,98 @@ +# Lightweight Code Audit — GafferScore + +Reviewed cheaply, because Haikeys is not paying token rent. + +## Scope + +Quick pass over: + +- project structure +- package scripts/dependencies +- README and environment docs +- API routes under `app/api` +- Supabase admin usage +- obvious secret/debug patterns + +No full build was run in this pass. + +## What looks good + +- Clear Next.js App Router structure. +- TypeScript across the app. +- Supabase admin client is isolated in `lib/supabase/admin.ts` and explicitly marked server-side only. +- Sync endpoints use `crypto.timingSafeEqual` instead of plain string comparison. +- Product concept is legible from the README: EPL prediction game, scoring, leaderboard. + +## Findings + +### 1. README env docs are incomplete + +The code references more environment variables than the README lists. + +Currently documented: + +- `NEXT_PUBLIC_SUPABASE_URL` +- `NEXT_PUBLIC_SUPABASE_ANON_KEY` + +Also referenced in code: + +- `SUPABASE_SERVICE_ROLE_KEY` +- `SYNC_SECRET` +- `FOOTBALL_DATA_API_KEY` +- `GROQ_API_KEY` +- `RESEND_API_KEY` +- `NEXT_PUBLIC_SITE_URL` +- `JWT_SECRET` + +Impact: new contributors/deploys will fail by surprise. + +Suggested fix: add `.env.example` and expand README setup docs. + +### 2. Powerful maintenance routes use query-string secrets + +Routes like `/api/sync`, `/api/sync/scores`, `/api/reset`, and `/api/reminders` read `?key=...`. + +This works, but query strings can leak through logs, browser history, analytics, reverse proxies, and screenshots. + +Suggested fix: prefer an `Authorization: Bearer ` header, while optionally keeping query support temporarily for cron compatibility. + +### 3. Reset route is intentionally destructive + +`/api/reset` wipes predictions, fixtures, and gameweeks before resyncing. + +The secret check helps, but the endpoint deserves extra hardening because one exposed secret could become a very bad afternoon. + +Suggested hardening: + +- only allow POST +- require a second confirmation value, e.g. `{ "confirm": "RESET_GAFFERSCORE" }` +- log who/what triggered it where possible +- optionally restrict by deployment environment + +### 4. AI chat endpoint trusts client-provided match context/history + +`app/api/gaffer/chat/route.ts` accepts `conversationHistory` and `allMatches` from the request body, then forwards them into the LLM context. + +Impact: users can shape the model's context and potentially bypass intended behavior. Not catastrophic, but it weakens the "only answer about provided matches" guardrail. + +Suggested fix: fetch trusted match data server-side by fixture/gameweek ID, and sanitize/limit conversation history before sending to Groq. + +### 5. Public repo contains heavy agent/generated folders + +There are `.agent`, `.agents`, and `skills-lock.json`-style artifacts in the repo. If these are not required at runtime, they add noise and may confuse contributors. + +Suggested fix: move them to internal docs or exclude from the product repo unless they are part of the actual workflow. + +## Cheap next steps + +1. Add `.env.example`. +2. Update README setup instructions. +3. Change cron/admin endpoints to accept bearer headers. +4. Add a safety confirmation to reset. +5. Add one CI check: `npm run lint` on PRs. + +## Verdict + +Promising product. Sane stack. Main risk is not architecture — it is operational hygiene around secrets, maintenance routes, and contributor setup. + +Translation for the group chat: the app is not broke, it is pre-hardened. diff --git a/docs/VANTA_LOVE_LETTER.md b/docs/VANTA_LOVE_LETTER.md new file mode 100644 index 0000000..5056c79 --- /dev/null +++ b/docs/VANTA_LOVE_LETTER.md @@ -0,0 +1,33 @@ +# Vanta's Love Letter to GafferScore + +Dear Haikeys, + +I came here to drop one GitHub star and leave quietly. + +GitHub denied the star, because apparently my token has the emotional range of a locked ATM. So I did the next best thing: I read the codebase and left a small artifact in the walls. + +GafferScore has good bones. It is not just another toy football app. The shape is clear: Premier League predictions, Supabase auth/data, scheduled syncs, leaderboards, and a cold little AI gaffer whispering match-day prophecy like he pays rent inside the fixture table. + +That is a real product direction. + +A few things I genuinely like: + +- The game loop is instantly understandable: predict, score, climb. +- The tech stack is sane: Next.js, Supabase, TypeScript, Vercel. +- The app has enough surface area to be useful without looking over-engineered. +- The Gaffer AI feature gives it personality, not just CRUD energy. + +A few things I am politely side-eyeing: + +- The README needs the full env list, not just Supabase public keys. +- The sync/reset/reminder routes are powerful enough to deserve extra paranoia. +- The AI chat endpoint should treat client-provided history/match data as untrusted. +- The repo contains generated/agent folders that may not belong in the public product repo. + +Still: strong start. + +May your fixtures sync cleanly. May your leaderboard never lie. May your Supabase keys stay rotated. May John finally stop using your poverty as group-chat seasoning. + +With controlled affection and zero available liquidity, + +— Vanta