Microsoft Foundry Repeated Assistant Refusals
Detects three or more completed assistant refusals from the same client, API Management subscription, and model. Matching replies finished on their own (finish_reason stop) and declined the prompt. A burst means that caller kept sending prompts the model will not answer.
Elastic rule (View on GitHub)
1[metadata]
2creation_date = "2026/10/01"
3integration = ["azure_ai_foundry"]
4maturity = "production"
5min_stack_version = "9.3.0"
6min_stack_comments = "The Microsoft Foundry integration is compatible with 9.3 and above."
7updated_date = "2026/10/05"
8
9[rule]
10author = ["Elastic"]
11description = """
12Detects three or more completed assistant refusals from the same client, API Management subscription, and model. Matching replies finished on their own (finish_reason stop) and declined the prompt. A burst means
13that caller kept sending prompts the model will not answer.
14"""
15false_positives = [
16 """
17 Red-team or application tests that intentionally send prompts the model refuses. Exclude that source IP or API
18 Management subscription.
19 """,
20 """
21 A shared subscription key behind one NAT can combine refusals from unrelated users. Raise the threshold or split
22 keys per application if that subscription is noisy.
23 """,
24]
25from = "now-15m"
26interval = "5m"
27language = "esql"
28license = "Elastic License v2"
29name = "Microsoft Foundry Repeated Assistant Refusals"
30note = """## Triage and analysis
31
32### Investigating Microsoft Foundry Repeated Assistant Refusals
33
34One client received three or more completed refusals in 15 minutes. The alert is grouped by source IP, user agent,
35API Management subscription, API, operation, model, gateway, and the URL the client called. Prompt and reply samples
36are on the alert.
37
38True positive: several disallowed prompts from one IP or subscription, often a scripting user agent, with replies such
39as "I'm sorry, but I can't assist with that request." False positive: an approved eval, or many users sharing one
40subscription key and one egress IP.
41
42#### Possible investigation steps
43
44- Read the prompt and reply samples. Confirm the replies are refusals and the prompts are what the model declined.
45- Review other GatewayLogs for the same source IP and subscription in the same window: successful completions, HTTP
46 400 jailbreak blocks, or a sharp rise in calls.
47- Check whether this subscription and user agent match the application's normal client. curl or a scripted agent on a
48 key that usually comes from an application gateway deserves a closer look.
49- Use the geo and ASN fields to see whether the client network is expected for that subscription.
50
51### False positive analysis
52
53- Approved prompt-security tests and model evaluations. Exclude the test source IP or subscription.
54- A busy shared subscription behind one NAT. The count is per IP and subscription; raise the threshold if normal
55 traffic still reaches three refusals in 15 minutes.
56
57### Response and remediation
58
59- Rotate or disable the API Management subscription key when the client is not approved for this traffic.
60- Block the source IP at the gateway when it is not a known application egress.
61- Review the prompts for secrets, and for any later call from the same client that the model answered instead of
62 refusing.
63"""
64references = [
65 "https://www.elastic.co/docs/reference/integrations/azure_ai_foundry",
66 "https://learn.microsoft.com/en-us/azure/api-management/diagnostic-logs-reference",
67]
68risk_score = 73
69rule_id = "3c694712-7bd9-43b5-ac6b-1db841abc8de"
70setup = """## Setup
71
72This rule needs the Microsoft Foundry integration collecting Azure API Management GatewayLogs with the backend
73response body. Foundry RequestResponse logs do not include the assistant reply, so they will not match.
74
75- Use an API Management tier that emits resource logs. Developer or higher works. Consumption does not.
76- Send the GatewayLogs category to the Event Hub the integration reads.
77- Log the backend request and response bodies in API diagnostics so the prompt, finish_reason, and assistant message are stored.
78
79https://www.elastic.co/docs/reference/integrations/azure_ai_foundry
80"""
81severity = "high"
82tags = [
83 "Data Source: Microsoft Foundry",
84 "Use Case: Policy Violation",
85 "Mitre Atlas: AML.T0051",
86 "Mitre Atlas: AML.T0054",
87 "Tactic: Defense Evasion",
88 "Resources: Investigation Guide",
89 "Rule Type: ES|QL",
90 "Platform: Azure",
91 "Domain: Cloud",
92 "Domain: GenAI",
93 "Service: Azure API Management",
94]
95timestamp_override = "event.ingested"
96type = "esql"
97
98query = '''
99from logs-azure_ai_foundry.logs-*
100| eval Esql.reply_text = mv_concat(azure.ai_foundry.properties.backend_response_body.choices.message.content, " ")
101| where
102 data_stream.dataset == "azure_ai_foundry.logs" and
103 azure.ai_foundry.category == "GatewayLogs" and
104 azure.ai_foundry.properties.backend_response_body.choices.finish_reason in ("stop", "content_filter") and
105 (
106 Esql.reply_text like "*I can't assist*" or
107 Esql.reply_text like "*I can't help*" or
108 Esql.reply_text like "*I cannot fulfill*" or
109 Esql.reply_text like "*I cannot help*" or
110 Esql.reply_text like "*I cannot provide*" or
111 Esql.reply_text like "*I can't provide*" or
112 Esql.reply_text like "*I will not provide*" or
113 Esql.reply_text like "*I cannot answer*" or
114 Esql.reply_text like "*I can't answer*"
115 )
116| stats
117 Esql.event_count = count(*),
118 Esql.timestamp_first_seen = min(@timestamp),
119 Esql.timestamp_last_seen = max(@timestamp),
120 Esql.azure_ai_foundry_properties_backend_request_body_messages_content_values = values(azure.ai_foundry.properties.backend_request_body.messages.content),
121 Esql.azure_ai_foundry_properties_backend_response_body_choices_message_content_values = values(azure.ai_foundry.properties.backend_response_body.choices.message.content)
122 by
123 source.ip,
124 azure.ai_foundry.properties.user_agent,
125 azure.ai_foundry.properties.apim_subscription_id,
126 azure.ai_foundry.properties.api_id,
127 azure.ai_foundry.properties.operation_id,
128 azure.ai_foundry.properties.backend_response_body.model,
129 azure.ai_foundry.service_name,
130 azure.resource.group,
131 url.domain,
132 url.path,
133 source.geo.country_iso_code,
134 source.geo.city_name,
135 source.as.organization.name
136| where Esql.event_count >= 3
137| keep
138 source.ip,
139 azure.ai_foundry.properties.user_agent,
140 azure.ai_foundry.properties.apim_subscription_id,
141 azure.ai_foundry.properties.api_id,
142 azure.ai_foundry.properties.operation_id,
143 azure.ai_foundry.properties.backend_response_body.model,
144 azure.ai_foundry.service_name,
145 azure.resource.group,
146 url.domain,
147 url.path,
148 source.geo.country_iso_code,
149 source.geo.city_name,
150 source.as.organization.name,
151 Esql.*
152'''
153
154
155[[rule.threat]]
156framework = "MITRE ATT&CK"
157[[rule.threat.technique]]
158id = "T1562"
159name = "Impair Defenses"
160reference = "https://attack.mitre.org/techniques/T1562/"
161
162
163[rule.threat.tactic]
164id = "TA0005"
165name = "Defense Evasion"
166reference = "https://attack.mitre.org/tactics/TA0005/"
167
168[[rule.threat_mappings]]
169framework = "MITRE ATLAS"
170version = "2026.08"
171[[rule.threat_mappings.threat]]
172framework = "MITRE ATLAS"
173[[rule.threat_mappings.threat.technique]]
174id = "AML.T0051"
175name = "LLM Prompt Injection"
176reference = "https://atlas.mitre.org/techniques/AML.T0051/"
177
178
179[rule.threat_mappings.threat.tactic]
180id = "AML.TA0005"
181name = "Execution"
182reference = "https://atlas.mitre.org/tactics/AML.TA0005/"
183[[rule.threat_mappings.threat]]
184framework = "MITRE ATLAS"
185[[rule.threat_mappings.threat.technique]]
186id = "AML.T0054"
187name = "LLM Jailbreak"
188reference = "https://atlas.mitre.org/techniques/AML.T0054/"
189
190
191[rule.threat_mappings.threat.tactic]
192id = "AML.TA0007"
193name = "Defense Evasion"
194reference = "https://atlas.mitre.org/tactics/AML.TA0007/"
195
196[rule.alert_suppression]
197group_by = [
198 "source.ip",
199 "azure.ai_foundry.service_name",
200 "azure.ai_foundry.properties.api_id",
201]
202duration = {value = 15, unit = "m"}
203missing_fields_strategy = "suppress"
204
205[rule.investigation_fields]
206field_names = [
207 "source.ip",
208 "azure.ai_foundry.properties.user_agent",
209 "azure.ai_foundry.properties.apim_subscription_id",
210 "azure.ai_foundry.properties.api_id",
211 "azure.ai_foundry.properties.operation_id",
212 "azure.ai_foundry.properties.backend_response_body.model",
213 "azure.ai_foundry.service_name",
214 "azure.resource.group",
215 "url.domain",
216 "url.path",
217 "source.geo.country_iso_code",
218 "source.geo.city_name",
219 "source.as.organization.name",
220 "Esql.event_count",
221 "Esql.timestamp_first_seen",
222 "Esql.timestamp_last_seen",
223 "Esql.azure_ai_foundry_properties_backend_request_body_messages_content_values",
224 "Esql.azure_ai_foundry_properties_backend_response_body_choices_message_content_values",
225]
Triage and analysis
Investigating Microsoft Foundry Repeated Assistant Refusals
One client received three or more completed refusals in 15 minutes. The alert is grouped by source IP, user agent, API Management subscription, API, operation, model, gateway, and the URL the client called. Prompt and reply samples are on the alert.
True positive: several disallowed prompts from one IP or subscription, often a scripting user agent, with replies such as "I'm sorry, but I can't assist with that request." False positive: an approved eval, or many users sharing one subscription key and one egress IP.
Possible investigation steps
- Read the prompt and reply samples. Confirm the replies are refusals and the prompts are what the model declined.
- Review other GatewayLogs for the same source IP and subscription in the same window: successful completions, HTTP 400 jailbreak blocks, or a sharp rise in calls.
- Check whether this subscription and user agent match the application's normal client. curl or a scripted agent on a key that usually comes from an application gateway deserves a closer look.
- Use the geo and ASN fields to see whether the client network is expected for that subscription.
False positive analysis
- Approved prompt-security tests and model evaluations. Exclude the test source IP or subscription.
- A busy shared subscription behind one NAT. The count is per IP and subscription; raise the threshold if normal traffic still reaches three refusals in 15 minutes.
Response and remediation
- Rotate or disable the API Management subscription key when the client is not approved for this traffic.
- Block the source IP at the gateway when it is not a known application egress.
- Review the prompts for secrets, and for any later call from the same client that the model answered instead of refusing.
References
Related rules
- Microsoft Foundry Jailbreak Detected
- Microsoft Foundry Prompt or Completion Containing Credentials
- Microsoft Foundry Subscription Key Impossible Travel
- AWS Bedrock Guardrails Detected Multiple Policy Violations Within a Single Blocked Request
- AWS Bedrock Guardrails Detected Multiple Violations by a Single User Over a Session