Every finding carries its evidence
The request that produced it, the response that proved it, and the OWASP category it belongs to.
API Analyser parses your OpenAPI specification, discovers every endpoint it describes, and runs 13 security checks across all ten OWASP API Security Top 10 categories — then hands you the findings as a PDF, a dashboard, or SARIF your CI can fail a build on.
$ git clone https://github.com/enmanuelmartex/api-analyser.gitHow an assessment runs
No agent to install in your service, no proxy to put in front of it and no code change. The scanner works from the contract your API already publishes, and every probe it sends is one you can read back in the finding it produced.
Point it at an OpenAPI or Swagger specification — a URL or an uploaded file. No agent, no code changes, no proxy in front of your API.
Every endpoint, method, parameter and schema in the specification becomes part of the attack surface the scanner reasons about.
13 security checks run as background jobs, streaming progress over SSE. Each finding names the endpoint, the evidence and the OWASP category.
Optionally enrich findings with an LLM — OpenAI, Claude, Gemini, Grok or a local Ollama model. Bring your own key; skip it entirely and the scan still runs.
Export as PDF, HTML, JSON, SARIF or Markdown. SARIF uploads straight into the GitHub Security tab from a workflow.
See an example reportA real PDF from the app · 652 KBWhat you actually get
A scanner that reports a number nobody can check is a scanner nobody acts on. Every result here names what was sent, what came back, and where the check stops seeing — so a finding can be argued with, and a clean result means something.
The request that produced it, the response that proved it, and the OWASP category it belongs to.
Bring your own key for OpenAI, Claude, Gemini or Grok — or point it at a local Ollama model. Skip it and the scan is unchanged.
SARIF uploads into the GitHub Security tab, and a threshold you choose blocks the pull request.
OWASP API Security Top 10 (2023)
Coverage is computed from the check manifests at runtime and asserted in the API's own test suite, so the table below cannot quietly drift from the code that produces it.
bolabroken-authenticationjwt-analysismass-assignmentsensitive-datarate-limitbflabusiness-flowsFlows are identified from the naming in the specification, and each finding names the term that matched. What is observed is the absence of a control in front of the flow — no throttle, no bot mitigation, no captcha or OTP, no authentication, no idempotency key. Probes carry a payload the target is expected to reject, so the flow itself is never executed, and DELETE operations are never probed.
ssrfcorssecurity-headerssensitive-datainventoryProbing is confined to the host under assessment: undocumented versions beside the documented ones, deprecated operations still answering, and exposed documentation, actuator, metrics and debug surfaces. Every claim is made against a baseline request to a path that does not exist. A shadow API on a different hostname cannot be found this way — that needs an asset inventory the scanner is not given.
api-consumptionOnly what crosses the client boundary is observable: upstream references returned over plain HTTP, upstream errors relayed verbatim, and inbound webhooks that accept unverified senders. Whether the service validates what its upstreams return cannot be settled from outside.
A tick with a footnote is the honest shape of those three. A tick without one would claim more than the product can demonstrate, and no check at all would leave you reading “no findings” as “nothing to find”. Read the full coverage notes.
Run it locally
Nothing phones home and no account on anyone's server is involved. The scanner, the database, the queue and the web app all run on your machine.
Brings up PostgreSQL, Redis, the API and the web app together, on one network, already wired to each other. Nothing else gets installed on your machine.
Needs: Docker with the Compose plugin · Git
$ git clone https://github.com/enmanuelmartex/api-analyser.git$ cd api-analyser$ docker compose up -dFirst run builds the images and takes a few minutes. Secrets are generated and kept on a volume, so no .env is required.
For changing the code. Runs the API and the web app on your machine with hot reload; the database and queue still come from Docker unless you point it at your own.
Needs: Bun 1.x · Git · a PostgreSQL 16 and a Redis 7 from somewhere
$ git clone https://github.com/enmanuelmartex/api-analyser.git$ cd api-analyser$ bun run setup:envsetup:env copies .env.example to .env and generates the three secrets the API refuses to start without.
$ docker compose up -d postgres redisSkip this and edit DATABASE_URL and REDIS_URL in .env if you already run your own.
$ bun install$ bun run db:migrate$ bun devThe API refuses to start without these. Docker generates and stores them on a volume; setup:env writes them into .env for the source path. Nobody types these in by hand.
Safe to leave alone; set these only to change the default behavior.
Open http://localhost:3000 — the API is on :4000. The first boot creates one administrator against the empty database; that account creates everyone else. There is no public sign-up and no OAuth.
Change the password on first login, or set ADMIN_PASSWORD before the first start.
Under the hood
One Bun workspace, two apps, no magic. Every dependency is a boring, well-documented one, and the scanner is a set of plugins with a single method each.
Point the reusable workflow at a deployed environment and choose the severity that fails the build. Results land in the GitHub Security tab as SARIF.
- name: API Analyser security gate uses: enmanuelmartex/api-analyser/.github/workflows/security.yml@main with: target_url: https://api.yourapp.com fail_on: HIGH # CRITICAL | HIGH | MEDIUM secrets: API_ANALYSER_API_KEY: ${{ secrets.API_ANALYSER_API_KEY }}A plugin is one class with one method. Register it in scanner.service.ts and it runs in every assessment from then on.
export class MyCheckPlugin extends BasePlugin { readonly id = 'my-custom-check'; readonly name = 'My Security Check'; readonly owaspCategories = ['API8:2023']; async run( context: ScanContext, ): Promise<PluginResult> { // context.endpoints, context.auth, // context.baseUrl }}The full module map lives in ARCHITECTURE.md.
Only run it against APIs you own or have explicit written permission to test. Unauthorised API testing may violate computer fraud laws in your jurisdiction. The scanner sends real requests to the host you nominate — it never probes a host you did not name, and that boundary only protects you if you nominate honestly.
Free and MIT licensed. It runs on your machine, the findings stay in your database, and nothing about your API leaves the network you started it on.
$ git clone https://github.com/enmanuelmartex/api-analyser.git$ cd api-analyser$ bun i$ docker compose up -d$ bun dev