CVE-2026-47851

Denial of Service
Affects
Spring AI
in
Spring
No items found.
Versions
>=1.0.0 <=1.0.9, >=1.1.0 <=1.1.8, 2.0.0
Exclamation circle icon
Patch Available

This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.

Overview

Spring AI is the Spring project's application framework for AI engineering, bringing Spring's portability and modular design to AI application development. It provides portable abstractions over chat models, embedding models and vector stores, along with the retrieval-augmented generation building blocks that feed them, including document readers that parse source files such as PDFs into the Document objects an application later chunks, embeds and indexes.

A Denial of Service (DoS) vulnerability (CVE-2026-47851) has been identified in the Spring AI PDF document reader, which allows attackers to exhaust the ingesting thread's stack and terminate document processing by supplying a PDF whose table of contents is deeply nested or contains a circular reference.

Per OWASP: The Denial of Service (DoS) attack is focused on making a resource (site, application, server) unavailable for the purpose it was designed. There are many ways to make a service unavailable for legitimate users by manipulating network packets, programming, logical, or resources handling vulnerabilities, among others. If a service receives a very large number of requests, it may cease to be available to legitimate users. In the same way, a service may stop if a programming vulnerability is exploited, or the way the service handles resources it uses.

This issue affects the PDF document reader of Spring AI.

Details

Module Info

Vulnerability Info

This High-severity vulnerability is found in the org.springframework.ai:spring-ai-pdf-document-reader package in the PDF document reader of Spring AI.

ParagraphPdfDocumentReader builds its paragraph tree from the PDF's document outline, the structure a PDF viewer renders as a table of contents. ParagraphManager walks that outline recursively, descending one Java stack frame per outline level, with no bound on how deep it will go and no record of which outline nodes it has already visited:

protected Paragraph generateParagraphs(Paragraph parentParagraph, PDOutlineNode bookmark, Integer level)
        throws IOException {

    PDOutlineItem current = bookmark.getFirstChild();

    while (current != null) {

        int pageNumber = getPageNumber(current);
        var nextSiblingNumber = getPageNumber(current.getNextSibling());
        if (nextSiblingNumber < 0) {
            nextSiblingNumber = getPageNumber(current.getLastChild());
        }

        var paragraphPosition = (current.getDestination() instanceof PDPageXYZDestination)
                ? ((PDPageXYZDestination) current.getDestination()).getTop() : 0;

        var currentParagraph = new Paragraph(parentParagraph, current.getTitle(), level, pageNumber,
                nextSiblingNumber, paragraphPosition);

        parentParagraph.children().add(currentParagraph);

        // Recursive call to go the current paragraph's children paragraphs.
        // E.g. go one level deeper.
        this.generateParagraphs(currentParagraph, current, level + 1);

        current = current.getNextSibling();
    }
    return parentParagraph;
}

The level parameter is carried purely for labelling: it is stored on each Paragraph and never compared against a ceiling. Nothing tracks the identity of the outline dictionaries already traversed either, so a PDF whose outline items point back at an ancestor is walked forever rather than being rejected as malformed. Both properties are attacker-controlled, because an outline is ordinary PDF file structure that any producer can emit and that no viewer needs to render for the reader to parse it.

The flattening pass that turns the finished tree into the reader's output recurses over the same structure with the same absence of a bound:

public List<Paragraph> flatten() {
    List<Paragraph> paragraphs = new ArrayList<>();
    for (var child : this.rootParagraph.children()) {
        flatten(child, paragraphs);
    }
    return paragraphs;
}

private void flatten(Paragraph current, List<Paragraph> paragraphs) {
    paragraphs.add(current);
    for (var child : current.children()) {
        flatten(child, paragraphs);
    }
}

A hostile or merely malformed PDF therefore drives generateParagraphs, and in the deeply-nested-but-acyclic case flatten as well, past the JVM's stack limit, raising a StackOverflowError on the thread performing ingestion. An Error is not caught by the reader's catch (Exception e) handler, so it propagates out of the reader and unwinds whatever was driving the ingestion. Any application that indexes documents supplied by users, fetched from shared storage, or pulled from a crawl reaches this path with a single upload and no authentication or user interaction, which matches the advisory's network-attackable, availability-only impact.

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.
  • If upgrading is not immediately possible, avoid ParagraphPdfDocumentReader for untrusted input and use a reader that does not walk the document outline, such as PagePdfDocumentReader or TikaDocumentReader.
  • Leverage a commercial support partner like HeroDevs for post-EOL security support.

Credits

  • No finder is credited in the upstream advisory for this issue.
Vulnerability Details
Severity
Level
CVSS Assessment
Low
>=0 <4
Medium
>=4 <6
High
>=6 <8
Critical
>=8 <10
High
ID
CVE-2026-47851
PROJECT Affected
Spring AI
Versions Affected
>=1.0.0 <=1.0.9, >=1.1.0 <=1.1.8, 2.0.0
NES Versions Affected
Published date
August 31, 2026
≈ Fix date
August 31, 2026
Category
Denial of Service
Vex Document
Download VEXHow do I use it?
Sign up for the latest vulnerability alerts fixed in
NES for Spring
Rss feed icon
Subscribe via RSS
or

By submitting the form I acknowledge receipt of our Privacy Policy.

Thanks for signing up for our Newsletter! We look forward to connecting with you.
Oops! Something went wrong while submitting the form.