DEV Community

jamilxt
jamilxt

Posted on

Spring AI 2.0.1 Fixed 7 CVEs. One of Them Lets a Prompt Call Tools You Never Advertised

I start most mornings the same way: coffee, then the Spring blog RSS feed, then whatever my agents did overnight. Yesterday the feed had something I did not expect in a maintenance release. Spring AI 2.0.1, announced August 21 by Christian Tzolov, is the first patch release on top of June's 2.0.0 GA. It carries more than 80 issues and pull requests. Buried under the streaming fixes and the tool call limits is a list most teams will scroll past: seven CVEs, all disclosed August 20, all fixed in 2.0.1.

I run Spring Boot services with Spring AI in production, and I write about Java and AI every week, so I went and read all seven advisories on spring.io instead of skimming the headline. Two of them change how I think about my own agent setup. If you have anything running on Spring AI 1.0.x, 1.1.x, or 2.0.0, here is what actually matters in this release, ranked by how likely it is to touch your system.

The one that should worry agent builders most

CVE-2026-59318: unadvertised tool dispatch via prompt injection. Rated MEDIUM, and I would argue that label undersells it for anyone running multi-tenant agents.

Here is the mechanism, straight from the advisory. In Spring AI's tool calling support, the per-request tool list is advertised to the model as a boundary, but that boundary is not fully enforced when a tool call is dispatched. Under certain conditions, a tool that was not made available to the current request could still be invoked, potentially leading to privilege escalation.

Read that again as an agent architect. Most of us scope tools per request on purpose: the customer-support agent gets lookupOrder and refundPolicy, not deleteUser or runMigrations. We assume the advertised list is the enforced list. It was not. A crafted prompt, say a support ticket body or a summarized web page, could coax the model into naming a tool outside the request's scope, and the global resolver fallback in DefaultToolCallingManager would happily dispatch it.

The CVSS vector on the advisory is network attack vector, high attack complexity, low privileges required, user interaction required, scope changed, high confidentiality impact. The "high complexity" and "user interaction" parts are why it lands at MEDIUM rather than HIGH. But the failure mode, your agent executing a tool the current caller was never supposed to reach, is a privilege escalation by any plain reading.

It was reported by Arjun Basnet of Securin and ChangMin Lee, and it affects Spring AI 2.0.0, 1.1.0 through 1.1.8, and 1.0.0 through 1.0.9. Upgrade to 2.0.1 and, per the advisory, no further mitigation is necessary.

Two genuinely nasty ones if you ingest documents

CVE-2026-47851: PDF outline recursion, rated HIGH. The advisory says analyzing a PDF with a deeply nested or cyclic table of contents can cause a StackOverflowError in the ingestion thread. The vector is network, low attack complexity, no privileges, no user interaction, high availability impact. That is the classic remote denial-of-service profile: anyone who can get a poisoned PDF into your ingestion pipeline, a resume uploader, a document chat feature, an email attachment flow, can kill the thread handling it.

I have built exactly this kind of pipeline. "Upload a PDF and ask questions about it" is probably the single most common Spring AI demo that graduates into production. If that is you, this CVE is your priority, and it is the highest rated of the seven.

CVE-2026-59294: arbitrary file write via path traversal in ResourceCacheService. The advisory is unusually specific. getCacheName() builds the on-disk cache filename by appending the URI fragment verbatim, without stripping path separators or .. sequences, and passes it to new File(resourceParentFolder, newFileName) before writing downloaded bytes there. If your application passes model or tokenizer URIs from a less trusted source, tenant configuration, an admin UI, an external catalogue, into TransformersEmbeddingModel.setModelResource() or setTokenizerResource(), a crafted URI fragment like #/../../../../opt/app/conf/override.properties writes attacker-controlled content outside the cache directory.

That is a full arbitrary file write primitive. Yes, it requires your app to accept resource URIs from users or tenants, which many do not. But if yours does, treat it as critical, because overwriting a config file or planting a jar is often one step from code execution.

The quieter four

CVE-2026-59308: semantic cache cross-tenant leak via SHA-256 truncation. The context hash used to isolate cached responses between different system prompts could allow cached answers to be shared across unrelated contexts. If you serve multiple tenants or multiple system prompts in front of one semantic cache, one tenant's question can be answered from another tenant's cached context. Reported by Wayde Shi of PayPal. Semantic caches feel like a pure cost optimization, which is exactly why isolation bugs in them get missed in code review.

CVE-2026-59319: RediSearch tag injection in RedisChatMemoryRepository. Allows cross-conversation data exposure. Related hardening also landed in 2.0.1: metadata values are now escaped in queries so values with special characters no longer break the query, clear() actually clears, and timestamps are reserved atomically so concurrent writers cannot collide.

CVE-2026-47852: predictable cache directory allows local ONNX model substitution. A local attacker who can predict the cache location can swap the ONNX model your embedding pipeline loads. This matters on shared hosts or multi-tenant CI boxes more than on a locked-down container.

CVE-2026-59279: unbounded persistent session allocation via repeated initialize requests. Straightforward resource exhaustion: repeated initialize requests allocate persistent sessions without bound until something gives.

The catch nobody is talking about

Here is the part of the fix tables that stopped me. For the 2.0.x line, the fix is 2.0.1, available in open source on Maven Central. But for teams on Spring AI 1.1.x and 1.0.x, the fixed versions, 1.1.9 and 1.0.10, are listed as Enterprise Support only.

Spring AI 2.0 requires Spring Boot 4.0 or 4.1 and Spring Framework 7. If your services are still on Boot 3.x, and plenty of production estates are, you cannot reach 2.0.1 by bumping a version number. Your realistic options are: hold your nose and stay vulnerable while you plan the Boot 4 migration, pay for Tanzu Spring enterprise support to get 1.1.9 or 1.0.10, or backport enough hardening yourself. None of those are one-liners. This is the quiet cost of framework version coupling, and it is worth raising in your next architecture sync if you have Spring AI on Boot 3.

The good news: 2.0.1 also gives you the guardrail you were missing

While the security fixes are the reason to upgrade, one new feature pairs so well with CVE-2026-59318 that I would adopt it in the same PR: tool call limits.

An agentic loop that never terminates is an expensive failure mode. ToolCallingAdvisor now accepts a configurable limit on the number of tool calls per request and raises ToolCallLimitExceededException when it is hit, and the exception path returns a single Generation so your error handling keeps the same shape as a normal response. Tool resolution fallback is also configurable now: you decide whether an unresolvable tool name fails fast or falls back, which is precisely the behavior class the prompt injection CVE lived in.

In code it looks roughly like this:

var advisor = ToolCallingAdvisor.builder()
        .toolCallingManager(toolCallingManager)
        .maxToolCalls(5)          // hard stop before the loop runs away
        .build();

var response = chatClient.prompt()
        .user(question)
        .advisors(advisor)
        .call();
Enter fullscreen mode Exit fullscreen mode

Check the upgrade notes for the exact builder API on your version, but the concept is the point: a budget on tool calls, enforced by the framework instead of by hope.

Upgrade gotchas I would check before bumping the version

Most applications move from 2.0.0 to 2.0.1 by bumping the version, but the release post calls out a few traps:

  • OpenAI strict mode now defaults to false. Generated tool schemas mark optional parameters by omitting them from required, which strict mode rejects, so any tool with an optional parameter was failing with a 400. Strict mode is now opt-in. If you explicitly want it, enable it explicitly.
  • The Redis chat memory auto-configuration artifact was renamed for consistency with the rest of the spring-ai-autoconfigure-* naming. Update the artifact id in your build file.
  • Deprecated Mistral AI chat models were retired. If you reference them by constant, switch to a supported model name.
  • Media builders use typed data overloads instead of accepting Object, so previously "working" untyped calls may no longer compile.
  • DeepSeekApi was revised to match the conventions of the other API clients.
  • The Couchbase vector store now uses the Spring Boot-managed client, so it participates in your existing cluster configuration rather than creating its own.

My upgrade checklist

Since a checklist is worth more than an opinion, here is the order I am doing this in, and what I would tell any team to copy:

  1. Inventory first. Grep your builds for spring-ai and sort by version line. Anyone on 1.0.x or 1.1.x is in the enterprise-fix-only zone and needs a decision, not a version bump.
  2. Rank your exposure. PDF ingestion in front of untrusted users? CVE-2026-47851 first. Tenant-supplied model or tokenizer URIs? CVE-2026-59294 first. Multi-tenant agents with scoped tools? CVE-2026-59318 first.
  3. Bump 2.0.0 to 2.0.1, then re-run your tool-calling integration tests against the new strict-mode default before you touch anything else.
  4. Add a tool call limit to every ToolCallingAdvisor, even a generous one. It costs nothing and bounds the blast radius of the next prompt injection that gets through.
  5. If you use a semantic cache across tenants or system prompts, verify isolation after upgrade with two different system prompts and one identical question. The cached answer must not cross.
  6. Rename the Redis chat memory artifact id before you wonder why auto-configuration silently stopped.

The bottom line

Spring AI is roughly two months past GA, and its first maintenance release is carrying seven security fixes, three of which, unadvertised tool dispatch, arbitrary file write, and PDF recursion DoS, are the kind of bug classes we spent twenty years beating out of web frameworks. That is not a reason to avoid Spring AI. It is the normal, healthy maturation of infrastructure software, and the team's disclosure hygiene, exact vectors, exact affected versions, fix tables, credited reporters, is genuinely good.

The lesson I am taking from it is narrower and more uncomfortable: the boundaries we draw in agent code, tool scopes, cache contexts, chat memory namespaces, are only as real as their enforcement. One of the seven CVEs existed precisely because a boundary was advertised to the model but not enforced by the runtime. Audit yours accordingly.

I write about Java, Spring Boot, and AI every week. Subscribe, it is free.

Have you shipped Spring AI into production yet, and are you on the 2.0 line or still on 1.x with Boot 3? What is your upgrade plan for these CVEs? I would genuinely like to know how other teams are handling the enterprise-only fix situation.

Top comments (0)