Features
What an audit checks, and what it deliberately does not
Five categories of finding
Each finding comes from a deterministic tool — a scanner that matched a rule at a line in a file. You can check every one of them yourself, because we show you the evidence.
Exposed credentials
API keys, database URLs, signing secrets and access tokens committed into the repository. We match on key formats, so a Stripe live key or a Supabase service role key is identified for what it is rather than flagged as a suspicious string.
Matched values are stored redacted. We prove a credential is present without keeping a copy of it.
Vulnerable dependencies
Your lockfile is read as data and each pinned version is checked against the public advisory database. You get the advisory identifier, what it allows, and the exact version that fixes it.
Supported: package-lock.json, pnpm-lock.yaml, yarn.lock, requirements.txt, poetry.lock, uv.lock, Pipfile.lock. No lockfile means no dependency findings — and the report says so.
Security patterns
Raw HTML rendered without sanitising, SQL assembled by joining strings, cookies set without the Secure flag, endpoints that fetch by id without an ownership check, permissive CORS.
Powered by Semgrep with a pinned ruleset, plus Bandit for Python. Rules in your repository are never loaded.
Reliability
Errors swallowed by a bare except, promises left unawaited, webhook handlers that return success before the work is done. The failures that do not crash anything — they just quietly lose data.
This is the category AI-assisted code fails most often, because the happy path is what gets tested.
Code quality
Untyped values at boundaries that matter, unused imports, request bodies written into logs. Not urgent, but the kind of thing that gets expensive once there is more code around it.
ESLint and Ruff, both running with our configuration rather than yours.
What we could not check
Every report names the languages it did not support, the files it skipped and why, and any scanner that ran short of time. Coverage you cannot see is not coverage.
Treated as a feature, not a footnote. You are hiring us because you cannot judge our thoroughness yourself.
Written for the person who has to act on it
Most security tooling produces output for security engineers. If you had one of those, you would not need us.
Ordered by what to do first
Not sorted by severity label alone. A committed live key outranks a dependency advisory that needs a specific set of conditions to matter.
Evidence on every finding
The file, the line, the matched snippet, the scanner and the rule id. Enough to verify it yourself or hand it to a contractor.
Confidence, stated plainly
High, medium or low — and where a tool cannot settle it, the finding is marked as requiring human review rather than presented as fact.
Explanations, clearly labelled
What it means and what to do, written in plain language and marked as AI-generated so you always know which part is the tool and which part is the explanation.
Fix tracking across re-scans
Re-run after a fix and the report tells you which findings you closed, which are still open, and which are new since last time.
No false reassurance
A clean report says no blocking issues were found in what was covered. It never says your code is secure, because no automated tool can honestly say that.
The tools we run, and how we run them
Deterministic scanners produce every finding. The language model groups and explains them — it cannot create a finding, hide one, or change its severity.
| Scanner | Covers | How it is run |
|---|---|---|
| Secret scanning | All file types | Pattern and entropy matching over file contents. Reads only. |
| Dependency audit | npm, pnpm, yarn, pip, Poetry, uv | Lockfiles parsed as data and checked against the advisory database. Nothing is ever installed. |
| Semgrep | JavaScript, TypeScript, Python | Pinned ruleset. Registry and in-repository rule discovery disabled. |
| ESLint | JavaScript, TypeScript | Our configuration, with config lookup disabled. Your eslint.config.js is read as text, never executed. |
| Ruff | Python | Parses to a syntax tree. Your modules are never imported. |
| Bandit | Python | Syntax-tree analysis for common security issues. |
Language support is JavaScript, TypeScript and Python today. Other languages in your repository are detected and reported as not covered rather than quietly ignored.
See it against your own code
Solo and Team each start with a 3-day trial of the full product. It needs a card, and GitHub access stays read-only and limited to the repositories you choose.
Start a 3-day trial