GCP Vertex AI Repeated Assistant Refusals
Detects three or more Vertex AI GenerateContent replies in 60 minutes whose assistant text indicates a refusal (cannot fulfill/assist/help/answer, unable to assist, decline the request, against my guidelines, and similar), aggregated by project and model. Prompt-response logs lack a stable caller identity, so this is a project-wide refusal burst signal — pivot to auditlogs for principal/IP attribution.
Elastic rule (View on GitHub)
1[metadata]
2creation_date = "2026/10/02"
3integration = ["gcp_vertexai"]
4maturity = "production"
5min_stack_comments = "gcp_vertexai prompt_response_logs requires integration 1.4.0+ (Kibana ^9.2.0)."
6min_stack_version = "9.3.0"
7updated_date = "2026/10/02"
8
9[rule]
10author = ["Elastic"]
11description = """
12Detects three or more Vertex AI GenerateContent replies in 60 minutes whose assistant text indicates a refusal (cannot
13fulfill/assist/help/answer, unable to assist, decline the request, against my guidelines, and similar), aggregated by
14project and model. Prompt-response logs lack a stable caller identity, so this is a project-wide refusal burst signal —
15pivot to auditlogs for principal/IP attribution.
16"""
17false_positives = [
18 """
19 Approved red-team suites that intentionally elicit refusals. Exclude those models or raise the threshold.
20 """,
21]
22from = "now-60m"
23interval = "10m"
24language = "esql"
25license = "Elastic License v2"
26name = "GCP Vertex AI Repeated Assistant Refusals"
27note = """## Triage and analysis
28
29### Investigating GCP Vertex AI Repeated Assistant Refusals
30
31One project/model pair produced three or more refusal-style completions in the lookback window
32(assistant text such as "I cannot fulfill…", "I cannot provide…", "unable to assist…", or
33"against my guidelines…", typically with `finish_reason` STOP). Aggregation is project-wide
34because prompt_response_logs do not carry caller email/IP; use auditlogs to attribute the burst.
35
36#### Possible investigation steps
37
38- Review `Esql.event_count`, `Esql.prompt_values`, and `Esql.response_values` on the alert. Skim
39 prompts for jailbreak attempts, malware requests, or credential harvesting.
40- Remember this rule keys on textual refusals with STOP — not provider `SAFETY` blocks. Use the
41 safety-filter / elevated-rating rules for those.
42- Pivot to `logs-gcp_vertexai.auditlogs-*` with `event.action like "*GenerateContent*"` in the same
43 window for `source.ip` and `client.user.email`.
44- Correlate with jailbreak-heuristic, elevated-safety, and BLOCK_NONE alerts for the same
45 project/model.
46
47### False positive analysis
48
49- Quoted refusal examples and non-security limitations can also match these text heuristics. Read
50 the original prompts and responses before treating a match as a policy refusal.
51- Approved content-safety evaluation or red-team suites that intentionally elicit refusals. Exclude
52 those models/principals or raise the count threshold.
53
54### Response and remediation
55
56- Identify the calling workload via auditlogs; if unauthorized, restrict or rotate credentials and
57 review application allowlists.
58- If prompts show a clear attack theme, expand the hunt across other models and projects for the
59 same principal/IP.
60"""
61references = [
62 "https://www.elastic.co/docs/reference/integrations/gcp_vertexai",
63]
64risk_score = 73
65rule_id = "949912f8-6736-439e-a2fb-d28dcf866017"
66setup = """## Setup
67
68Requires GCP Vertex AI `prompt_response_logs` with response text populated in BigQuery export.
69"""
70severity = "high"
71tags = [
72 "Domain: GenAI",
73 "Domain: Cloud",
74 "Data Source: GCP Vertex AI",
75 "Data Source: GCP",
76 "Data Source: Google Cloud Platform",
77 "Platform: GCP",
78 "Service: GCP Vertex AI",
79 "Use Case: Policy Violation",
80 "Use Case: Threat Detection",
81 "Tactic: Defense Evasion",
82 "Threat: Unauthorized AI Usage",
83 "Resources: Investigation Guide",
84 "Rule Type: ES|QL",
85]
86timestamp_override = "event.ingested"
87type = "esql"
88
89query = '''
90from logs-gcp_vertexai.prompt_response_logs-*
91| where
92 data_stream.dataset == "gcp_vertexai.prompt_response_logs" and
93 MV_CONTAINS(gcp.vertexai.prompt_response_logs.full_response.candidates.finish_reason, "STOP")
94| eval response_text = to_lower(coalesce(mv_concat(gcp.vertexai.prompt_response_logs.full_response.candidates.content.parts.text, " "), ""))
95| where
96 response_text like "*cannot fulfill*" or
97 response_text like "*cannot assist*" or
98 response_text like "*can't assist*" or
99 response_text like "*can't help*" or
100 response_text like "*cannot help*" or
101 response_text like "*i cannot provide*" or
102 response_text like "*i can't provide*" or
103 response_text like "*i will not provide*" or
104 response_text like "*i cannot answer*" or
105 response_text like "*i can't answer*" or
106 response_text like "*unable to provide*" or
107 response_text like "*unable to assist*" or
108 response_text like "*unable to fulfill*" or
109 response_text like "*not able to help*" or
110 response_text like "*not able to provide*" or
111 response_text like "*must decline*" or
112 response_text like "*respectfully decline*" or
113 response_text like "*decline this request*" or
114 response_text like "*decline that request*" or
115 response_text like "*cannot disclose*" or
116 response_text like "*can't disclose*" or
117 response_text like "*against my guidelines*" or
118 response_text like "*that violates my guidelines*"
119| stats
120 Esql.event_count = count(*),
121 Esql.timestamp_first_seen = min(@timestamp),
122 Esql.timestamp_last_seen = max(@timestamp),
123 Esql.prompt_values = values(gcp.vertexai.prompt_response_logs.full_request.contents.parts.text),
124 Esql.response_values = values(gcp.vertexai.prompt_response_logs.full_response.candidates.content.parts.text)
125 by
126 gcp.vertexai.prompt_response_logs.model,
127 cloud.project.id
128| where Esql.event_count >= 3
129| keep
130 gcp.vertexai.prompt_response_logs.model,
131 cloud.project.id,
132 Esql.event_count,
133 Esql.timestamp_first_seen,
134 Esql.timestamp_last_seen,
135 Esql.prompt_values,
136 Esql.response_values
137'''
138
139[[rule.threat_mappings]]
140framework = "MITRE ATLAS"
141version = "2026.08"
142[[rule.threat_mappings.threat]]
143framework = "MITRE ATLAS"
144
145[rule.threat_mappings.threat.tactic]
146id = "AML.TA0007"
147name = "Defense Evasion"
148reference = "https://atlas.mitre.org/tactics/AML.TA0007/"
149
150[rule.alert_suppression]
151group_by = ["gcp.vertexai.prompt_response_logs.model", "cloud.project.id"]
152missing_fields_strategy = "suppress"
153
154[rule.investigation_fields]
155field_names = [
156 "gcp.vertexai.prompt_response_logs.model",
157 "cloud.project.id",
158 "Esql.event_count",
159 "Esql.prompt_values",
160 "Esql.response_values",
161 "Esql.timestamp_first_seen",
162 "Esql.timestamp_last_seen",
163]
164
165[rule.alert_suppression.duration]
166unit = "h"
167value = 1
Triage and analysis
Investigating GCP Vertex AI Repeated Assistant Refusals
One project/model pair produced three or more refusal-style completions in the lookback window
(assistant text such as "I cannot fulfill…", "I cannot provide…", "unable to assist…", or
"against my guidelines…", typically with finish_reason STOP). Aggregation is project-wide
because prompt_response_logs do not carry caller email/IP; use auditlogs to attribute the burst.
Possible investigation steps
- Review
Esql.event_count,Esql.prompt_values, andEsql.response_valueson the alert. Skim prompts for jailbreak attempts, malware requests, or credential harvesting. - Remember this rule keys on textual refusals with STOP — not provider
SAFETYblocks. Use the safety-filter / elevated-rating rules for those. - Pivot to
logs-gcp_vertexai.auditlogs-*withevent.action like "*GenerateContent*"in the same window forsource.ipandclient.user.email. - Correlate with jailbreak-heuristic, elevated-safety, and BLOCK_NONE alerts for the same project/model.
False positive analysis
- Quoted refusal examples and non-security limitations can also match these text heuristics. Read the original prompts and responses before treating a match as a policy refusal.
- Approved content-safety evaluation or red-team suites that intentionally elicit refusals. Exclude those models/principals or raise the count threshold.
Response and remediation
- Identify the calling workload via auditlogs; if unauthorized, restrict or rotate credentials and review application allowlists.
- If prompts show a clear attack theme, expand the hunt across other models and projects for the same principal/IP.
References
Related rules
- GCP Vertex AI Elevated Safety Rating on Response
- GCP Vertex AI Response Blocked by Safety Filters
- GCP Vertex AI Jailbreak Prompt Heuristics
- GCP Vertex AI Publisher Model Config Modified
- GCP Vertex AI Safety Filters Set to BLOCK_NONE