Library / SDK
OWASP/OFFAT avatar
OWASP/OFFAT

OWASP OFFAT: source-aware API security testing

The OWASP OFFAT tool autonomously assesses your API for prevalent vulnerabilities, though full compatibility with OAS v3 is pending. The project remains a work in progress, continuously evolving towards completion.

677 stars93 forksPythonMIT

At a glance

What is it?
OWASP OFFAT maps an API from its source code, proves source to sink flows on a code property graph, executes the resulting test plan with a Rust engine and reports findings as SARIF. It suits teams that own the code and want route-level evidence, and it is explicitly a work in progress with a retired first-generation CLI.
Who is it for?
Adopt OWASP OFFAT if your team owns the API source, can point the tool at a repository, and wants security findings that carry a code flow and an HTTP route instead of a bare scanner list. Do not adopt it against systems you are not authorised to test, since live runs require an explicit --yes and send real requests, and do not start from the pip install offat package, which the README describes as retired.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Mapping the API from code instead of probing it blind

OFFAT stands for OFFensive Api Tester, and the README describes it as source-aware API security testing. The source-aware part is the differentiator. Most API security tooling starts from a running service and probes it, which means the tool learns about your API by sending requests and seeing what comes back. OFFAT starts from the code instead.

The stated pipeline is the whole design in one line:

bash
map -> code graph -> threat model -> test-gen -> execute (Rust) -> triage (AI) -> report

Mapping covers decorator routes, routers, and OpenAPI or Swagger specifications, and the result is written into one canonical Bundle file. From there the tool builds a code graph, derives a threat model, generates a test plan, executes it, triages the results and produces a report. Every stage after mapping consumes the artefact of the previous one, which is why the Bundle format sits at the centre of the repository.

The problem this addresses is coverage. A scanner that only sends requests cannot tell you whether a parameter is reachable, whether a missing authorisation check is actually exposed on a route, or whether a dangerous sink is called by anything the API serves. Code-aware analysis can attach each finding to the HTTP route that reaches it, and can prove the data flow from a source to a sink on a code property graph for Python and JavaScript or TypeScript.

The scope is bounded in a way worth noting. The property graph covers Python and JS or TS. The code base is read as source; the tool does not need to run your application to build the graph. That is what makes the source-only mode possible at all, and it is also the boundary of the analysis.

The Bundle contract that every stage passes through

The repository layout shows a project that is really a set of cooperating packages rather than one program. bundle/ holds the Bundle contract, which the README describes as the schema, IO and validation layer. platform/ provides the offat-platform command that runs mapping, the property graph, the threat model, test generation and triage. engine-rs/ is offat-engine, the Rust execution engine. whitebox/ is the code-graph taint engine and SAST pipeline. graybox/ is SAST fused with endpoint reachability. triager/ is the shared AI and heuristic triager. reporter/ produces SARIF, Markdown, HTML and compliance reports, and viz/ is a static Bundle visualizer.

That structure has a concrete consequence. Because the stages communicate through a validated file rather than through Python objects, you can inspect the intermediate state. The Bundle is a document, and the visualizer renders it statically, so you can look at what the mapper understood about your API before any test is executed. For a security tool, that is a meaningful safety property: the map is reviewable before it becomes traffic.

The Makefile in the repository confirms the same modularity at the level of developer commands. There are targets for building the engine, building it with the HTTP/3 and QUIC transport enabled, running the engine unit tests, installing the platform and its libraries, byte-compiling the Python packages, smoke-testing the visualizer render logic, and an offline end-to-end check that maps, builds the property graph, derives the threat model and generates tests:

bash
engine: ## Build the Rust execution engine (HTTP/1.1+2, WebSocket, gRPC) -> engine-rs/target/release/offat-engine
	cd engine-rs && cargo build --release

The offline smoke target is worth noting for CI. It exercises the entire chain up to test generation without sending a request, which makes it possible to keep the pipeline honest in a build without a target system.

Source-only first, live runs only with an explicit --yes

The quick start in the README is careful to separate the safe mode from the dangerous one, and that separation is the first thing to internalise.

bash
git clone https://github.com/OWASP/OFFAT.git && cd OFFAT
pip install ./bundle ./triager './whitebox[graph]' ./graybox ./platform ./reporter
make engine                                   # Rust engine, only for live runs

# source-only: no traffic is sent
offat-platform run /path/to/repo -o app.offat.json --report-dir reports

# live, against a target you are authorized to test
offat-platform run /path/to/repo --url https://api.your-authorized-target.test \
  --identities examples/identities.example.json --yes -o app.offat.json --fail-on high

Install the Python packages from the local checkout, build the Rust engine with make engine if you intend to run live tests, and start with the source-only invocation. That run maps the repository, builds the graph, derives the threat model, generates the plan and writes a Bundle file plus reports, without sending anything to a server.

The live invocation adds three things that matter. The --url option points the engine at a target. The --identities option supplies an identity file, and the repository carries an example at examples/identities.example.json, which is how the identity-aware checks for BOLA and BFLA get more than one role to compare. The --yes flag is the acknowledgement that real requests will be sent. The --fail-on high option makes the command exit non-zero when high severity findings appear, which is what you want in a pipeline.

Two more source-only entry points exist for narrower jobs. offat-whitebox runs the code-graph SAST with AI triage, and offat-graybox runs SAST prioritised by endpoint reachability, which is the cheaper option when the graph is too slow or the language is out of scope.

There is also a container path, with the README giving docker build -t offat -f docker/Dockerfile .

What the Rust engine can talk to

The execution engine is the part that touches the target, and it is written in Rust for a reason the README does not spell out but the design implies: it is the component that has to speak several wire protocols and do it quickly and safely.

The engine covers HTTP/1.1 and HTTP/2, WebSocket and gRPC, with HTTP/3 plus QUIC as an opt-in feature rather than the default. The Makefile has a dedicated target for that variant, which sets a Rust feature flag and an unstable configuration flag on the request library:

bash
engine-http3: ## Build the engine with the HTTP/3 + QUIC transport enabled
	cd engine-rs && RUSTFLAGS="--cfg reqwest_unstable" cargo build --release --features http3

The reason HTTP/3 sits behind a flag rather than being on by default is the nature of the dependency it needs. A QUIC-capable build pulls in an unstable configuration of the HTTP client, which is a deliberate trade of stability for protocol coverage. For most API testing the older transports are enough, and the README notes that the engine is only needed at all for live runs.

The most interesting capability in the list is the identity-aware differential check for BOLA and BFLA. Broken object level authorisation and broken function level authorisation are the two findings that are hardest to detect with a single request, because the response you get is often a well-formed 200 with data you should not have seen. If the engine holds two identities, it can send the same request as both and compare, which turns a silent authorisation failure into an observable difference.

Triage with an AI model or without one

Findings from a code-aware test plan are still findings that need prioritising, and OFFAT offers two routes for that. The primary route is a token-disciplined AI triager, described as cached, batched and model-tiered. The alternative is a deterministic offline heuristic, which produces the same kind of prioritisation without a model call at all.

That dual path is the important design decision. A team with a strict policy on sending code or findings to a model service can use the offline heuristic and keep the whole pipeline local, which is exactly what the source-only and offline smoke modes are built for. A team that wants better prioritisation can use the model path and accept that behaviour.

The token discipline is a cost-control mechanism rather than a safety one. Caching means repeated findings are not re-sent on every run, batching means many findings travel in one request instead of many, and model tiering means the expensive model is reserved for the cases that need it. Without those three, an AI triage step over a large API becomes the most expensive part of the pipeline, and it would be the part that fails first.

The reporter package is the last stage and it is unusually wide. The README lists SARIF 2.1.0 output with code flows attached, Markdown, HTML, an OWASP API Top 10 compliance report, and JUnit output for test reporting systems. The SARIF output is the one that matters most for adoption, because it means a finding with a source to sink path can land in a code scanning dashboard with a location rather than a line in a text file.

A work in progress with a retired first generation

The project description is candid: full compatibility with OAS v3 is still pending and the project remains a work in progress. The README adds a detail that matters for anyone arriving from search results, since the classic spec-only Python CLI installed with pip install offat from src/ has been retired in favour of the platform, and its last release remains on PyPI.

That retirement creates a versioning trap. The release list contains v0.19.4 from 2025-06-01, v0.20.0-beta from 2024-08-28, and v0.19.3 from 2024-07-30, so the highest version number is not the newest tag. A user who finds the PyPI package and reads the version numbers will not be able to tell which artefact corresponds to the current architecture, and the default branch is dev rather than a release branch.

The repository layout shows the current development surface: an action.yml for the GitHub Action, a Makefile, agents/, commands/ and skills/ directories, an OFFAT-AI.md file, SECURITY.md, CONTRIBUTING.md, a pre-commit configuration, a docs/ directory, and an examples/ folder with an identities example and vulnerable code samples under examples/vuln-code/.

The licence is MIT, distributed under the MIT License according to the README, with the LICENSE.md file at the root. The project sits under the OWASP organisation, its homepage is owasp.org/OFFAT/, and the documentation site covers installation, usage and the GitHub Action, which means the parts of the workflow that matter most for a team, running it in CI, are documented separately from the README.

Editorial conclusion

Adopt OWASP OFFAT if your team owns the API source, can point the tool at a repository, and wants security findings that carry a code flow and an HTTP route instead of a bare scanner list. Do not adopt it against systems you are not authorised to test, since live runs require an explicit --yes and send real requests, and do not start from the pip install offat package, which the README describes as retired. Verify first that your Python and JS or TS code is inside the property graph scope, that the Rust engine builds for your platform, and that you are ready to accept preview-level behaviour, since the last push was on 2026-10-20 and the newest tag is v0.19.4 from 2025-06-01 under the MIT licence.

Frequently asked questions

What does OWASP OFFAT do differently from a black box API scanner?

It maps the API from source code, using decorator routes, routers and OpenAPI or Swagger specs, then proves source to sink data flows on a code property graph for Python and JS or TS. Every finding is attached to the HTTP route that reaches it.

How do I install OWASP OFFAT and run it without sending traffic?

Clone the repository, pip install ./bundle ./triager './whitebox[graph]' ./graybox ./platform ./reporter, and then run offat-platform run /path/to/repo -o app.offat.json --report-dir reports, which the README marks as source-only with no traffic sent. The Rust engine is only needed for live runs.

What does a live OFFAT run require?

A target URL, an identities file such as examples/identities.example.json, and the explicit --yes acknowledgement, because live runs send real requests. The README states that only systems and code you own or are authorised to assess may be tested.

What output formats does OWASP OFFAT produce?

The README lists SARIF 2.1.0 with code flows, Markdown, HTML, an OWASP API Top 10 compliance report, and JUnit. The tool can also be run with a deterministic offline heuristic instead of the AI triager when model calls are not wanted.

Official sources

  1. License: MIT
  2. OWASP/OFFAT on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/owasp-offat.svg)](https://hysenlabs.com/projects/owasp-offat)

Was this useful?