CVE-2026-44241
Micronaut Framework: Unbounded formattersCache in TimeConverterRegistrar Allows Memory Exhaustion via Accept-Language Header
Micronaut Framework is a JVM-based full stack Java framework designed for building modular, easily testable JVM applications. From 4.3.0 to before 4.10.22, 3.10.6, and 3.8.14, TimeConverterRegistrar caches DateTimeFormatter instances in an unbounded ConcurrentHashMap<String, DateTimeFormatter> whose key is derived from the @Format annotation pattern concatenated with the locale from the HTTP Accept-Language header. Because Locale.forLanguageTag() accepts arbitrary BCP 47 private-use extensions (en-x-a001, en-x-a002, …), an unauthenticated attacker can generate an unlimited number of unique cache keys by sending requests with novel locale tags, growing the cache until heap memory is exhausted and the JVM crashes. This vulnerability is fixed in 4.10.22, 3.10.6, and 3.8.14.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- None
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
7.5
Micronaut Framework: Unbounded formattersCache in TimeConverterRegistrar Allows Memory Exhaustion via Accept-Language Header
Micronaut Framework is a JVM-based full stack Java framework designed for building modular, easily testable JVM applications. From 4.3.0 to before 4.10.22, 3.10.6, and 3.8.14, TimeConverterRegistrar caches DateTimeFormatter instances in an unbounded ConcurrentHashMap<String, DateTimeFormatter> whose key is derived from the @Format annotation pattern concatenated with the locale from the HTTP Accept-Language header. Because Locale.forLanguageTag() accepts arbitrary BCP 47 private-use extensions (en-x-a001, en-x-a002, …), an unauthenticated attacker can generate an unlimited number of unique cache keys by sending requests with novel locale tags, growing the cache until heap memory is exhausted and the JVM crashes. This vulnerability is fixed in 4.10.22, 3.10.6, and 3.8.14.
- Record
CVE-2026-44241- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-400
- Updated
- 2026-11-02
EUVDEUVD-2026-29860
7.5
Micronaut Framework is a JVM-based full stack Java framework designed for building modular, easily testable JVM applications. From 4.3.0 to before 4.10.22, 3.10.6, and 3.8.14, TimeConverterRegistrar caches DateTimeFormatter instances in an unbounded ConcurrentHashMap<String, DateTimeFormatter> whose key is derived from the @Format annotation pattern concatenated with the locale from the HTTP Accept-Language header. Because Locale.forLanguageTag() accepts arbitrary BCP 47 private-use extensions (en-x-a001, en-x-a002, …), an unauthenticated attacker can generate an unlimited number of unique cache keys by sending requests with novel locale tags, growing the cache until heap memory is exhausted and the JVM crashes. This vulnerability is fixed in 4.10.22, 3.10.6, and 3.8.14.
- Record
EUVD-2026-29860- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-8hjv-92q9-g4xj
7.5
Micronaut has unbounded `formattersCache` in `TimeConverterRegistrar` that Allows Memory Exhaustion via `Accept-Language` Header
Summary
TimeConverterRegistrar caches DateTimeFormatter instances in an unbounded ConcurrentHashMap<String, DateTimeFormatter> whose key is derived from the @Format annotation pattern concatenated with the locale from the HTTP Accept-Language header. Because Locale.forLanguageTag() accepts arbitrary BCP 47 private-use extensions (en-x-a001, en-x-a002, …), an unauthenticated attacker can generate an unlimited number of unique cache keys by sending requests with novel locale tags, growing the cache until heap memory is exhausted and the JVM crashes. This is structurally identical to the recently patched GHSA-2hcp-gjrf-7fhc (DefaultHtmlErrorResponseBodyProvider), but TimeConverterRegistrar.formattersCache was not covered by that fix.
Details
The vulnerable cache is declared in context/src/main/java/io/micronaut/runtime/converters/time/TimeConverterRegistrar.java at line 123:
// TimeConverterRegistrar.java:123
private final Map<String, DateTimeFormatter> formattersCache = new ConcurrentHashMap<>();
The getFormatter method at line 434 inserts into this map with no eviction or size limit:
// TimeConverterRegistrar.java:434-443
private DateTimeFormatter getFormatter(String pattern, ConversionContext context) {
var key = pattern + context.getLocale(); // locale from Accept-Language header
var cachedFormatter = formattersCache.get(key);
if (cachedFormatter != null) {
return cachedFormatter;
}
var formatter = DateTimeFormatter.ofPattern(pattern, context.getLocale());
formattersCache.put(key, formatter); // NO SIZE CHECK — unbounded growth
return formatter;
}
The attacker-controlled locale flows into the cache key through this call chain:
- HTTP header parsed —
HttpHeaders.findAcceptLanguage()athttp/src/main/java/io/micronaut/http/HttpHeaders.java:766-771callsLocale.forLanguageTag(part)directly on the rawAccept-Languagevalue:
// HttpHeaders.java:766-771
default Optional<Locale> findAcceptLanguage() {
return findFirst(HttpHeaders.ACCEPT_LANGUAGE)
.map(text -> {
String part = HttpHeadersUtil.splitAcceptHeader(text);
return part == null ? Locale.getDefault() : Locale.forLanguageTag(part);
});
}
- Locale planted in ConversionContext —
AbstractRouteMatch.newContext()atrouter/src/main/java/io/micronaut/web/router/AbstractRouteMatch.java:373-378passes the request locale into the conversion context for every route argument binding:
// AbstractRouteMatch.java:373-378
private <E> ArgumentConversionContext<E> newContext(Argument<E> argument, HttpRequest<?> request) {
return ConversionContext.of(
argument,
request.getLocale().orElse(null), // ← attacker-controlled via Accept-Language
request.getCharacterEncoding()
);
}
- Unbounded cache insert — When any temporal argument annotated with
@Formatis bound,TimeConverterRegistrar.getFormatter(pattern, context)is called and inserts a newDateTimeFormatterfor each uniquepattern + localekey.
This path is triggered for any route endpoint with a @Format-annotated temporal parameter. This is an officially documented and commonly used Micronaut pattern, demonstrated in the framework's own test suite:
// test-suite/.../BindingController.java:105 (official Micronaut example)
@Get("/dateFormat")
public String dateFormat(@Format("dd/MM/yyyy hh:mm:ss a z") @Header ZonedDateTime date) {
return date.toString();
}
TimeConverterRegistrar is an @Internal core bean registered unconditionally in every Micronaut application — it is not optional or user-configured. By contrast, the DefaultHtmlErrorResponseBodyProvider cache patched in GHSA-2hcp-gjrf-7fhc now uses a ConcurrentLinkedHashMap bounded at 100 entries; TimeConverterRegistrar.formattersCache remains an unbounded plain ConcurrentHashMap.
PoC
…
- Record
GHSA-8hjv-92q9-g4xj- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-400
- Updated
- 2026-11-02
NVD
7.5
Micronaut Framework is a JVM-based full stack Java framework designed for building modular, easily testable JVM applications. From 4.3.0 to before 4.10.22, 3.10.6, and 3.8.14, TimeConverterRegistrar caches DateTimeFormatter instances in an unbounded ConcurrentHashMap<String, DateTimeFormatter> whose key is derived from the @Format annotation pattern concatenated with the locale from the HTTP Accept-Language header. Because Locale.forLanguageTag() accepts arbitrary BCP 47 private-use extensions (en-x-a001, en-x-a002, …), an unauthenticated attacker can generate an unlimited number of unique cache keys by sending requests with novel locale tags, growing the cache until heap memory is exhausted and the JVM crashes. This vulnerability is fixed in 4.10.22, 3.10.6, and 3.8.14.
- Record
CVE-2026-44241- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-400
- Updated
- 2026-11-02
OSV
7.5
Micronaut Framework: Unbounded formattersCache in TimeConverterRegistrar Allows Memory Exhaustion via Accept-Language Header
Micronaut Framework is a JVM-based full stack Java framework designed for building modular, easily testable JVM applications. From 4.3.0 to before 4.10.22, 3.10.6, and 3.8.14, TimeConverterRegistrar caches DateTimeFormatter instances in an unbounded ConcurrentHashMap<String, DateTimeFormatter> whose key is derived from the @Format annotation pattern concatenated with the locale from the HTTP Accept-Language header. Because Locale.forLanguageTag() accepts arbitrary BCP 47 private-use extensions (en-x-a001, en-x-a002, …), an unauthenticated attacker can generate an unlimited number of unique cache keys by sending requests with novel locale tags, growing the cache until heap memory is exhausted and the JVM crashes. This vulnerability is fixed in 4.10.22, 3.10.6, and 3.8.14.
- Record
CVE-2026-44241- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-400
- Updated
- 2026-11-02
OSVGHSA-8hjv-92q9-g4xj
7.5
Micronaut has unbounded `formattersCache` in `TimeConverterRegistrar` that Allows Memory Exhaustion via `Accept-Language` Header
Summary
TimeConverterRegistrar caches DateTimeFormatter instances in an unbounded ConcurrentHashMap<String, DateTimeFormatter> whose key is derived from the @Format annotation pattern concatenated with the locale from the HTTP Accept-Language header. Because Locale.forLanguageTag() accepts arbitrary BCP 47 private-use extensions (en-x-a001, en-x-a002, …), an unauthenticated attacker can generate an unlimited number of unique cache keys by sending requests with novel locale tags, growing the cache until heap memory is exhausted and the JVM crashes. This is structurally identical to the recently patched GHSA-2hcp-gjrf-7fhc (DefaultHtmlErrorResponseBodyProvider), but TimeConverterRegistrar.formattersCache was not covered by that fix.
Details
The vulnerable cache is declared in context/src/main/java/io/micronaut/runtime/converters/time/TimeConverterRegistrar.java at line 123:
// TimeConverterRegistrar.java:123
private final Map<String, DateTimeFormatter> formattersCache = new ConcurrentHashMap<>();
The getFormatter method at line 434 inserts into this map with no eviction or size limit:
// TimeConverterRegistrar.java:434-443
private DateTimeFormatter getFormatter(String pattern, ConversionContext context) {
var key = pattern + context.getLocale(); // locale from Accept-Language header
var cachedFormatter = formattersCache.get(key);
if (cachedFormatter != null) {
return cachedFormatter;
}
var formatter = DateTimeFormatter.ofPattern(pattern, context.getLocale());
formattersCache.put(key, formatter); // NO SIZE CHECK — unbounded growth
return formatter;
}
The attacker-controlled locale flows into the cache key through this call chain:
- HTTP header parsed —
HttpHeaders.findAcceptLanguage()athttp/src/main/java/io/micronaut/http/HttpHeaders.java:766-771callsLocale.forLanguageTag(part)directly on the rawAccept-Languagevalue:
// HttpHeaders.java:766-771
default Optional<Locale> findAcceptLanguage() {
return findFirst(HttpHeaders.ACCEPT_LANGUAGE)
.map(text -> {
String part = HttpHeadersUtil.splitAcceptHeader(text);
return part == null ? Locale.getDefault() : Locale.forLanguageTag(part);
});
}
- Locale planted in ConversionContext —
AbstractRouteMatch.newContext()atrouter/src/main/java/io/micronaut/web/router/AbstractRouteMatch.java:373-378passes the request locale into the conversion context for every route argument binding:
// AbstractRouteMatch.java:373-378
private <E> ArgumentConversionContext<E> newContext(Argument<E> argument, HttpRequest<?> request) {
return ConversionContext.of(
argument,
request.getLocale().orElse(null), // ← attacker-controlled via Accept-Language
request.getCharacterEncoding()
);
}
- Unbounded cache insert — When any temporal argument annotated with
@Formatis bound,TimeConverterRegistrar.getFormatter(pattern, context)is called and inserts a newDateTimeFormatterfor each uniquepattern + localekey.
This path is triggered for any route endpoint with a @Format-annotated temporal parameter. This is an officially documented and commonly used Micronaut pattern, demonstrated in the framework's own test suite:
// test-suite/.../BindingController.java:105 (official Micronaut example)
@Get("/dateFormat")
public String dateFormat(@Format("dd/MM/yyyy hh:mm:ss a z") @Header ZonedDateTime date) {
return date.toString();
}
TimeConverterRegistrar is an @Internal core bean registered unconditionally in every Micronaut application — it is not optional or user-configured. By contrast, the DefaultHtmlErrorResponseBodyProvider cache patched in GHSA-2hcp-gjrf-7fhc now uses a ConcurrentLinkedHashMap bounded at 100 entries; TimeConverterRegistrar.formattersCache remains an unbounded plain ConcurrentHashMap.
PoC
…
- Record
GHSA-8hjv-92q9-g4xj- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-400
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
io.micronaut:micronaut-contextMaven |
4.3.0 up to 4.10.22 | 4.10.22 |
io.micronaut:micronaut-contextMaven |
3.10.0 up to 3.10.6 | 3.10.6 |
io.micronaut:micronaut-contextMaven |
before 3.8.14 | 3.8.14 |
io.micronaut:micronaut-contextMaven |
4.3.0 up to 4.10.22, plus listed versions 4.10.04.10.14.10.104.10.114.10.12 Show 45 moreShow less4.10.134.10.144.10.154.10.164.10.174.10.184.10.194.10.24.10.204.10.214.10.34.10.44.10.54.10.64.10.74.10.84.10.94.3.14.3.104.3.114.3.124.3.134.3.144.3.154.3.164.3.174.3.24.3.34.3.44.3.54.3.64.3.74.3.84.3.94.4.04.4.14.4.104.4.24.4.34.4.44.4.54.4.64.4.74.4.84.4.9 |
4.10.22 |
io.micronaut:micronaut-contextMaven |
3.10.0 up to 3.10.6, plus listed versions 3.10.13.10.23.10.33.10.43.10.5 |
3.10.6 |
io.micronaut:micronaut-contextMaven |
before 3.8.14, plus listed versions 2.4.02.4.12.4.22.4.32.4.4 Show 45 moreShow less2.5.02.5.12.5.102.5.112.5.122.5.132.5.22.5.32.5.42.5.52.5.62.5.72.5.82.5.93.0.03.0.0-M13.0.0-M23.0.0-M33.0.0-M43.0.0-M53.0.0-RC13.0.13.0.23.0.33.1.03.1.13.1.23.1.33.1.43.2.03.2.13.2.23.2.33.2.43.2.53.2.63.2.73.3.03.3.0-M13.3.13.3.23.3.33.3.43.4.03.4.1 |
3.8.14 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-44241 is a high vulnerability (CVSS 7.5) in Micronaut-projects Micronaut-core, published May 6, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.64%, the 50th percentile. Fixed in io.micronaut:micronaut-context 4.10.22.
References
github.com/micronaut-projects/micronaut-core/security/advisories/GHSA-8hjv-92q9-g4xj
github.com/micronaut-projects/micronaut-core/commit/48f05ae8dc4157816fe0050c5cf730be7d44f8b3
github.com/micronaut-projects/micronaut-core/commit/c2048ab740c2efdfe227813f203176e0ef93f892
github.com/micronaut-projects/micronaut-core/commit/c6ca8782de7338732e887d090f38c9e941bcb284
github.com/micronaut-projects/micronaut-core/releases/tag/v3.10.6
github.com/micronaut-projects/micronaut-core/releases/tag/v3.8.14
Left out here: 115 versions and the rest of 2 source descriptions. The JSON record has them.