guide
Detection engineering in AI-era clouds
Most evidence you need to detect attacks on AI workloads is off by default, billable, capped or sampled. A field guide to the telemetry that exists today.
Detecting attacks on AI workloads is not a matter of reading prompts. The durable signal is the effect: which identity acted, under whose delegated authority, which tool executed, what data was retrieved, and where it went. The hard engineering problem is that most of that evidence is off by default, billable, capped, sampled, or structurally absent exactly where a model turns text into action.
Scope, assumptions, and where this guide is thin
This guide is for detection engineers who already run a detection practice for conventional cloud and are extending it to AI workloads. It assumes you operate at least one of AWS, Azure or Google Cloud, have CloudTrail-equivalent ingestion into a SIEM or log lake, can change logging configuration but cannot rewrite the applications, and that per-event costs constrain what you turn on.
In scope: managed model platforms (Amazon Bedrock including Bedrock AgentCore, Amazon SageMaker AI, Azure AI Foundry and Azure OpenAI, Google's model platform), direct consumption of the OpenAI and Anthropic APIs and the enterprise admin surfaces those providers expose, agent runtimes and tool calling including tools mediated by the Model Context Protocol (MCP), retrieval systems and vector indexes, and the non-human identities holding it together. Also the practice itself: threat-informed design, data-quality validation, detection-as-code, testing, tuning, and coverage measurement.
Out of scope, deliberately: model safety, alignment and content moderation as ends in themselves (they appear only where they emit telemetry you can consume, such as guardrail interventions); training-time attacks on weights and corpora; red-team methodology and payload construction; and vendor or SIEM selection. Retrieval-corpus poisoning is in scope, because it is an operational data-plane event with a log signature.
Where this guide is thin. The AWS material runs roughly three times the depth of the Azure and Google Cloud material. That reflects what each vendor documents publicly at the level of named events, field names, default states and caps, not a judgment about the platforms. AWS enumerates data-event resource types and log record fields; Azure documents categories and span names but not record schemas; Google publishes an audited-operations table that is strong on retrieval and silent on generative calls. If you run primarily on Azure or Google Cloud, read the AWS sections as the worked example of a method you will apply with fewer documented anchors, deriving more of your telemetry map from your own test calls. Self-hosted inference gets a short section and no worked detections, because there is no vendor telemetry contract to write against.
Everything factual here was verified against primary documentation on 2026-08-06. Where a source was unreachable or a behaviour could not be confirmed, the text says so in place. The gaps are the deliverable, because a detection programme built on an optimistic telemetry map fails silently. There are two ways to describe AI detection coverage: "we have a rule for that technique" and "we have evidence that would let a rule fire." Only the second is worth measuring.
What AI-era cloud changes
Five things change in ways that matter to a log pipeline. None is that attackers now write clever text.
A new data plane that looks like text. The model request and response payload is where the semantically interesting content lives, and cloud audit logs were designed to record who called which API against which resource, not request bodies. The API-call record survives; the payload does not. The Bedrock CloudTrail example for InvokeModel shows requestParameters containing only { "modelId": "stability.stable-diffusion-xl-v0" } and "responseElements": null. The SageMaker AI guide states that InvokeEndpoint, InvokeEndpointAsync, Sample and SampleWithResponseStream "don't log the request parameters." That is a design decision, and the right one for a general-purpose audit log. The payload has to come from a separate, opt-in feature or not at all.
Execution decided at runtime. In a conventional service the set of downstream calls is fixed at deploy time and reviewable in code. In an agent it is chosen at inference time from a tool catalogue, conditioned on content that may have arrived from a document, a web page, an email or a retrieved chunk. Control flow is data-dependent in a way static review cannot bound. MITRE ATLAS names this as AML.T0053 (AI Agent Tool Invocation) and AML.T0086 (Exfiltration via AI Agent Tool Invocation), the latter describing how "sensitive information can be encoded into the tool's input parameters and transmitted to an adversary-controlled location (such as an inbox, document, or server) as part of a seemingly legitimate action."
Identity that is delegated, chained and short-lived. A human authenticates to an application; the application assumes a role or acquires a token; an agent runtime acquires a workload identity; the agent acquires an audience-bound token for one MCP server; the MCP server calls a downstream API with its own credential. Five identities, four exchanges, each a candidate place for the audit chain to break. Bedrock AgentCore implements agent identities as workload identities with credential providers and an inbound JWT authorizer, which is coherent at the identity layer and does not by itself produce a joinable trail at the logging layer.
Third-party trust delivered as a tool definition. An MCP server supplies tool names, descriptions and schemas that enter the model's context and influence its behaviour. The 2025-06-18 MCP specification stated that descriptions of tool behaviour "should be considered untrusted, unless obtained from a trusted server." ATLAS AML.T0110 (AI Agent Tool Poisoning) covers the attack, noting it may target "a tool's model-visible definition, executable implementation, or runtime responses." A tool definition is a configuration item with a supply chain, and changes to it belong in your change-detection pipeline alongside IAM policy changes.
Cost as an attack surface with a delayed alarm. ATLAS AML.T0034.002 (Agentic Resource Consumption) describes coercing an agent into "computationally expensive tool calls that waste resources and consume API budgets." Token counters and cost APIs carry the signal, but aggregation windows set a floor on detection latency. Anthropic's messages usage report supports 1m, 1h and 1d bucket widths, with data typically appearing "within 5 minutes of API request completion"; the cost report is daily buckets only. Minutes if you poll usage, days if you rely on billing.
The system model
Everything below uses one seven-layer model. Detections attach to layers; investigations traverse them; coverage is measured per boundary.
Diagram 1. The seven-layer AI workload model. Alt text: a left-to-right flow from caller, to application orchestrator, to identity broker, to model endpoint, with retrieval feeding the model and tool execution feeding downstream resources. Four trust boundaries are marked as dotted links: TB1 application to identity broker, TB2 model to tool execution, TB3 tool to downstream resource, TB4 retrieval to model.
- TB1, application to identity broker. A human principal becomes a machine principal. The last point at which a user identity is unambiguously present in a log record.
- TB2, model to tool execution. Text becomes action. The highest-value boundary in the model and, on most platforms, the least instrumented by default.
- TB3, tool to downstream resource. Action becomes effect. Well instrumented, since these are ordinary cloud API calls, but usually not joinable back to the invocation that caused them without work on your part.
- TB4, retrieval to model. Untrusted content enters the context. Retrieval logs generally record that a query occurred, not what came back.
Layers 6 and 7 produce telemetry your existing programme already consumes. Get the correlation right and much of your AI coverage comes from rules you have already written, enriched with the agent context that says which invocation caused the call.
Identity, delegation and the audit chain
The correlation backbone is identity, and the specific problem is delegated authority: an agent acting with permissions a human granted it, possibly re-delegated to a sub-agent or a tool.
Diagram 2. Delegated authority through an agent stack. Alt text: a sequence diagram showing a human principal calling an application, which exchanges credentials at an identity broker, starts an agent session, obtains an audience-bound token, calls an MCP server, which calls a downstream API. Annotations name the log source at each hop, and a note marks where the human identity is lost.
Where the chain breaks
Four breaks recur, and they are structural rather than misconfigurations.
Role assumption flattens the caller. CloudTrail records AssumeRole with the calling principal and subsequent calls with the assumed-role session identity. The join is the role session name and session ARN, which works only if the application sets a meaningful session name. An application that uses a constant session name for every user has destroyed human attribution at the first hop, and no downstream logging recovers it.
Token exchange at the authorization server. MCP's current revision, 2026-07-28, requires clients to implement RFC 8707 resource indicators: the resource parameter MUST be present in both authorization and token requests and MUST identify the target MCP server, "regardless of whether authorization servers support it." Servers MUST validate that the token was issued for them as intended audience, and "MCP servers MUST NOT accept or transit any other tokens." That produces useful telemetry — one token request per target server, audience named — but it lives at the authorization server, and most teams are not ingesting it.
Re-delegation is invisible unless something issues an identifier. When an agent spawns a sub-agent or hands off a task, there is typically no protocol-level artefact recording the parent-child relationship in a queryable form.
The downstream log shows the wrong principal. Once a tool implementation uses its own service credential, the downstream audit log records that credential. The human is two or three hops away and, absent an injected correlation identifier, unrecoverable.
OCSF's delegation object is the first serious standard for this
The Open Cybersecurity Schema Framework has grown an AI model whose centre of gravity is delegation rather than prompts. There is no AI category; the eight categories are unchanged, and AI is a profile, ai_operation, with four attributes: ai_agent, ai_model, delegation and message_context.
delegation describes "a durable authorization context that a principal issues to a delegate." Its uid is required, issuer_uid names the trusted issuing authority, and parent_uid chains delegations into a directed acyclic graph "that supports lineage queries across the chain of authority." The identifier must be generated by a trusted issuing authority "rather than self-asserted by the delegate," which is the correct security property and the reason most current systems cannot populate the field honestly. The schema also warns that the authority graph "is distinct from any agent instantiation or orchestration hierarchy," so do not read a parent delegation as evidence that one agent spawned another.
ai_agent separates a stable logical uid from an instance_uid identifying "a single materialization of the agent: a conversation, session, or run"; carries its own version distinct from the model version; allows a charter file with hashes and signatures; and has a type_id enum covering Unknown (0), Native (1), LangChain (2), AutoGen (3), CrewAI (4) and Other (99). instance_uid is not a process identifier: the schema says it "may persist across restarts" and "may span multiple cooperating runtime components," so several events can legitimately share one.
The applicability change between releases says where the standards body thinks AI events live. In OCSF 1.8.0 the profile applied to three classes: process_activity (1007), api_activity (6003) and datastore_activity (6005). In 1.9.0 it applies to 44 of 87, adding file_activity, scheduled_job_activity, dns_activity, http_activity, authentication, authorize_session and web_resource_access_activity among others. That is the schema conceding the central point of this guide: AI actions show up in ordinary logs everywhere, not in a special AI log.
Why prompt content is a weak alert signal
"Detect malicious prompts" is the most common answer to "how do we detect attacks on our AI systems," and it is not a detection strategy. Four arguments, then the replacement. This is CloudSecOps judgment assembled from primary evidence, not a claim any vendor makes.
Base rates. A production assistant at modest volume processes millions of benign turns a month. A classifier on that stream at any realistic precision produces a queue dominated by false positives, and unlike a failed authentication there is no cheap confirmatory pivot: adjudicating a suspected malicious prompt means reading the text, understanding the business context, and judging whether the response was harmful. That cost scales with usage, not with attacker activity.
Paraphrase and encoding. The mapping from intent to text is one-to-many without bound. Getting an agent to send a document to an external address has unbounded surface forms across languages, encodings, indirection through a retrieved document, and instructions embedded in image or file content. Content matching on an unbounded surface is the position signature-based detection lost from two decades ago.
Non-determinism. The same input does not reliably produce the same action. A prompt that triggers a tool call once may not the next time, at a different temperature, with a different system prompt version, or after a model upgrade. A rule whose ground truth is "this text is dangerous" asserts something only conditionally true, and the condition is not in the log.
Observability cost and legal exposure. On AWS the only first-party source of request and response bodies is Bedrock model invocation logging: separate, off by default, capped at 100 KB inline, with larger or binary payloads written to a separate S3 object and only a reference left in the log entry. On Azure, content recording in Foundry Agent Service tracing is gated on AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED, default false. In OpenTelemetry's GenAI conventions, gen_ai.input.messages, gen_ai.output.messages, gen_ai.system_instructions and gen_ai.tool.definitions are all Opt-In. A full-body store is also a durable collection of user-supplied text that may contain personal data, credentials, source code and regulated material, which puts the retention question on your data protection officer's desk.
The industry's own artefacts agree, which is the part worth dwelling on:
| Artefact | What it says about prompt content |
|---|---|
OCSF message_context | prompt_text and response_text are marked optional |
| OTel GenAI spans | Message content attributes are Opt-In; the spec documents uploading content to external storage rather than inlining it |
| GuardDuty AI Protection | Impact:IAMUser/PromptInjection.Direct is Low severity by default |
| Bedrock model invocation logging | Off by default, 100 KB inline cap, modality-selectable |
Table 1. What four primary artefacts imply about prompt content as an alerting signal. Verified 2026-08-06.
The GuardDuty case is sharpest. It is the only prompt-content-derived finding AWS ships, rated Low by default, and it requires a separately configured Bedrock Guardrail with a prompt-attack content filter. When a guardrail evaluates an invocation, Bedrock records the evaluation in CloudTrail data events, which GuardDuty analyses, so the chain depends on AWS::Bedrock::Guardrail data events being enabled, which they are not by default. The same page warns that guardrail attachment is per-request unless you "enforce it broadly" using Amazon Bedrock policies in AWS Organizations rather than "relying on individual applications to attach it to each request." Three preconditions, each of which fails quietly. A provider with full visibility into its own inference stack does not treat prompt content as a high-confidence signal.
What replaces it
Detect the effect. Six effect classes carry the signal, and each maps to telemetry your pipeline can already consume: identity use (which principal invoked, from where, under what session and delegation), tool invocation (which tool, with what arguments, in which session), data retrieval (which index or corpus, how often, by whom), data movement (which downstream object, and its sensitivity), egress (where bytes went, by which path), and consumption (tokens, invocations and cost per identity and model).
Prompt content still has a job: it is investigation context, not alert logic. When an effect-side detection fires, the prompt and response are the difference between a twenty-minute triage and a two-day one. Retain content in a short-retention, tightly access-controlled tier you query during an investigation, and keep the alerting metadata in a long-retention tier. That is not an argument for feeding content to a classifier and paging on the output.
One exception: guardrail interventions are worth alerting on in aggregate. A single intervention is noise. A tenfold change in intervention rate for one identity, model or application version is a real signal about a change in the traffic or the guardrail, and it costs nothing to compute.
The telemetry map
Draw this for your own estate before writing a rule. The exercise takes a day and reliably changes what teams build first, because it surfaces how much assumed evidence is not being collected.
| Source | What it records | Default state | Bodies? | Cost class |
|---|---|---|---|---|
CloudTrail management events (bedrock.amazonaws.com) | InvokeModel, InvokeModelWithResponseStream, Converse, ConverseStream: caller, model id, region | On with any trail | No | Included |
| CloudTrail data events (Bedrock, S3 Vectors, SageMaker) | Agent, knowledge base, flow, guardrail, session and tool operations | Off | No | Per event, billable |
| Bedrock model invocation logging | Request and response bodies, token counts, identity.arn, requestMetadata | Off | Yes, capped | Storage plus delivery |
| CloudTrail network activity events | API calls traversing your VPC endpoints, including denied ones | Off | No | Per event, billable |
| AgentCore built-in metrics | Sessions, latency, duration, token usage, error rates | On | No | CloudWatch metrics |
| AgentCore logs and spans | Agent, memory, gateway and tool activity; OTel spans | Off, multi-step setup | Depends on instrumentation | Logs plus trace storage |
Azure Microsoft.CognitiveServices/accounts resource logs | Five categories including Request and Response Logs | Off until a diagnostic setting exists | Category-dependent | Ingestion and retention |
| Azure Foundry Agent tracing | invoke_agent, execute_tool and related spans | Tracing configurable; content off | Only if content recording enabled | App Insights ingestion |
| Google Cloud Data Access audit logs | endpoints.predict, findNeighbors, ragFiles.*, memories.*, sessions | Off except BigQuery | No | Logging ingestion |
| OpenAI Audit Logs API | 144+ admin event types; no inference records | Available to admin key | No | API access |
| Anthropic Compliance API | Activity feed with actor, IP, user agent, event type | Requires Compliance Access Key for content | Content only via separate key | API access |
Table 2. Telemetry map for AI workloads, verified 2026-08-06. "Default state" assumes a freshly provisioned service in an account that already has an organisation trail or equivalent.
Diagram 3. Where AI telemetry originates and whether it reaches your log store by default. Alt text: three subgraphs for AWS, Azure and Google Cloud, each showing AI components mapped to the log sources they produce, with default-on and default-off labelled in text. Two terminal nodes mark evidence that does not reach a customer SIEM at all.
Amazon Bedrock
The control plane and data plane split is inverted from the usual mental model, and it is the single most useful fact in the guide. InvokeModel, InvokeModelWithResponseStream, Converse and ConverseStream are CloudTrail management events with eventSource: bedrock.amazonaws.com, so they are on by default in any account with a trail. Everything agentic is a data event, and CloudTrail states that "by default, trails and event data stores do not log data events. Additional charges apply for data events."
| Resource type | Operations | Why you would enable it |
|---|---|---|
AWS::Bedrock::AgentAlias | InvokeAgent | The only record that an agent ran at all |
AWS::Bedrock::InlineAgent | InvokeInlineAgent | Agents defined at call time, not registered |
AWS::Bedrock::KnowledgeBase | Retrieve, RetrieveAndGenerate | Retrieval volume and identity per knowledge base |
AWS::Bedrock::FlowAlias | InvokeFlow | Multi-step orchestration execution |
AWS::Bedrock::Guardrail | Guardrail evaluations | Prerequisite for the GuardDuty prompt-injection finding |
AWS::Bedrock::Session, ::Tool | Session and tool operations | Session lifecycle and tool-level attribution |
AWS::S3Vectors::VectorBucket, ::Index | Vector bucket and index operations | Retrieval-store access and modification |
AWS::SageMaker::Endpoint | InvokeEndpointWithResponseStream (see note) | Self-hosted and fine-tuned model usage |
Table 3. AI-relevant CloudTrail data-event resource types, verified 2026-08-06. The CloudTrail page lists eighteen AWS::Bedrock::* resource types and names operations for only some, so confirm the operations column by enabling and observing. Note also the documentation inconsistency: the CloudTrail page lists only InvokeEndpointWithResponseStream under AWS::SageMaker::Endpoint, while the SageMaker developer guide lists InvokeEndpoint, InvokeEndpointAsync and InvokeEndpointWithResponseStream. Test empirically rather than picking a winner.
The consequence, plainly: in a default AWS account you can see that a model was called, and you cannot see that an agent ran, that a knowledge base was queried, or that a guardrail fired.
Bedrock model invocation logging, configured through PutModelInvocationLoggingConfiguration, is the only first-party source of bodies. Five properties decide whether it produces usable evidence:
- It covers
Converse,ConverseStream,InvokeModelandInvokeModelWithResponseStreamonly, and notInvokeAgent,RetrieveorInvokeFlow. If your workload is agentic, this feature does not see it. - It is scoped to one endpoint. Logging "is only supported for calls made through the
bedrock-runtimeendpoint. Calls made through other endpoints, such as the Responses API on thebedrock-mantleendpoint, are not currently captured by invocation logging." A workload that migrates endpoints loses body logging with no configuration change on your side. - Bodies are capped at 100 KB inline; larger and binary payloads go to S3 under a data prefix with only a reference in the log entry, and for those payloads only S3 is supported as a destination. A CloudWatch-Logs-only configuration loses them.
- Modality is selectable across Text, Image, Embedding and Video. A team that enabled Text only has no record of image or document content passed through Converse, and nothing warns them.
- Destinations must be in the same account and Region as the workload.
Record fields: schemaType (ModelInvocationLog), schemaVersion, timestamp, accountId, region, requestId, operation, modelId, identity.arn, requestMetadata, input.inputContentType, input.inputBodyJson, input.inputTokenCount, output.outputContentType, output.outputBodyJson, output.outputTokenCount.
Two fields matter. identity.arn is the join key back to CloudTrail and is "captured automatically." requestMetadata is "the only field supplied by the caller," making it the best place to inject a correlation identifier and, for the same reason, a field you must not treat as trustworthy evidence.
Guardrail telemetry read directly, rather than through GuardDuty, is structured and useful. Converse returns stopReason: guardrail_intervened and, with "trace": "enabled", a trace.guardrail.inputAssessment.<guardrail-id> object containing topicPolicy, contentPolicy, wordPolicy, sensitiveInformationPolicy and contextualGroundingPolicy, plus invocationMetrics with guardrailProcessingLatency, per-policy usage units, and guardrailCoverage.textCharacters.guarded and .total. guardrailCoverage is the data-quality field nobody uses: if guarded is materially less than total, the guardrail evaluated only part of the text and any detection on its output is partially blind for that request. Alert on the ratio, not only on the interventions.
CloudTrail network activity events let a VPC endpoint owner record API calls traversing their endpoints. Off by default, billable, configured with an advanced event selector using eventCategory = NetworkActivity and eventSource = <service>, with optional filtering on errorCode = VpceAccessDenied, vpcEndpointId or userIdentity.arn. The supported eventSource list includes bedrock.amazonaws.com and bedrock-agentcore.amazonaws.com, alongside sagemaker.amazonaws.com, secretsmanager.amazonaws.com, kms.amazonaws.com, lambda.amazonaws.com, s3.amazonaws.com, rolesanywhere.amazonaws.com, qbusiness.amazonaws.com, nova-act.amazonaws.com and transform-agents.amazonaws.com. It is the best available primitive for detecting AI use outside approved network paths, with one limit: it records traffic through your VPC endpoints. Calls reaching the public service endpoint from outside your VPCs, including stolen credentials used from anywhere on the internet, produce no network activity event.
Bedrock AgentCore
AgentCore emits OpenTelemetry-compatible data into CloudWatch. Built-in metrics (session count, latency, duration, token usage, error rates) need no configuration. Logs and spans for Agent, Memory, Gateway and Tools resources require explicit enablement, and the setup is multi-step enough that partial completion is the normal outcome. Default log groups are /aws/bedrock-agentcore/runtimes/<agent_id>-<endpoint_name> for runtimes, /aws/vendedlogs/bedrock-agentcore/{memory|gateway}/APPLICATION_LOGS/{resource-id} for memory and gateway, and aws/spans as the shared span destination.
Unified span delivery into the agent's own log group requires all of: CloudWatch Transaction Search enabled; the X-Ray trace segment destination set to CloudWatch Logs; a resource policy allowing xray.amazonaws.com to call logs:PutLogEvents; the execution role holding logs:PutResourcePolicy on the agent log group; ADOT aws-opentelemetry-distro 0.18.0 or later; and UNIFIED_TRACES_DESTINATION_ENABLED=true. Six preconditions, each of which belongs in your telemetry-health suite, because failing any one produces a partially-populated pipeline rather than an error.
One correctness problem deserves emphasis: X-Ray indexing is sampled. aws xray update-indexing-rule sets a DesiredSamplingPercentage. A detection counting tool calls over a window against sampled spans under-counts, and the under-count is invisible in the result. Either set sampling to 100% for security-relevant span types, or scale thresholds by the sampling rate and document that you did. The resource surface is expanding — Agent, Memory, Payments, Gateway, Tools and Policy types carry logs — so re-enumerate the log-group inventory rather than hard-coding it.
Amazon SageMaker AI
InvokeEndpoint, InvokeEndpointAsync and InvokeEndpointWithResponseStream are data events on AWS::SageMaker::Endpoint, off by default and billable, and the developer guide states these calls "don't log the request parameters." For self-hosted and fine-tuned models on SageMaker the AWS-native evidence is: an endpoint was invoked, by this identity, at this time. Payload capture comes from SageMaker's own data capture configuration or from application instrumentation, not from CloudTrail.
Azure AI Foundry and Azure OpenAI
Supported resource log categories for Microsoft.CognitiveServices/accounts are Audit Logs, Azure OpenAI Request Usage, Managed Network Events, Request and Response Logs and Trace Logs. None are collected until a diagnostic setting exists, which is the Azure equivalent of the data-events problem.
Azure OpenAI also operates an abuse-monitoring store. The models are stateless, with no prompts or completions stored in the model, but when abuse monitoring is active, prompts and completions are retained in a store accessible only to authorised Microsoft employees through secure access workstations with just-in-time approval, and only for data already flagged. Customers may apply for modified abuse monitoring; the state is observable as the ContentLogging attribute. That store is not your telemetry. It is a Microsoft-side control you cannot query during an incident. Any architecture diagram showing prompt content flowing from Azure OpenAI into your SIEM by default is wrong, and the misconception is worth correcting explicitly with stakeholders.
Azure AI Foundry Agent Service is the better source for agent behaviour, emitting OpenTelemetry spans following the GenAI conventions — invoke_agent, execute_tool, agent_to_agent_interaction, agent.state.management, agent_planning, execute_task — with tool.call.arguments and tool.call.results attributes, exported to Azure Monitor Application Insights. Content recording is gated on AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED, default false. Spans without content still tell you which tool ran in which session, which is the high-value part.
Entra ID audit logs are documented as capturing "Agents – operations performed, service principal changes, agent ID details." The audit activity reference page has no dedicated Agent category; service principal lifecycle sits under ApplicationManagement and permission grants under RoleManagement. That inconsistency was unresolved on the verification date, and attempts to reach a Microsoft product page for an Entra agent identity offering returned 404, so this guide names no product. Write detections against ApplicationManagement and RoleManagement activities, which are verified.
Google Cloud
Google renamed the product and moved the documentation: cloud.google.com/vertex-ai/docs/general/audit-logging now redirects to a docs.cloud.google.com/gemini-enterprise-agent-platform/... path, and the Cloud Logging service index lists aiplatform.googleapis.com under the display name Gemini Enterprise Agent Platform and discoveryengine.googleapis.com as Agent Search/Discovery Engine. The service identifier is unchanged: serviceName: aiplatform.googleapis.com, resource type audited_resource, log names of the form projects/PROJECT_ID/logs/cloudaudit.googleapis.com%2Fdata_access. Write queries against the identifier, not the display name.
Data Access audit logs are disabled by default and not written unless explicitly enabled, with BigQuery the exception. Reading the data_access stream requires roles/logging.privateLogViewer, a second-order problem worth planning for: your pipeline's service account needs it and your analysts may not have it, so an alert can fire on evidence the responder cannot open.
From the audited-operations table, on a page marked "Last updated 2026-08-06 UTC":
- DATA_READ includes
endpoints.predict,endpoints.rawPredict,endpoints.predictLongRunning,endpoints.explain,indexEndpoints.findNeighbors(vector similarity query),memories.retrieve,ragFiles.get,ragFiles.list,sessions.get,sessions.list,sessionEvents.list. - DATA_WRITE includes
indexes.upsertDatapoints,indexes.removeDatapoints,indexes.create,indexes.patch,ragFiles.import,ragFiles.upload,ragFiles.delete,sandboxEnvironments.execute,sessions.create/update/delete,sessionEvents.append. - Admin Activity includes
ragCorpora.create/delete,memories.create/delete/generate/purge/update,sandboxEnvironments.create/delete/snapshot,semanticGovernancePolicies.create/update/delete, plus endpoint, model and pipeline lifecycle operations.
That is a rich retrieval and memory surface, better enumerated than either competitor for vector and RAG operations. And there is a hole. A text search of that page on 2026-08-06 returns zero occurrences of generateContent, streamGenerateContent, publishers, countTokens, reasoningEngines or agentEngines. The table covers deployed-endpoint prediction, vector search, RAG file management, memory and sessions, but as documented it does not enumerate the publisher-model generative call path most Gemini workloads use.
Absence from a documented table is not proof that no log line is emitted. It is a documented coverage gap, and it converts an assumption into a required test: issue a generateContent call in a project with Data Access audit logging enabled for aiplatform.googleapis.com and confirm whether a data_access entry appears. Do not build a Google Cloud model-invocation detection on the assumption that it does. A related feature, request-response logging to BigQuery, could not be verified at all — both candidate documentation URLs returned 404 — so this guide does not assert it exists.
Direct model-provider APIs
For organisations calling OpenAI or Anthropic directly, the enterprise telemetry is identity events plus aggregated counters. This is structural rather than a configuration mistake: there is no per-request log with a source IP for inference calls.
OpenAI. GET /organization/audit_logs requires an admin key and covers over 144 event types across api_key.*, login.*, logout.*, user.*, group.*, project.*, service_account.*, role.*, role_assignment.*, certificate*, ip_allowlist.*, workload_identity_provider.*, tenant.sso_connection.* and scim.enabled/scim.disabled, with records carrying id, effective_at and type. The Admin API also governs per-project Hosted Tool Permissions (code interpreter, file search, image generation, web search, MCP) and Model Permissions as allowlists or denylists; changes to those are exactly the configuration drift a detection should watch, since enabling the MCP hosted tool changes a project's blast radius. Usage endpoints include /organization/usage/completions, /embeddings, /images, /moderations, /audio_speeches, /audio_transcriptions, /code_interpreter_sessions, /file_search_calls, /web_search_calls, /vector_stores, plus /organization/costs, with group-by dimensions including api_key_id, project_id, user_id, model, batch, service_tier and vector_store_id. No endpoint exposes conversation or prompt content.
Anthropic. The Admin API sits at /v1/organizations/* with x-api-key: sk-ant-admin... or an OAuth bearer; service accounts, federation issuers and federation rules require an org:admin OAuth token and are not reachable with an Admin API key, a detail that bites when part of your collector returns 403 for no visible reason. The Compliance API at /v1/compliance/* provides GET /v1/compliance/activities, an activity feed with event id, timestamp, organisation id, actor (user email, IP address, user agent), event type such as claude_chat_created, and associated resource ids, rate-limited to 600 requests per minute; content endpoints require a separate Compliance Access Key created in the product. Usage and cost live at /v1/organizations/usage_report/messages (group by model, workspace_id, service_tier, api_key_id, context_window, inference_geo; bucket widths 1m, 1h, 1d; fields including uncached_input_tokens, cached_input_tokens, cache_creation_tokens, output_tokens) and /v1/organizations/cost_report, daily buckets only.
The 1m bucket width is the most operationally useful fact here, with one constraint: minute buckets are capped at 1,440 per query, so a collector that stops for more than a day cannot backfill at minute granularity. Poll continuously and store the buckets yourself.
For agentic coding, /v1/organizations/usage_report/claude_code returns daily per-user aggregates — session counts, lines added and removed, commits, pull requests, and accept/reject counts for edit_tool, multi_edit_tool, write_tool and notebook_edit_tool — with no prompt content, no tool-call detail and no per-session breakdown. If developer agents write to your repositories, your detection surface is source control and CI, not the provider.
Self-hosted models
If you run inference yourself, on GPU instances, in Kubernetes, or behind your own gateway, none of the above applies: no vendor audit log, no invocation logging feature, no usage API, no guardrail trace. Your telemetry is whatever your serving stack and its sidecars emit, plus ingress and mesh access logs, container logs, node-level process and network telemetry, and identity events from whatever fronts the endpoint. That is not a worse position, and it is the only configuration in which you can decide unilaterally to log bodies, tool calls and token counts in one schema. The cost is that every field in the normalisation section becomes something you emit rather than map. This guide offers no worked detections for that case, because the observable names depend entirely on your stack.
What to enable first, and what it costs
The default configuration on all three clouds records the wrong half. Management-plane and admin events are on; the operations describing what an AI system did to data are off, opt-in, or billable. That is defensible from the provider's side, since data events at AI volumes are expensive, but the enablement decision is yours and it has a price tag.
CloudSecOps recommends this order:
- Bedrock
AWS::Bedrock::AgentAliasandAWS::Bedrock::KnowledgeBasedata events, or the equivalent Google Data Access logs foraiplatform.googleapis.com, or Azure diagnostic settings on your Foundry accounts. Without these you have no record that an agent ran or that retrieval occurred, which makes six of the twelve scenarios below unimplementable. Volume is bounded by agent invocation rate, usually far below model invocation rate. AWS::Bedrock::Guardraildata events, if you run guardrails: a prerequisite for the GuardDuty prompt-injection finding and for intervention-rate monitoring.- Model invocation logging with an S3 destination and every modality your workload uses, scoped to workloads where content retention is legally cleared, with short retention. Text-only selection is the most common quiet failure.
- Network activity events on
bedrock.amazonaws.comandbedrock-agentcore.amazonaws.com, filtered initially toerrorCode = VpceAccessDeniedif cost is a concern. Denied calls are low volume and high signal. - AgentCore logs and spans, with the six preconditions verified individually and sampling set deliberately.
Scope expensive selectors by resource rather than enabling account-wide. Advanced event selectors filter on resource ARN, so you can enable knowledge base data events for the two production knowledge bases holding regulated content and leave the fifty experimental ones off. The cost conversation with a platform team goes very differently when the ask is scoped.
A governance angle makes this easier to fund. EU AI Act Article 12(1) states that "high-risk AI systems shall technically allow for the automatic recording of events (logs) over the lifetime of the system," and Article 12(2) requires logging sufficient to identify risk situations under Article 79(1), facilitate post-market monitoring under Article 72, and support deployer monitoring under Article 26(5). Article 113, as amended by Regulation (EU) 2026/1744 (the Digital Omnibus on AI, published in the Official Journal on 24 July 2026 and in force from 27 July 2026), defers the Chapter III Sections 1, 2 and 3 high-risk requirements to 2 December 2027 for systems classified high-risk under Article 6(2) and Annex III, and to 2 August 2028 for AI that is a safety component of the regulated products covered by Article 6(1) and Annex I. Article 12 sits in Section 2, so the logging obligation quoted above rides on those dates rather than on 2 August 2026. Where systems fall in scope, the logging you need for detection substantially overlaps the logging you need for compliance. This reading is from the EUR-Lex texts of both regulations; take legal advice before relying on it, because scope classification, not logging capability, is the hard part.
The counter-pressure belongs in the same conversation. A full-body invocation log is a durable store of user-supplied content; data protection obligations push against retaining it while Article 12 pushes toward long-lived records. The levers that let you hold both positions: modality selection, body truncation, the OpenTelemetry pattern of uploading content to external storage referenced from the span, field-level redaction, hashing of high-risk fields, and a two-tier design with a short-retention content tier and a long-retention metadata tier.
One consequence teams underestimate: the content tier is itself a high-value target. Raw prompts and completions from a production assistant will contain credentials, personal data and internal source code, indexed and queryable by design. Give it its own access controls, its own retention, and its own detection — read access by a principal that is not an on-call responder in an open case is worth an alert.
Normalising: an OCSF-aligned invocation event
Detection logic written against five vendor-specific log shapes does not survive a platform migration or a second cloud. The normalisation target that exists today is OCSF's ai_operation profile. Use api_activity (6003) for model and tool invocations and datastore_activity (6005) for retrieval and vector operations: both carried the profile in 1.8.0, they remain the natural fit, and they keep you compatible with consumers that have not adopted 1.9.0's wider applicability.
Illustrative — a normalised model invocation event produced by an agent. OCSF ai_operation profile fields as defined on the schema server at 1.9.0 and in main (1.10.0-dev) on 2026-08-06. Validate against the schema version your pipeline targets; attribute names in a -dev branch can change.
{
"metadata": {
"version": "1.9.0",
"profiles": ["cloud", "ai_operation"],
"product": { "name": "Amazon Bedrock", "vendor_name": "AWS" },
"log_name": "bedrock-model-invocation",
"uid": "b0a1f2c3-4d5e-6f70-8192-a3b4c5d6e7f8"
},
"class_uid": 6003,
"category_uid": 6,
"activity_id": 1,
"time": 1786000000000,
"cloud": { "provider": "AWS", "region": "us-east-1", "account": { "uid": "123456789012" } },
"actor": {
"user": { "uid": "AROAEXAMPLEID:agent-session-9f2", "type": "AssumedRole" },
"session": { "uid": "agent-session-9f2", "created_time": 1785999880000 }
},
"src_endpoint": { "ip": "10.24.11.7", "vpc_uid": "vpc-0example" },
"api": {
"operation": "Converse",
"service": { "name": "bedrock-runtime" },
"request": { "uid": "3f2a9c81-0e4b-4a2e-9d55-1c7b8e0a4d21" },
"response": { "code": 200 }
},
"ai_agent": {
"uid": "svc.invoice-triage",
"instance_uid": "run-2026-08-06T09:14:22Z-7c1",
"version": "4.2.1",
"type_id": 99,
"type": "internal-orchestrator",
"ai_model": {
"ai_provider": "AWS",
"name": "anthropic.claude-3-5-sonnet-20241022-v2:0",
"version": "v2:0"
}
},
"delegation": {
"uid": "dlg-8f14c2a9",
"issuer_uid": "https://sts.example.com",
"created_time": 1785999870000
},
"message_context": {
"uid": "conv-4d21a7",
"service": { "name": "bedrock-runtime" },
"ai_role_id": 2,
"prompt_tokens": 3184,
"completion_tokens": 412
},
"unmapped": {
"guardrail_coverage_guarded": 3110,
"guardrail_coverage_total": 3184,
"stop_reason": "end_turn"
}
}
Five deliberate choices there.
Model identity sits inside ai_agent, not at the top level. The profile is explicit: top-level ai_model is for "direct model invocations where no autonomous agent is involved," and "for agent-mediated operations, model identity is carried within ai_agent.ai_model instead." Emitting both is a common normalisation error that double-counts when you aggregate by model.
message_context carries service. The object has an at_least_one constraint on application and service; a message_context with neither is schema-invalid. Its uid is the documented home for the conversation or session identifier.
prompt_text and response_text are absent. They are optional in the schema, and leaving them out of the alerting tier is the position argued earlier. Carry them in a separate content record keyed by api.request.uid.
guardrailCoverage is carried in unmapped. There is no profile attribute for it, and it is what tells you whether the guardrail signal for this request is trustworthy. Dropping it during normalisation is real information loss.
delegation.parent_uid is omitted. Most current systems cannot populate it honestly. If your issuing authority does not mint delegation identifiers, leave the whole object out rather than self-asserting one: a self-asserted chain that looks authoritative is worse than an absent one.
For a tool call, the same class carries message_context.ai_role_id = 3 (Tool), api.operation set to the tool name, and the downstream target in resources. Keeping model and tool calls in one class with a role discriminator makes session reconstruction one scan rather than a union.
Illustrative — field mapping. Source field names verified 2026-08-06; OTel GenAI attributes are Development-status and will churn.
| Source field | OTel GenAI attribute | OCSF path |
|---|---|---|
identity.arn (Bedrock invocation log) | — | actor.user.uid |
modelId | gen_ai.request.model | ai_agent.ai_model.name (agent) or ai_model.name (direct) |
operation | gen_ai.operation.name | api.operation |
requestId | gen_ai.response.id | api.request.uid |
input.inputTokenCount | gen_ai.usage.input_tokens | message_context.prompt_tokens |
output.outputTokenCount | gen_ai.usage.output_tokens | message_context.completion_tokens |
requestMetadata.<your key> | gen_ai.conversation.id | message_context.uid |
| — | gen_ai.provider.name | ai_agent.ai_model.ai_provider |
input.inputBodyJson | gen_ai.input.messages (Opt-In) | message_context.prompt_text (optional) |
Table 4. Bedrock invocation log to OTel GenAI to OCSF field mapping.
The GenAI semantic conventions have moved out of the main semantic-conventions repository into open-telemetry/semantic-conventions-genai; the old page states it has moved and is no longer maintained there. Status is Development, not stable. Only gen_ai.operation.name and gen_ai.provider.name are Required on the inference span. Span types defined are Inference, Embeddings, Retrievals, Fetch response, Memory, and Execute tool. Pin the convention version you map against, currently referencing semantic-conventions v1.44.0, and expect to revisit.
Correlation: manufacturing the join key
This is where most AI detection programmes stall. You have a model invocation record, an agent span, a tool call, a downstream S3 GetObject, and an egress flow, and nothing joins them.
Diagram 4. Correlation key graph. Alt text: a flow diagram distinguishing joins that exist natively, meaning human principal to role session to CloudTrail identity and W3C trace context across instrumented hops, from joins that must be injected by the application, shown as dotted edges: conversation id, agent identifiers, and delegation identifiers.
Solid edges exist for free. Dotted edges are engineering work, and no amount of log configuration substitutes for them. Five joins matter:
Human principal to machine principal. Set a meaningful role session name at the point of assumption, a user identifier or a hashed one, and record the mapping. One line of application code, and the difference between attributable and unattributable agent activity. Where the broker is an OAuth authorization server, the token request's sub and the resource parameter give the same join; ingest those logs.
Invocation to session. On Bedrock, use requestMetadata: it appears in the invocation log record and is the only AWS-native field designed for this. Put a conversation identifier, the agent uid, the agent instance_uid, and the delegation identifier if you have one. Keep the values opaque, because this field lands in a log store.
Session to tool call. Where OTel instrumentation exists, gen_ai.conversation.id is the conditionally-required attribute and W3C trace context propagates across spans for free. Where it does not, the tool implementation must accept and log a correlation identifier, which means changing the tool wrapper rather than the agent.
Tool call to downstream effect. The hardest join and the highest investigative payoff. Three approaches in descending order of reliability: pass the correlation identifier into the downstream call in a field the downstream audit log records (an S3 object tag, a database session variable, a sessionName on a nested role assumption); use a distinct downstream identity per agent so the principal itself is the join; or fall back on time-window correlation, which is a heuristic and should be labelled as one in the case notes.
Anything to network flow. VPC Flow Logs and network activity events carry source IP and endpoint. For ephemeral compute, capture the IP-to-workload mapping at the time of the flow, because the workload is gone later.
A trust caveat that changes how you use these keys. requestMetadata is, in AWS's words, "the only field supplied by the caller," and role session names are caller-supplied too. An adversary who controls the workload controls both: they can omit the correlation identifier or set it to another session's value. Treat injected identifiers as an investigative aid and a data-quality signal, never as attribution evidence in a case where workload compromise is in scope. Platform-populated fields resist this: identity.arn, CloudTrail userIdentity, source IP, and delegation identifiers minted by an issuing authority. Where the two disagree, the platform field wins, and the disagreement is itself worth an alert.
Two protocol identifiers are worth harvesting. MCP's 2026-07-28 revision carries the protocol version in the _meta key io.modelcontextprotocol/protocolVersion and, on Streamable HTTP, in the MCP-Protocol-Version header — a cheap fingerprint for finding MCP traffic at a proxy or WAF, and therefore MCP usage you did not know about. server/discover is a mandatory RPC in that revision returning supported versions, capabilities and identity in one request, so a client enumerating it across many endpoints is a reconnaissance signal mapping to AML.T0084.001.
The join key you need usually does not exist and you have to manufacture it. Budget for that as application work in the programme plan, not as a logging configuration change. A programme that assumes the joins are free produces alerts nobody can investigate.
Threat-informed design with ATLAS 2026.07
MITRE ATLAS is the usable threat model here. The current release is 2026.07, released 2026-07-31, format version 6.0.0, with 16 tactics, 178 techniques and 37 mitigations. One warning first: the legacy dist/ATLAS.yaml in the same repository is version 5.6.0 and carries an in-file deprecation notice. Pipelines pinned there consume stale data and badly under-represent agent techniques, because the agent-focused set is recent. Point ingestion at dist/v6/ and pin the content version, so a monthly release does not silently change your denominator.
| ID | Name | Observable effect to detect |
|---|---|---|
| AML.T0053 | AI Agent Tool Invocation | Tool call records, downstream API calls under agent identity |
| AML.T0086 | Exfiltration via AI Agent Tool Invocation | Outbound tool calls with large or unusual arguments; egress |
| AML.T0098 | AI Agent Tool Credential Harvesting | Secrets and KMS access under agent identity |
| AML.T0110 | AI Agent Tool Poisoning | Change to tool definition, implementation or server endpoint |
| AML.T0080 | AI Agent Context Poisoning | Writes to memory and session stores |
| AML.T0082 | RAG Credential Harvesting | Retrieval queries followed by secret-shaped output or access |
| AML.T0070, AML.T0071 | RAG Poisoning, False RAG Entry Injection | Index upserts, corpus file writes, ragFiles.import |
| AML.T0064 | Gather RAG-Indexed Targets | Retrieval volume anomalies per identity |
| AML.T0034.002 | Agentic Resource Consumption | Token and invocation rate anomalies |
| AML.T0084.001 | Discover AI Agent Configuration: Tool Definitions | server/discover enumeration; tool listing calls |
| AML.T0040 | AI Model Inference API Access | Invocation from an unexpected principal, region or network path |
| AML.T0051 | LLM Prompt Injection | Guardrail intervention rate change (weak, see earlier) |
| AML.T0055 | Unsecured Credentials | Credential use from an unexpected network path |
Table 5. ATLAS 2026.07 techniques mapped to detectable effects. Technique names verified against the 2026.07 content release. The right-hand column is CloudSecOps interpretation, not ATLAS text.
One gap in the threat model affects how you tag rules: ATLAS 2026.07 contains no technique for impairing or disabling AI telemetry. AML.T0031 is "Erode AI Model Integrity," a different thing, and tagging a logging-tamper rule with it is wrong. Tag those rules with ATT&CK T1562.008 (Impair Defenses: Disable or Modify Cloud Logs) and leave the ATLAS field empty rather than reaching for an approximate match, which corrupts coverage reporting in both frameworks.
The alignment anchor on the mitigation side is AML.M0024, AI Telemetry Logging: "Implement logging of inputs and outputs of deployed AI models. When deploying AI agents, implement logging of the intermediate steps of agentic actions and decisions, data access and tool use, installation commands, and identity of the agent." Read it as a specification and check your estate clause by clause. The related mitigations M0026 to M0032 and M0036 — privileged agent permissions, single-user agent permissions, tool permissions, human-in-the-loop, restricting tool invocation on untrusted data, memory hardening, component segmentation, resource limits — are mostly preventive, but each has a corresponding "this control changed or was bypassed" detection, and those are cheap rules with high value.
OWASP's Top 10 for LLM Applications moved to the 2026 edition in early August 2026, days before the verification date: LLM01 Prompt Injection, LLM02 Sensitive Information Disclosure and LLM03 Excessive Agency through to LLM10 Improper Output Handling, with Unbounded Consumption now at LLM06 and a new LLM08 Hidden Context Exposure. There is a separate Agentic Security Initiative. It is a better vocabulary for talking to application teams; ATLAS is the better structure for detection coverage.
Twelve detection scenarios
Each scenario states a hypothesis, log sources, logic, expected false positives, tuning lever, triage question, and — the part that makes this a field guide rather than a rule list — what it cannot see. No false-positive rates appear, because none have been measured.
Two things before you read them as twelve separate builds. Scenarios 1, 4, 5, 6 and 12 are the same shape: a per-identity baseline over a rolling window, alerting on first-seen values or on a rate well above that identity's own history. What differs is the log source, the entity the baseline is keyed on, and the blind spot. Build the shape once as a parameterised job and instantiate it five times; treating them as five hand-written rules multiplies maintenance without adding coverage. Scenarios 2, 3, 7, 9, 10 and 11 are genuinely different logic: change detection, topology assertion, and telemetry health.
Second, the confidence grades in the coverage matrix use this rubric. High: the observable is directly and unambiguously produced by the behaviour, the telemetry is on by default or trivially enabled, and a true positive is confirmable from the alert payload alone. Medium: the observable is produced by the behaviour but shared with common benign activity, or the telemetry requires enablement, or confirmation needs a second source. Low: the observable is a proxy rather than the behaviour, or the telemetry is sampled, partial or absent for a significant share of traffic.
1. Unexpected model invocation
Hypothesis. A principal invokes a model it has never invoked, or one your organisation has not approved, or from a Region you do not operate in. Maps to AML.T0040.
Sources. CloudTrail management events on bedrock.amazonaws.com for InvokeModel and Converse (on by default); Google Cloud endpoints.predict in Data Access logs (must be enabled); Azure OpenAI Request Usage (needs a diagnostic setting).
Logic. Baseline (modelId, region) pairs per principal over 30 days; alert on a first-seen pair for a principal with at least 14 days of history. Route principals with insufficient history to a low-priority queue rather than discarding them.
False positives and tuning. Model upgrades change modelId, cross-Region inference profiles route legitimately outside your deployment Regions, and development accounts have no stable baseline. Normalise modelId to a family before baselining, and allowlist inference profile ARNs rather than Regions.
Triage question. Did an approved deployment change the model, and is there a change record in the preceding 24 hours?
What it cannot see. Direct calls to OpenAI or Anthropic produce no per-request record at all. This covers managed platforms and is blind to the direct-API path, which for many organisations is the larger volume.
2. Compromised machine identity
Hypothesis. Credentials belonging to an AI workload are being used by someone else. Maps to AML.T0055.
Sources. CloudTrail userIdentity with sourceIPAddress and userAgent; GuardDuty anomalous-behaviour findings; Entra ApplicationManagement audit activities; OpenAI api_key.* and service_account.* events; Anthropic Compliance API activities with actor IP and user agent.
Logic. Per service identity, baseline source ASNs, user agents and calling patterns. Alert on a new ASN, or on an interactive user agent for an identity only ever used by an SDK. Combine with network activity events: an agent execution role appearing outside its expected VPC endpoint is a strong signal.
False positives and tuning. Pipeline changes, new CI runners and SDK upgrades that change the user agent are noise. A developer debugging with a service credential is not a false positive, it is a finding: split it into a separate lower-severity detection rather than suppressing it.
Triage question. Does the source network path correspond to a workload that is supposed to hold this credential?
What it cannot see. A stolen credential used from inside the legitimate workload's own network path, such as a compromised container using its instance role, is indistinguishable from normal use at the identity layer. That case needs workload-level evidence.
3. Excessive or widening agent permissions
Hypothesis. An agent's authority is broader than its tool set requires, or is being widened. Inverse of AML.M0026 to M0028.
Sources. IAM and role policy change events; OpenAI Model Permissions and Hosted Tool Permissions changes; AgentCore credential provider changes; MCP authorization server logs.
Logic. Two rules. The posture rule compares each agent role's permitted actions against actions observed over 30 days and reports the delta. The change rule alerts on any new permission granted to an agent identity, or on enabling a hosted tool that expands blast radius, of which the MCP hosted tool on an OpenAI project is the clearest example. Where you run an authorization server, add the MCP-specific version: the 2026-07-28 specification defines step-up authorization as a 403 with WWW-Authenticate: Bearer error="insufficient_scope", scope="…", so a sequence of such challenges followed by re-authorization with a widened scope union is a scope-escalation trace visible without any application change.
False positives and tuning. Feature launches widen scope legitimately and permission reviews produce bulk changes. Join to change management and downgrade approved changes rather than suppressing them, because "approved change that granted an agent write access to a production bucket" is exactly what you want to read.
Triage question. Which tool in the agent's catalogue requires this permission, and when was that tool last used?
What it cannot see. Permission breadth is a posture signal. It says the agent could do something, never that it did.
4. Suspicious tool execution
Hypothesis. An agent invoked a tool it does not normally invoke, in an unusual order, or with arguments outside its normal shape. Maps to AML.T0053, and AML.T0086 where the tool has an outbound path.
Sources. AgentCore Tools and Gateway logs with OTel spans; Azure Foundry execute_tool spans; downstream service audit events under the agent identity.
Logic. Baseline per-agent tool call sequences. Alert on a tool never seen for this agent uid, on a within-session call rate an order of magnitude above the agent's baseline, or on a tool call following retrieval from a corpus that accepts external content. That last is the closest observable proxy for indirect prompt injection, and it should be phrased as a correlation, not as a claim about the prompt.
False positives and tuning. New agent versions add tools, retries produce duplicate spans, and evaluation harnesses exercise the full catalogue by design. Key the baseline on ai_agent.uid plus ai_agent.version, so a version bump starts a new baseline rather than firing on every new tool.
Triage question. Which session and which retrieval preceded this tool call, and did that retrieval touch externally-sourced content?
What it cannot see. Tool call arguments are opt-in on every platform surveyed, so you know a tool ran, not what it did. A tool wrapping an ordinary cloud API is invisible in the agent layer without the span: you see the downstream call with no idea an agent caused it.
5. Prompt-driven data access
Hypothesis. Model or agent activity caused access to data the workload does not normally touch.
Sources. Downstream resource logs — S3 data events, database audit logs, SaaS audit APIs — joined to the model or session identifier. Deliberately not the prompt.
Logic. Per agent identity, baseline the set of downstream resources accessed. Alert on first-seen access to a resource classified sensitive, or on object-read volume well above the session baseline. The join is the correlation identifier from the previous section; without it the rule degrades to "this identity read something new," which is still worth having.
False positives and tuning. New data sources onboarded to a knowledge base, backfills and reindexing jobs. Classify resources first: first-seen access to an unclassified bucket is low priority, the same access to a bucket tagged regulated is not.
Triage question. Which invocation and session preceded this access, and does the content match the agent's stated purpose?
What it cannot see. Without an injected correlation identifier you can attribute the access to the identity, not to a specific invocation. Time-window correlation is a heuristic; label it as one so no one treats it as proof.
6. Retrieval of sensitive documents
Hypothesis. Someone is using a retrieval system to enumerate or extract the corpus. Maps to AML.T0064 and AML.T0082.
Sources. Retrieve and RetrieveAndGenerate on AWS::Bedrock::KnowledgeBase (data events); Google Cloud indexEndpoints.findNeighbors, ragFiles.get, ragFiles.list, memories.retrieve (Data Access); AWS::S3Vectors::VectorBucket and ::Index data events; OpenAI /organization/usage/file_search_calls and /vector_stores.
Logic. Retrieval-rate anomaly per identity per knowledge base, plus a distinct-query-count rule that catches systematic enumeration where total volume stays modest. Pair with ragFiles.list: a listing call from an identity that normally only retrieves is an enumeration signal.
False positives and tuning. Evaluation runs, relevance-tuning experiments and newly launched features generate exactly this pattern. Require evaluation harnesses to run under a distinct identity: one platform requirement that removes an entire false-positive class, and the highest-value tuning intervention in this list. It also creates a bypass, since anyone who can run under the evaluation identity sits outside these baselines, so treat that identity as privileged, restrict who can assume it, and monitor its assumption separately.
Triage question. Was there a corresponding model invocation for each retrieval, or is retrieval happening without inference?
What it cannot see. Retrieval logs record that a query happened, not which documents came back. You can answer "how much did they ask for," never "what did they get." For the S3 Vectors resource types the documentation on the verification date did not enumerate covered operations, so whether a similarity query is logged distinctly from a vector write is a test to run in your own account.
7. Cross-account and cross-project activity
Hypothesis. AI resources are being invoked from, or reaching into, an account or project outside the intended boundary.
Sources. CloudTrail recipientAccountId versus userIdentity.accountId; cross-account role assumption chains; Google Cloud cross-project service account use; Bedrock cross-Region inference profile ARNs.
Logic. Alert where the calling account differs from the resource-owning account and the pair is not approved. Extend to resource policies: a change to a Bedrock resource policy or knowledge base policy that adds an external principal is a high-value, low-volume rule.
False positives and tuning. Centralised AI platform accounts are designed to be called cross-account, so in that architecture the rule inverts and you alert on calls that are not cross-account from an approved account. Encode the intended topology explicitly rather than baselining it, because baselines learn whatever misconfiguration existed when you turned them on.
Triage question. Is the calling account inside the same trust boundary, and does an approved integration exist?
What it cannot see. In a single-account estate this rule has nothing to compare and should not be built. It also misses cross-boundary access through a shared identity rather than a cross-account call, which is the common shape in smaller estates.
8. Abnormal token or cost consumption
Hypothesis. An identity is consuming inference capacity far outside its pattern, through abuse, a runaway loop, or coerced expensive tool calls. Maps to AML.T0034.002.
Sources. Bedrock invocation log token counts; GuardDuty Impact:IAMUser/CostHarvesting (Low severity, baselining average input and output token volume per identity and account); OpenAI /organization/usage/* and /organization/costs; Anthropic /v1/organizations/usage_report/messages at 1m.
Logic. Per-identity token-rate anomaly over a short window, plus a cumulative-spend rule against a budget. Use the 1m bucket where available: minute versus day granularity is the difference between a contained incident and a bill.
False positives and tuning. Batch jobs, load tests, migrations that double traffic during cutover, and product launches. Require batch and evaluation workloads to run under identities tagged as such, exclude them from the short-window rule, and keep them in the budget rule.
Triage question. Is consumption concentrated in one session or spread across many, and does the output token count match the input pattern?
What it cannot see. Aggregation windows delay detection, and a slow-burn cost attack inside your normal growth curve will not trip a rate rule. The budget rule catches it eventually, at the cost of dwell time. Say that when you present the coverage.
9. Telemetry disablement and silent degradation
Hypothesis. Someone disabled AI logging, or it degraded on its own. Tagged ATT&CK T1562.008; ATLAS has no corresponding technique.
Sources. CloudTrail StopLogging, UpdateTrail, DeleteTrail, PutEventSelectors; DeleteModelInvocationLoggingConfiguration and PutModelInvocationLoggingConfiguration; GuardDuty DefenseEvasion:IAMUser/BedrockLoggingDisabled (Medium severity, from CloudTrail management events, not part of AI Protection); Azure diagnostic setting deletion; Google Cloud audit config changes. Plus volume and field-presence detections.
Logic. The configuration-change rule is straightforward and most mature teams have the CloudTrail version already. The second rule matters more: alert when event volume for each AI source drops below a floor derived from its own trailing baseline, and when the population rate of a required field drops. A modality change on invocation logging, a body-cap effect on large payloads, or an X-Ray sampling reduction produces no configuration event you can alert on in the stream you are watching, and in the sampling case none at all in the data plane. Only volume and field-presence detections catch those.
False positives and tuning. Quiet periods, deployment freezes and holiday weekends produce genuine drops. Use a day-of-week-aware baseline and require the drop to persist across two consecutive windows.
Triage question. Did the volume drop coincide with a deployment, a configuration change, or nothing at all? "Nothing at all" is the interesting answer.
What it cannot see. If a source was never enabled there is no baseline to drop from. Telemetry-health detections monitor what you have, not what you are missing; that is what the coverage matrix is for.
10. Model or agent use outside approved network paths
Hypothesis. Model or agent APIs are being reached from a network path that is not the approved one.
Sources. CloudTrail network activity events on bedrock.amazonaws.com and bedrock-agentcore.amazonaws.com, particularly errorCode = VpceAccessDenied; egress proxy logs to provider domains; the MCP-Protocol-Version header at a proxy.
Logic. Alert on any VpceAccessDenied for AI event sources, since denied calls are low volume and disproportionately interesting. Separately, alert on egress proxy connections to model provider domains from workloads with no approved AI integration, which is how you find shadow AI usage; the MCP header gives the same discovery for tool servers.
False positives and tuning. Misconfigured new deployments generate VpceAccessDenied in bulk during rollout. Group by principal and alert on the principal rather than the event, so a broken deployment produces one alert rather than ten thousand.
Triage question. Does an approved integration exist for this workload and this provider?
What it cannot see. Network activity events record traffic through your VPC endpoints. A credential used from outside your VPCs entirely, reaching the public service endpoint, generates no such event, so this detection is strongest at proving misuse inside your network and weakest against the case you most fear. Traffic that never leaves the host has no artefact either: stdio MCP transports, in-process tools, and agents on developer endpoints are invisible to this whole class.
11. Secret exposure through AI workflows
Hypothesis. Credentials are being read, emitted or created through an AI workflow. Maps to AML.T0098 and AML.T0083.
Sources. Secrets Manager and KMS data events joined to agent identity; guardrail sensitiveInformationPolicy assessments; source-control secret scanning; OpenAI api_key.created attributed to a non-human principal.
Logic. Alert on secret retrieval by an agent identity with no approved secret dependency, and on a spike in sensitiveInformationPolicy assessments for one agent. The api_key.created rule is small and worth having: a key created by a service account rather than a human is either automation you know about or something to look at.
False positives and tuning. Credential rotation, and legitimate secret access at startup producing a burst at deployment time. Baseline per agent version and exclude the first five minutes after a deployment event.
Triage question. Which secret, and does the agent's tool catalogue require it?
What it cannot see. Secrets read from an agent's own configuration file or environment variables, the case AML.T0083 describes, leave no distinct event. This covers secret services, not secret material sitting in a config.
12. Abuse of ephemeral execution
Hypothesis. An agent is being used to run code in ephemeral compute for purposes outside its task.
Sources. Lambda Invoke data events; Google Cloud sandboxEnvironments.execute (DATA_WRITE); OpenAI /organization/usage/code_interpreter_sessions; short-lived role or task creation followed immediately by use.
Logic. Alert on code interpreter or sandbox execution by identities with no history of it, and on execution-rate anomalies. Join to egress where you have it: sandbox execution followed by outbound connections to a new destination is the pattern worth paging on.
False positives and tuning. Data analysis features use code execution heavily and unevenly. Separate identities per feature; shared identities make every behavioural baseline worse.
Triage question. What did the sandbox connect to, and did anything leave it?
What it cannot see. The compute is gone before you triage. Only control-plane records and network residue survive, so without the execution's own logs captured at the time the investigation ends at "something ran."
Detection as code, and the portability problem
Version your detections, review them, test them in CI, deploy them from a pipeline. That is settled practice. The format is not, and for AI telemetry there is a specific gap.
The Sigma rules specification is at version 2.1.0, released 2025-08-02, as is the correlation rules specification. The taxonomy appendix, which defines what SigmaHQ accepts in shared rules, lists for cloud only product: aws with service: cloudtrail; product: azure with service in activitylogs, auditlogs, riskdetection, pim or signinlogs; and product: gcp with service in gcp.audit or google_workspace.admin.
There is no bedrock, openai, aiplatform, genai or agent log source in the Sigma taxonomy. Any rule against Bedrock invocation logs, AgentCore spans, or an OpenAI audit feed is either a CloudTrail rule, which is portable and upstreamable, or a rule with a non-standard log source that will be neither.
The workable response is not to wait. Define a documented custom log source namespace, keep the mapping layer that translates it to each backend in the same repository as the rules, and treat the mapping as reviewable code. When the taxonomy adds AI sources you rename in one place. What you should not do is write AI rules directly in your SIEM's native query language and skip the abstraction, because that is the decision you cannot reverse cheaply.
Illustrative — Sigma 2.1.0 rule targeting AI logging tamper on AWS. Uses a taxonomy-supported log source, so it is portable. The |exists modifier is defined in the 2.1.0 specification, but backend support varies; confirm your pySigma backend emits a field-existence predicate, and test against your own CloudTrail field naming. Not executed.
title: AI Telemetry Configuration Disabled or Narrowed on AWS
id: 8f3c1a20-6b4e-4c17-9d2a-5e7f0b1c4d93
status: experimental
description: >
Detects disabling or narrowing of the logging that AI detections depend on:
Bedrock model invocation logging, CloudTrail trails, and event selectors that
carry Bedrock and SageMaker data events. Put is included alongside Delete
because a Put can narrow modality coverage or redirect the destination while
looking like an enable.
references:
- https://docs.aws.amazon.com/bedrock/latest/userguide/model-invocation-logging.html
- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/logging-data-events-with-cloudtrail.html
author: CloudSecOps
date: 2026-08-06
tags:
- attack.defense-evasion
- attack.t1562.008
logsource:
product: aws
service: cloudtrail
detection:
selection_bedrock:
eventSource: bedrock.amazonaws.com
eventName:
- DeleteModelInvocationLoggingConfiguration
- PutModelInvocationLoggingConfiguration
selection_trail:
eventSource: cloudtrail.amazonaws.com
eventName:
- StopLogging
- DeleteTrail
- UpdateTrail
- PutEventSelectors
filter_failed_calls:
errorCode|exists: true
condition: (selection_bedrock or selection_trail) and not filter_failed_calls
falsepositives:
- Infrastructure-as-code deployments that reapply trail and logging configuration
- Planned migration of log destinations between accounts or Regions
level: high
Three notes. filter_failed_calls excludes API calls that failed, on the basis that CloudTrail populates errorCode only on failure; the Sigma specification warns that handling of empty and null values "depends on the target SIEM system," so verify the translation rather than assuming it. Because a Put can degrade logging while looking like an enable, triage must diff the configuration before and after, which the event alone does not give you. And no ATLAS tag appears, for the reason given earlier. The companion rule you actually want, matching on Bedrock invocation-log content, cannot use a standard log source at all — the portability gap in one concrete example.
Illustrative — CloudTrail Lake query, Trino SQL dialect (CloudTrail Lake supports Trino SELECT statements). Validate table naming against your own event data store; the event-data-store-ID-as-table-name convention was not confirmed in the documentation reviewed. Not executed.
-- Retrieval burst followed by downstream object access by the same principal.
-- Requires AWS::Bedrock::KnowledgeBase and AWS::S3::Object data events enabled.
WITH retrieval AS (
SELECT
useridentity.arn AS principal,
date_trunc('minute', eventtime) AS bucket_min,
count(*) AS retrieve_count
FROM your_event_data_store_id
WHERE eventsource = 'bedrock.amazonaws.com'
AND eventname IN ('Retrieve', 'RetrieveAndGenerate')
AND eventtime > current_timestamp - interval '24' hour
GROUP BY 1, 2
HAVING count(*) > 50
),
downstream AS (
SELECT
useridentity.arn AS principal,
eventtime,
eventname,
element_at(resources, 1).arn AS target
FROM your_event_data_store_id
WHERE eventsource IN ('s3.amazonaws.com', 'lambda.amazonaws.com')
AND eventname IN ('GetObject', 'Invoke')
AND eventtime > current_timestamp - interval '24' hour
)
SELECT
r.principal,
r.bucket_min,
r.retrieve_count,
count(d.eventtime) AS downstream_calls,
array_agg(DISTINCT d.target) AS targets
FROM retrieval r
JOIN downstream d
ON d.principal = r.principal
AND d.eventtime BETWEEN r.bucket_min AND r.bucket_min + interval '5' minute
GROUP BY 1, 2, 3
ORDER BY downstream_calls DESC;
The 50-retrieval threshold and five-minute window are placeholders; derive both from your own traffic, because a threshold copied from an article is a threshold nobody validated against your data. The query also correlates on principal and time alone, the weak form of the join described earlier, so its output is a lead rather than a finding.
Illustrative — Kusto Query Language fragment for Azure Monitor Application Insights, targeting Foundry Agent Service tool spans. Attribute names follow the OpenTelemetry GenAI conventions, which are Development status and subject to change. Not executed.
dependencies
| where name in ("execute_tool", "invoke_agent")
| extend tool = tostring(customDimensions["gen_ai.tool.name"]),
agent = tostring(customDimensions["gen_ai.agent.name"]),
conv = tostring(customDimensions["gen_ai.conversation.id"])
| summarize calls = count(), tools = make_set(tool) by agent, conv, bin(timestamp, 5m)
| where calls > 100 or array_length(tools) > 8
Validation, testing and regression
One change to the lifecycle you already run matters more than it sounds: the telemetry check becomes a blocking gate before logic is written. In conventional cloud detection you can usually assume CloudTrail is there. For AI workloads that assumption is wrong often enough that writing logic first wastes the effort. The gate asks three questions, all answered from your own environment rather than from documentation: does the event exist, are the fields populated on real production traffic rather than on your test call, and is the record complete once truncation, sampling and modality filtering are accounted for. The third is where AI telemetry differs most, because the failure mode is a partial record that looks whole.
Illustrative — detection development gate. Adapt the evidence requirements to your platform.
GATE 1 Telemetry existence
[ ] Activity generated in a controlled account, record located, event id captured
[ ] Log source, destination store and retention documented in the rule metadata
[ ] Cost class recorded (included / per-event / storage) and owner informed
GATE 2 Field presence
[ ] Every field referenced by the logic sampled over 7 days of production traffic
[ ] Population rate recorded per field; any field below 95% flagged in the rule
[ ] Truncation, sampling percentage and modality coverage checked explicitly
GATE 3 Logic and adversary model
[ ] ATLAS technique named, or explicitly recorded as having no ATLAS mapping
[ ] Evasion hypothesis written: how would an informed adversary avoid this?
[ ] "What it cannot see" paragraph written before the rule is merged
GATE 4 Validation
[ ] Positive test case: synthetic activity that must fire
[ ] Negative test case: benign activity that must not fire
[ ] Degradation test: rule alerts when its own source stops producing events
GATE 5 Operations
[ ] Triage question stated in one sentence, answerable from the alert payload
[ ] Suppressions expressed as code with an expiry date and an owner
[ ] Coverage matrix entry created with a confidence grade and a review date
The degradation test is the one teams skip and the one that pays. A detection whose data source silently stops is worse than no detection, because it reports the absence of attacks.
The tests below catch AI-specific failure modes. Run them against a controlled workload on a schedule, not once at build time.
| Test id | Condition asserted | Failure meaning |
|---|---|---|
| TC-01 | A synthetic Converse call appears in the model invocation log within 15 minutes | Logging disabled, wrong Region, wrong endpoint, or destination misconfigured |
| TC-02 | input.inputBodyJson is complete for a payload under 100 KB | Truncation or serialisation fault upstream of the log |
| TC-03 | A payload over 100 KB produces an S3 reference, not a dropped body | No S3 destination configured; large payloads are being lost |
| TC-04 | An image sent through Converse produces an Image-modality record | Modality selection excludes a modality in production use |
| TC-05 | guardrailCoverage.guarded equals .total for a fully covered request | Guardrail evaluated part of the text; guardrail detections partially blind |
| TC-06 | An agent span carries a non-null instance_uid | Instrumentation incomplete; per-run investigation impossible |
| TC-07 | Every tool-call event carries the correlation identifier | Correlation injection broken; downstream joins will fail |
| TC-08 | Token counts are non-zero on a known-non-empty request | Field population regression |
| TC-09 | Per-source event volume within the trailing 14-day band | Silent degradation, sampling change, or a genuine traffic change |
| TC-10 | Span count matches expected sampling percentage within tolerance | X-Ray indexing sampling changed; threshold rules now under-count |
Table 6. Validation test cases for AI telemetry. Each should fail loudly and route to the detection engineering team, not the SOC queue.
| Signal | Threshold shape | What it catches that a config alert misses |
|---|---|---|
| Per-source event volume | Day-of-week-aware floor over 2 consecutive windows | Sampling changes, upstream failures, modality filtering |
| Required-field population rate | Percentage below a per-field SLO | Schema drift, SDK upgrades that drop attributes |
| Log group inventory | New or missing groups matching AI patterns | New agent resources deployed without observability |
| Guardrail coverage ratio | Median guarded/total below 1.0 | Partial guardrail evaluation, invisible in config |
| Correlation key presence | Percentage of tool events with a session id | Application changes that break the join silently |
Table 7. Telemetry-health detections that run continuously, as opposed to the scheduled tests. CloudSecOps recommendations; thresholds must be derived per environment.
Tuning: the false positives specific to AI telemetry
Six generators produce most of the noise, and they differ from conventional cloud noise.
| Generator | Why it fires | What to do |
|---|---|---|
| Evaluation harnesses and load tests | Exercise the full tool catalogue and retrieval corpus by design | Require distinct identities; treat those identities as privileged and monitor their assumption |
| Model migrations | Change modelId, token ratios and latency at once, resetting every model-keyed baseline | Put a baseline-reset step in the migration runbook |
| Retries and streaming | Produce duplicate spans and several records per logical invocation | Deduplicate on requestId before counting anything |
| Autoscaling agents | instance_uid cardinality is unbounded | Baseline on ai_agent.uid, never on instance_uid |
| Batch and asynchronous invocation | Arrives in bursts that look like rate anomalies | Treat StartAsyncInvoke and relatives as a separate population |
| Cross-Region inference profiles | Route legitimately to Regions you did not deploy in | Allowlist profile ARNs, not Regions |
Table 8. False-positive generators specific to AI telemetry, with the tuning response. CloudSecOps analysis.
Two principles. Express suppressions as code with an owner and an expiry date, so a suppression added during an incident does not become permanent by inattention. And be sceptical of allowlists keyed on model identifiers: they age badly, because model versions turn over faster than anyone revisits a rule. Key on model family or on the deployment that owns the model, and let the version float.
Coverage measurement that does not flatter itself
"We detect 14 of 178 ATLAS techniques" is close to meaningless. It counts rules, not evidence, and it rewards writing rules against telemetry you do not have. Measure on two axes instead: for each scenario record the technique and the availability of the telemetry it depends on, then grade the combination against the rubric stated earlier.
| Scenario | Primary telemetry | Availability | Confidence | Known gap |
|---|---|---|---|---|
| 1 Unexpected model invocation | CloudTrail mgmt events | On by default | Medium–High on managed platforms | Blind to direct-API inference |
| 2 Compromised machine identity | CloudTrail identity + network events | Partial | Medium | Same-path credential theft |
| 3 Excessive agent permissions | IAM changes, authorization server scope logs | Partial | Medium | Posture only, not use |
| 4 Suspicious tool execution | Agent spans | Off by default, sampled | Medium–High with spans, Low without | Tool arguments opt-in |
| 5 Prompt-driven data access | Downstream resource logs | On or cheap | Medium | Attribution needs injected key |
| 6 Sensitive retrieval | KB and vector data events | Off by default, billable | Medium | Does not record documents returned |
| 7 Cross-account activity | CloudTrail | On by default | Medium | No signal in a single-account estate |
| 8 Token and cost anomaly | Usage APIs, invocation logs | Available, aggregated | Medium | Latency floor from bucketing |
| 9 Telemetry degradation | Config events plus volume rules | Buildable now | High | No baseline for never-enabled sources |
| 10 Use outside approved paths | Network activity events | Off by default, billable | Medium | Blind to calls that never touch your VPC endpoints |
| 11 Secret exposure | Secrets/KMS data events | Partial | Low–Medium | Config-file secrets leave no event |
| 12 Ephemeral execution abuse | Lambda/sandbox data events | Off by default | Low–Medium | Compute gone before triage |
Table 9. Detection coverage matrix. Confidence grades are CloudSecOps judgment against the rubric above, based on telemetry availability and the specificity of the observable. No false-positive or true-positive rates have been measured, and no grade here is an efficacy estimate.
Report the availability column to leadership alongside the coverage column. "We address nine of twelve priority scenarios, six of which depend on telemetry that is currently off" is a sentence that gets budget. "We have 75% coverage" is a sentence that gets nothing, and deserves to.
Investigation workflow
Diagram 5. Investigation pivots from an AI alert. Alt text: a decision tree beginning at an alert, resolving identity, attempting delegation reconstruction, then pulling session, tool calls, retrievals, downstream calls, egress and consumption, ending in three terminal states: contain, escalate as unresolvable with the gap recorded, or tune.
The terminal state that matters most is the middle one. "We could not determine what this agent did because the tool spans were sampled at 5%" is a legitimate investigation outcome, and recording it as a telemetry defect with a ticket is how the telemetry gets fixed. Closing it as a false positive is how the gap becomes permanent.
Evidence to collect, in order: the alert's own record with its raw source; IAM or IdP records for the principal within the session window; all invocation records sharing the correlation identifier; tool-call spans with arguments if available; retrieval records with corpus identifiers; downstream audit events for the identity in the window; flow logs and proxy records for the source addresses; and the usage report for the identity across the window. Capture the source-IP-to-workload mapping while the workload still exists. Where workload compromise is in scope, treat caller-supplied fields as untrusted and reconstruct from platform-populated ones.
Response constraints
Containment in an agent environment fails in ways that surprise teams used to conventional cloud response.
| Action | What it stops | What it does not stop | Side effect |
|---|---|---|---|
| Terminate the agent session | Further turns in that session | Tokens already issued to tools; in-flight downstream calls | Loss of session state and evidence if not captured first |
| Disable the machine identity | New authentications | Existing short-lived credentials until expiry | Production outage if the identity is shared |
| Revoke the OAuth token | That token's use at the bound resource | Other tokens in the delegation chain | Agent may silently re-authorize if the grant persists |
| Tighten the guardrail | Matching content across all traffic | Any effect already produced; non-text modalities | Global blast radius; affects every consumer of that guardrail |
| Purge memory or session store | Reuse of poisoned context | Content already copied into other stores or outputs | Destroys evidence unless snapshotted first |
| Block egress destination | That destination | Alternative destinations reachable by the same tool | Breaks legitimate integrations sharing the path |
| Disable the tool | Future invocations of that tool | Equivalent capability via another tool | Agent may route around it with a different tool |
Table 10. Response constraint matrix. CloudSecOps analysis; verify credential-expiry and token-revocation behaviour for your own identity providers, which vary.
Three constraints are counter-intuitive enough to repeat. Killing a session does not revoke credentials that session already handed to a tool, so the exfiltration path can survive the containment action. Purging a memory store destroys the evidence of what was in it, so snapshot first. And guardrail changes are global to every consumer of that guardrail, which makes tightening one a change-management event rather than a unilateral incident action.
Blind spots you cannot engineer away
- No per-request logs for direct-API inference. Identity events and aggregated counters, not a per-request record with a source IP. Structural.
- Provider-side content stores you cannot query. Azure OpenAI's abuse monitoring store is a Microsoft control, not your evidence.
- Sampled spans. X-Ray indexing sampling makes agent span data a fraction of reality, and the fraction is invisible in query results.
- Publisher-model call paths that may not be enumerated. Google Cloud's audited-operations table did not list
generateContenton 2026-08-06. Test in your own project. - Endpoint-scoped logging features. Bedrock invocation logging covers only
bedrock-runtime; a workload migration can remove body logging with no configuration change. - Agents outside your cloud. Coding agents and desktop assistants on endpoints produce no cloud telemetry; your evidence is endpoint logs, source control and CI.
- stdio MCP transports. No network artefact and no HTTP header to fingerprint.
- Body caps and modality filters. A 100 KB cap and a Text-only selection both produce records that look complete.
- Tool arguments. Opt-in everywhere surveyed, so "which tool ran" is available and "what it was asked to do" generally is not.
- Documents returned by retrieval. Logs record the query event, not the result set.
- Self-hosted inference. No vendor audit log exists at all.
- Caller-supplied correlation fields.
requestMetadataand role session names are untrustworthy under workload compromise.
If you are a small team
Most of this guide assumes an organisation with a platform team, an identity team, and someone who can change application code. Several recommendations do not survive contact with a team of two or three.
What still applies without headcount: the telemetry map, a day of work that pays immediately; the five telemetry-health detections, which never become obsolete; the default-on sources (CloudTrail management events, provider admin and usage APIs, IdP events); and scenarios 1, 2, 8 and 9, which need no application change and no billable data events. That is a defensible AI detection programme.
What to defer, and to say you are deferring: OCSF normalisation, which pays off at the second platform and costs at the first; authorization server log ingestion, which assumes you run one; per-feature identity separation, which is a platform standard rather than a detection task; and agent span collection with its six preconditions. Recording those in the coverage matrix as "not built, telemetry not collected" is more useful to your future self than a rule that was never tested. In a single-account estate, drop scenario 7 rather than building a rule with nothing to compare, and put the effort into scenario 5.
Minimum viable telemetry architecture
Diagram 6. Phased telemetry build. Alt text: three phases feeding a normalisation layer that outputs to detections and case management, with a separate short-retention content tier. Phase one is default-on sources and provider APIs, phase two is scoped data events and correlation injection, phase three is spans, network events and authorization server logs.
Phase 1, weeks one and two, free or near-free. Ingest what is already on: CloudTrail management events including the Bedrock runtime operations, IdP and IAM change events, and the provider admin and usage APIs. Build scenarios 1, 2, 7, 8 and 9, and the telemetry-health rules before anything else, because everything after depends on knowing when a source degrades. Cost: engineering time and API polling.
Phase 2, month one, costed and scoped. Items 1 to 3 of the enablement order above, scoped by resource ARN to production, plus the correlation identifier injected through requestMetadata — the application change that scenarios 4, 5 and 6 depend on. Cost: per-event charges bounded by scoping, plus one application change per workload.
Phase 3, quarter one, the expensive tail. Items 4 and 5, plus authorization server logs, where the delegation and scope-escalation evidence lives. Normalise into the OCSF shape so the rules survive your next platform decision.
If you do only one thing from this guide, do the telemetry-health rules in phase 1. They are cheap, they never become obsolete, and they are the only thing standing between you and a coverage report describing detections that stopped working months ago.
Minimum viable action list
- Draw your own telemetry map using Table 2 as the template, and mark every off-by-default source.
- Generate one deliberate activity per AI log source and confirm the record arrives.
- Check whether Bedrock model invocation logging is enabled, and check its modality selection specifically.
- Confirm an S3 destination exists for payloads over the 100 KB inline cap.
- Confirm your workloads call the
bedrock-runtimeendpoint, since invocation logging does not cover others. - Enable Bedrock agent alias and knowledge base data events, scoped by resource ARN.
- Enable guardrail data events if you run guardrails, alert on the
guardrailCoverageratio, and check whether guardrails are enforced organisation-wide or attached per request. - Test empirically whether
generateContentproduces a Data Access audit entry in your Google Cloud project. - Create diagnostic settings on every Azure Foundry and Azure OpenAI account.
- Grant your detection pipeline's service account
roles/logging.privateLogViewerand confirm responders can readdata_access. - Inject a correlation identifier into
requestMetadataon every Bedrock invocation, and document that it is caller-supplied. - Set meaningful role session names wherever an application assumes a role for AI work.
- Move evaluation harnesses and load tests to distinct identities, and restrict who can assume them.
- Set X-Ray indexing sampling deliberately for security-relevant spans, and document the value your thresholds assume.
- Build the five telemetry-health detections in Table 7 before writing any new content-based rule.
- Pin ATLAS ingestion to the versioned
dist/v6/path, not the deprecateddist/ATLAS.yaml. - Give the prompt-content tier its own access controls and alert on reads outside an open case.
- Record telemetry availability alongside technique coverage in your coverage report.
References
AWS
- Amazon Bedrock model invocation logging
- Logging Amazon Bedrock API calls using AWS CloudTrail
- Using guardrails with the Converse API
- Logging data events with AWS CloudTrail
- Logging network activity events with AWS CloudTrail
- Working with AWS CloudTrail Lake
- Logging Amazon SageMaker AI API calls with AWS CloudTrail
- Amazon GuardDuty AI Protection finding types
- Amazon GuardDuty active finding types
- Amazon GuardDuty foundational data sources
- Amazon Bedrock AgentCore observability
- Configuring AgentCore observability
- AgentCore service-provided observability by resource type
- Amazon Bedrock AgentCore Identity
- Amazon S3 Vectors
Microsoft Azure
- Monitoring data reference for Azure OpenAI
- Monitor Azure OpenAI
- Data, privacy and security for Azure OpenAI
- Tracing in Azure AI Foundry Agent Service
- Microsoft Entra ID audit logs
- Microsoft Entra audit activity reference
Google Cloud
- Google Cloud services with audit logs
- Cloud Audit Logs overview
- Audit logging for the Gemini Enterprise Agent Platform
- Enable audit logs for model endpoint usage
Model providers
- OpenAI Audit Logs API reference
- OpenAI Usage API reference
- OpenAI Admin API reference
- Anthropic Admin API
- Anthropic Compliance API
- Anthropic Usage and Cost API
- Claude Code Analytics API
Standards and schemas
- OCSF schema server version index
- OCSF
ai_operationprofile definition - OCSF
delegationobject definition - OCSF
ai_agentobject definition - OCSF
message_contextobject definition - OpenTelemetry GenAI semantic conventions notice of move
- OpenTelemetry GenAI spans specification
- MCP specification versioning
- MCP 2026-07-28 authorization specification
- MCP 2025-06-18 specification
- Sigma rules specification
- Sigma taxonomy appendix
- SigmaHQ CloudTrail logging-disabled rule
Threat models and regulation
- MITRE ATLAS release manifest
- MITRE ATLAS 2026.07 content
- OWASP GenAI LLM Top 10 2026
- Regulation (EU) 2024/1689 (AI Act) on EUR-Lex, Articles 12 and 113
- Regulation (EU) 2026/1744 (Digital Omnibus on AI) on EUR-Lex, amending Article 113
Validity and revision
Verification date: 2026-08-06. Every factual claim above was checked against the primary source listed in the references on that date. Where a source could not be retrieved or a behaviour could not be confirmed, the text says so in place. No code, query or rule in this guide has been executed.
Items that were not verifiable and should be tested rather than trusted: whether generateContent and streamGenerateContent produce Google Cloud Data Access audit entries; the current URL and status of Google request-response logging to BigQuery (both candidate URLs returned 404); the current product name and status of Microsoft's Entra agent identity offering (product pages returned 404, so only the audit-log page's wording is relied on here); GuardDuty AI Protection enablement mechanics, pricing and regional availability; the CloudTrail Lake event-data-store table naming convention; and which S3 Vectors operations are covered by data events.
One version note to carry forward: OCSF 1.9.0 is both the schema server default and the newest tagged GitHub release, published 2026-08-03, with 1.10.0-dev listed on the server. The release before it was 1.8.0, published 2026-03-18. Pin the version your pipeline targets and re-check before upgrading.
Version-dependent claims, most volatile first: MCP protocol revision 2026-07-28 (revisions have landed roughly every five to seven months); OpenTelemetry GenAI semantic conventions, still Development status in a newly separated repository; OCSF, whose profile applicability changed substantially between 1.8.0 and 1.9.0; MITRE ATLAS, monthly content releases with 2026.07 current; the OWASP Top 10 for LLM Applications, which moved from the 2025 to the 2026 edition days before the verification date and renumbered most of the list; AgentCore resource types and log group patterns, expanding; GuardDuty AI Protection finding types, a new family likely to grow; Google Cloud product naming, recently changed and possibly not settled; Sigma 2.1.0 and its taxonomy, which has no AI log source; and the OpenAI and Anthropic documentation domains, both of which moved within the last cycle.
Recommended review date: 2026-11-30. Re-verify earlier if any of the following occurs: a new MCP protocol revision; an OCSF minor release; a Sigma taxonomy update adding AI log sources; a GuardDuty AI Protection expansion; or a change in your own model platform, which invalidates the telemetry map faster than any external event.
- detection-engineering
- ai-security
- cloud-security
- bedrock
- ocsf
- mcp
- telemetry
- sigma
- non-human-identity
- atlas
The service behind this work
Security automation and detection engineering
Vendors sell detection. We build fixes — detections as code, AI-assisted triage, and remediation pipelines engineered inside your AWS and Google Cloud accounts, in your repositories, owned by your team when we leave.
Related reading
All articles →AI governance for engineers, not lawyers
Translate AI governance into systems you can build: inventory reconciled against runtime telemetry, deployment gates that block, human oversight you can measure, and evidence that survives review. With EU AI Act dates as amended in July 2026.
· 77 min read
MCP security threat model
A threat model for Model Context Protocol deployments pinned to specification revision 2026-07-28: trust boundaries, a 41-row threat register with attacker capability and impact per row, three attack trees, and an account of what MCP declines to defend.
· 75 min read
Prompt-injection testing methodology
A repeatable methodology for testing prompt injection in LLM, RAG and agent systems: rules of engagement, test matrix, trial protocols under non-determinism, severity, evidence capture and retest.
· 73 min read