Skip to content

Fixable CVEs in stirlingtools/stirling-pdf (0 critical, 14 high) #1004

Open

Description

@github-actions

The weekly CVE scan found fixable HIGH or CRITICAL vulnerabilities in stirlingtools/stirling-pdf. This body is rewritten by every scan, and the issue closes itself when a scan finds the pinned digest clean.

The fix is normally the Dependabot bump for this image. If upstream has not released one, the fixed versions below say what to wait for.

stirlingtools/stirling-pdf:3.1.0

Stacks: sensitive. Digest: sha256:b5b9e400c086e5334a4947a0d7cd589808f743f29267d7f41c18200cf4bc48b4.

Severity ID Package Installed Fixed in
HIGH CVE-2026-89407 com.fasterxml.jackson.core:jackson-core 2.21.5 2.18.11, 2.21.7, 2.22.3
HIGH CVE-2026-89425 com.fasterxml.jackson.core:jackson-core 2.21.5 2.21.7, 2.22.3, 2.18.11
HIGH CVE-2026-68497 com.fasterxml.jackson.core:jackson-databind 2.21.5 2.18.10, 2.21.6, 2.22.2
HIGH CVE-2026-91776 com.fasterxml.jackson.core:jackson-databind 2.21.5 2.18.11, 2.21.7, 2.22.3
HIGH CVE-2026-91777 com.fasterxml.jackson.core:jackson-databind 2.21.5 2.21.7, 2.18.11, 2.22.3
HIGH CVE-2026-84782 libssl3t64 3.0.13-0ubuntu3.12 3.0.13-0ubuntu3.16
HIGH CVE-2026-84782 openssl 3.0.13-0ubuntu3.12 3.0.13-0ubuntu3.16
HIGH CVE-2026-75140 org.jsoup:jsoup 1.15.3 1.23.2
HIGH CVE-2026-75140 org.jsoup:jsoup 1.15.4 1.23.2
HIGH CVE-2026-89407 tools.jackson.core:jackson-core 3.1.5 3.1.7, 3.2.2
HIGH CVE-2026-89425 tools.jackson.core:jackson-core 3.1.5 3.1.7, 3.2.3
HIGH CVE-2026-68497 tools.jackson.core:jackson-databind 3.1.5 3.2.2, 3.1.6
HIGH CVE-2026-91776 tools.jackson.core:jackson-databind 3.1.5 3.1.7, 3.2.3
HIGH CVE-2026-91777 tools.jackson.core:jackson-databind 3.1.5 3.1.7, 3.2.3

Written by .github/workflows/cve-scan.yml in this run.

Activity

  1. added
    securityA credential, an exposure, or a control that is weaker than recorded
    sensitivestacks/sensitive on trinity (VLAN 99)
    on Oct 7, 2026
  2. Gerrrt commented on Oct 10, 2026

    @Gerrrt
    Owner

    Triage, 2026-10-10: waiting on upstream. Nothing to change here, and the next bump won't fix this.

    • A newer release exists, but it doesn't clear these findings. Stirling-PDF 3.1.0 came out on 2026-10-05, after Dependabot's last run, so its PR should open on Sunday. I scanned stirlingtools/stirling-pdf:3.1.0@sha256:b5b9e400… (the current :latest) with the same trivy invocation scripts/scan-images.sh uses (aquasec/trivy:0.75.0, --severity HIGH,CRITICAL --ignore-unfixed). It reports the same 14 HIGH findings as the pinned 3.0.2: jackson-core/databind 2.21.5 and 3.1.5, jsoup 1.15.3/1.15.4, and Ubuntu's openssl 3.0.13-0ubuntu3.12. Merging that PR won't close this issue. Review it as a feature release on its own merits (it adds Stirling Office Convert, beta).
    • Upstream is partway there. On main, Jackson 2 is forced to 2.22.3 (build(deps): bump jackson2Version from 2.22.2 to 2.22.3 Stirling-Tools/Stirling-PDF#8265, merged 2026-09-30), but 3.1.0 still ships 2.21.5. Jackson 3 and the base image are pinned by Pin Netty, Jackson and base image Python deps past the Spring BOM Stirling-Tools/Stirling-PDF#8252, which is still open. I found nothing upstream for jsoup 1.23.2 or the base image's openssl.
    • What to wait for: a Stirling-PDF release after 3.1.0 that includes Pin Netty, Jackson and base image Python deps past the Spring BOM Stirling-Tools/Stirling-PDF#8252 and a rebuilt base image. Its Dependabot PR on stacks/sensitive gets merged, homelab-converge-sensitive deploys it within the hour (Nothing converges stacks/sensitive: the tier with the real data is the only one deployed by hand #533), and the next scan closes this issue if the new digest is clean.
    • Exposure while we wait: Stirling has no published port and is reachable only through Caddy, by the household. It runs as uid 1001 with no capabilities and a read-only root filesystem. Every document lives in a memory-backed /tmp, and LibreOffice runs under Landlock and seccomp (STIRLING_LO_SANDBOX=required). The findings are in parsers (Jackson's JSON and jsoup's HTML), so the input that could reach them is what a household member uploads. With 0 critical findings, we're not building and patching our own image. We take upstream's release instead.
  3. Gerrrt commented on Oct 10, 2026

    @Gerrrt
    Owner

    This is waiting on upstream. Nothing in the repo needs to change.

    • The pinned digest is current: stirlingtools/stirling-pdf:3.0.2 (sha256:c1959253…). pin-digests.sh reports no drift, so the tag hasn't been rebuilt since it was pinned.
    • Docker Hub has 3.1.0, but I scanned it with trivy 0.75.0 and the same flags: the same 14 HIGH.
    • The fixed versions exist (table above), but no published image includes them yet. Mostly:
      • com.fasterxml.jackson.core:jackson-databind: 3 findings, fixed in 3 different versions (see the table)
      • tools.jackson.core:jackson-databind: 3 findings, fixed in 2 different versions (see the table)
      • com.fasterxml.jackson.core:jackson-core: 2 findings, fixed in 2 different versions (see the table)
      • org.jsoup:jsoup: 2 findings, fixed in 1.23.2
      • tools.jackson.core:jackson-core: 2 findings, fixed in 2 different versions (see the table)
      • libssl3t64: 1 finding, fixed in 3.0.13-0ubuntu3.16
      • and 1 more across 1 other packages

    When upstream rebuilds or ships a release with these fixes, Dependabot should open the bump. Check it with trivy and check_image_pins.py, then deploy. The next scan will then close this issue.

  4. Gerrrt commented on Oct 10, 2026

    @Gerrrt
    Owner

    Rechecked 2026-10-10. Upstream hasn't moved: 3.1.0 is still the latest release (digest sha256:b5b9e400…, unchanged), and Stirling-Tools/Stirling-PDF#8252 is still open. Dependabot's #1138 (3.0.2 → 3.1.0) is open with CI green. As triaged above, it doesn't change this issue's findings.

    jsoup was the one finding without a known upstream owner. It has one now. Scanning 3.1.0 with our pinned trivy gives its two copies:

    Copy Path in image Comes from
    1.15.3 inside app/lib/jinjava-2.8.4.jar HubSpot's jinjava shades jsoup (relocated to jinjava.org.jsoup), so no Gradle force in Stirling can replace it
    1.15.4 app/lib/jsoup-1.15.4.jar the unshaded jar, resolved from Stirling's dependency graph

    jinjava 2.8.4 is the latest on Maven Central. The fix is HubSpot/jinjava#1337 ("Bump jsoup to 1.23.2"), which is open, along with issues HubSpot/jinjava#1335 and #1342. So jsoup clears only after a jinjava release ships that change and Stirling bumps jinjavaVersion.

    Updated wait-list, all already reported upstream, so nothing to file:

  5. Gerrrt commented on Oct 10, 2026

    @Gerrrt
    Owner

    The 14 HIGHs, traced, 2026-10-10

    There are no criticals (a fresh trivy 0.75.0 scan of the pinned stirling-pdf:3.0.2@sha256:c1959253… agrees). The 14 HIGH rows are 7 CVEs, because Jackson 2.21.5 and Jackson 3.1.5 each carry the same five. For each advisory I took its precondition and searched the bytecode of Stirling's own jars (app.jar, proprietary-3.0.2-plain.jar, common-3.0.2-plain.jar) and all 410 jars in /app/lib. I also mapped the paths Stirling serves before login (RequestUriUtils, UserAuthenticationFilter). With SECURITY_ENABLELOGIN: "true" set, those are auth/login, auth/refresh, invite/*, webhooks/, workflow/participant/ and some GET info routes.

    Result: nothing is reachable without a login or a signing invitation. One Jackson CVE (a CPU DoS) is reachable by a signing participant or by someone who can create a signing workflow.

    CVE Package What the bug needs What 3.0.2 does Reachable?
    CVE-2026-89425 jackson-core A parser built with JsonFactory.createParser(DataInput) No class in any of the 410 jars calls a createParser(DataInput) overload. No
    CVE-2026-68497 jackson-databind Binding JSON into javax.xml.datatype.Duration/XMLGregorianCalendar fields No Stirling class declares either type. No
    CVE-2026-91776 jackson-databind @JsonTypeInfo(use = NAME, defaultImpl = …) (a fallback for unknown type IDs) Four Stirling classes use @JsonTypeInfo with NAME (Condition, ConditionInput, Schedule, AiEngineProgressDetail). None sets defaultImpl, so an unknown ID fails instead of being cached. No
    CVE-2026-91777 jackson-databind @JsonIdentityInfo collections or maps with forward references No Stirling class uses @JsonIdentityInfo. No
    CVE-2026-89407 jackson-core (regex in NumberInput.looksLikeValidNumber) Jackson coercing a JSON string into a Double/Float/BigDecimal/Number (databind's NumberDeserializers/StdDeserializer, and Jackson 3's StringNode) Only one request path binds a string into numeric fields: POST /api/v1/workflow/participant/submit-signature. Its wetSignaturesData is read into WetSignatureMetadata, which has Double x, y, width and height. That route is login-exempt, but submitSignature first runs ensureSigningEnabled, then findByShareToken(participantToken) (else 404), then rejects expired or completed participants and inactive sessions, and only after that parses the JSON. No other @RequestBody controller binds a DTO with numeric fields. The login DTOs (UsernameAndPassMfa, MfaCodeRequest) have none. Only with a valid participant token for an active signing session, or for a logged-in user who creates one. The effect is CPU DoS of Stirling, not data access.
    CVE-2026-75140 jsoup 1.15.4, and 1.15.3 shaded in jinjava jsoup's XML tree builder (XmlTreeBuilder / Parser.xmlParser()) on deeply nested, uniquely-namespaced input The only class outside jsoup that calls jsoup is flexmark's FlexmarkHtmlConverter (HTML to Markdown), which uses the HTML parser, never xmlParser/XmlTreeBuilder. The jinjava copy is used only by AiDocumentHtmlRenderer, and we run with AIENGINE_ENABLED: "false". No
    CVE-2026-84782 Ubuntu openssl 3.0.13 A DTLS handshake with a suspended write Stirling is Java (JSSE). OpenSSL in the image serves LibreOffice, Python and the CLI tools, none of which speaks DTLS. No

    What that leaves. One CPU-DoS path, open to whoever holds a signing link, or to a household member who can create one. Stirling isn't published and is reachable only through Caddy at pdf.matrix.elysium, and it has a CPU ceiling. This doesn't change the triage above: no rebuild, and take the upstream release that brings Jackson 2.22.3/3.1.7 (Stirling-Tools/Stirling-PDF#8265 is merged and #8252 is open).

    What would change this:

    • Enabling the AI engine, which brings the jinjava/jsoup path back.
    • Any Stirling release that adds a JSON endpoint with numeric fields before login.
    • Sending signing links to people outside the household.

    To reproduce:

    IMG=stirlingtools/stirling-pdf:3.0.2@sha256:c195925341aa35ee5932d9c225edef48a92e46a1280963180d79619cb5f29e6e
    # run python3 inside the image (--network none) over /app/app.jar and /app/lib/*.jar, matching class bytes for
    #   createParser(Ljava/io/DataInput;), Ljavax/xml/datatype/(Duration|XMLGregorianCalendar);, JsonIdentityInfo, JsonTypeInfo+defaultImpl,
    #   org/jsoup/parser/XmlTreeBuilder|xmlParser, and the field table of workflow/dto/WetSignatureMetadata

    🤖 Traced by Claude Code

  6. Gerrrt commented on Oct 10, 2026

    @Gerrrt
    Owner

    The signing path, traced to the end, 2026-10-10: CVE-2026-89407 isn't reachable either

    This follows up the trace above, which left one Jackson CVE as "reachable only with a valid participant token" through POST /api/v1/workflow/participant/submit-signature. I traced that path to the end against the running sensitive-stirling-pdf on trinity. The signing feature is off, so the path closes before any JSON is parsed, for anonymous and logged-in users alike.

    The gate. WorkflowSessionService.ensureSigningEnabled() reads storage.enabled and storage.signing.enabled and throws 403 "Group signing is disabled" unless both are true. The live /configs/settings.yml has storage.enabled: false and storage.signing.enabled: false, and no STORAGE_* environment variable overrides them.

    Every route hits the gate first. All 21 mapped workflow endpoints call ensureSigningEnabled as their first invocation:

    • WorkflowParticipantController: getSessionByToken, getParticipantDetails, submitSignature, declineParticipation, getDocument, validateCertificate.
    • SigningSessionController: listSessions, createSession, getSession, deleteSession, addParticipants, removeParticipant, getSessionPdf, finalizeSession, getSignedPdf, listSignRequests, getSignRequestDetail, getSignRequestDocument, signDocument, declineSignRequest, validateCertificate.

    What sits behind it, for when signing is on. submitSignature does these in order:

    1. ensureSigningEnabled;
    2. checks participantToken is present;
    3. findByShareToken (404 otherwise);
    4. rejects an expired participant, a completed participant, or an inactive session;
    5. calls buildSubmissionMetadata.

    buildSubmissionMetadata parses wetSignaturesData with Jackson 3 (tools.jackson.databind.ObjectMapper) into WetSignatureMetadata. That has Double x/y/width/height, which is where a string-to-number coercion would hit the CVE-2026-89407 regex. Creating a session (SigningSessionController) needs a login and the same gate.

    Updated verdict for #1004: none of the 14 HIGHs is reachable as deployed. The previous comment's one exception is closed by storage.signing.enabled: false.

    What would change this: turning on storage.enabled together with storage.signing.enabled (both marked ALPHA in Stirling's settings). If that's ever wanted, take the Jackson 3.1.7 release first.

    To reproduce:

    docker exec sensitive-stirling-pdf sh -c 'env | grep -i ^STORAGE_; awk "/^storage:/{p=1} p&&/^[^ #]/&&!/^storage:/{p=0} p" /configs/settings.yml | grep -E "^  (enabled|signing):|^    enabled:"'

    🤖 Traced by Claude Code

  7. Gerrrt commented on Oct 11, 2026

    @Gerrrt
    Owner

    Re-traced on 3.1.0, 2026-10-11, with a correction to the 3.0.2 verdict

    #1138 moved Stirling-PDF to 3.1.0@sha256:b5b9e400…, and it has been running on trinity since 2026-10-10 22:29 UTC. A fresh trivy 0.75.0 scan gives the same 14 HIGH rows (7 CVEs) as 3.0.2. I re-ran the whole trace against 3.1.0 (own jars app.jar, proprietary-3.1.0-plain.jar, common-3.1.0-plain.jar, plus all of /app/lib), and re-checked the live settings.

    Unchanged from 3.0.2:

    • Not before login.
    • No createParser(DataInput) (89425), and no javax.xml.datatype fields (68497).
    • @JsonTypeInfo is only on Condition/ConditionInput/Schedule/AiEngineProgressDetail, none with defaultImpl (91776). No @JsonIdentityInfo (91777).
    • No jsoup XML parser anywhere (75140). jinjava is only in AiDocumentHtmlRenderer, and AIENGINE_ENABLED=false.
    • OpenSSL's CVE is DTLS (84782).
    • The login-exempt paths are the same set. The only JSON controller there binding numeric fields is WorkflowParticipantController, which is still behind ensureSigningEnabled. Live, storage.enabled, storage.signing.enabled and storage.sharing.enabled are all false, with SECURITY_ENABLELOGIN=true.

    Correction: CVE-2026-89407 is reachable after login, on 3.0.2 and 3.1.0 alike. The earlier comments checked only @RequestBody DTOs, but 94 of Stirling's own classes run ObjectMapper themselves on JSON form fields. Here are the triggers in jackson-databind:

    • 2.x calls looksLikeValidNumber from NumberDeserializer.deserialize and StdDeserializer._parseFloatPrimitive, plus the Double/Float/BigDecimal deserializers.
    • 3.x calls it from those too, and from every StringNode._tryParseAs* (Integer, Long, BigInteger, Float, Double, BigDecimal). So tree-model code reading a JSON string node as any number hits the regex.

    Two ordinary tools do that with user input, both on Jackson 3 and identical in 3.0.2:

    • Form fill (FormFillController, /api/v1/form, then FormPayloadParser) reads numbers from parsed JSON tree nodes.
    • PDF↔JSON (PdfJsonConversionService) binds user JSON into PdfJson* types with float/double fields.

    So a logged-in Stirling user can submit a long digit-run string where a number is expected and burn CPU quadratically in the request thread. This is from the code structure; I didn't exercise it. The other 13 classes found by the same search sit behind features that are off or unconfigured: AI classify and workflows, vector DB and external API integrations, the license check, policy store, account linking, math auditor and portal.

    Exposure: a Stirling login is required, and accounts are the household's. Stirling is capped at cpus: 2, so it degrades Stirling only, not trinity. There's no data access. This is the same class of finding as "authenticated users can make the service do expensive work", and it doesn't change the decision: take the upstream Jackson 3.1.7/2.21.7 release when it ships. #1004's open items are still Stirling-Tools/Stirling-PDF#8252 (Jackson 3 and the base image) and a release containing it.

    To reproduce:

    IMG=stirlingtools/stirling-pdf:3.1.0@sha256:b5b9e400c086e5334a4947a0d7cd589808f743f29267d7f41c18200cf4bc48b4
    # python3 in the image (--network none): in jackson-databind-3.1.5.jar, StringNode._tryParseAs* and StdDeserializer._parseFloatPrimitive
    # reference looksLikeValidNumber; in app.jar, FormFillController (/api/v1/form) and FormPayloadParser use tools/jackson/databind + JsonNode number reads
    docker exec sensitive-stirling-pdf sh -c 'env | grep -E "^(AIENGINE_|SECURITY_ENABLELOGIN)"; grep -nE "^  enabled:|^  signing:|^    enabled:" /configs/settings.yml | sed -n 1,6p'

    🤖 Traced by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    securityA credential, an exposure, or a control that is weaker than recordedsensitivestacks/sensitive on trinity (VLAN 99)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions