Sample report. The app, the people and the findings are illustrative; a real report follows the same layout.
Evidence report: Taskflow
- App
- Taskflow, a team task tracker built with Lovable and Supabase
- Package
- Harden, fixed price, scope approved 2 October 2026
- Version checked
- Repository commit
4f1c9e2, production deploy of 16 October 2026
- Work period
- 3 to 16 October 2026
- Report date
- 16 October 2026
- Accepted by
- Founder review, PostVibe
Summary
Six findings in the agreed scope: two critical, two high, two medium. All six are fixed and each fix has a check you can run yourself. Two limits remain and are listed at the end. Nothing outside the scope was changed.
| Finding | Severity | Status |
| API key shipped in the client bundle | Critical | Fixed, verified |
| Row level security off on 3 tables | Critical | Fixed, verified |
| No rate limit on /api/auth | High | Fixed, verified |
| Secrets still present in git history | High | Fixed, verified |
| No backups configured for the database | Medium | Fixed, verified |
| 0 tests, 41 unhandled error paths | Medium | Baseline in place, 12 paths left |
Findings
1. API key shipped in the client bundle Critical
- Found
- The Supabase service role key was compiled into the public JavaScript bundle at
/assets/index-8d2f.js.
- Impact
- Anyone opening the site could read and change every row in the database, bypassing all access rules.
- Fix
- The key was rotated in Supabase. The client now uses the public anon key only; admin actions moved to an edge function that holds the new key on the server.
- How we checked
curl -s https://taskflow.example/assets/*.js | grep -c service_role returns 0. The old key is rejected by the API with 401.
- Result
- No server key in any public file. The rotated key is stored only in the edge function settings.
- Limits
- Requests made with the old key before rotation cannot be audited: Supabase keeps 7 days of logs on this plan.
2. Row level security off on 3 tables Critical
- Found
- Tables
tasks, comments and invites had row level security disabled.
- Impact
- A signed-in user of one team could read and edit the tasks of every other team.
- Fix
- Row level security enabled on all three tables, with policies that limit each row to members of its team. Migration
20261008_rls_team_scope.sql.
- How we checked
- An automated test signs in as a member of team A and tries to read, insert and update rows of team B. All three are refused.
- Result
- Cross-team access refused for every table in the schema, not only the three above.
- Limits
- Storage buckets were out of scope; their rules were reviewed but not changed.
3. No rate limit on /api/auth High
- Found
- The sign-in endpoint accepted unlimited attempts from one address.
- Impact
- Passwords could be guessed by brute force, and the endpoint could be flooded.
- Fix
- A limit of 10 attempts per 15 minutes per address and per account, with a clear message to the user.
- How we checked
- A script sends 11 sign-in attempts in a row; the 11th is answered with 429.
- Result
- Brute force slowed to a rate that makes it impractical.
- Limits
- A distributed attack from many addresses needs a WAF; recommended, not part of this scope.
4. Secrets still present in git history High
- Found
- Two old
.env files with a Stripe test key and an email provider key were in earlier commits.
- Impact
- Anyone with read access to the repository, now or later, could use those keys.
- Fix
- Both keys revoked and reissued. History rewritten to drop the files; a secret scan now runs on every push.
- How we checked
gitleaks detect --log-opts="--all" on the cleaned repository finds nothing.
- Result
- No secrets in the history; new ones are blocked before they land.
- Limits
- Existing clones made before the rewrite still hold the old files. The keys in them no longer work.
5. No backups configured for the database Medium
- Found
- Point in time recovery was off and no other copy of the data existed.
- Impact
- A bad migration or a deleted table would have been permanent.
- Fix
- Daily backups enabled with 14 days of retention, plus a weekly export to separate storage.
- How we checked
- Yesterday's backup was restored into a fresh project; row counts match production for every table.
- Result
- A tested restore, not just a backup setting.
- Limits
- Up to 24 hours of data can be lost between daily backups.
6. 0 tests, 41 unhandled error paths Medium
- Found
- No automated tests. 41 places where an error from the database or a third party was ignored or shown as a blank screen.
- Impact
- Every change risked breaking something unnoticed; users saw failures without explanation.
- Fix
- A test baseline of 38 tests covering sign-in, teams, tasks and invites, run on every deploy. 29 error paths now show a message and are logged.
- How we checked
- The test suite passes in the deploy pipeline; a deploy with a failing test is stopped.
- Result
- A baseline you can grow, and a rules file for your AI tool so new code keeps it green.
- Limits
- 12 error paths in rarely used admin screens are listed in the appendix and not yet handled.
What this report does not cover
- Anything outside the approved scope: storage buckets, the marketing site, the mobile app.
- Changes made after 16 October 2026 by you, your team or your AI tool.
- Third party services beyond their configuration in this app.
How to check this report yourself
The report is signed with our Ed25519 key. The signature proves the document was not changed after we issued it. It does not prove the audit found everything; the checks above are what prove each fix.
sha256sum taskflow-report-2026-10-16.pdf
openssl pkeyutl -verify -pubin -inkey postvibe-report.pub -rawin \
-in taskflow-report-2026-10-16.pdf -sigfile taskflow-report-2026-10-16.sig
Our public key is published with every report. A changed byte in the file and the check fails.
Run a free scan