The Attribution Graph Is Only as Good as Its Evidence

Akash Mandal

Akash Mandal

The Attribution Graph Is Only as Good as Its Evidence

Every security product now draws an identity graph for AI agents. What decides whether you can actually contain an incident is the quality of the runtime evidence underneath.

https://cdn.sanity.io/images/7yls9lz6/production/e8d9f044dc68798003ef82f862be0b34bcd5c9c3-1672x941.png

TL;DR

  • Identity attribution for AI agents is a checkbox feature now. Most tools can draw the same graph: user, agent, tool, resource. Its trustworthiness comes from the evidence each edge was built from.
  • Evidence comes in grades. Low-grade sources (static config, naming, agent self-reporting, gateway logs, posture snapshots, control-plane audit logs) describe what was possible, declared, or self-reported. High-grade sources (eBPF direct observation, the Kubernetes service-account-to-IAM binding, CloudTrail corroboration, identity resolution) record what happened. Both can draw a graph; only one supports a confident containment action.
  • Our running case: an AI agent uses a shared GitHub Personal Access Token (PAT) through a Model Context Protocol (MCP) server. The action is authorized and succeeds, and the audit trail names the wrong actor, because accountability collapses at the credential boundary.
  • Containment depends on this. You can only revoke, scope down, or block what you can attribute with confidence. The fix for the PAT case is an agent-owned, scoped, short-lived credential, plus telemetry good enough to prove which credential did what.

An agent did something. Who gets the page?

Picture an ordinary setup. An internal AI agent helps engineers ship changes: it reads a Slack request, calls an LLM to plan the work, and acts on GitHub through an MCP server. To let it talk to GitHub, someone created a classic Personal Access Token with repo and read:org scope and dropped it in the agent’s config.

One morning the agent opens a pull request that deletes a production workflow file. It is caught in review, no harm done. But someone asks the obvious question: who did this?

GitHub’s audit log says the action was performed by alice, because Alice created the PAT months ago. Alice was asleep. The agent did it, on a Slack request, planned by an LLM call, through an MCP server several agents share. GitHub sees none of that; from its side, Alice pushed a branch and opened a PR. That is the entire evidence trail the platform gives you.

The action was authorized. It was logged. And the log names the wrong actor. Every downstream decision, who to notify, what to revoke, whether to block, now rests on evidence that is technically accurate and practically useless.

https://cdn.sanity.io/images/7yls9lz6/production/392797e266c2d76eb8db8b663b0d04823dedaaba-1672x941.png

The graph is easy. The evidence is the product.

At the marketing layer, almost everyone claims identity attribution for AI workloads, and the graphs look about the same: a user, an agent, a tool or database. The difference is underneath. Each edge was populated from some source of evidence, and those sources are not equal. An edge from a live parse of the network payload is a different claim than one inferred from a config file, a naming convention, or a control-plane log that arrived ten minutes late. They render identically while carrying very different confidence.

That is the whole argument: attribution is only as trustworthy as the telemetry it is built from. Containment (revoking a credential, scoping a permission, blocking an egress path) only works if you can attribute behavior precisely enough to touch the right thing without breaking the wrong thing. In the PAT story, weak evidence leaves two bad options: do nothing, or disable Alice. So the useful question about any attribution feature is what each edge is made of, and whether you would bet a containment action on it.

What weak evidence looks like

Most products that claim agent attribution build a similar-looking graph. The difference is what the edges are made of. These are the common low-grade sources, each producing a plausible graph but not strong enough, on its own, for a containment action.

  • Static config, naming, and posture inference. Reading manifests, Terraform, pod names, labels, or CSPM and permission scans to infer what an agent can reach. It describes what is declared or allowed at a point in time, never what ran, and breaks the moment two agents share a deployment or a config drifts.
  • Agent-framework self-reporting. The agent, its SDK, or the orchestration framework emits its own trace. Only as honest as the agent, only covers instrumented frameworks, and goes dark when the agent is misconfigured or compromised, which is when you need it most.
  • LLM or egress gateway logs. A proxy in front of model and tool traffic sees only what routes through it. An agent that calls an API directly is invisible, and the logs usually carry a source IP rather than a workload identity.
  • Control-plane audit logs alone. CloudTrail, GitHub audit, provider logs. They name the credential’s principal (the PAT owner), cover the control plane only, and arrive delayed. This is the exact failure in the PAT story: accurate, still pointing at the wrong actor.
  • Flow and DNS logs. Who connected to whom at the IP or hostname level. No payload, no method, no credential, no data class. You learn the agent reached github.com, not what it did there.

The tell is the same: they describe what was possible, declared, or self-reported, not what was directly observed. Any of them can draw a graph; none lets you revoke the right credential with confidence.

What high-grade evidence looks like

High-grade evidence has one property the sources above lack: it is a direct observation or an authoritative binding. Below are the sources that produce it, and the grade each carries. The question stays the same: would you bet a containment action on this edge?

https://cdn.sanity.io/images/7yls9lz6/production/22943356eb2e1bc17a727e5e09ba2e7525b52182-1672x941.png

eBPF runtime sensors: direct observation

The base layer is kernel-level capture of the traffic the workload sends and receives. Most of it is TLS, so the sensor hooks the TLS boundary with uprobes on the SSL library (OpenSSL SSL_read/SSL_write), plus GoTLS for static Go binaries and a JVM path for Java. That is where the payload exists as plaintext, and where the sensor sees what never reaches an application log. (The SSL/TLS Trap covers why naive capture goes blind on encrypted traffic.)

For AI workloads it parses the protocols that matter, on top of a general protocol layer with connection tracking and PII classification (Protocol Intelligence Layer). It captures LLM traffic by host (openai.com, anthropic.com, Bedrock) and MCP, extracting the method (TOOL CALL, RESOURCE READ, and the rest) and labeling each side as AI_AGENT, MCP_CLIENT, or MCP_SERVER (From Packets to Agent Chains covers chaining these into workflows). When the agent calls GitHub or Slack over HTTPS, the same TLS-boundary capture path exposes the Authorization header in cleartext. The credential is fingerprinted, never stored raw. That a specific pod presented a specific fingerprint to github.com and issued a create-PR call is direct observation, the strongest edge in the graph.

https://cdn.sanity.io/images/7yls9lz6/production/fb3a99a7a90379dbc2efc98e186014d0cc8a8a42-1672x941.png

Kubernetes API: authoritative binding

The sensor tells you which pod made the call. The Kubernetes API tells you what that pod is: its deployment, namespace, and service account, and from the service account the workload-identity binding (IRSA, Workload Identity, Pod Identity) to the IAM role and its permissions. This is an authoritative binding, not an inference: the cluster is the source of truth for which identity a pod runs as, which anchors the agent node to a real set of permissions and lets you compare granted against used.

CloudTrail: delayed corroboration

CloudTrail records the control-plane API calls the agent’s cloud identity makes. Different grade: delayed, and limited to cloud-provider calls, so MCP tool calls, LLM calls, and database queries never appear. Its value is the delta against eBPF. A call eBPF saw that CloudTrail confirms is corroborated; a CloudTrail entry with no workload-side counterpart is access that bypassed the application path (console actions, cross-account AssumeRole, automation outside the cluster). Use it for corroboration and gap detection, without relying on it as the primary record.

Identity integrations: resolved enrichment

This layer resolves delegated identity, and it is where the PAT problem gets solved on the evidence side. When the sensor observes a credential fingerprint, an integration resolves it against the issuing provider, where the provider’s API exposes that metadata. A GitHub PAT resolves to its owning user, email, MFA status, and the orgs and repos in scope; an OpenAI project key to its owning user or service account; a Slack token to its user and workspace via auth.test; an AWS or GCP identity to its IAM principal. What each provider exposes varies, so treat the fields as best-available. The lookup is keyed on the fingerprint; the raw credential is never stored.

The grade here is “resolved enrichment”: a lookup that attaches ownership and properties to an observed credential, rather than a direct observation of behavior. It is what turns “pod agent-7f8a used credential fp_9c2a” into “the agent acted as Alice’s PAT, which has org-wide repo scope and no expiry.” That single resolution exposes the delegation that GitHub’s audit log hides.

https://cdn.sanity.io/images/7yls9lz6/production/eedcfd84243cbcd1d74b0ad970f75e2e54cf811b-1672x941.png

Putting this into practice at Aurva. The four sources above are the evidence layer we built: an eBPF sensor at the TLS boundary, Kubernetes informers for the service-account-to-IAM binding, CloudTrail for corroboration, and provider integrations that resolve a credential fingerprint to its owner. The grading matters more than the specific stack: whatever tool you use, ask what each edge is made of, and hold out for direct observation wherever a containment decision depends on it.

Stitching it up without losing the thread

Individually each source is a slice; the value is the join, which preserves context across every hop so the result reads as one chain. We cover the join mechanics, keys, and fragile spots in a companion post (Runtime Identity Attribution with eBPF, IAM, and CloudTrail). In short: the workload node comes from eBPF plus Kubernetes, the resource node from cloud metadata, the identity node from the credential resolution, all time-aligned so the LLM plan, MCP tool call, and GitHub write read as one sequence.

Applied to our incident, the stitched fact reads:

Pod agent-7f8a (deployment ship-agent, namespace prod), running as service account agent-sa, took a request from Slack channel #eng-bots, made a planning call to openai gpt-4, then issued an MCP TOOL CALL that opened an HTTPS connection to github.com presenting credential fp_9c2a. That fingerprint resolves to Alice’s classic PAT: org-wide repo, no expiry, MFA on. The call was create_pull_request against prod-infra, deleting a workflow file.

Every clause comes from a different source and grade. Remove the credential resolution and you are back to “Alice did it”; remove the eBPF layer and the MCP call and LLM plan vanish.

Why containment lives or dies on this

Here evidence grade stops being academic. With only GitHub’s audit log, your options are: notify Alice (wrong person), revoke her PAT (breaking her access and every other agent quietly sharing it), or do nothing. None stops the agent, which can just run again. With the stitched, graded evidence, containment becomes specific: you know the pod, its service account, the credential fingerprint, and the call, so you can block that pod’s egress to github.com, rotate the specific credential, and scope the replacement to the one repository the agent needs, all without paging Alice.

That is the link between the two halves of the premise: better evidence widens the set of containment actions you can safely take. Coarse evidence shrinks that set to “nothing” or “something destructive.” Precise evidence expands it to “the exact thing.”

The actual fix: stop delegating through a human’s PAT

The evidence tells you what is wrong; the remediation is a design change. A classic PAT is the wrong credential for an agent for three reasons the telemetry made visible: it is owned by a human, so every action attributes to that human; it is long-lived, so a leak is durable; and it is broadly scoped, so the blast radius is org-wide. Replace it with a credential that inverts all three:

  • Agent-owned. A GitHub App installation token attributes actions to the app installation, not to whoever created a PAT, so the audit log finally names the agent.
  • Short-lived. Installation tokens expire in about an hour; an OIDC-federated token minted per task lasts only that long. A leaked credential stops being useful quickly.
  • Narrowly scoped. Grant the app the specific repositories and permissions the agent needs, not org-wide repo. That turns “could touch everything” into “can touch these three repos, read-only plus PRs.”
https://cdn.sanity.io/images/7yls9lz6/production/04cce4aee566cea5fe37e82f3b81c15c7dc41f2e-1672x941.png

The telemetry then verifies the change worked: after the switch, the sensor should see the agent presenting a short-lived installation token that resolves to the app rather than a human, scoped to the repos you expect. A PAT fingerprint reappearing on that path is a regression worth an alert.

Honest limitations

  • Resolution depends on the provider exposing owner metadata. GitHub, OpenAI, and Slack expose enough; some providers do not, or rate-limit the lookups. Where resolution fails, treat that edge as the lower grade it is.
  • eBPF coverage stops at the host. Agents in serverless (Lambda, Fargate, Cloud Run) or fully managed SaaS are outside kernel visibility, so you fall back to control-plane logs, a real drop in grade.
  • Fingerprint resolution has a freshness window. A credential’s owner, scope, or MFA status can change after it was last resolved; a stale resolution is weaker than a fresh one.
  • The credential fix has ecosystem gaps. Not every tool supports app-style or OIDC-federated credentials yet. For the ones that still only offer long-lived tokens, scope tightly, rotate often, and lean on the telemetry to catch misuse.
  • Containment is still a decision. Blocking egress or revoking a live credential has its own blast radius. Good evidence makes the action safe to consider; it does not remove the need for judgment.

Key takeaways

  • Drawing an identity graph for AI agents is a bare minimum. Whether you can trust it, and act on it, depends on the evidence under each edge.
  • Evidence comes in grades: direct observation from eBPF, authoritative bindings from Kubernetes, delayed corroboration from CloudTrail, resolved enrichment from identity integrations. They render the same on the graph and carry very different confidence.
  • Shared PATs and API keys collapse every upstream identity into one credential owner, so the audit log names a human who did not act. Resolving the credential fingerprint to its owner exposes that delegation.
  • Containment is the test. You can only revoke, scope, or block what you can attribute with confidence, and the fix for the PAT case is an agent-owned, short-lived, narrowly scoped credential that the telemetry can verify

Common Questions

Q: Why does an AI agent’s audit log name the wrong person?

A: Agents usually authenticate with a credential a human created, most often a Personal Access Token or API key. The provider attributes the action to that credential’s owner, not the agent that used it, so the log is accurate about the credential and wrong about the actor.

Q: What is the difference between high-grade and low-grade attribution evidence?

A: High-grade evidence is a direct observation (the actual request on the wire) or an authoritative binding (the cluster’s record of which identity a pod runs as). Low-grade evidence is inferred, declared, or self-reported: static config, naming conventions, agent SDK traces, gateway logs, or posture snapshots. All can draw the same graph; only high-grade evidence is safe to base a containment action on.

Q: What should an AI agent use instead of a PAT to authenticate to tools?

A: An agent-owned, short-lived, narrowly scoped credential: a GitHub App installation token or a per-task OIDC-federated token, scoped to the repositories and permissions the agent needs. The audit log then names the agent, and containment no longer requires disabling a human.

aurva-logo

USA

AURVA INC. 1241 Cortez Drive, Sunnyvale, CA, USA - 94086

India

Aurva, 4th Floor, 2316, 16th Cross, 27th Main Road, HSR Layout, Bengaluru – 560102, Karnataka, India

aicpa-logoiso-logo

© 2025 Aurva. All rights reserved.Terms of ServicePrivacy Policy

twitterlinkeding
Aurva