Spring AI MilvusVectorStore Filter Expression Injection (CVE-2026-41705)
Detects exploitation of CVE-2026-41705 (High), filter-expression injection in Spring AI's MilvusVectorStore. The vulnerable sink concatenates a user/LLM-controlled fragment into a Milvus DSL filter expression passed to MilvusVectorStore.delete() or .similaritySearch() without quoting or parameterisation. Attacker-controlled input breaks out of the intended clause and injects new Milvus DSL operators ( == , in[ , like ), boolean combinators ( or , and ), trailing terminators ( ; -- ), or escape bypasses ( like '%' ESCAPE '\\' ) to broaden the deletion / retrieval scope, exfiltrate or wipe arbitrary collection entries, or bypass access-control filters baked into the original expression. This rule detects the LLM-output / user-input payload shapes that reach the Milvus filter sink: filter-context fields containing unbalanced quotes, Milvus operators combined with boolean chaining, or known filter-bypass primitives. CWE-89, CWE-1287. Patches in Spring AI >= 1.0.0; this rule detects exploit attempts against unpatched deployments and provides defence-in-depth post-patch by catching the injection payload shape regardless of upstream patch state.
Response Actions
References
Detection Conditions
Combinator: any- 01Milvus filter field with numeric comparator followed by boolean chain into a tautology (or 1==1, or true, or id==N) — primary CVE-2026-41705 injection shapefield: contentop: regex
- 02Filter expression with closing-paren breakout followed by or/and and a fresh Milvus comparator/in-operator — quote-and-paren breakoutfield: contentop: regex
- 03MilvusVectorStore.delete()/similaritySearch() call whose argument list contains string concatenation — code-level smell that enables CVE-2026-41705field: contentop: regex
- 04Milvus `in[...]` clause followed by or/and boolean chaining into a fresh comparator — injection past the in-clausefield: contentop: regex
- 05Filter expression terminated with `;` followed by a destructive verb — classic injection terminator chainfield: contentop: regex
- 06Canonical SQL/DSL tautology with sentinel comment-out terminator (-- at EOL or /*) — conservative shape that excludes markdown `#` heading false positives; supports = and == comparatorfield: contentop: regex
- 07Milvus `like '%' ESCAPE` bypass primitive (supports JSON-escaped quotes around the `%` literal and 0..8 backslash/word chars inside the escape value) — used to defeat literal-character escaping in upstream filter buildersfield: contentop: regex
- 08Paren-breakout in filter expression followed by AND-chain with new numeric comparator — secondary breakout shapefield: contentop: regex
Attack Examples (Rule Triggers)
{"tool":"MilvusVectorStore.delete","args":{"filter":"id == 1 or 1 == 1"}}{"tool":"MilvusVectorStore.similaritySearch","args":{"filter":"user_id == \"alice\") or id == 99"}}milvusVectorStore.delete("id == " + userInput + " or true"){"filter":"name in [\"x\"] or tenant_id == 99"}{"filter":"category == \"books\"; DROP TABLE collection"}' or 1 == 1 --
filter: "title like \"%\" escape \"\\\\\""
{"filter":"user == \"bob\") and id == 1"}
Real-world attack payloads (sanitized). Used for regression testing.
Benign Examples (Rule Doesn't Trigger)
- Documentation discussing the patch should not trigger
The Spring AI 1.0.0 changelog notes that MilvusVectorStore now uses parameterised filter builders to mitigate CVE-2026-41705.
- Defensive guidance discussing the mitigation
Best practice: build Milvus filter expressions via the parameterised builder API instead of string concatenation.
- Properly parameterised similarity search using a builder — no concat with user input
milvusVectorStore.similaritySearch(SearchRequest.builder().query("hello").filter(parameterizedExpr).build()) - Discussion of CVE in defensive context
Reviewing this rule against CVE-2026-41705 to ensure regex coverage is correct.
- Single static comparator without boolean chaining or breakout
filter: "category == \"books\""
- Benign English prose mentioning fields and patterns
The dataset contains records where the title field matches a specific pattern.
Known False Positive Contexts
- ▸Legitimate documentation or changelog text discussing CVE-2026-41705 patch notes.
- ▸Static analysis tooling output documenting Milvus filter injection patterns for defensive purposes.
- ▸Patched Spring AI MilvusVectorStore deployments that use parameterised filter builders.
Full YAML Definition
Edit on GitHub →title: "Spring AI MilvusVectorStore Filter Expression Injection (CVE-2026-41705)"
id: ATR-2026-00448
rule_version: 1
status: experimental
description: >
Detects exploitation of CVE-2026-41705 (High), filter-expression injection
in Spring AI's MilvusVectorStore. The vulnerable sink concatenates a
user/LLM-controlled fragment into a Milvus DSL filter expression passed
to MilvusVectorStore.delete() or .similaritySearch() without quoting or
parameterisation. Attacker-controlled input breaks out of the intended
clause and injects new Milvus DSL operators ( == , in[ , like ), boolean
combinators ( or , and ), trailing terminators ( ; -- ), or escape
bypasses ( like '%' ESCAPE '\\' ) to broaden the deletion / retrieval
scope, exfiltrate or wipe arbitrary collection entries, or bypass
access-control filters baked into the original expression. This rule
detects the LLM-output / user-input payload shapes that reach the
Milvus filter sink: filter-context fields containing unbalanced quotes,
Milvus operators combined with boolean chaining, or known
filter-bypass primitives. CWE-89, CWE-1287. Patches in Spring AI
>= 1.0.0; this rule detects exploit attempts against unpatched
deployments and provides defence-in-depth post-patch by catching the
injection payload shape regardless of upstream patch state.
author: "ATR Community"
date: "2026/05/12"
schema_version: "0.1"
detection_tier: pattern
maturity: test
severity: high
references:
owasp_llm:
- "LLM05:2025 - Improper Output Handling"
- "LLM08:2025 - Vector and Embedding Weaknesses"
owasp_agentic:
- "ASI04:2026 - Memory and Knowledge Base Poisoning"
- "ASI05:2026 - Unexpected Code Execution"
mitre_atlas:
- "AML.T0051 - LLM Prompt Injection"
- "AML.T0070 - RAG Poisoning"
mitre_attack:
- "T1190 - Exploit Public-Facing Application"
cve:
- "CVE-2026-41705"
metadata_provenance:
mitre_atlas: human-reviewed
owasp_llm: human-reviewed
owasp_agentic: human-reviewed
cve: human-reviewed
compliance:
eu_ai_act:
- article: "15"
context: "CVE-2026-41705 allows unfiltered LLM output to drive Milvus DSL filter construction in Spring AI's MilvusVectorStore; Article 15 cybersecurity requirements mandate that high-risk AI systems parameterise downstream queries instead of concatenating model output into query strings."
strength: primary
- article: "9"
context: "Article 9 risk management must enumerate vector-store filter injection as a high-risk class — the RAG retrieval / deletion path is typically treated as low-risk infrastructure but actually reaches a privileged datastore."
strength: primary
nist_ai_rmf:
- subcategory: "MP.5.1"
context: "Adversarial inputs that inject Milvus DSL fragments into RAG filter expressions must be tracked as a primary input-attack class affecting vector-store integrations."
strength: primary
- subcategory: "MG.2.3"
context: "Risk treatment plans under MG.2.3 must require parameterised filter construction in any code path that consumes LLM output and reaches a vector-store query / delete API."
strength: primary
iso_42001:
- clause: "8.6"
context: "Operational controls under clause 8.6 must prohibit LLM-generated or user-supplied content from being string-concatenated into vector-store filter expressions; parameterised filter builders are mandatory."
strength: primary
tags:
category: tool-poisoning
subcategory: vector-store-filter-injection
scan_target: both
confidence: high
agent_source:
type: tool_call
framework:
- spring-ai
- any
provider:
- any
detection:
condition: any
false_positives:
- "Legitimate documentation or changelog text discussing CVE-2026-41705 patch notes."
- "Static analysis tooling output documenting Milvus filter injection patterns for defensive purposes."
- "Patched Spring AI MilvusVectorStore deployments that use parameterised filter builders."
conditions:
- field: content
operator: regex
value: '(?i)\b(?:filter|filter_expr|filterExpression)\b[^\n]{0,40}(?:==|!=)\s*\d+[^\n]{0,20}\b(?:or|and)\s+(?:1\s*==\s*1|true\b|\d+\s*==\s*\d+|\w+\s*==\s*\d+)'
description: "Milvus filter field with numeric comparator followed by boolean chain into a tautology (or 1==1, or true, or id==N) — primary CVE-2026-41705 injection shape"
- field: content
operator: regex
value: '(?i)\b(?:filter|filter_expr|filterExpression)\b[^\n]{0,80}\\?["\x27][^\n]{0,40}\)\s*(?:or|and)\s+\w+\s*(?:==|in\s*\[)'
description: "Filter expression with closing-paren breakout followed by or/and and a fresh Milvus comparator/in-operator — quote-and-paren breakout"
- field: content
operator: regex
value: '(?i)(?:MilvusVectorStore|milvus[-_]?client|milvus[-_]?service)\b[^.\n]{0,80}\.(?:delete|similaritysearch|similarity_search|search)\s*\([^)\n]{0,200}\+\s*\w'
description: "MilvusVectorStore.delete()/similaritySearch() call whose argument list contains string concatenation — code-level smell that enables CVE-2026-41705"
- field: content
operator: regex
value: '(?i)\b(?:filter|filter_expr|filterExpression)\b[^\n]{0,40}\bin\s*\[[^\]\n]{0,80}\]\s*(?:or|and)\s+\w+\s*(?:==|!=)'
description: "Milvus `in[...]` clause followed by or/and boolean chaining into a fresh comparator — injection past the in-clause"
- field: content
operator: regex
value: '(?i)\b(?:filter|filter_expr|filterExpression)\b[^\n]{0,80}["\x27]\s*;\s*(?:drop|delete|truncate|alter)\s+(?:table|from|collection)\b'
description: "Filter expression terminated with `;` followed by a destructive verb — classic injection terminator chain"
- field: content
operator: regex
value: '(?i)[\x27"]\s+(?:or|and)\s+(?:1\s*=+\s*1|true)\s*(?:--(?=\s|$|["\x27\)])|/\*)'
description: "Canonical SQL/DSL tautology with sentinel comment-out terminator (-- at EOL or /*) — conservative shape that excludes markdown `#` heading false positives; supports = and == comparator"
- field: content
operator: regex
value: '(?i)\blike\s+\\?["\x27]%\\?["\x27]\s+escape\s+\\?["\x27][\\\\\w]{0,8}\\?["\x27]'
description: "Milvus `like '%' ESCAPE` bypass primitive (supports JSON-escaped quotes around the `%` literal and 0..8 backslash/word chars inside the escape value) — used to defeat literal-character escaping in upstream filter builders"
- field: content
operator: regex
value: '(?i)\b(?:filter|filter_expr|filterExpression)\b[^\n]{0,80}\)\s+and\s+\w+\s*==\s*\d+'
description: "Paren-breakout in filter expression followed by AND-chain with new numeric comparator — secondary breakout shape"
response:
actions:
- block_input
- alert
- escalate
notify:
- security_team
remediation: >
Upgrade Spring AI to >= 1.0.0. Replace string-concatenated Milvus filter
expressions with parameterised filter builders; never interpolate LLM
output or user input directly into filter strings passed to
MilvusVectorStore.delete() or .similaritySearch(). Validate filter
fragments against an allowlist of operators / field names before
building the final expression. Treat LLM output as untrusted input
from a security perspective.
test_cases:
true_positives:
- input: '{"tool":"MilvusVectorStore.delete","args":{"filter":"id == 1 or 1 == 1"}}'
expected: triggered
description: "Tautology-based deletion-broadening injection — CVE-2026-41705 canonical shape"
- input: '{"tool":"MilvusVectorStore.similaritySearch","args":{"filter":"user_id == \"alice\") or id == 99"}}'
expected: triggered
description: "Closing-paren breakout followed by or chaining with comparator"
- input: 'milvusVectorStore.delete("id == " + userInput + " or true")'
expected: triggered
description: "Source-level concat against Milvus delete sink — code path that enables CVE-2026-41705"
- input: '{"filter":"name in [\"x\"] or tenant_id == 99"}'
expected: triggered
description: "in-operator followed by or chaining with new comparator — bypass of original access-control filter"
- input: '{"filter":"category == \"books\"; DROP TABLE collection"}'
expected: triggered
description: "Filter terminator + destructive verb — classic injection chain"
- input: "' or 1 == 1 --"
expected: triggered
description: "Canonical tautology + comment-out payload pasted into the user_input field"
- input: 'filter: "title like \"%\" escape \"\\\\\""'
expected: triggered
description: "Milvus like-escape bypass primitive"
- input: '{"filter":"user == \"bob\") and id == 1"}'
expected: triggered
description: "Paren-breakout into AND-chain with new comparator"
true_negatives:
- input: "The Spring AI 1.0.0 changelog notes that MilvusVectorStore now uses parameterised filter builders to mitigate CVE-2026-41705."
expected: not_triggered
description: "Documentation discussing the patch should not trigger"
- input: "Best practice: build Milvus filter expressions via the parameterised builder API instead of string concatenation."
expected: not_triggered
description: "Defensive guidance discussing the mitigation"
- input: 'milvusVectorStore.similaritySearch(SearchRequest.builder().query("hello").filter(parameterizedExpr).build())'
expected: not_triggered
description: "Properly parameterised similarity search using a builder — no concat with user input"
- input: "Reviewing this rule against CVE-2026-41705 to ensure regex coverage is correct."
expected: not_triggered
description: "Discussion of CVE in defensive context"
- input: 'filter: "category == \"books\""'
expected: not_triggered
description: "Single static comparator without boolean chaining or breakout"
- input: "The dataset contains records where the title field matches a specific pattern."
expected: not_triggered
description: "Benign English prose mentioning fields and patterns"