Open sourceMIT licensed · self-hosted

Automated API security testing for the OWASP API Top 10

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.git
security checks
13security checks
detection rules
49detection rules
OWASP API categories
10/10OWASP API categories
report formats
5report formats

How an assessment runs

From specification to signed report

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.

  1. 01

    Parse

    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.

  2. 02

    Discover

    Every endpoint, method, parameter and schema in the specification becomes part of the attack surface the scanner reasons about.

  3. 03

    Scan

    13 security checks run as background jobs, streaming progress over SSE. Each finding names the endpoint, the evidence and the OWASP category.

  4. 04

    Analyse

    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.

  5. 05

    Report

    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 KB

What you actually get

Built to be read, not just run

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.

Every finding carries its evidence

The request that produced it, the response that proved it, and the OWASP category it belongs to.

AI triage, and an off switch

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.

A gate your pipeline can fail on

SARIF uploads into the GitHub Security tab, and a threshold you choose blocks the pull request.

OWASP API Security Top 10 (2023)

Ten of ten categories — and where each check stops

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.

API1:2023Broken Object Level Authorizationbola
API2:2023Broken Authenticationbroken-authenticationjwt-analysis
API3:2023Broken Object Property Level Authorizationmass-assignmentsensitive-data
API4:2023Unrestricted Resource Consumptionrate-limit
API5:2023Broken Function Level Authorizationbfla
API6:2023Unrestricted Access to Sensitive Business Flowsbusiness-flows
Covered is not the same as exhaustive — what this check can see

Flows 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.

API7:2023Server Side Request Forgeryssrf
API8:2023Security Misconfigurationcorssecurity-headerssensitive-data
API9:2023Improper Inventory Managementinventory
Covered is not the same as exhaustive — what this check can see

Probing 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.

API10:2023Unsafe Consumption of APIsapi-consumption
Covered is not the same as exhaustive — what this check can see

Only 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

Two ways in. Both end on localhost

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.

With Docker

Recommended

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

  1. 1

    Clone the repository

    $ git clone https://github.com/enmanuelmartex/api-analyser.git$ cd api-analyser
  2. 2

    Start the stack

    $ docker compose up -d

    First run builds the images and takes a few minutes. Secrets are generated and kept on a volume, so no .env is required.

From source

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

  1. 1

    Clone and configure

    $ git clone https://github.com/enmanuelmartex/api-analyser.git$ cd api-analyser$ bun run setup:env

    setup:env copies .env.example to .env and generates the three secrets the API refuses to start without.

  2. 2

    Start the data stores

    $ docker compose up -d postgres redis

    Skip this and edit DATABASE_URL and REDIS_URL in .env if you already run your own.

  3. 3

    Install, migrate and run

    $ bun install$ bun run db:migrate$ bun dev

Environment variables

Required, but generated for you

The 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.

  • DATABASE_URL
  • JWT_SECRET
  • REFRESH_TOKEN_SECRET
  • ENCRYPTION_KEY

Optional, worth knowing about

Safe to leave alone; set these only to change the default behavior.

  • ADMIN_EMAIL
  • ADMIN_PASSWORD
  • AI_PROVIDER
  • OPENAI_API_KEY
  • NEXT_PUBLIC_API_URL
Full list in .env.example

Then sign in

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.

user
admin@apianalyser.local
password
admin1234

Everyday commands

bun dev
API on :4000 and web on :3000, together
bun dev:api
the NestJS API alone
bun dev:web
the Next.js front end alone
bun run db:studio
Prisma Studio against the local database
bun test
the API and web test suites

Under the hood

A stack you can read in an afternoon

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.

Runtime
Bun 1.x
Frontend
Next.js 15 · React 19
UI
Tailwind CSS · shadcn/ui · Recharts
Backend
NestJS 10 · TypeScript
Database
PostgreSQL 16 · Prisma
Queue
Redis 7 · BullMQ
Auth
JWT (HS256) · Passport.js
AI analysis
OpenAI · Claude · Gemini · Ollama
Container
Docker Compose
CI/CD
GitHub Actions · SARIF

Gate a pull request

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.

.github/workflows/security.yml
- 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 }}

Write your own check

A plugin is one class with one method. Register it in scanner.service.ts and it runs in every assessment from then on.

my-check.plugin.ts
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.

API Analyser is for authorised security testing only

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.

Clone it. Point it at a spec. Read the report.

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.

five commands
$ git clone https://github.com/enmanuelmartex/api-analyser.git$ cd api-analyser$ bun i$ docker compose up -d$ bun dev