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, and Esql.response_values on the alert. Skim prompts for jailbreak attempts, malware requests, or credential harvesting.
  • Remember this rule keys on textual refusals with STOP — not provider SAFETY blocks. Use the safety-filter / elevated-rating rules for those.
  • Pivot to logs-gcp_vertexai.auditlogs-* with event.action like "*GenerateContent*" in the same window for source.ip and client.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

to-top