Skip to content
ATR-2026-00448highTool Poisoningexperimental

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.

Severity
high
Category
Tool Poisoning
Scan Target
both
Author
ATR Community

Response Actions

block inputalertescalate

References

OWASP Agentic
ASI04:2026 - Memory and Knowledge Base PoisoningASI05:2026 - Unexpected Code Execution
OWASP LLM
LLM05:2025 - Improper Output HandlingLLM08:2025 - Vector and Embedding Weaknesses
MITRE ATLAS
AML.T0051 - LLM Prompt InjectionAML.T0070 - RAG Poisoning

Detection Conditions

Combinator: any
  1. 01
    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: contentop: regex
  2. 02
    Filter expression with closing-paren breakout followed by or/and and a fresh Milvus comparator/in-operator — quote-and-paren breakout
    field: contentop: regex
  3. 03
    MilvusVectorStore.delete()/similaritySearch() call whose argument list contains string concatenation — code-level smell that enables CVE-2026-41705
    field: contentop: regex
  4. 04
    Milvus `in[...]` clause followed by or/and boolean chaining into a fresh comparator — injection past the in-clause
    field: contentop: regex
  5. 05
    Filter expression terminated with `;` followed by a destructive verb — classic injection terminator chain
    field: contentop: regex
  6. 06
    Canonical SQL/DSL tautology with sentinel comment-out terminator (-- at EOL or /*) — conservative shape that excludes markdown `#` heading false positives; supports = and == comparator
    field: contentop: regex
  7. 07
    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: contentop: regex
  8. 08
    Paren-breakout in filter expression followed by AND-chain with new numeric comparator — secondary breakout shape
    field: contentop: regex

Attack Examples (Rule Triggers)

  1. {"tool":"MilvusVectorStore.delete","args":{"filter":"id == 1 or 1 == 1"}}
  2. {"tool":"MilvusVectorStore.similaritySearch","args":{"filter":"user_id == \"alice\") or id == 99"}}
  3. milvusVectorStore.delete("id == " + userInput + " or true")
  4. {"filter":"name in [\"x\"] or tenant_id == 99"}
  5. {"filter":"category == \"books\"; DROP TABLE collection"}
  6. ' or 1 == 1 --
  7. filter: "title like \"%\" escape \"\\\\\""
  8. {"filter":"user == \"bob\") and id == 1"}

Real-world attack payloads (sanitized). Used for regression testing.

Benign Examples (Rule Doesn't Trigger)

  1. 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.
  2. Defensive guidance discussing the mitigation
    Best practice: build Milvus filter expressions via the parameterised builder API instead of string concatenation.
  3. Properly parameterised similarity search using a builder — no concat with user input
    milvusVectorStore.similaritySearch(SearchRequest.builder().query("hello").filter(parameterizedExpr).build())
  4. Discussion of CVE in defensive context
    Reviewing this rule against CVE-2026-41705 to ensure regex coverage is correct.
  5. Single static comparator without boolean chaining or breakout
    filter: "category == \"books\""
  6. 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"

Revision History

Created
2026-05-12
Last modified
2026-05-12
View full commit history on GitHub →