
This repository provides a comprehensive security remediation of denial-of-service and allocation of resources without limits or throttling security vulnerabilities reported in CVE-2025-52999, GHSA-2m67-wjpj-xhg9 and sonatype-2022-6438 while maintaining full compatibility with jackson‑core 2.13.5.
Данная ветка (2.13.5-CVE-2025-52999-sonatype-2022-6438-GHSA-2m67-wjpj-xhg9) содержит комплексное устранение уязвимостей типа «отказ в обслуживании» (DoS) и «выделение ресурсов без ограничений или регулирования», затрагивающих jackson-core 2.13.5. В ней представлен API StreamReadConstraints —
совместимый с API, введённым в jackson-core 2.15.0, но расширенный более широким охватом парсеров
и дополнительными защитами от векторов атак — устраняющий атаку исчерпания глубины вложенности
(CVE-2025-52999), проблему выделения ресурсов без ограничений или регулирования (SNYK-JAVA-COMFASTERXMLJACKSONCORE-15365924), обход ограничения длины документа (SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 / GHSA-2m67-wjpj-xhg9) и атаку исчерпания длины числового токена (Sonatype-2022-6438), оставаясь при этом совместимым с публичной поверхностью API jackson-core версии 2.13.5.
| Ветка | Устранённые уязвимости |
|---|---|
2.13.5-CVE-2025-52999-sonatype-2022-6438 | CVE-2025-52999, Sonatype-2022-6438, SNYK-JAVA-COMFASTERXMLJACKSONCORE-15365924 |
2.13.5-CVE-2025-52999-sonatype-2022-6438-GHSA-2m67-wjpj-xhg9 | Все вышеуказанные + GHSA-2m67-wjpj-xhg9 (обход ограничения длины документа) |
Исходная ветка (2.13.5-CVE-2025-52999-sonatype-2022-6438) устраняет три уязвимости
и сохранена на удалённом репозитории sasso. Данная ветка расширяет её дополнительным устранением
GHSA-2m67-wjpj-xhg9,
которая применяет maxDocumentLength во всех путях парсера.
Запись NVD
Официальное описание NVD
jackson-core содержит базовые низкоуровневые инкрементальные ("потоковые") абстракции парсера и генератора, используемые Jackson Data Processor. В версиях до 2.15.0, если пользователь разбирает входной файл, содержащий глубоко вложенные данные, Jackson может выбросить
StackOverflowErrorпри особенно большой глубине. jackson-core 2.15.0 содержит настраиваемый лимит того, насколько глубоко Jackson будет проходить по входному документу; по умолчанию допустимая глубина составляет 1 000. jackson-core выбрасываетStreamConstraintsExceptionпри достижении лимита. jackson-databind также выигрывает от этого изменения, поскольку использует jackson-core для разбора JSON-входных данных. В качестве меры предосторожности пользователям следует избегать разбора входных файлов из ненадёжных источников.
Мера предосторожности: избегайте разбора JSON-входных данных из ненадёжных источников до развёртывания исправленной версии.
Основная причина: До версии 2.15.0 JsonParser не накладывал ограничений на глубину вложенности
JSON-документа. Каждый токен массива [ или объекта { вызывал JsonReadContext.createChildArrayContext() /
createChildObjectContext(), выделяя новый узел контекста в куче и увеличивая цепочку ссылок.
Злоумышленник может создать документ с десятками тысяч уровней вложенности, что приведёт к исчерпанию
стека потока или памяти кучи Java Virtual Machine.
Уязвимый путь кода:
Та же самая отсутствующая проверка достигается через каждую из четырёх реализаций парсера:``` JsonParser.nextToken() // common entry point │ ├─ ReaderBasedJsonParser → _parsePunctuationMark() ├─ UTF8StreamJsonParser → _parsePunctuationMark() ├─ UTF8DataInputJsonParser → _parsePunctuationMark() └─ NonBlockingJsonParserBase → _startArrayScope() / _startObjectScope() │ ▼ _parsingContext.createChildArrayContext() // '[' encountered _parsingContext.createChildObjectContext() // '{' encountered ⚠ no depth check — context chain grows without bound
Все четыре реализации `JsonParser` имеют этот недостаток. Атака одинаково эксплуатируема через любую из них — независимо от того, поступают ли данные через `InputStream`, `Reader`, `DataInput` или асинхронный неблокирующий API подачи.
**Векторы атаки:**
| # | Стратегия | Пример полезной нагрузки | Приращение глубины | Затронутые парсеры |
|---|-----------|--------------------------|--------------------|---------------------|
| 1 | Вложенность массивов | `[[[…]]]` — 1,001 последовательных токенов `[` | +1 за каждый `[` | Все четыре |
| 2 | Вложенность объектов | `{"k":{"k":{…}}}` — 1,001 последовательных токенов `{` | +1 за каждый `{` | Все четыре |
| 3 | Чередующаяся вложенность | `[{"k":[{"k":…}]}]` — 1,001 смешанных токенов `[`/`{` | +1 за каждый `[` или `{` | Все четыре |
Ключевые наблюдения:
- **Вложенность массивов — минимальные накладные расходы:** требуются только токены `[` и `]`; не требуется ключей, значений или пробелов. Полезная нагрузка размером 2 002 байта из 1 001 пары скобок достаточна для превышения лимита по умолчанию в 1 000.
- **Вложенность объектов — усиленное давление на кучу:** каждый `{` дополнительно выделяет слот ключа `JsonReadContext` поверх узла цепочки глубины, усугубляя потребление памяти на экстремальных глубинах.
- **Чередующаяся вложенность — обход межсетевого экрана веб-приложений (WAF):** средства защиты на основе сопоставления шаблонов, которые обнаруживают повторяющиеся последовательности `[[[` или `{{{`, не замечают чередующейся вложенности токенов; счетчик глубины парсера увеличивается одинаково независимо от типа токена.
- **Неблокирующий подаватель — та же полезная нагрузка, другая поверхность доставки:** все три стратегии вложенности одинаково эксплуатируемы через `NonBlockingJsonParser` и `ByteArrayFeeder` API. Документ может быть доставлен сколь угодно малыми частями; `NonBlockingJsonParserBase._startArrayScope()` / `_startObjectScope()` увеличивают счетчик глубины для каждого токена, открывающего контекст, независимо от того, как поступают байты, накапливая глубину при нескольких вызовах `feedInput()`.
Все три стратегии вложенности доступны через любую из четырех реализаций `JsonParser`. В версии 2.13.5 разбор выполняется молча; с этим исправлением все три вектора вызывают `StreamConstraintsException: Depth (1001) exceeds the maximum allowed nesting depth (1000)`.
---
### Sonatype-2022-6438 — Неограниченная длина числового токена
**Детали безопасности**
| Поле | Значение |
|------|----------|
| ID Sonatype | [sonatype-2022-6438](https://guide.sonatype.com/vulnerability/sonatype-2022-6438/security-details) |
| Описание | jackson-core — Отказ в обслуживании (DoS) |
| Опубликовано | 2022-12-07 |
| Источник | Sonatype |
| Оценка CVSS v3.1 | **7.5 ВЫСОКИЙ** — `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H` |
| CWE | [CWE-770](https://cwe.mitre.org/data/definitions/770.html) — Выделение ресурсов без ограничений или регулирования |
| Оценка EPSS | 0% |
| PR исправлений в апстриме | [jackson-core#827](https://github.com/FasterXML/jackson-core/pull/827), [jackson-core#846](https://github.com/FasterXML/jackson-core/pull/846) |
**Уязвимые методы (по данным Sonatype)**
| Метод | Примечания |
|-------|------------|
| `com.fasterxml.jackson.core.base.ParserBase._parseSlowInt(I)V` | Уязвимый параметр: индекс 0 |
| `com.fasterxml.jackson.core.base.ParserBase.convertNumberToBigDecimal()V` | |
| `com.fasterxml.jackson.core.base.ParserMinimalBase.getValueAsDouble(D)D` | Уязвимый параметр: индекс 0 |
| `com.fasterxml.jackson.core.util.TextBuffer.contentsAsDecimal()` | Возвращает `BigDecimal` |
| `com.fasterxml.jackson.core.util.TextBuffer.contentsAsDouble(Z)D` | |
| `com.fasterxml.jackson.core.util.TextBuffer.contentsAsFloat(Z)F` | |
Каждый из этих методов обрабатывает необработанный буфер цифр без предварительной проверки его длины. Передача достаточно длинного числового токена вызывает неограниченное выделение кучи и истощение ЦП, когда JVM пытается создать экземпляр `BigInteger` или `BigDecimal` из неограниченного содержимого буфера.
---
**Основная причина:** До версии 2.15.0 `JsonParser` не накладывал ограничений на длину байтов для токенов целых чисел, научной нотации, простых чисел с плавающей запятой или составных чисел с плавающей запятой. Когда парсер выделяет свой внутренний `_textBuffer` для накопления цифр, злоумышленник может предоставить число с миллионами цифр, в результате чего буфер будет расти без ограничений и в конечном итоге исчерпает память кучи.
**Уязвимые пути кода:**
Существуют два структурно различных уязвимых пути — один общий для трех синхронных парсеров, и второй независимый путь через асинхронный парсер.
**Путь A — синхронные парсеры** (три реализации, один общий приемник):```
JsonParser.nextToken() // common entry point
│
├─ ReaderBasedJsonParser ─┐
├─ UTF8StreamJsonParser ├─→ ParserBase.resetInt() / resetFloat()
└─ UTF8DataInputJsonParser ─┘ │
▼
_textBuffer.contentsAsString()
⚠ no length check — buffer grows without bound
Путь B — асинхронный парсер (независимый путь кода, отдельно незащищённый):``` NonBlockingJsonParser.nextToken() │ ├─ _startPositiveNumber() / _startNegativeNumber() // integer paths ├─ _finishNumberIntegralPart() ├─ _startFloat() // floating-point paths ├─ _finishFloatFraction() └─ _finishFloatExponent() │ ▼ writes _intLength / _fractLength / _expLength directly ⚠ never calls ParserBase.resetInt() / resetFloat() ⚠ constraint validation bypassed entirely
Неблокирующий (асинхронный) парсер представляет собой особенно заметную поверхность атаки: он содержит собственные
циклы накопления цифр в `_startPositiveNumber`, `_startNegativeNumber`,
`_finishNumberIntegralPart`, `_startFloat`, `_finishFloatFraction` и `_finishFloatExponent`,
которые устанавливают `_intLength` / `_fractLength` / `_expLength` напрямую, минуя
`ParserBase.resetInt()` или `resetFloat()`. Это означает, что исправление из вышестоящего PR #827 (которое добавляло
проверку только в `ParserBase`) **оставило `NonBlockingJsonParser` полностью незащищённым**.
Это было обнаружено в ходе расширенного анализа поверхности атаки и исправлено в этой ветке.
**Векторы атаки:**
| # | Форма токена | Пример полезной нагрузки | `intLen` | `fractLen` | `expLen` | Всего | Ограничение |
|---|--------------|--------------------------|----------|------------|----------|-------|-------------|
| 1 | Целое число | `999…` — 199 999 последовательных цифр | 199 999 | 0 | 0 | 199 999 | `validateIntegerLength` |
| 2 | Десятичная дробь | `0.999…` — 1-цифровое целое, 1 001-цифровая дробь | 1 | 1 001 | 0 | 1 002 | `validateFPLength` |
| 3 | Научная нотация | `1e999…` — 1-цифровая мантисса, 1 001-цифровой порядок | 1 | 0 | 1 001 | 1 002 | `validateFPLength` |
| 4 | Составное число с плавающей точкой | `0.999…e999…` — 500-цифровая дробь, 500-цифровой порядок | 1 | 500 | 500 | 1 001 | `validateFPLength` |
Ключевые наблюдения:
- **Целое число — знак не является цифрой:** ведущий `-` исключается из накопления цифр; `-999…` и `999…` дают одинаковые значения `intLen` и срабатывают при одном и том же пороге.
- **Десятичная дробь — короткое целое, неограниченная дробь:** целая часть может состоять из одной цифры (0), а дробная часть может расти неограниченно; сама десятичная точка не учитывается.
- **Научная нотация — компактная, но катастрофическая:** при ~1 004 байтах это самая маленькая эффективная полезная нагрузка; она принуждает к созданию `BigDecimal` с порядком ±10^1001, что требует неограниченного выделения промежуточной памяти, несмотря на малый размер токена.
- **Составное число с плавающей точкой — обход раздельного ограничения:** при `fractLen = 500` и `expLen = 500` ни один из компонентов по отдельности не достигает порога в 1 000 цифр. Единая проверка `validateFPLength(intLen + fractLen + expLen)` — единственная защита, закрывающая этот пробел.
- **Неблокирующий парсер — независимый обход:** все четыре формы токена, указанные выше, независимо эксплуатируемы через `NonBlockingJsonParser` с помощью `ByteArrayFeeder`. В отличие от синхронных парсеров, `NonBlockingJsonParser` накапливает цифры в частных циклах (`_startPositiveNumber`, `_finishNumberIntegralPart`, `_startFloat`, `_finishFloatFraction`, `_finishFloatExponent`), которые напрямую записывают `_intLength` / `_fractLength` / `_expLength`, полностью минуя `ParserBase.resetInt()` и `resetFloat()`. Вышестоящий PR #827, который модифицировал только `ParserBase`, поэтому оставил этот парсер полностью незащищённым, что потребовало шести независимых исправлений в точках вызова в рамках данной коррекции.
Все четыре формы токена и обход неблокирующего парсера отклоняются на этапе токенизации — до того, как будет создан какой-либо `BigDecimal` или `BigInteger` —
с помощью `validateIntegerLength` и `validateFPLength` в `ParserBase` (синхронные
парсеры) и в шести специализированных точках вызова в `NonBlockingJsonParser` (асинхронный парсер). В версии 2.13.5 все четыре формы токена молча принимаются; после данной коррекции каждый парсер выбрасывает
`StreamConstraintsException: Number length (N) exceeds the maximum length (1000)`.
> **Примечание по `UTF8DataInputJsonParser` с большими полезными нагрузками:** Парсер DataInput имеет
> существующее ограничение внутреннего буфера в 65 536 байт ([jackson-core#493](https://github.com/FasterXML/jackson-core/issues/493)).
> Документы, превышающие этот размер (например, полезные нагрузки PoC из 199 999 цифр ≈ 200 КБ), вызывают
> `ArrayIndexOutOfBoundsException` до того, как сработает ограничение длины. Защита по длине числа
> для `UTF8DataInputJsonParser` поэтому проверяется на более коротких полезных нагрузках
> (≤ 1 001 цифра), где ошибка всё ещё воспроизводится.
---
### SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 / GHSA-2m67-wjpj-xhg9 — Обход ограничения длины документа
**Сведения об уязвимости**
| Поле | Значение |
|------|----------|
| Snyk ID | [SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551](https://security.snyk.io/vuln/SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551) |
| GitHub Advisory | [GHSA-2m67-wjpj-xhg9](https://github.com/FasterXML/jackson-core/security/advisories/GHSA-2m67-wjpj-xhg9) |
| Описание | Выделение ресурсов без ограничений или регулирования |
| Дата раскрытия | 2026-04-04 |
| Оценка CVSS v4.0 | **8.7 ВЫСОКИЙ** |
| CWE | [CWE-770](https://cwe.mitre.org/data/definitions/770.html) — Выделение ресурсов без ограничений или регулирования |
| Затронутые версии | [2.8.0, 2.21.2) |
| Исправление в основной ветке | jackson-core 2.18.7, 2.21.2 или новее |
| Коммиты с исправлением | [74c9ee25](https://github.com/FasterXML/jackson-core/commit/74c9ee25) (3.x), [7ce3622f](https://github.com/FasterXML/jackson-core/commit/7ce3622f) (2.18.x) |
**Основная причина:** Даже если `StreamReadConstraints.maxDocumentLength` настроен, версии до
2.18.7 / 2.21.2 не применяют ограничение длины документа ни в одном пути парсера. Блокирующие парсеры
(`UTF8StreamJsonParser`, `ReaderBasedJsonParser`) никогда не проверяют совокупное количество прочитанных байтов на соответствие
настроенному лимиту. Асинхронный парсер (`NonBlockingJsonParser`) аналогично не имеет проверки в
`feedInput()`. `UTF8DataInputJsonParser` не имеет механизма отслеживания общего количества потреблённых байтов.
В jackson-core 2.13.5 `maxDocumentLength` вообще не существовало, то есть не было способа
ограничить размер документа. Данная коррекция вводит поле `maxDocumentLength` в
`StreamReadConstraints` и применяет его во всех путях парсера.
**Уязвимые пути парсеров:**
| Парсер | Путь | Точка контроля |
|--------|------|----------------|
| `UTF8StreamJsonParser` | `InputStream` → `_loadMore()` | Проверяет `_currInputProcessed + count` после каждой дозагрузки буфера и в конце файла |
| `ReaderBasedJsonParser` | `Reader` → `_loadMore()` | Тот же шаблон, что и в `UTF8StreamJsonParser` |
| `NonBlockingJsonParser` | `feedInput()` | Проверяет `_currInputProcessed + _origBufferLen` после каждого блока входных данных |
| `UTF8DataInputJsonParser` | `DataInput` | Fail-fast: выбрасывает `StreamConstraintsException`, если `maxDocumentLength` настроен |
**Сценарий атаки:** Злоумышленник отправляет валидный, но слишком большой JSON-документ (например, глубоко вложенную
или сильно повторяющуюся структуру размером в гигабайты) сервису, который настроил
`maxDocumentLength` для предотвращения истощения ресурсов. Без применения ограничения парсер обрабатывает
весь документ независимо от настроенного лимита, потребляя неограниченную память и процессорное время.
---
## Влияние на безопасность
Все четыре уязвимости удалённо эксплуатируемы без какой-либо аутентификации:
- Любой сервис, который обрабатывает подконтрольный злоумышленнику JSON через `JsonParser` (напрямую или через
Jackson Databind, который оборачивает `jackson-core`), находится под угрозой.
- Атака тривиально конструируется — нескольких сотен байт JSON достаточно, чтобы вызвать
неограниченное потребление ресурсов.
- Конфиденциальность и целостность не затронуты; отказ в обслуживании (DoS) — единственный класс воздействия.
---
## Детали исправления
### Официальное обновление (рекомендуется)
Обновитесь до **jackson-core 2.15.4** или любой более поздней стабильной версии. Все выпуски 2.15.x и 2.16+
включают API `StreamReadConstraints` с безопасными значениями по умолчанию.```xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.15.4</version>
</dependency>
Если немедленное обновление до более поздней версии невозможно, эта ветка применяет комплексное исправление к кодовой базе 2.13.5. Она вводит StreamReadConstraints и StreamConstraintsException с API, совместимым с версией 2.15.x, подключает ограничения ко всем четырём реализациям JsonParser — включая неблокирующий парсер, который не был охвачен исправлением в основной ветке — и устанавливает следующие ограничения по умолчанию:
| Ограничение | Ограничение по умолчанию |
|---|---|
| Максимальная глубина вложенности | 1,000 |
| Максимальная длина числового токена | 1,000 цифр |
| Максимальная длина строкового токена | 1,000,000 символов |
Максимальная величина масштаба BigDecimal |
StreamReadConstraints (356 строк)Неизменяемый объект-значение, содержащий ограничения на чтение потока для каждого парсера, создаваемый через Builder:```java // Default constraints (used by all parsers unless overridden) StreamReadConstraints defaults = StreamReadConstraints.defaults();
// Custom constraints StreamReadConstraints custom = StreamReadConstraints.builder() .maxNestingDepth(500) .maxNumberLength(2000) .maxStringLength(5000000) .build();
Методы валидации (вызываются парсерами для каждого нового токена):```java
void validateNestingDepth(int depth) throws StreamConstraintsException;
void validateIntegerLength(int length) throws StreamConstraintsException;
void validateFPLength(int length) throws StreamConstraintsException;
void validateStringLength(int length) throws StreamConstraintsException;
void validateBigIntegerScale(int scale) throws StreamConstraintsException;
Exception message format:
"Depth (%d) exceeds the maximum allowed nesting depth (%d)""Number length (%d) exceeds the maximum length (%d)""String length (%d) exceeds the maximum length (%d)""BigDecimal scale (%d) magnitude exceeds maximum allowed (%d)"StreamConstraintsException (52 строки)Расширяет StreamReadException (сам являющийся JsonProcessingException). Выбрасывается исключительно методами проверки StreamReadConstraints.
base/ParserBase.javaДобавлено поле _streamReadConstraints (по умолчанию StreamReadConstraints.defaults()).
resetInt() и resetFloat() теперь проверяют длину числового токена сразу после его накопления:```java
protected void resetInt(boolean negative, int intLen) throws IOException {
_streamReadConstraints.validateIntegerLength(intLen);
// … existing reset logic …
}
protected void resetFloat(boolean negative, int intLen, int decLen, int expLen) throws IOException { int totalLen = intLen + decLen + expLen; _streamReadConstraints.validateFPLength(totalLen); // … existing reset logic … }
Новые вспомогательные методы `_createChildArrayContext()` и `_createChildObjectContext()` оборачивают создание контекста
проверкой глубины:```java
protected JsonReadContext _createChildArrayContext(int line, int col) throws IOException {
_streamReadConstraints.validateNestingDepth(_parsingContext.getNestingDepth() + 1);
return _parsingContext.createChildArrayContext(line, col);
}
Все четыре парсера теперь вызывают новые вспомогательные функции проверки глубины вместо прямого обращения к _parsingContext.createChild*():
json/ReaderBasedJsonParser.javajson/UTF8StreamJsonParser.javajson/UTF8DataInputJsonParser.javajson/async/NonBlockingJsonParserBase.javaJsonStreamContext.javaДобавлен getNestingDepth(), который обходит цепочку родительских контекстов для вычисления абсолютной глубины:```java
public int getNestingDepth() {
int depth = 0;
JsonStreamContext curr = this;
while ((curr = curr.getParent()) != null) {
depth++;
}
return depth;
}
#### `json/JsonReadContext.java`
Сигнатуры `createChildArrayContext()` и `createChildObjectContext()` обновлены для проброса
`throws IOException`.
#### `json/async/NonBlockingJsonParser.java` (только Sonatype-2022-6438)
Это основное дополнительное исправление, обнаруженное за пределами исходного PR #827. Шесть
сайтов завершения чисел, которые обходили `ParserBase.resetInt()`, были индивидуально исправлены:
| Метод | Добавленная проверка |
|--------|------------------|
| `_startPositiveNumber()` — быстрый возврат | `validateIntegerLength(_intLength)` |
| `_startNegativeNumber()` — быстрый возврат | `validateIntegerLength(_intLength)` |
| `_finishNumberIntegralPart()` — окончательный возврат | `validateIntegerLength(_intLength)` |
| `_finishToken()` случай `MINOR_NUMBER_INTEGER_DIGITS` | `validateIntegerLength(_intLength)` |
| `_startFloat()` / `_finishFloatFraction()` / `_finishFloatExponent()` окончательные возвраты | `validateFPLength(_intLength + _fractLength + _expLength)` |
| `_finishToken()` случаи `MINOR_NUMBER_FRACTION_DIGITS` / `MINOR_NUMBER_EXPONENT_DIGITS` | `validateFPLength(...)` |
Все сайты достижимы: методы быстрого пути обрабатывают случай, когда полное число доступно
в одном вызове `feedInput()`; случаи состояния возобновления `MINOR_*` обрабатывают фрагментированный
случай, когда цифры числа поступают через несколько вызовов. Оба пути должны быть защищены.
---
## Тестовое покрытие
### CVE-2025-52999 — Тесты глубины вложенности
| Тестовый файл | Тестовый метод | Режимы парсера | Покрывает |
|-----------|-------------|-------------|--------|
| `read/ArrayParsingTest.java` | `testCVE_2025_52999` | потоковый | Рекурсивный обход, симулирующий реальное использование приложения; подтверждает `StreamConstraintsException` на глубине 1001 (исправлено) и `StackOverflowError` на глубине 20 000 (2.13.5) |
| `read/ArrayParsingTest.java` | `testCustomNestingDepthConstraint` | прямой API | `StreamReadConstraints.builder().maxNestingDepth(5)` — геттер, проход на пределе, превышение вызывает исключение |
| `read/ArrayParsingTest.java` | `testObjectNestingDepthLimit` | потоковый | Объекты точно на пределе 1000 (проход), объекты на уровне 1001 вызывают исключение |
| `read/ArrayParsingTest.java` | `testDataInputParserDepthLimit` | `UTF8DataInputJsonParser` | `UTF8DataInputJsonParser` обеспечивает ограничение глубины на уровне 1001 |
| `read/ArrayParsingTest.java` | `testNonBlockingParserDepthLimit` | неблокирующий | `NonBlockingJsonParser` обеспечивает ограничение глубины на уровне 1001 |
### Sonatype-2022-6438 — Тесты длины чисел
| Тестовый файл | Тестовый метод | Режимы парсера | Покрывает |
|-----------|-------------|-------------|--------|
| `read/NumberOverflowTest.java` | `testSonatype_2022_6438` | `ALL_MODES` | Целое число на пределе (проход), целое число выше предела (отказ), число с плавающей точкой на пределе (проход), число с плавающей точкой выше предела (отказ), экспонента научной нотации на пределе (проход), экспонента выше предела (отказ) — все четыре парсера |
| `read/NumberOverflowTest.java` | `testNonBlockingParserNumericLengthLimit` | неблокирующий | `NonBlockingJsonParser` обеспечивает ограничение длины чисел |
| `read/NumberOverflowTest.java` | `testNonBlockingParserExponentLengthLimit` | неблокирующий | `NonBlockingJsonParser` обеспечивает ограничение длины экспоненты научной нотации через `validateFPLength` |
| `read/NumberOverflowTest.java` | `testCustomMaxNumberLengthConstraint` | прямой API | `StreamReadConstraints.builder().maxNumberLength(5)` — геттер, проход на пределе, превышение вызывает исключение для обеих `validateIntegerLength()` и `validateFPLength()`, формат сообщения об ошибке |
> **Легенда режимов парсера:**
> - `ALL_STREAMING_MODES` = `UTF8StreamJsonParser` (потоковый), `UTF8StreamJsonParser` (дросселированный), `ReaderBasedJsonParser`
> - `ALL_MODES` = три выше + `UTF8DataInputJsonParser`
> - `non-blocking` = `NonBlockingJsonParser` через `ByteArrayFeeder`
> - `stream` = `UTF8StreamJsonParser` (стандартный `JsonFactory.createParser`)
### SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 — Тесты длины документа
| Тестовый файл | Тестовый метод | Режимы парсера | Покрывает |
|-----------|-------------|-------------|--------|
| `constraints/LargeDocReadTest.java` | `testInputStreamExceedsLimit` | `UTF8StreamJsonParser` | Парсер InputStream обеспечивает `maxDocumentLength` — документ 20K отклонён с лимитом 10K |
| `constraints/LargeDocReadTest.java` | `testInputStreamUnderLimitSucceeds` | `UTF8StreamJsonParser` | Парсер InputStream принимает документ в пределах лимита |
| `constraints/LargeDocReadTest.java` | `testReaderExceedsLimit` | `ReaderBasedJsonParser` | Парсер Reader обеспечивает `maxDocumentLength` — документ 20K отклонён с лимитом 10K |
| `constraints/LargeDocReadTest.java` | `testReaderUnderLimitSucceeds` | `ReaderBasedJsonParser` | Парсер Reader принимает документ в пределах лимита |
| `constraints/LargeDocReadTest.java` | `testAsyncExceedsLimit` | `NonBlockingJsonParser` | Асинхронный парсер обеспечивает `maxDocumentLength` — документ 20K отклонён в `feedInput()` |
| `constraints/LargeDocReadTest.java` | `testAsyncUnderLimitSucceeds` | `NonBlockingJsonParser` | Асинхронный парсер принимает документ в пределах лимита |
| `constraints/LargeDocReadTest.java` | `testDataInputWithDocLengthLimitFails` | `UTF8DataInputJsonParser` | Парсер DataInput быстро завершается ошибкой, когда настроен `maxDocumentLength` |
| `constraints/LargeDocReadTest.java` | `testDataInputWithoutDocLengthLimitWorks` | `UTF8DataInputJsonParser` | Парсер DataInput работает нормально без настройки `maxDocumentLength` |
| `constraints/LargeDocReadTest.java` | `testDefaultFactoryNoLimit` | `UTF8StreamJsonParser` | Фабрика по умолчанию (без лимита) принимает большие документы |
---
## Результаты полного набора тестов```
Tests run: 957, Failures: 0, Errors: 0, Skipped: 0
Все 957 тестов проходят на исправленной сборке. Разбивка добавленных новых тестов безопасности:
Команда сборки:
mvn clean verify -Ptest -Pstrict -pl ...```bash ./mvnw test
---
## Результаты валидации
### Регрессионное тестирование против jackson-core 2.13.5
Методы тестирования CVE были разработаны так, чтобы **не проходить** на кодовой базе 2.13.5 и **проходить** на исправленной сборке.
**`NumberOverflowTest#testSonatype_2022_6438`** против 2.13.5:```
FAIL — Sonatype-2022-6438 VULNERABILITY PRESENT: parser returned VALUE_NUMBER_INT
for a 1001-digit integer — number length limit is not enforced
ArrayParsingTest#testCVE_2025_52999 против 2.13.5:```
FAIL — CVE-2025-52999 VULNERABILITY PRESENT: StackOverflowError after 20000 nesting
levels — parser enforces no depth limit (2.13.5)
**Оба теста на исправленной сборке: ПРОЙДЕНЫ.**
---
## Доказательство концепции
### CVE-2025-52999 — DoS глубины вложенности```java
// Build a 1001-level nested array document (just over the limit)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1001; i++) sb.append('[');
for (int i = 0; i < 1001; i++) sb.append(']');
JsonFactory factory = new JsonFactory();
JsonParser parser = factory.createParser(sb.toString());
try {
while (parser.nextToken() != null) { } // ← throws on 1001st '['
System.err.println("VULNERABLE: no exception thrown");
} catch (StreamConstraintsException e) {
System.out.println("REMEDIATED: " + e.getMessage());
// Depth (1001) exceeds the maximum allowed nesting depth (1000)
} finally {
parser.close();
}
Все четыре формы токенов вызывают validateFPLength / validateIntegerLength перед любой
попыткой преобразования BigDecimal / BigInteger.```java
JsonFactory factory = new JsonFactory();
// ── 1. Long integer ────────────────────────────────────────────────────────── String longInt = "9".repeat(1001); // 1001-digit integer // Negative form works identically: "-" + "9".repeat(1001)
// ── 2. Long fractional part (floating-point) ───────────────────────────────── // intLen=1, fractLen=1001, total=1002 String longFloat = "0." + "9".repeat(1001);
// ── 3. Long exponent / scientific notation ─────────────────────────────────── // intLen=1, fractLen=0, expLen=1001, total=1002 — only 1,004 bytes on the wire String longExp = "1e" + "9".repeat(1001);
// ── 4. Combined fractional + exponent ──────────────────────────────────────── // intLen=1, fractLen=500, expLen=500, total=1001 — neither part alone is over the limit String combined = "0." + "9".repeat(500) + "e" + "9".repeat(500);
for (String payload : new String[]{ longInt, longFloat, longExp, combined }) { JsonParser parser = factory.createParser("[" + payload + "]"); try { parser.nextToken(); // START_ARRAY parser.nextToken(); // ← throws on the number token System.err.println("VULNERABLE: " + payload.substring(0, 20) + "…"); } catch (StreamConstraintsException e) { System.out.println("REMEDIATED: " + e.getMessage()); // Number length (N) exceeds the maximum length (1000) } finally { parser.close(); } }
---
## Руководство по миграции
### Вариант 1: Обновите до jackson-core 2.15.4+ (рекомендуется)```xml
<!-- Maven -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.15.4</version>
</dependency>
<!-- Or with Jackson BOM -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>2.15.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
С версии 2.15+ вы также можете изменять лимиты во время выполнения, если ваше приложение действительно требует более глубокой вложенности или более длинных чисел:```java JsonFactory factory = JsonFactory.builder() .streamReadConstraints(StreamReadConstraints.builder() .maxNestingDepth(2000) .maxNumberLength(10000) .maxDocumentLength(50_000_000L) .build()) .build();
### Вариант 2: Применить эту меру безопасности```bash
git clone https://github.com/sassoftware/jackson-core.git
cd jackson-core
git checkout 2.13.5-CVE-2025-52999-sonatype-2022-6438-GHSA-2m67-wjpj-xhg9
./mvnw install -DskipTests
Затем обновите зависимость вашего проекта, чтобы использовать локально установленный артефакт 2.13.5-SNAPSHOT, или разверните его в вашем внутреннем репозитории артефактов.
Всего: 16 файлов изменено, 1,606 вставок (по данным git diff jackson-core-2.13.5 --stat).
StreamReadConstraints API (2.15 javadoc): https://javadoc.io/doc/com.fasterxml.jackson.core/jackson-core/2.15.4/com/fasterxml/jackson/core/StreamReadConstraints.htmlЛюбое приложение, которое:
jackson-databind)уязвимо для обеих атак.
Даже после исправления рассмотрите:
maxRequestSize в контейнерах сервлетов)
чтобы отклонять чрезвычайно большие тела запросов до вызова синтаксического анализатора.StreamReadConstraints если ваше приложение законно требует больших или более глубоких
документов — настройте ограничения до минимума, необходимого для вашего случая использования.| Параметр | Требование |
|---|---|
| JDK сборки | Java 8 (JDK 1.8) или новее — эта ветка требует Java 8+ для сборки и тестирования |
| Минимальная среда выполнения JRE | Java 8 или новее |
| Maven | 3.6.3 или новее (встроенный wrapper ./mvnw удовлетворяет этому автоматически) |
Примечание: Оригинальный выпуск jackson-core 2.13.5 был нацелен на Java 6 (
-source 1.6 -target 1.6). Начиная с этой ветки безопасности, Java 8 или новее требуется как для сборки, так и для выполнения. Никакая публичная API-поверхность Jackson 2.13 не изменена; только среды выполнения JDK 6/7 теряют совместимость из-за требований Maven и JDK.
./mvnw test
./mvnw install -DskipTests
---
## Лицензия
Лицензировано в соответствии с [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0).```
Copyright 2024–2025 The Jackson Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Автор исследований и устранения уязвимостей безопасности : Jinwoo Hwang (https://JinwooHwang.com)
| ID | Тип | Серьёзность | CVSS | Исправление в восходящем проекте |
|---|
| CVE-2025-52999 | Отказ в обслуживании — неограниченная глубина вложенности | Высокий | 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) | jackson-core 2.15.0 |
| Sonatype-2022-6438 / SNYK-JAVA-COMFASTERXMLJACKSONCORE-7569538 | Отказ в обслуживании — неограниченная длина числового токена | Высокий | 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) | jackson-core 2.15.0 |
| SNYK-JAVA-COMFASTERXMLJACKSONCORE-15365924 | Выделение ресурсов без ограничений или регулирования | Высокий | 8.7 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) | jackson-core 2.18.6, 2.21.1 или выше. |
| SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 / GHSA-2m67-wjpj-xhg9 | Выделение ресурсов без ограничений или регулирования — обход ограничения длины документа | Высокий | 8.7 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) | jackson-core 2.18.7, 2.21.2 или выше. |
| Версия | CVE-2025-52999 | Sonatype-2022-6438 / SNYK-JAVA-COMFASTERXMLJACKSONCORE-7569538 | SNYK-JAVA-COMFASTERXMLJACKSONCORE-15365924 | SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 |
|---|
| 2.13.5 | Уязвима | Уязвима | Уязвима | Уязвима |
| 2.13.5-CVE-2025-52999-sonatype-2022-6438 | Устранена | Устранена | Устранена | Уязвима |
| 2.13.5-CVE-2025-52999-sonatype-2022-6438-GHSA-2m67-wjpj-xhg9 | Устранена | Устранена | Устранена | Устранена |
| 2.14.x | Уязвима | Уязвима | Уязвима | Уязвима |
| 2.15.x | Устранена | Устранена | Уязвима | Уязвима |
| 2.16.x | Устранена | Устранена | Уязвима | Уязвима |
| 2.17.x | Устранена | Устранена | Уязвима | Уязвима |
| 2.18.6+ | Устранена | Устранена | Устранена | Уязвима |
| 2.18.7+ | Устранена | Устранена | Устранена | Устранена |
| 2.21.1+ | Устранена | Устранена | Устранена | Уязвима |
| 2.21.2+ | Устранена | Устранена | Устранена | Устранена |
| Поле | Значение |
|---|
| CVE ID | CVE-2025-52999 |
| Опубликовано | 2025-06-25 |
| Последнее изменение | 2025-06-26 |
| Источник (CNA) | GitHub, Inc. |
| Оценка CVSS v4.0 | 8.7 HIGH — CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N |
| CWE | CWE-121 — Переполнение стека |
| Уведомление GitHub | GHSA-h46c-h94j-95f3 |
| PR с исправлением в восходящем проекте | jackson-core#943 |
| 100,000 |
| Тестовый файл | Новые / расширенные методы |
|---|
read/ArrayParsingTest.java | testCVE_2025_52999, testCustomNestingDepthConstraint, testObjectNestingDepthLimit, testDataInputParserDepthLimit, testNonBlockingParserDepthLimit |
read/NumberOverflowTest.java | testSonatype_2022_6438 (расширено для включения случаев с экспонентой), testNonBlockingParserNumericLengthLimit, testCustomMaxNumberLengthConstraint, testNonBlockingParserExponentLengthLimit |
constraints/LargeDocReadTest.java | testInputStreamExceedsLimit, testInputStreamUnderLimitSucceeds, testReaderExceedsLimit, testReaderUnderLimitSucceeds, testAsyncExceedsLimit, testAsyncUnderLimitSucceeds, testDataInputWithDocLengthLimitFails, testDataInputWithoutDocLengthLimitWorks, testDefaultFactoryNoLimit |
| Файл | Тип изменения | Изменено строк | Описание |
|---|
src/main/java/.../StreamReadConstraints.java | Новый | +356 | Конфигурация ограничений + 5 методов проверки |
src/main/java/.../exc/StreamConstraintsException.java | Новый | +52 | Тип исключения для нарушений ограничений |
src/main/java/.../base/ParserBase.java | Изменен | +44 | Хуки глубины/длины в сбросе и вспомогательных методах контекста |
src/main/java/.../json/JsonReadContext.java | Изменен | +6 | Проброс throws IOException |
src/main/java/.../json/ReaderBasedJsonParser.java | Изменен | +30 | Использование вспомогательных методов _createChild* для проверки глубины |
src/main/java/.../json/UTF8StreamJsonParser.java | Изменен | +26 | Использование вспомогательных методов _createChild* для проверки глубины |
src/main/java/.../json/UTF8DataInputJsonParser.java | Изменен | +26 | Использование вспомогательных методов _createChild* для проверки глубины |
src/main/java/.../json/async/NonBlockingJsonParserBase.java | Изменен | +4 | Использование вспомогательных методов _createChild* для проверки глубины |
src/main/java/.../json/async/NonBlockingJsonParser.java | Изменен | +9 | НОВЫЙ — validateIntegerLength / validateFPLength в 6 местах завершения чисел; validateDocumentLength в feedInput() |
src/main/java/.../JsonStreamContext.java | Изменен | +20 | Добавлен метод getNestingDepth() |
src/main/java/.../TSFBuilder.java | Изменен | +15 | Добавлено поле _streamReadConstraints и сеттер streamReadConstraints() |
src/main/java/.../JsonFactory.java | Изменен | +12 | Проводка ограничений построителя в конструкторы; быстрый отказ DataInput для maxDocumentLength |
src/test/java/.../read/NumberOverflowTest.java | Изменен | +241 | testSonatype_2022_6438 (включая случаи с экспонентой), testNonBlockingParserNumericLengthLimit, testNonBlockingParserExponentLengthLimit, testCustomMaxNumberLengthConstraint |
src/test/java/.../read/NumberParsingTest.java | Изменен | +39 | Обновлены 3 места вызова verifyException |
src/test/java/.../read/ArrayParsingTest.java | Изменен | +143 | testCVE_2025_52999, testCustomNestingDepthConstraint |
src/test/java/.../constraints/LargeDocReadTest.java | Новый | +200 | 9 тестов для проверки соблюдения maxDocumentLength во всех путях синтаксического анализатора |
| Компонент | Версия |
|---|
| Base tag | jackson-core-2.13.5 |
| Branch | 2.13.5-CVE-2025-52999-sonatype-2022-6438-GHSA-2m67-wjpj-xhg9 |
| Инструмент сборки | Maven (wrapper: ./mvnw) |
| Фреймворк тестирования | JUnit 3 / TestCase-style |
| Количество тестов | 957 |