Guides

The Free Code Audit Checklist (DIY Before You Book)

Audit yourself first

Before you book any code audit, ours included, you should run the cheap checks yourself. Partly because several of the most dangerous findings take under an hour to detect and you shouldn’t wait on a third party for them. Partly because a team that has done a self-audit gets far more out of a professional one: the obvious stuff is cleared, so the external eyes go straight to the hard problems.

This is the actual checklist, not a teaser. Work through it top to bottom; the ordering is by risk, not by effort. Budget a day or two of one engineer’s time for a typical SaaS codebase.

1. Secrets

The highest-severity, fastest-to-check item on the list.

  • Scan the repo, including history:
# pick one; both are free
gitleaks detect --source . -v
trufflehog git file://. --only-verified
  • Grep your built frontend bundle for things that should never reach a browser: sk_live, service_role, AKIA, -----BEGIN, your database URL.
  • Check .gitignore actually covers .env, key files, and cloud credentials; then verify none were committed before the ignore rule existed (that’s what the history scan is for).
  • Anything found: rotate, don’t just delete. A secret in git history is public forever; removal without rotation is theater.

2. Authentication and authorization

Authentication is “who are you”; authorization is “what may you do.” The second is where codebases fail, especially AI-generated ones, where permission checks tend to live in the UI and nowhere else.

  • Test as a hostile user, with curl, not the app. Log in as user A, take user B’s resource ID, request it directly. Repeat for update and delete. This single test finds broken object-level authorization, the most common serious flaw we see.
  • Enumerate admin/privileged endpoints and confirm each checks the role server-side; a hidden button is not a permission model.
  • Check session basics: tokens expire, logout actually invalidates, password reset tokens are single-use and short-lived.
  • If you’re on Supabase/Firebase: read every RLS rule / security rule. using (true) on a multi-tenant table is a breach with a datestamp TBD.
  • Rate limiting exists on login, signup, and password reset. If not, you’re offering free credential-stuffing infrastructure.

3. Input validation

  • Every database query uses parameterization; grep for string concatenation or template literals building SQL. Raw query escape hatches (raw(), $queryRawUnsafe, string-built WHERE clauses) get individual review.
  • Server-side validation exists for every write endpoint. Client-side validation is UX, not security.
  • File uploads validate type and size server-side, and uploads are stored outside the web root (or in object storage) with generated names.
  • Anything user-provided that gets rendered is escaped by the framework, and every place someone bypassed the framework (dangerouslySetInnerHTML, v-html, innerHTML) is individually justified.

4. Dependencies

  • Run the free scanners: npm audit / pip-audit / bundler-audit, or turn on GitHub Dependabot. Triage criticals and highs; don’t aim for zero warnings, aim for zero unreviewed criticals.
  • Check for abandonware: your framework and top-10 direct dependencies: when was each last released? A core dependency dead for three years is a migration you haven’t scheduled yet.
  • Lockfile is committed and CI installs from it (npm ci, not npm install).
  • Count your dependencies: if two HTTP clients and three date libraries coexist, nobody is curating the supply chain.

5. Error handling and logging

  • Force a failure and watch what happens. Kill the database connection locally, hit an endpoint. Graceful error, or stack trace to the client? Stack traces leak schema, paths, and library versions.
  • Error reporting exists (Sentry or similar) and someone actually receives it. “We find out from support tickets” is a finding.
  • Grep for swallowed errors: empty catch {} blocks, except: pass, promises without rejection handling. Each one is a place the system lies to you.
  • Logs don’t contain secrets or personal data; spot-check what your logger prints for a login request and a payment.
  • Outbound calls have timeouts. An HTTP call with no timeout means a hung third party can pin every worker you own.

6. Tests

The question is not coverage percentage; it’s whether the paths that cost money are protected.

  • Money paths have tests: billing, subscription changes, anything that calls Stripe.
  • Auth paths have tests: login, permission checks, tenant isolation.
  • Tests run in CI on every PR and a red build actually blocks merging. Tests that can be skipped, will be.
  • The suite runs in minutes, not hours; slow suites stop being run, then stop being true.
  • If you have near-zero tests: don’t pledge 80% coverage. Add characterization tests around the money and auth paths this month, and grow from there.

7. Performance and scale

Full treatment in performance bottlenecks a code audit reveals and auditing database queries for scale; the self-audit minimum:

  • Enable query statistics (pg_stat_statements or slow-query log) and look at the top 10 by total time. Any surprises?
  • Grep for the N+1 shape: a query inside a loop over query results.
  • Every list endpoint has a LIMIT/pagination. Test one with a large offset or a big dataset if you can.
  • Run a bundle analyzer on the frontend (npx source-map-explorer dist/assets/*.js): is the biggest thing in the bundle something every page needs?
  • Background-work check: emails, PDFs, third-party calls. On the request path, or in a queue?

8. Operational readiness

  • Backups exist AND a restore has been tested. An untested backup is a hope, not a backup.
  • A new engineer could run the project locally from the README; actually try it in a clean environment.
  • Deployment and rollback are documented and don’t depend on one person’s memory. (More on why this matters in documentation and maintainability.)
  • You’d know within minutes if production went down, from monitoring, not from a customer.

Scoring it honestly

Don’t compute a percentage; the items aren’t equal. Instead: anything failed in sections 1 to 3 gets fixed this week; those are exploitable-by-strangers problems. Sections 4 to 5 get tickets with owners this sprint. Sections 6 to 8 go into the backlog as scheduled work, prioritized the way we describe in how to prioritize fixes after an audit.

And write down what you found, even briefly. A dated self-audit note is worth real money in due diligence later; it demonstrates the thing acquirers actually pay for, which is a team that knows its own system.

What a self-audit can’t see

This checklist finds the failure modes that are checkable: the greppable, testable, yes/no items. What it can’t do is judge the things that require having read a hundred other codebases: whether your architecture will survive your roadmap, which of your 40 findings actually matter, where the design is quietly fighting the business. That’s the difference between a checklist and an audit.

Get a free code audit

If you ran this list and want a second pair of eyes, or you ran it and the results scared you, that’s exactly what the free audit is for. A Webisoft engineer manually reviews your codebase (everything above, plus the judgment-layer questions a checklist can’t ask) and sends you a written, prioritized report. It’s genuinely free, done by a human, and the report is yours with no obligation attached. Get a free code audit. Worst case, you confirm your self-audit was right.

FAQ

Frequently asked questions

How long should a self-audit with this checklist take?

Budget a day or two of one engineer's time for a typical SaaS codebase. The highest-risk items go much faster than that: a secrets scan and the hostile-user authorization test together take under an hour, which is exactly why you should run them before anything else.

Do I need to buy any tools to run this checklist?

No. Everything on the list uses free tooling: gitleaks or trufflehog for secrets, npm audit or pip-audit for dependencies, curl for the authorization tests, and your database's own query statistics for the performance checks. The scarce resource is an engineer's attention, not software.

I found a secret in my git history. Is deleting the commit enough?

No. A secret that ever landed in history should be treated as public forever, because anyone who cloned or forked the repo already has it. Rotate the credential so the leaked value stops working, then clean up the history if you like. Removal without rotation is theater.

If I fail items in several sections, what do I fix first?

Don't compute a score; the items aren't equal. Anything failed in sections 1 to 3 (secrets, auth, input validation) gets fixed this week, because those are exploitable by strangers. Sections 4 and 5 become tickets with owners this sprint, and sections 6 to 8 go into the backlog as scheduled work.

If I pass the whole checklist, do I still need a professional audit?

You're in far better shape than most, but a checklist only finds the checkable, yes/no failure modes. It can't judge whether your architecture will survive your roadmap or which of your findings actually matter, which takes having read many other codebases. That judgment layer is what the free audit adds: a Webisoft engineer reads the code manually and sends a written report, at no cost.

Free · no strings shown later

Get your free code audit

A repo URL and two minutes of your time. A Webisoft engineer sends back written findings (security, tech debt, performance) with file-and-line specifics.

Request my free audit