CVE-2026-47852
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
Spring AI is the Spring project for AI engineering, applying Spring's portable service abstractions and dependency injection model to chat models, embedding models, vector stores, and related AI services so that Java applications can consume them through a common API. Its Transformers module runs sentence-transformer embedding models locally through ONNX Runtime and the Deep Java Library, downloading the ONNX model and tokenizer files an application asks for and caching them on the local file system so they are not fetched again on every start.
An Incorrectly Configured Access Control vulnerability (CVE-2026-47852) has been identified in the resource cache used by the Transformers embedding model, which allows attackers to place a substituted ONNX model or tokenizer at a predictable cache location and have the application load it in place of the genuine artifact.
Per OWASP: Access control enforces policy such that users cannot act outside of their intended permissions. Failures typically lead to unauthorized information disclosure, modification, or destruction of all data or performing a business function outside the user's limits.
This issue affects the local Transformers embedding support of Spring AI.
Details
Module Info
- Product: Spring AI
- Affected packages:
org.springframework.ai:spring-ai-transformers - Affected versions: >=1.0.0 <=1.0.9, >=1.1.0 <=1.1.8, 2.0.0
- GitHub repository: https://github.com/spring-projects/spring-ai
- Published packages: https://central.sonatype.com/artifact/org.springframework.ai/spring-ai-transformers
- Package manager: Maven
- Fixed in:
- NES for Spring AI nes-v1.0.11 and nes-v1.1.10
- Spring AI 2.0.1 (OSS)
Vulnerability Info
This High-severity vulnerability is found in the org.springframework.ai:spring-ai-transformers package in the local Transformers embedding support of Spring AI.
ResourceCacheService caches the ONNX model and tokenizer resources that TransformersEmbeddingModel downloads. Its no-argument constructor derives the cache root from a fixed, world-guessable name under the shared system temporary directory, and creates that directory with default permissions if it does not already exist:
public ResourceCacheService() {
this(new File(System.getProperty("java.io.tmpdir"), "spring-ai-onnx-generative").getAbsolutePath());
}
public ResourceCacheService(File rootCacheDirectory) {
Assert.notNull(rootCacheDirectory, "Cache directory can not be null.");
this.cacheDirectory = rootCacheDirectory;
if (!this.cacheDirectory.exists()) {
logger.info("Create cache root directory: " + this.cacheDirectory.getAbsolutePath());
this.cacheDirectory.mkdirs();
}
Assert.isTrue(this.cacheDirectory.isDirectory(), "The cache folder must be a directory");
}
TransformersEmbeddingModel falls back to that no-argument constructor whenever no explicit cache directory is configured, so on a default deployment the model cache lives at a path every local account can predict, for example /tmp/spring-ai-onnx-generative on Linux. A local user on a multi-user or shared-container host can pre-create that directory tree with permissions of their choosing before the application starts, then plant files under it. Because the cache is consulted before anything is downloaded, the embedding model loads the attacker's ONNX model or tokenizer instead of the genuine artifact, and the attacker controls the weights, and therefore the embeddings, of a process that believes it is running a known model. The pre-created directory also survives the cache reset path, which only deletes and recreates the same fixed location.
The path each cached artifact resolves to is derived from the requested resource itself, with no check that the result stays inside the cache root:
private File getCachedFile(Resource originalResource) throws IOException {
var resourceParentFolder = new File(this.cacheDirectory,
UUID.nameUUIDFromBytes(pathWithoutLastSegment(originalResource.getURI())).toString());
resourceParentFolder.mkdirs();
String newFileName = getCacheName(originalResource);
return new File(resourceParentFolder, newFileName);
}
private String getCacheName(Resource originalResource) throws IOException {
String fileName = originalResource.getFilename();
String fragment = originalResource.getURI().getFragment();
return !StringUtils.hasText(fragment) ? fileName : fileName + "_" + fragment;
}
The file name is assembled from the resource file name plus its URI fragment and handed straight to new File(resourceParentFolder, newFileName), so a model or tokenizer URI supplied from a less trusted source can steer the downloaded bytes to a location outside the configured cache directory. The remediation makes the default cache root a fresh per-process temporary directory rather than a fixed name, and rejects any resolved cache path whose canonical form does not sit inside the cache directory.
This vulnerability was introduced in 2024 with Spring AI 0.8.0.
Mitigation
Only recent versions of Spring AI receive community support. Older lines are End-of-Life and will not receive public updates to address this issue.
Users of the affected components should apply one of the following mitigations:
- Upgrade to a currently supported version of Spring AI.
- Configure an explicit, application-private model cache directory instead of relying on the default location, and ensure that model and tokenizer URIs come from a trusted source.
- Leverage a commercial support partner like HeroDevs for post-EOL security support.
Credits
- No finder is credited in the upstream advisory for this issue.