Coverage summary — API paths, GET operations, object-id endpoints tested
Findings ranked by severity — CROSS_USER_IDOR, SHARED_OBJECT, UNAUTH_ACCESS flagged HIGH
Tested endpoints — every operation and the ids probed
Methodology — spec-driven, two-user, GET-only
Machine-readable JSONL also emitted to results.jsonl for export / SIEM pipelines.
Target Spec
Point the workflow at an OpenAPI / Swagger document — no manual endpoint list needed.
Two options:
1. URL — paste the spec URL into spec-url (default: Petstore demo). Change it to your target's Swagger/OpenAPI JSON.
2. Storage upload — upload your own .json spec to Trickest Storage, then wire it into ingest-spec. Disconnect fetch-spec → parse-operations if you want the uploaded spec to take priority.
> Swagger 2.0 and OpenAPI 3.x are both supported. The spec is the only input you need to change.
How It Works
1 · Enumerate
parse-operations reads the spec and finds every GET with an object-reference param (id, uuid, key…). Required params are filled from the spec's own examples. Output is a JSONL test plan.
2 · Probe (distributed, two-user)
For each target, the object id is varied (adjacent, zero, canary) and three requests are sent:
| Request | Auth |
|---|---|
| Unauthenticated | none |
| As User A | Bearer <USER_A_TOKEN> |
| As User B | Bearer <USER_B_TOKEN> |
Distributed across the fleet via batch-output-v2.
3 · IDOR / BOLA signals
| Finding | Meaning |
|---|---|
| UNAUTH_ACCESS | 200 with no auth — no auth check |
| CROSS_USER_IDOR | User A & B both 200, different bodies — each reads the other's object |
| SHARED_OBJECT | Both 200, same body — no per-user isolation |
> Tokens are optional — leave the placeholders for unauthenticated-only. GET-only, safe and idempotent. Authorized targets only.
Overview
Broken object level authorization sits at the top of the OWASP API Security Top
10, and it is the class a scanner is worst at, because every request is
individually valid. The endpoint exists, the id is well-formed, the response is a
200. There is no signature to match, only an object that belonged to somebody
else.
So the useful question is not what the bug is, it is which operations you managed
to test for it. This drives the test from the API's own specification. You get
every GET operation that takes an object reference, probed with varied ids and
with authorization removed, and a list of the objects that came back when they
should not have. Coverage follows the API as it grows rather than trailing it,
which makes it worth running on every release as
continuous security testing.
That enumeration step is the whole difference. A crawl or a proxy recording only
reaches the routes something already called, so the endpoints added last sprint,
the admin ones nobody links to, and the ones only a mobile client touches never
get tested. The spec lists them the day they ship, names the parameter that
carries the object reference, and supplies the example values needed to build a
request the server will accept. Testing what the API says it offers beats testing
what you happened to observe.
It is GET-only by design. Nothing here writes, so a run cannot create, modify or
delete an object in the API under test.
Pipeline
Read the OpenAPI or Swagger specification URL.
The spec is fetched and parsed.
Every GET operation taking an object reference is enumerated, with its
parameters and expected responses.
Each operation is requested with adjacent and canary ids and with
authorization stripped, distributed across the fleet.
Objects that returned without authorization are written to a PDF report.
Inputs
OpenAPI spec URL. The Swagger 2.0 or OpenAPI 3.x document describing the
API under test. The run drives entirely off it, so there is no endpoint list to
maintain by hand. The default is the public Swagger Petstore, an API published
for testing, so a first run has a target you are allowed to hit.
Bearer token. Optional, and empty by default. Left empty, every probe is
unauthenticated. Supply one and the same operations are also requested as a
logged-in caller, which is how cross-object access gets tested.
Object id variations. The altered ids each operation is probed with,
adjacent and canary values by default.
Outputs
Operation inventory. Every GET operation in the spec that takes an object
reference, with the parameter carrying it, so you can see the tested surface
before you read a single finding.
Probe results. Each request as sent, paired with the object that came
back. An unauthenticated 2xx carrying object data is the finding, and the
recorded request replays by hand.
PDF report. Spec coverage, the object-id endpoints tested, and the IDOR
and BOLA findings ranked by severity.
Sample output
From a completed run against petstore.swagger.io, the API published for testing.
Three of the eight GET operations take an object reference, and each is
enumerated with the parameter that carries it:
op_id
method
path
param
param_in
param_type
filled_path
getPetById
GET
/pet/{petId}
petId
path
integer
/pet/1
getOrderById
GET
/store/order/{orderId}
orderId
path
integer
/store/order/1
getUserByName
GET
/user/{username}
username
path
string
/user/user1
getUserByName
GET
/user/{username}
username
path
string
/user/user2
Parsing the spec produces the coverage figure before a single request is sent:
Four ids returned four objects to an unauthenticated caller, and the differing
body hashes are what separate that from one generic response repeated back. The
other two operations answered 404 at every id they were probed with, so the run
records them as tested rather than as findings. That pairing of request and
response is what makes the report actionable, because the argument is never about
whether the endpoint exists, only about who should be able to read it.
FAQ
Why drive the test from a spec instead of crawling the API?
A crawl reaches only the routes something already called. The spec lists every
operation on the day it ships, names the parameter that carries the object
reference, and carries example values for the other required parameters, so the
baseline request is one the server accepts rather than one you guessed at.
How is this different from pointing a vulnerability scanner at the same API?
A scanner matches signatures, and authorization bugs have none. This does not
look for a pattern in a response, it compares who asked with what came back
across the operations the spec says take an object id.
Does the scan write to the API?
No. Only GET operations are enumerated and probed, so a run cannot create,
modify or delete an object in the API under test.
Can it test as a logged-in user, not just anonymously?
Yes, by supplying a Bearer token. Note what that gives you: the comparison is
between one authenticated caller and no caller at all, plus ids that are not that
caller's. It is not a two-account diff, so there is no second set of credentials
to configure.
What does a finding look like?
The request as sent, paired with the object that came back. Either an
unauthenticated 2xx returning object data, or an id belonging to somebody else
returning data. Because the full request is recorded, whoever fixes it can replay
it rather than argue about reproduction steps.
Related workflows
Find SSRF in HTTP Parameters. Reach for this instead when the
parameter you distrust takes a URL rather than an object id, and the bug is
where the server fetches rather than who owns the record.
Directory & Content Discovery.
Reach for this instead when there is no spec to read and you need to find the
endpoints before you can test authorization on them.
Find Reflected XSS on a Domain. Reach for this instead when the API
renders into a browser and the question is what the response does client-side,
not who it belongs to.