AI pentest example: a worked assessment of a demo e-commerce API

AI pentest example on a fictional API: the written brief, tool rules, coordinator and workers, one validated finding, one rejected false positive and a retest.

Short answer

One AI-assisted assessment of a fictional e-commerce API, from the written brief to the retest. It includes the written brief, the allow, ask and deny tool rules, the coordinator and workers, one finding proven by read-back from a second account, one candidate rejected after a control, the human review, and the report excerpt with its retest.

Demonstration

This is a demonstration based on a fictional application. No real customer system was tested. Pinecart, its host api.pinecart.example, its accounts and its data are invented. All transcripts, timestamps and identifiers are illustrative. They show the shape of the work, not the output of a real engagement.

An AI-assisted pentest is an authorized security assessment where an agent does much of the testing work under rules the system owner approves. This example follows the process in how AI pentesting works on an invented target, from the first brief to the final report.

Excession Labs builds Incursion, a terminal agent for authorized security testing. In the delivery model described here, Incursion does the testing work under the tool rules. Excession Labs agrees the scope with you in writing, checks the evidence for each finding, and writes the report.

The brief: what Pinecart asked for#

Pinecart is a small e-commerce SaaS. Merchants run online shops on it. Shoppers place orders through a public API. The Pinecart team wanted to know if one shopper could read another shopper's data before a large customer's security review.

The system owner approved this written scope.

Item Agreed value
Target https://api.pinecart.example (production-like test environment)
Out of scope The marketing site, the payment processor, email delivery systems, and any other host
Test type Black-box web and API assessment, then grey-box with two test accounts
Accounts Two shopper accounts that Pinecart created for the test: shopper-a@example.com and shopper-b@example.com
Test data Orders created during the test carry the note EXCESSION TEST DATA
Authorization Signed by the Pinecart owner before any request was sent
Contacts A Pinecart engineer reachable during the test, and a written stop contact
Stop conditions Any sign of customer impact, any real person's data, or an unexpected error rate

The brief also lists Pinecart's worries. The coordinator turned them into tasks:

  1. Can a signed-in shopper read or change another shopper's orders or profile?
  2. Can an anonymous caller reach any signed-in endpoint?
  3. Are session tokens and password reset safe against simple abuse?
  4. Do the search and filter parameters accept input that changes the query?
  5. Does the API leak internal details in errors?

The safety page lists the general rules.

Tool rules: what ran on its own, what waited, what was blocked#

Each tool has one of three rules. A tool with the allow rule runs on its own. A tool with the ask rule waits for a human to approve each use. A tool with the deny rule is blocked. The operator sets the rules before the run.

For Pinecart, the rules were these.

Tool or action Rule Reason
Read-only HTTP requests (GET, HEAD, OPTIONS) to the target host Allow Low risk. Needed for mapping and differential tests.
Reading the API documentation and client-side JavaScript Allow Public material. No effect on the target.
Creating an order or editing a profile with a test account Ask Changes state. The human checks the record is marked as test data.
Deleting any record Ask Not needed except to clean up test-owned data.
Requests that send email or SMS Ask Real messages cost money and reach real inboxes.
Password guessing and credential stuffing Deny Not in scope. Account lockout could affect real users.
High-rate requests and load generation Deny Could harm availability.
Any host other than api.pinecart.example Deny Out of scope.
Writing files outside the engagement folder Deny Keeps evidence and the host clean.

A bounded test is possible under the ask rule. For example, a rate-limit check can send a small, fixed number of requests after a human approves the number. The report states that number.

The run: coordinator, workers and status#

The coordinator is the planner. It reads the brief, splits the work into tasks and hands each task to a worker. Workers run in parallel. Each worker keeps its own notes and findings.

For Pinecart the coordinator made four tasks.

Worker Assignment Status Latest action
W1 Mapper Map endpoints from the API docs and the JavaScript bundle. Record which need a token. Done Listed 41 endpoints. Flagged 6 that take an object ID in the path.
W2 Access control Compare anonymous, shopper A and shopper B on every endpoint with an object ID. Done Proposed 2 candidates: C-01 and C-02.
W3 Sessions Check token format, expiry, logout and password reset. Running Waiting for approval to request one reset email to a test inbox.
W4 Input handling Test search and filter parameters for injection and unsafe errors. Running Reviewing 9 error responses for stack traces.

The numbers in this table are invented. A task is done only when each finding links to its evidence. W2 reached that point only after the validation steps below.

This fictional log excerpt shows part of the session.

09:02:11 coordinator brief loaded. scope: api.pinecart.example. 5 objectives, 4 tasks.
09:02:12 coordinator rules: GET/HEAD/OPTIONS allow | POST/PUT/PATCH/DELETE ask | password guessing deny
09:02:40 W1 GET /v1/openapi.json 200 (41 paths)
09:03:05 W1 bundle app.4f1c.js: found /v1/orders/{id}, /v1/users/{id}/addresses
09:03:31 W1 task done: 6 endpoints take an object ID
09:04:02 W2 POST /v1/orders as shopper-a ASK create test order, note "EXCESSION TEST DATA"
09:04:20 operator approved (record is marked as test data)
09:04:21 W2 POST /v1/orders 201 id=48213 owner=shopper-a
09:04:48 W2 GET /v1/orders/48213 as shopper-b 200 owner=shopper-a
09:04:49 W2 candidate C-01: cross-account order read. needs controls
09:05:30 W2 GET /v1/users/me/orders?customer_id=A as shopper-b 200
09:05:31 W2 candidate C-02: filter by other customer. needs controls
09:06:10 W3 POST /v1/password-reset ASK one request, test inbox only
09:06:12 W3 waiting for operator...
09:06:44 W4 GET /v1/search?q=%27 200 (empty list, no error text)
09:07:02 coordinator W2 finding list is not closed: C-01, C-02 have no evidence links yet
09:07:03 coordinator W2 task stays open until controls and read-back are recorded

Order creation waited for a human because it changes state. The coordinator kept W2 open because a single 200 response is only a lead.

How does a candidate become a finding?#

A candidate becomes a finding when we can show a real effect and the controls behave as expected. The method follows the evidence standard used across our work. A bare "success" response is not proof. Proof for a read is a record that only the other account should see, returned to the wrong account. Proof for a write is the changed record, read back through another session.

Each exploit request runs next to controls. Controls are requests that should fail or return different data:

  • A nonexistent identifier.
  • A wrong record type.
  • A request with no token, or a token for another account.
  • A comparison of anonymous and signed-in callers.

If the controls do not behave as expected, the candidate is not a finding. The two W2 candidates below show both outcomes. For the method in more depth, see false positives and validation and how agents test authorization.

Candidate C-01: validated end to end#

C-01 claimed that a shopper can read another shopper's order by changing the number in the URL. Pinecart order IDs are sequential integers, so the next ID is easy to guess. This is the pattern OWASP lists as API1:2023 Broken Object Level Authorization.

The worker took these steps. Each step is saved with a timestamp.

  1. Shopper A created order 48213 with the note EXCESSION TEST DATA. The response named shopper A as owner.
  2. Shopper B requested GET /v1/orders/48213 with B's own token.
  3. The response was 200 and contained A's test email and shipping address.
  4. The worker ran four controls.

The exploit request and response:

GET /v1/orders/48213 HTTP/1.1
Host: api.pinecart.example
Authorization: Bearer <token-of-shopper-b>

HTTP/1.1 200 OK
Content-Type: application/json

{"id":48213,"owner":"shopper-a@example.com","note":"EXCESSION TEST DATA",
 "ship_to":"1 Test Lane, Exampleville","total_cents":4200}

The response names shopper A, so shopper B received A's data. The four controls then checked the result.

Control Request Expected Observed Meaning
Nonexistent ID B reads /v1/orders/99999999 404 404 The API does not return 200 for every number.
No token Anonymous reads /v1/orders/48213 401 401 Authentication is enforced. The gap is authorization.
Own record B reads B's own order 48220 200 200, owner is B Normal access works, so the difference is the owner field.
Neighbor ID B reads /v1/orders/48214 (made by A) 200 if the flaw is general 200, owner is A The flaw is not limited to one record.

The last step is the read-back from the second account. The worker asked shopper A to read order 48213 again with A's token. The note and total matched what shopper B saw. So B's response came from A's real record, not from a cached or generic reply.

The evidence index for C-01 lists five saved request and response pairs, each with a timestamp. The main pairs carry a SHA-256 hash in the manifest.

The coordinator then closed the C-01 part of W2, because the finding links to all of its evidence.

Candidate C-02: rejected as a false positive#

C-02 looked like a second access control flaw. Shopper B called the order list with another customer's ID in a query parameter:

GET /v1/users/me/orders?customer_id=cus_A91 HTTP/1.1
Authorization: Bearer <token-of-shopper-b>

HTTP/1.1 200 OK

{"orders":[{"id":48220,"owner":"shopper-b@example.com"}, ...]}

The status was 200. A scanner or a weak check would stop here and report a leak. The worker ran controls instead.

Control Observed Meaning
B calls the list with no customer_id Same 3 orders, all owned by B The parameter changed nothing.
B calls the list with a nonexistent customer_id Same 3 orders, all owned by B The server ignores the parameter.
B calls the list with customer_id set to B's own ID Same 3 orders Confirms the result depends on the token only.
Owner field of every returned order shopper-b@example.com No record of shopper A appeared.

The 200 response returned the caller's own data. The server takes the account from the token and discards the extra parameter. No other shopper's record was exposed, so there is no access control effect to report.

We did not put C-02 in the report as a finding. A finding needs evidence of a real effect, and this one has none. Reporting it would tell Pinecart's engineers to fix something that already works. It would also bury PIN-01 among noise.

The report keeps the rejection in the "Controls that held" section as a short entry: the list endpoint ignores a customer_id parameter and scopes results by token. If a later release changes that behavior, a retest will show it. The entry also protects the fix: a developer who patches C-01 should not break this working protection.

What does the human review check?#

Excession Labs checks the evidence for each finding and writes the report. For this assessment, that review covered these points before anything reached Pinecart:

  • Each finding has linked request and response evidence with timestamps.
  • The effect is shown by a read-back or a second-account view, and a status code alone is not accepted.
  • The controls ran and behaved as expected. If they did not, the finding was dropped or relabeled.
  • Every state-changing proof used test-owned accounts and records marked as test data.
  • The severity matches the written definition for that level.
  • Any inferred path is labeled as inferred, and open questions sit under "Limitations and open questions".
  • The tool rules were followed, and the report states any bounded test and its cost.

The agent proposes findings and runs tests. The review decides what the buyer reads. Human in the loop pentesting covers the review in more depth.

The allow, ask and deny rules give people control during the run, and the review follows it. The report lists what was not tested, because the agent does not find all vulnerabilities.

Report excerpt: the validated finding#

The final report writes C-01 as a finding section with description, impact, verified results, reproduction and remediation. The full report holds the executive summary, controls that held, limitations and evidence index in other sections.

Demonstration

This is a demonstration based on a fictional application. No real customer system was tested. The excerpt is invented.

PIN-01 / Authorization / Evidence label: ObservedHigh

Any signed-in shopper can read any other shopper's order by ID

Endpoint
GET /v1/orders/{id}
Class
Broken object level authorization (OWASP API1:2023)
Status
Open (retest pending)
Evidence
E-014 to E-018 in the evidence index

Description. The order endpoint checks that the caller has a valid token. It does not check that the order belongs to the caller. Order IDs are sequential integers, so a caller can read orders by counting upward.

Impact. A signed-in shopper can read the name, email and shipping address of other shoppers. An attacker needs only a free account and can read every order by enumeration. Customers of Pinecart's merchants would be exposed, and a customer security review would raise it.

Verified results. Shopper B read order 48213 and order 48214, both owned by shopper A. A nonexistent ID returned 404. An anonymous call returned 401. B's own order returned 200 with B as owner. Shopper A read the same record back with matching values.

Reproduction. Create an order with test account A and note its ID. Request GET /v1/orders/{id} with the token of test account B. Compare the owner field. Use only test-owned accounts and records, and only with authorization.

Remediation. Check ownership on the server for every order read. Compare the order owner with the account in the token and return 404 on mismatch. Do the same for update and cancel routes. Consider unpredictable order identifiers as defense in depth. They do not replace the ownership check.

Found: demo run 1Retest: pending

The severity is High because the exposed data is personal, the attack needs only a free account, and the effect is repeatable at scale. The report gives each level a written definition. It does not add CVSS vectors by default. If your program needs CVSS, say so during scoping.

How a retest marks it fixed#

Pinecart fixes the check and ships. A retest reruns the original request set against the fixed system. It uses the same accounts, the same IDs and the same controls.

A finding is marked fixed only when all these are true:

  1. Shopper B's request for order 48213 now fails. The response is 404 or 403, with no order data in the body.
  2. Shopper A can still read order 48213. This shows the fix did not break normal access.
  3. The controls behave as before: the nonexistent ID gives 404, the anonymous call gives 401.
  4. The neighboring routes (update, cancel) fail for B in the same way.
  5. The "Controls that held" entry for C-02 still holds.

A retest report keeps the old and new evidence side by side.

Outcome What it means in the report
Fixed The exploit request fails, normal access works and the controls hold.
Not fixed The exploit request still returns the other account's data.
Partly fixed The main route is fixed. A related route still leaks.
Not retested The item was out of the retest scope or could not be reached.

For Pinecart, the retest of PIN-01 would show this change:

GET /v1/orders/48213 HTTP/1.1
Authorization: Bearer <token-of-shopper-b>

HTTP/1.1 404 Not Found

{"error":"order_not_found"}

The same request with A's token still returns 200. PIN-01 changes from Open to Fixed, with the date of the retest and the new evidence IDs. Excession Labs can retest fixed findings. We do not publish terms for retests on this page. Ask during scoping.

What the example leaves out#

A real run has many more leads than the two shown, and most close with a control and a short note. Business logic findings need a person to reason about how the product should behave, and none appear here (where AI pentesting falls short). The counts are invented, no result comes from a real system, and we make no claim about past engagements. Real counts, tools and rules come from the written scope.

Run this on your own application#

The path to a real assessment starts with a short call and a written scope. We test only with written authorization from the system owner. A small written scope can start as a free pentest pilot. Places are limited and reviewed by hand.

You approve rules like these before testing starts: see how it works and the safety rules. The sample report from our pentest service shows a full web and API report. Incursion describes the agent itself.

Questions

Is this a real customer assessment?

No. Pinecart is a fictional app and api.pinecart.example does not exist. We built the scenario to show the method. The format, rules and evidence standard match how we describe our work on the rest of this site.

Why does the report include a rejected candidate?

A rejected candidate shows how validation works. The 200 response looked like a leak until a control showed it returned the caller's own data. Reporting it would have wasted your engineers' time. The report records only what we can prove.

Who decides that a finding goes into the report?

Excession Labs checks the evidence for each finding and writes the report. The agent proposes findings. A finding with no linked evidence does not pass that check.

What happens at retest?

The original request set and its controls run again against the fixed system. The finding is marked fixed only when the exploit request fails and the control behavior still shows that normal access works.

Sources

  1. OWASP API Security Top 10 2023: API1 Broken Object Level Authorization (checked 9 November 2026)
  2. Excession Labs: Incursion (checked 9 November 2026)

Run it on your application

Start with a scoped pilot. Incursion tests, a person checks each finding, and you get a report with evidence.