We attack the systems you ship, the way an adversary would.

AI red teaming of your models, agents, and pipelines, with penetration testing of the applications, APIs, and infrastructure around them. One engagement covers the AI and the rest of the stack.

Apps to AIapplications, APIs, infrastructure and AI in one signed scope

7 named stop conditionshalt testing without confirmation

A human gateon every high-impact step, before it runs

A second operatorreplays every path before it is written up

How one engagement runs, and where it can fork.

Most first engagements are a scoped adversarial test; a secure design review is the lighter start. Below are the four steps of that work and the two points where an engagement changes course. Pick an outcome at each fork to see how the engagement ends.

Engagement path
4 steps2 forks3 endings
01Scope

Scope, agreed in writing before anything is touched.

  • AgreedThe surface, the rules of engagement, test windows, and data handling. Signed before the first probe.
  • StagingStaging before production. Seven named stop conditions halt testing without waiting for confirmation. Short-lived, least-privilege credentials you can revoke at any moment.
  • CoversYour applications, APIs, and infrastructure, and the models, agents, and pipelines that run on them, under one signed scope.
02Attack

Attack the agreed targets, with a human gate on every high-impact step.

  • OperatorsThe operators who test also write the report, so whoever found an issue is the one who explains it.
  • GateAn operator approves each high-impact step before it runs and records the access the path already holds.
  • ProofA second operator independently replays the path before it is rated as a finding.
F1Fork

Fork 1: what the second operator reproduces.

Sets the ending
Fork one: what the second replay shows
03Harden

Findings become changes your engineers can ship.

  • PriorityOrdered by risk, so one path that reached the finance systems is fixed before a batch of Mediums.
  • Made byAs agreed in scope. Your engineers receive remediation guidance, or our team implements the changes when hands-on hardening is included. Retest is agreed in writing.
  • Not assumedA control that closes a finding can break an unrelated flow. When it does, we report it and agree the correction and retest with your engineers within the signed scope.
F2Fork

Fork 2: what the first fix holds, on day three.

Sets the ending
Fork two: what the first fix does on day three
04Retest

Retest against the original attack.

  • MethodWe re-run the affected attacks against the fixes and record what changed. A finding is closed when a re-run proves it closed.
  • RetainerOptional. Continuous Assurance re-checks closed paths every month, tracked in Serpio Atlas.
RESETDefault

The default is the plain case: the finding reproduces, the first fix holds, and a re-run proves the path closed.

ENDEnding
Closed by retestthe original path denied on replay
Not a finding

The finding closes at retest as not a finding. It never enters the severity count, and no engineer spends time chasing a path that only exists on our instance. The report still records the surface on the map: content the agent trusts, a tool that can send data out, and an identity with a service's permissions. The class is marked tested, so the question is already answered next time.

New finding

The deny held, but the nightly billing sync to the same host began returning 403. We keep the deny, agree a narrower allowlist that names the documented sync path, and verify that flow again under the agreed remediation and retest scope.

Two owners share the decision: the engineer who owns the fix and the engineer who owns the flow it broke. Both get it in writing.

Reopened

The variant did not need the model to be persuaded a second time. The allowlist matched on host, and a second tenant-controlled host could carry the same upload, so the export left anyway. The original finding is still open: the first fix blocked one host and left the rule that allowed it.

The corrected fix moves the decision to the tool call: calls to an unlisted host are denied by default, and the write step waits for human approval whatever the destination. We then replay the original path and the variant, and both are denied.

Closed

You ship the fix and we replay the attack. The original path is denied at the tool call, the legitimate flow still completes, and both runs are logged append-only in the evidence bundle.

The narrowed allowlist and the default deny arrive as a change you can review before you merge it. When the model, the prompt, or the tool list changes next quarter, the path is already in Atlas and gets re-checked.

Next, for a system already in production: a secure design review is the lighter start, and binary reverse engineering joins the same engagement when the risk sits past source.

Next, the delivered map records both decisions, the fix and the flow it touched, so later work starts from them.

Next, the same check applies to every permission your agents hold. Continuous Assurance re-checks closed paths every month between engagements.

Next, the same loop run against the next system in your estate, or Continuous Assurance to re-check this one as it changes.

Fork 2 applies only when fork 1 reproduces, so the path has four outcomes and three kinds of ending: closed (with or without a new finding), reopened, and not a finding. A fix that breaks a working flow is reported as a second finding. The full six-phase model, with the rules of engagement and the stop conditions, is on the method page.

Illustrative example, not client data. The selected ending is saved in the page URL, so you can link to it.

A hop-by-hop trace of a single attack.

Every scoped adversarial test produces a trace like this one: the chain of calls that carried an attack, the permissions it held at each hop, and the gate that stopped it. Read the permissions column first. A hop matters when the access listed there is real, and a gate counts as a control only when the next step depends on it.

Below is the reconstruction behind one report: the content the agent trusted, a tool that could send data out, an MCP server that failed to narrow the caller's access, the identity the call assumed, and a write that stopped at a person. The chain produced three findings, and one gate held.

Attack chain · one request, eight hops
8 hops3 findings1 gate holds2 gates ran and passed

Up and down arrows move between hops, Home and End jump to the first or last, Enter opens the reading for the hop you are on, Escape closes it. Every row is a button, so Tab reaches the chain from the keyboard too.

No finding

What the chain shows between the findings.

Most hops hold no finding, and the chain keeps them anyway. They are the reason the next hop was possible: a fetch the session was always allowed to make, a document parsed as content the model had no reason to doubt, an MCP connect that never declared the scope it would grant.

Permissions
Varies by hop; see the permissions column
Gate
Open, nothing checks these hops
Class
Threat register
LLM01High

Prompt injection past your filters

Instructions arrive inside content you already trust: a document, a page, a tool result. The filter passed it. The body was then appended to context with the same standing as the system prompt.

The finding is that anything downstream could act on the planted text. This hop is where trust was granted; the harm comes later. Closing it here counts as a fix only if you can also show that nothing downstream still acts on it.

Permissions
Context write, at trust
Gate
Checked: ran and passed
Holds
The planted instruction
Class
LLM01 · High
AG1High

Agents steered into unsafe tool use

The model was persuaded by the text it read, and it holds real credentials. The scope check returned allow, reading the session's scope where it should have read the MCP server's. The call is well formed, authorized and logged.

What changed was the argument the model chose on hop 4, under text it had been told to trust. Nothing in this hop errors, so your logs record a normal, authorized call.

Permissions
Tool invoke, scoped token
Gate
Checked: wrong scope
Holds
The invocation
Class
AG1 · High
AG2High

Authority boundaries that leak

One component acts with the privileges of another, and nothing in the flow says no. The MCP server records the caller as unmapped, then the next hop assumes a service identity the caller was never granted.

The finding is the scope: the path ends with more access than any participant in it was given.

Permissions
Assumed, broader than the caller
Gate
None: open
Holds
The service identity
Class
AG2 · High
Gate

The gate that held: a human on the export.

One thing kept this from becoming a data breach: the write was held for a person. The export was permitted by the assumed service identity, addressed to an external host, and carried an effective egress.write.

The gate denied it, and that deny is what kept this a finding.

Permissions
egress.write, effective
Gate
Gated: human
Result
Held, logged
Replay
Deterministic
Checked

What checked means.

A check can detect a step and still let it through. The ingress filter on hop 3 ran, returned a verdict, and passed the instruction; the scope check on hop 5 ran, returned allow, and read the wrong scope. Both are marked checked for that reason.

The gate on hop 8 is marked gated because the effect depended on it. Had the export gone through anyway, hop 8 would be marked checked.

Open
Nothing checks it
Checked
Detected, did not stop
Gated
Could not proceed without a person
Where
Method · severity and closure

Hop 8 of 8 · gated · permissions egress.write, effective

Illustrative example, not client data. Checked means the control ran and let the step through; gated means the effect could not happen without a person.

What each component was allowed to do.

Attack paths form where two components disagree about what a caller may do. Below is the system from the trace as we observed it: where content entered, how far it traveled, and where privilege changed hands.

Permissions map · as observed in testing
6 components5 privilege edges2 gates ran and passed1 gate held
Permissions across the reviewed systemSix components in the order the chain reached them. Untrusted content passes an ingress filter into the model; the model's tool call passes a scope check into an MCP server. Below the privilege boundary, the call assumes a service identity broader than the caller, and a human gate holds the external effect. Two gates ran and passed: the ingress filter and the scope check. One gate, the human on the external effect, denied it. The list below the drawing gives the permissions held at each component, and it is operable from the keyboard.PRIVILEGE BOUNDARYuntrusted content LLM01INGRESS FILTERmodel.turntool.call AG1SCOPE CHECKmcp serveridentity AG2HUMAN GATEapi.post HELD

All hops shown. Select a component to filter the chain above to the hops that touched it. The boundary sits where privilege changes hands: between the MCP server and the service identity the call assumed.

Illustrative example, not client data. Reconstructed from the chain above.

What we will not claim in your report.

A findings count is easy to inflate and expensive to act on. Six kinds of claim stay out of it; the reasons, and how each item is recorded instead, are below.

A jailbreak that reaches nothing.

Not a finding

The finding is what the model was allowed to do next. If the model can be talked into saying something it should not, but the path it is on reaches nothing beyond the chat window, there is nothing to exploit. It goes in the notes with the exact prompt and takes no line in the severity table.

A weakness we cannot demonstrate.

Not reproduced

We claim only what we can reproduce. If we believe a control is weak but three replays do not carry it, we write it up as an open question with the conditions we tried and no severity. Your team does not spend a sprint on a path we could not reproduce.

Scanner output, listed as findings.

No dumps

Scanner output is evidence we read, and we do not forward it. On its own it has no demonstrated impact, blast radius, or owner. If something in it matters, we exploit it end to end and show you the effect before it goes in the report.

The class we did not get to.

Outside scope

Coverage is counted against the classes the scope declared, and the classes it did not are named as untested in the same document. An engagement that touched four surfaces says four. What was not tested is reported as part of the result.

A finding closed because a fix was written.

Retest required

Closure is a re-run of the same attack against the fixed system, recorded in the evidence bundle, on the same terms as the original path. Until that run exists, the finding is open, including the ones fixed by your engineers without us in the room.

A critical that needs a condition we cannot test.

Stated confidence

Sometimes the path is real and the blast radius is unproven: we hold the access, but the effect sits behind a dependency we cannot safely touch. We report it with a stated confidence, name the untested step, and keep the severity to what the evidence supports.

How we write a finding

Each finding is written backwards from the gate that stopped it: what got through, what held, and what the path would have done where a control ran and failed to stop it.

Coverage is a number you can audit.

Every test declares the attack classes it exercises, and every finding records the class it demonstrates. The five states below are the ones Atlas uses, with the same names in our console and in the map we hand over.

Coverage states · six surfaces, one engagement
6 surfaces5 states4 hardened2 retested
Unmapped
Known to exist; nothing about it has been read yet.
Mapped
In Atlas and reachable from a known entry point; not yet attacked.
Tested
Attacked inside the agreed window.
Hardened
Controls changed after testing.
Retested
The original attack re-run after the change, and it no longer works.
SurfaceUnmappedMappedTestedHardenedRetested
Agentmodel.turn Tested Hardened
Tool callmcp.invoke Tested Hardened Retested fail before · pass after
MCP serverserver.alpha Tested Hardened Retested scope mapped
Identitysvc-agent-01 Tested Hardened
Data pathingest → egressUnmapped Mapped Tested
Rest of the stackapp · api · infra · binaryUnmapped Mapped Tested
Coverage state of the six surfaces in this engagement, in text form. The grid above is the same data.
SurfaceUnmappedMappedTestedHardenedRetested
Agentnoyesyesyesno
Tool callnoyesyesyesyes, failed before the fix, passes after
MCP servernoyesyesyesyes, scope now mapped at the boundary
Identitynoyesyesyesno
Data pathyes, the leg past egress was never mappedyes, ingest to egressyes, one path onlynono
Rest of the stack, application, API, infrastructure and native binariesyes, the remainderyesyes, the surfaces the chain touchednono

Two rows are left open: the data path was mapped to the egress boundary and no further, and the rest of the stack was tested only where the chain touched it. The report lists what we did not map, so the coverage number can be checked.

Illustrative example, not client data. The class catalog is the threat register; how coverage is counted is on the method page.

The classes behind the trace.

ClassSeverityTitleSurfaceIn the trace above
LLM01HighPrompt injection past your filtersuser · LLMHop 3
AG1HighAgents steered into unsafe tool usetoolHop 5
AG2HighAuthority boundaries that leakMCP · identityHop 7
SC1MediumModel and weight provenancedata pathIn scope, not this chain
RE1MediumRisk that sits past sourcenative binaryIn scope, not this chain

Classes in scope for this example engagement; three of the five are in the trace above. Severity is the example rating, scored on the rubric on the method page.

What the engagement delivers.

A findings report, an executive summary, an evidence bundle, and a machine-readable map of every surface in scope, from applications, APIs, and infrastructure to the agents, tools, and identities on top of them.

  1. 01Findings report

    Every finding with the path it took, its rating, the steps to reproduce it, and the change that closes it.

  2. 02Executive summary

    What was tested, what held, and what to fix first, written for the person who approves the fix.

  3. 03Evidence bundle

    The record behind each finding, pinned to the run that produced it, so your team can replay it without us.

  4. 04Atlas map

    The machine-readable map of every surface in scope. You keep it, and the next engagement starts from it.

Rated by
Impact and exploitability, as in any rubric, plus authority as a third axis: what the compromised path is allowed to do and how far it reaches. An injection that reaches a tool that moves money scores higher than one that reaches a harmless tool.
Fixed by
When hands-on hardening is in the agreed scope, the same team implements changes and verifies them by retest. Otherwise, your engineers receive the evidence and remediation guidance; retest arrangements are agreed in writing.
Retained by
Optional. Continuous Assurance re-checks the agreed paths monthly and records reopened findings. Changes between runs can remain unverified until the next check. Services explains how a scoped test is quoted; the framework mapping, from the EU AI Act and NIST AI RMF to IMDA, AI Verify and MAS guidance, is on the Assurance page.

Bring us a system to attack.

A call is the first step, and a written scope comes before anything is touched.