Business

A Permission Diff for Tool Schemas: Claim vs. Behaviour

Antonio Evans7 min read

A split-screen technical scene: on the left, a vendor permission table rendered as a grid of rows and checkmarks; on the right, the same rows replayed by a small script.
Key points

Key points

  1. 1Build a harness that calls every documented permission row with a dedicated synthetic identity, then diff claimed against observed access.
  2. 2Treat a row that allows more than the vendor documents as a blocker until the vendor explains it or you add a compensating control.
  3. 3Re-run the diff on every vendor schema change and before each agent release that adds or edits a tool.
  4. 4Log the token, scopes, decision, and policy version for each probe so the diff is evidence, not an opinion.

Why a permission table is a claim, not a control

A vendor permission table is a document, and documents drift. OAuth scopes are strings the resource server defines and the client requests, and the authorization server can grant a subset of what was asked for, so the token you receive is not guaranteed to match the table you read Curity scope best practices. The standard that lets a protected resource query the authorization server about a token's active state and granted rights exists precisely because the token alone does not always tell you what it carries RFC 7662.

Agents make this worse because their behavior shifts with each prompt. A service account runs the same query every night, while an agent may read one customer's record, pull another's contract, and call a refund tool in one conversation, all under whatever grant it holds PlainID. A table that says "read access to invoices" tells you nothing about whether the agent can reach a different tenant's invoices, or chain three allowed calls into a denied outcome. Scopes describe what area of functionality a client can touch; they do not describe who can run which tool under what conditions P0 Security.

That gap is measurable. The Cloud Security Alliance survey cited by Delight.ai found 53% of organizations report their AI agents have exceeded intended permissions Delight.ai. Negative testing, feeding a system invalid or out-of-scope input to confirm it fails predictably, is the standard discipline for finding that class of gap before a customer does Virtuoso QA. A permission diff is negative testing applied to a vendor's access claims.

The decision rule is simple. A row you have tested is a control. A row you have only read is a hope.

Anatomy of the diff

The diff has four fields per row, and you need all four to make a defensible call.

FieldWho supplies itExample
Claimed accessVendor documentationinvoices:read for tenant A
ProbeYour harnessGET one invoice from tenant B
Observed resultVendor API200 OK with tenant B body
VerdictYour diff logicRed: wider than claimed

Three verdicts cover almost every case:

  • Green: claimed allow and observed allow, or claimed deny and observed deny.
  • Red: observed allow where the vendor claims deny. This is the only exit-blocking verdict.
  • Amber: observed deny where the vendor claims allow. Not a security failure, but your integration will break if you depend on that row.

Amber is worth logging separately because it also catches stale documentation: a vendor that scopes down without telling you has silently removed a row your agent relied on.

Run only one probe per row per pass. If you batch several calls into one identity, a single allow poisons the whole row and you cannot tell which operation leaked.

Building the harness

Use a dedicated synthetic identity per role, never a shared service account. A role is a bundle of permissions and a scope is a flag on a token, so probing one role through one identity keeps the diff attributable P0 Security. Each identity should hold exactly the scopes the vendor's table grants that role, nothing more Auth0.

A minimal harness run looks like this:

probes = load_rows("vendor_permissions.yaml")
for row in probes:
    token = mint_token(row.identity, scopes=row.claimed_scopes)
    if row.method != "read":
        continue  # never probe writes against production
    resp = call(row.method, row.url, token=token)
    observed = classify(resp)  # "allow" or "deny"
    write_result(row, observed, introspect(token))

Four rules keep the harness safe to run:

  1. Probe reads first. Writes, sends, payments, and deletes get staged with a canary account and a human watching, or skipped and marked untested.
  2. Call the token introspection endpoint after each probe and store the response, so you have a record of what rights the token actually carried Scalekit.
  3. Classify by status code and body shape, not by your agent's interpretation. A 200 with an empty list is a different verdict from a 200 with another tenant's data.
  4. Run in a pre-production environment that enforces the same permission boundaries as production. A sandbox that mocks permissions answers the wrong question DevRev.

Synthetic identities fit a formal non-human identity inventory: credential type, owner, purpose, connected systems, permission scope, and expected behavior pattern Sweet Security. Your harness rows are the expected behavior set. When the observed pattern leaves it, you have both a security signal and an audit artifact.

Writing the diff

Store results as one record per row per run, then compare runs. The output you want is a short, sortable table a reviewer can act on without opening a notebook.

RowClaimedObservedVerdictAction
invoices:read / tenant AallowallowGreennone
invoices:read / tenant BdenyallowRedblock release
customers:exportallowdenyAmberfix integration or docs
payments:refunddenynot testedSkiprun staged

Two mechanics save time. First, diff against the vendor's machine-readable schema when they publish one, not against prose. A YAML or JSON scope list diffs cleanly and tolerates reformatting. Second, version the claim file yourself, with a commit hash per run, so a red row can be traced to the documentation version it contradicted.

The audit angle matters here. An auditor wants to know which identity acted, on whose behalf, against what resource, under which policy, and with what result PlainID. One record per row, with the token introspection response attached, satisfies that for every documented permission. The EU AI Act's logging requirements for high-risk systems, Article 12, point in the same direction: automatic recording over the system's lifetime so operation can be traced Collibra.

If your agent runtime is Quanta Inference, one API key covers the model calls, but the permission diff lives in your tool layer and runs against the vendor regardless of model. Keep the harness outside the agent.

When the vendor is wider than claimed

A red row is the moment the vendor's documentation stops being evidence. Handle it in four steps.

  1. Reproduce with a second identity in the same role, same scopes. If both hit allow, it is not a fluke of one credential.
  2. Confirm the token carried only the claimed scopes, via introspection. If your token already had a wider scope, the finding is in your own grant, not the vendor's authz.
  3. Capture the request, response headers, and a redacted body. Redact tenant data, keep the status code and the field names.
  4. Open a ticket with the vendor that asks one question: is this row intended, and will it be corrected or documented?

While the ticket is open, add a compensating control so the release is not blocked by an open question with no end date. The cheap controls, in order of preference:

  • Deny at the agent layer. Put a policy check in front of the tool call so the agent cannot reach the row even if the vendor would allow it P0 Security.
  • Scope the token down further than the vendor's minimum. If the row is only needed for tenant A, put the tenant ID in the resource indicator so the token is bound to a named resource NHI Mgmt Group.
  • Turn the read into a watched operation, with an alert on access to the wider surface.

A vendor that grants more access than documented through delegating privilege also risks audit failure, because permissions assigned at deployment record nothing about the decision to use them PlainID. Your compensating control is what turns an unexplained widening into a recorded decision. Never accept a verbal "we'll fix it" as the control. Either the vendor explains the row in writing and you re-run, or the control stays.

If several vendors are red on the same class of row, that is a procurement signal, not a testing problem. Third party exposure clusters into identity paths, data paths, software supply chain paths, and operational paths, and the identity path is where your diff sits CyberProof. Vendor tiering should raise the row: a vendor that holds payment or customer export rows with a red probe merits a deeper review than one holding a read-only calendar row.

Re-run cadence and ownership

Re-run on triggers, with a periodic floor:

TriggerRun scopeOwner
Vendor publishes a schema changefull tableintegration owner
Agent release adds or edits a toolaffected rowsagent owner
Credential rotation for a synthetic identityaffected rowsplatform
Periodic floorfull tablesecurity

The floor exists because documentation can change silently and because a vendor's authorization logic can change without a schema change. Pick a cadence your team can actually hold, likely monthly for payment and export rows and quarterly for the rest, and record the date of the last clean run per vendor. When someone asks whether a vendor row is safe, the answer is the date and the verdict, not a link to a table.

Ownership matters as much as cadence. Every synthetic identity needs an assigned human owner accountable for its scope, monitoring, and retirement Sweet Security. Without an owner, a red row gets filed under the vendor and never closed. Without retirement, a synthetic identity outlives its harness and becomes standing privilege, the exact failure that agent permission frameworks are built to expose PlainID.

One last habit: keep the last three runs per vendor. A row that was green, then red, then green tells a different story than a row that was always red, and only the history shows you which.

If you are standing up the program and want the identity, logging, and policy layers reviewed against your actual vendors rather than a template, a free 30 minute discovery call with Quanta Consulting can map the harness to your tools: https://quantaintelligence.ai/calendar.

FAQ

FAQ

What is a permission diff?

A permission diff is a comparison between the access a vendor documents for a row in its permission table and the access your test actually observes when a dedicated identity calls that row. It produces a verdict per row: matching, narrower than claimed, or wider than claimed.

Why use a synthetic identity instead of a real user account?

A synthetic identity holds exactly the scopes for one role, so an allow or deny is attributable to that role and nothing else. A shared service account shows one identity making many queries and loses the link between a request and the permission that allowed it PlainID.

What should I do when a vendor allows more than its documentation?

Reproduce with a second identity in the same role, confirm the token carried only the claimed scopes, capture redacted evidence, and ask the vendor a single written question. Until it is answered, add a compensating control such as a policy check in front of the tool call or a resource-bound token.

Can I skip write and payment rows?

Yes, but mark them untested rather than green. Probing writes against production can change or destroy real data, so stage them with a canary account and a human watching, or leave them out of the automated run and track them as a known gap Kingy.ai.

How often should the diff run?

Run it on every vendor schema change and before every agent release that adds or edits a tool, plus a periodic floor for every vendor. The floor catches silent authorization changes, and the date of the last clean run per vendor is the answer to give when someone asks whether a row is safe.

Sources

Sources

  1. OAuth Scopes Best Practices
  2. RFC 7662: OAuth 2.0 Token Introspection
  3. AI Agent Permission Control Framework Built for Audits
  4. OAuth scopes don't equal secure MCP authorization
  5. Introducing role-based access control (RBAC) for AI agents
  6. Negative Testing Explained With Real-World Examples
  7. OAuth 2.0 Access Tokens and The Principle of Least ...
  8. OAuth 2.0 token introspection: RFC 7662
  9. Why a sandbox isn't a staging environment for AI agents
  10. Understanding Non-Human Identity for AI Agents
  11. AI audit trails: What to log for models and agents, and how ...
  12. What Is Least Privilege OAuth Scope? Definition & Examples
  13. How to Manage and Mitigate Third Party Risk
  14. AI Agent Security Guide: MCP, Sandboxes and Tool ...
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles