
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.
Este branch (2.13.5-CVE-2025-52999-sonatype-2022-6438-GHSA-2m67-wjpj-xhg9) contém uma remediação de segurança abrangente para vulnerabilidades de negação de serviço (DoS) e Alocação de Recursos sem Limites ou Aceleração que afetam o jackson-core 2.13.5. Ele introduz a API StreamReadConstraints —
alinhada com a API introduzida no jackson-core 2.15.0, mas estendida com uma cobertura mais ampla de analisadores
e proteções adicionais contra vetores de ataque — abordando um ataque de exaustão de profundidade de aninhamento
(CVE-2025-52999), Alocação de Recursos sem Limites ou Aceleração (SNYK-JAVA-COMFASTERXMLJACKSONCORE-15365924), uma violação de restrição de comprimento de documento (SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 / GHSA-2m67-wjpj-xhg9) e um ataque de exaustão de comprimento de token numérico (Sonatype-2022-6438), mantendo-se compatível com a superfície da API pública do jackson-core versão 2.13.5.
| Branch | Vulnerabilidades Abordadas |
|---|---|
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 | Todos os acima + GHSA-2m67-wjpj-xhg9 (violação de restrição de comprimento de documento) |
O branch original (2.13.5-CVE-2025-52999-sonatype-2022-6438) corrige três vulnerabilidades
e está preservado no repositório remoto sasso. Este branch o estende com a correção adicional de
GHSA-2m67-wjpj-xhg9,
que aplica maxDocumentLength em todos os caminhos do analisador.
Entrada NVD
Descrição Oficial NVD
jackson-core contém abstrações de baixo nível de analisador e gerador incrementais ("streaming") usadas pelo Processador de Dados Jackson. Em versões anteriores à 2.15.0, se um usuário analisar um arquivo de entrada e ele tiver dados profundamente aninhados, o Jackson pode acabar lançando um
StackOverflowErrorse a profundidade for particularmente grande. jackson-core 2.15.0 contém um limite configurável para quão profundo o Jackson irá percorrer em um documento de entrada, com padrão de profundidade permitida de 1.000. jackson-core lançará umaStreamConstraintsExceptionse o limite for atingido. jackson-databind também se beneficia desta alteração porque usa jackson-core para analisar entradas JSON. Como solução alternativa, os usuários devem evitar analisar arquivos de entrada de fontes não confiáveis.
Solução alternativa: evitar analisar entrada JSON de fontes não confiáveis até que a versão corrigida seja implantada.
Causa raiz: Antes da versão 2.15.0, JsonParser não impunha limite de quão profundamente um documento JSON poderia
ser aninhado. Cada token de array [ ou objeto { fazia com que JsonReadContext.createChildArrayContext() /
createChildObjectContext() alocasse um novo nó de contexto no heap e incrementasse uma cadeia de referência.
Um invasor pode criar um documento com dezenas de milhares de níveis aninhados, fazendo com que a Máquina Virtual Java
exceda sua pilha de thread ou memória heap.
Caminho de código vulnerável:
A mesma verificação ausente é alcançada através de cada uma das quatro implementações de analisador:``` 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
All four `JsonParser` implementations share this flaw. The attack is equally exploitable through
any of them — whether input arrives via `InputStream`, `Reader`, `DataInput`, or the async
non-blocking feeder API.
**Vetores de ataque:**
| # | Estratégia | Carga útil de exemplo | Incremento de profundidade | Parsers afetados |
|---|------------|-----------------------|----------------------------|------------------|
| 1 | Aninhamento de array | `[[[…]]]` — 1.001 tokens `[` consecutivos | +1 por `[` | Todos os quatro |
| 2 | Aninhamento de objeto | `{"k":{"k":{…}}}` — 1.001 tokens `{` consecutivos | +1 por `{` | Todos os quatro |
| 3 | Aninhamento alternado | `[{"k":[{"k":…}]}]` — 1.001 tokens mistos `[`/`{` | +1 por `[` ou `{` | Todos os quatro |
Observações importantes:
- **Aninhamento de array — sobrecarga mínima:** apenas tokens `[` e `]` são necessários; sem chaves, valores ou espaços em branco. Uma carga útil de 2.002 bytes com 1.001 pares de colchetes é suficiente para exceder o limite padrão de 1.000.
- **Aninhamento de objeto — pressão de heap amplificada:** cada `{` adicionalmente aloca um slot de chave `JsonReadContext` além do nó da cadeia de profundidade, agravando o consumo de memória em profundidades extremas.
- **Aninhamento alternado — evasão de Web Application Firewall (WAF):** as defesas de correspondência de padrões que detectam sequências repetidas `[[[` ou `{{{` são cegas ao aninhamento de tokens alternados; o contador de profundidade do parser incrementa de forma idêntica independentemente do tipo de token.
- **Alimentador não bloqueante — mesma carga útil, superfície de entrega distinta:** todas as três estratégias de aninhamento são igualmente exploráveis via `NonBlockingJsonParser` e a API `ByteArrayFeeder`. O documento pode ser entregue em partes arbitrariamente pequenas; `NonBlockingJsonParserBase._startArrayScope()` / `_startObjectScope()` incrementam o contador de profundidade em cada token de abertura de contexto, independentemente de como os bytes chegam, acumulando profundidade através de múltiplas chamadas `feedInput()`.
Todas as três estratégias de aninhamento são acessíveis através de qualquer uma das quatro implementações `JsonParser`.
Com a versão 2.13.5, o parsing ocorre silenciosamente; com esta correção, todos os três vetores lançam
`StreamConstraintsException: Depth (1001) exceeds the maximum allowed nesting depth (1000)`.
---
### Sonatype-2022-6438 — Comprimento Ilimitado de Token Numérico
**Detalhes de Segurança**
| Campo | Valor |
|-------|-------|
| ID Sonatype | [sonatype-2022-6438](https://guide.sonatype.com/vulnerability/sonatype-2022-6438/security-details) |
| Descrição | jackson-core — Negação de Serviço (DoS) |
| Publicado em | 2022-12-07 |
| Fonte | Sonatype |
| Pontuação CVSS v3.1 | **7.5 ALTA** — `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) — Alocação de Recursos Sem Limites ou Limitação |
| Pontuação EPSS | 0% |
| PRs de Correção Upstream | [jackson-core#827](https://github.com/FasterXML/jackson-core/pull/827), [jackson-core#846](https://github.com/FasterXML/jackson-core/pull/846) |
**Métodos Vulneráveis (conforme identificados pela Sonatype)**
| Método | Notas |
|--------|-------|
| `com.fasterxml.jackson.core.base.ParserBase._parseSlowInt(I)V` | Parâmetro vulnerável: índice 0 |
| `com.fasterxml.jackson.core.base.ParserBase.convertNumberToBigDecimal()V` | |
| `com.fasterxml.jackson.core.base.ParserMinimalBase.getValueAsDouble(D)D` | Parâmetro vulnerável: índice 0 |
| `com.fasterxml.jackson.core.util.TextBuffer.contentsAsDecimal()` | Retorna `BigDecimal` |
| `com.fasterxml.jackson.core.util.TextBuffer.contentsAsDouble(Z)D` | |
| `com.fasterxml.jackson.core.util.TextBuffer.contentsAsFloat(Z)F` | |
Cada um desses métodos processa o buffer de dígitos bruto sem primeiro validar seu comprimento. Passar um token numérico suficientemente longo desencadeia alocação de heap ilimitada e exaustão de CPU quando a JVM tenta instanciar um `BigInteger` ou `BigDecimal` a partir do conteúdo do buffer não restrito.
---
**Causa raiz:** Antes da versão 2.15.0, `JsonParser` não impunha nenhum limite no comprimento em bytes de tokens de inteiro, notação científica, ponto flutuante simples ou ponto flutuante composto. Quando um parser aloca seu `_textBuffer` interno para acumular dígitos, um invasor pode fornecer um número com milhões de dígitos, fazendo com que o buffer cresça sem limites e eventualmente esgote a memória heap.
**Caminhos de código vulneráveis:**
Existem dois caminhos vulneráveis estruturalmente distintos — um compartilhado pelos três parsers síncronos, e um segundo caminho independente através do parser assíncrono.
**Caminho A — parsers síncronos** (três implementações, um sink compartilhado):```
JsonParser.nextToken() // common entry point
│
├─ ReaderBasedJsonParser ─┐
├─ UTF8StreamJsonParser ├─→ ParserBase.resetInt() / resetFloat()
└─ UTF8DataInputJsonParser ─┘ │
▼
_textBuffer.contentsAsString()
⚠ no length check — buffer grows without bound
Caminho B — analisador assíncrono (caminho de código independente, separadamente desprotegido):``` 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
O parser não bloqueante (assíncrono) é uma superfície de ataque particularmente notável: ele contém seus próprios loops de acumulação de dígitos em `_startPositiveNumber`, `_startNegativeNumber`,
`_finishNumberIntegralPart`, `_startFloat`, `_finishFloatFraction` e `_finishFloatExponent`
que definem `_intLength` / `_fractLength` / `_expLength` diretamente, sem nunca passar por
`ParserBase.resetInt()` ou `resetFloat()`. Isso significa que a correção upstream do PR #827 (que adicionou
validação apenas a `ParserBase`) **deixou `NonBlockingJsonParser` completamente desprotegido**.
Isso foi descoberto durante uma análise estendida de superfície de ataque e corrigido neste branch.
**Vetores de ataque:**
| # | Forma do token | Exemplo de payload | `intLen` | `fractLen` | `expLen` | Total | Restrição |
|---|----------------|--------------------|----------|------------|----------|-------|-----------|
| 1 | Inteiro | `999…` — 199.999 dígitos consecutivos | 199.999 | 0 | 0 | 199.999 | `validateIntegerLength` |
| 2 | Fração decimal | `0.999…` — 1 dígito inteiro, 1.001 dígitos fracionários | 1 | 1.001 | 0 | 1.002 | `validateFPLength` |
| 3 | Notação científica | `1e999…` — 1 dígito significando, 1.001 dígitos de expoente | 1 | 0 | 1.001 | 1.002 | `validateFPLength` |
| 4 | Ponto flutuante composto | `0.999…e999…` — 500 dígitos fracionários, 500 dígitos de expoente | 1 | 500 | 500 | 1.001 | `validateFPLength` |
Observações principais:
- **Inteiro — caractere de sinal não é um dígito:** o `-` inicial é excluído da acumulação de dígitos; `-999…` e `999…` produzem valores `intLen` idênticos e disparam a restrição no mesmo limite.
- **Fração decimal — inteiro curto, fração ilimitada:** a parte inteira pode ser um único dígito (0) enquanto a parte fracionária cresce ilimitadamente; o próprio ponto decimal é excluído da contagem.
- **Notação científica — compacta, porém catastrófica:** com aproximadamente 1.004 bytes este é o menor payload eficaz; força a instanciação de um `BigDecimal` com escala ±10^1001, demandando alocação intermediária ilimitada no heap apesar do pequeno tamanho do token.
- **Ponto flutuante composto — evasão de limite dividido:** com `fractLen = 500` e `expLen = 500`, nenhum componente atinge individualmente o limite de 1.000 dígitos. A verificação unificada `validateFPLength(intLen + fractLen + expLen)` é a única defesa que fecha essa lacuna.
- **Parser não bloqueante — bypass independente:** todas as quatro formas de token acima são exploráveis independentemente através de `NonBlockingJsonParser` via `ByteArrayFeeder`. Diferentemente dos parsers síncronos, `NonBlockingJsonParser` acumula dígitos em loops privados (`_startPositiveNumber`, `_finishNumberIntegralPart`, `_startFloat`, `_finishFloatFraction`, `_finishFloatExponent`) que escrevem `_intLength` / `_fractLength` / `_expLength` diretamente, ignorando `ParserBase.resetInt()` e `resetFloat()` por completo. O PR upstream #827 — que modificou apenas `ParserBase` — portanto deixou este parser totalmente desprotegido, exigindo seis correções independentes de ponto de chamada nesta remediação.
Todas as quatro formas de token e o bypass não bloqueante são rejeitados no momento da tokenização — antes que qualquer `BigDecimal` ou `BigInteger` seja construído — por `validateIntegerLength` e `validateFPLength` em `ParserBase` (parsers síncronos) e em seis pontos de chamada dedicados em `NonBlockingJsonParser` (parser assíncrono). Com a versão 2.13.5, todas as quatro formas de token são aceitas silenciosamente; com esta remediação, todo parser lança
`StreamConstraintsException: Number length (N) exceeds the maximum length (1000)`.
> **Nota sobre `UTF8DataInputJsonParser` com grandes payloads:** O parser DataInput tem um
> limite interno de buffer pré-existente de 65.536 bytes ([jackson-core#493](https://github.com/FasterXML/jackson-core/issues/493)).
> Documentos que excedem esse tamanho (por exemplo, payloads PoC de 199.999 dígitos ≈ 200 KB) disparam um
> `ArrayIndexOutOfBoundsException` antes que a restrição de comprimento possa ser acionada. A proteção
> de comprimento numérico para `UTF8DataInputJsonParser` é, portanto, verificada com payloads mais curtos
> (≤ 1.001 dígitos) onde o bug ainda é reproduzível.
---
### SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 / GHSA-2m67-wjpj-xhg9 — Bypass da Restrição de Comprimento do Documento
**Detalhes de Segurança**
| Campo | Valor |
|-------|-------|
| ID Snyk | [SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551](https://security.snyk.io/vuln/SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551) |
| Aviso GitHub | [GHSA-2m67-wjpj-xhg9](https://github.com/FasterXML/jackson-core/security/advisories/GHSA-2m67-wjpj-xhg9) |
| Descrição | Alocação de Recursos sem Limites ou Controle de Fluxo |
| Divulgado | 2026-04-04 |
| Pontuação CVSS v4.0 | **8.7 ALTA** |
| CWE | [CWE-770](https://cwe.mitre.org/data/definitions/770.html) — Alocação de Recursos sem Limites ou Controle de Fluxo |
| Versões Afetadas | [2.8.0, 2.21.2) |
| Correção Upstream | jackson-core 2.18.7, 2.21.2 ou superior |
| Commits de Correção | [74c9ee25](https://github.com/FasterXML/jackson-core/commit/74c9ee25) (3.x), [7ce3622f](https://github.com/FasterXML/jackson-core/commit/7ce3622f) (2.18.x) |
**Causa raiz:** Mesmo quando `StreamReadConstraints.maxDocumentLength` está configurado, versões anteriores a
2.18.7 / 2.21.2 não impõem o limite de comprimento do documento em nenhum caminho do parser. Os parsers bloqueantes
(`UTF8StreamJsonParser`, `ReaderBasedJsonParser`) nunca validam os bytes acumulados lidos contra o
limite configurado. O parser assíncrono (`NonBlockingJsonParser`) também não possui validação em
`feedInput()`. O `UTF8DataInputJsonParser` não possui mecanismo para rastrear o total de bytes consumidos.
Em jackson-core 2.13.5, `maxDocumentLength` não existia, significando que não havia como
restringir o tamanho do documento. Esta remediação introduz o campo `maxDocumentLength` em
`StreamReadConstraints` e o impõe em todos os caminhos do parser.
**Caminhos de parser vulneráveis:**
| Parser | Caminho | Ponto de Imposição |
|--------|---------|--------------------|
| `UTF8StreamJsonParser` | `InputStream` → `_loadMore()` | Valida `_currInputProcessed + count` após cada recarga de buffer e no EOF |
| `ReaderBasedJsonParser` | `Reader` → `_loadMore()` | Mesmo padrão que `UTF8StreamJsonParser` |
| `NonBlockingJsonParser` | `feedInput()` | Valida `_currInputProcessed + _origBufferLen` após cada bloco de entrada |
| `UTF8DataInputJsonParser` | `DataInput` | Falha rápida: lança `StreamConstraintsException` se `maxDocumentLength` estiver configurado |
**Cenário de ataque:** Um atacante envia um documento JSON válido, porém excessivamente grande (por exemplo, uma estrutura profundamente aninhada ou altamente repetitiva com gigabytes) para um serviço que configurou
`maxDocumentLength` para evitar exaustão de recursos. Sem imposição, o parser processa o
documento inteiro independentemente do limite configurado, consumindo memória e CPU ilimitadas.
---
## Impacto na Segurança
Todas as quatro vulnerabilidades são remotamente exploráveis sem necessidade de autenticação:
- Qualquer serviço que analise JSON controlado pelo atacante via um `JsonParser` (diretamente ou através
do Jackson Databind, que encapsula `jackson-core`) está em risco.
- O ataque é trivialmente construível — algumas centenas de bytes de JSON são suficientes para desencadear
consumo ilimitado de recursos.
- Nenhum impacto na confidencialidade ou integridade; a disponibilidade (DoS) é a única classe de impacto.
---
## Detalhes da Remediação
### Atualização Oficial (Recomendada)
Atualize para **jackson-core 2.15.4** ou qualquer versão estável posterior. Todas as versões 2.15.x e 2.16+
incluem a API `StreamReadConstraints` com padrões seguros.```xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.15.4</version>
</dependency>
Se a atualização para uma versão posterior não for imediatamente viável, este branch aplica uma correção abrangente à base de código 2.13.5. Ele introduz StreamReadConstraints e StreamConstraintsException com uma API compatível com 2.15.x, conecta as restrições em todas as quatro implementações de JsonParser — incluindo o parser não bloqueante, que não foi coberto pela correção upstream — e aplica os seguintes limites padrão:
| Restrição | Limite Padrão |
|---|---|
| Profundidade máxima de aninhamento | 1.000 |
| Comprimento máximo de token numérico | 1.000 dígitos |
| Comprimento máximo de token de string | 1.000.000 caracteres |
Magnitude máxima da escala de BigDecimal |
StreamReadConstraints (356 linhas)Objeto de valor imutável que contém limites de leitura de stream por parser, construído através de um 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();
Métodos de validação (chamados pelos parsers em cada novo token):```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;
Formato das mensagens de exceção:
"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 linhas)Estende StreamReadException (que por sua vez é uma JsonProcessingException). Lançado exclusivamente pelos
métodos de validação de StreamReadConstraints.
base/ParserBase.javaAdicionado o campo _streamReadConstraints (padrão é StreamReadConstraints.defaults()).
resetInt() e resetFloat() agora validam o comprimento do token numérico imediatamente após o token
ter sido acumulado:```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 … }
Novos métodos auxiliares `_createChildArrayContext()` e `_createChildObjectContext()` envolvem a criação de contexto com uma verificação de profundidade:```java
protected JsonReadContext _createChildArrayContext(int line, int col) throws IOException {
_streamReadConstraints.validateNestingDepth(_parsingContext.getNestingDepth() + 1);
return _parsingContext.createChildArrayContext(line, col);
}
Todos os quatro parsers agora chamam os novos auxiliares de verificação de profundidade em vez de acessar _parsingContext.createChild*() diretamente:
json/ReaderBasedJsonParser.javajson/UTF8StreamJsonParser.javajson/UTF8DataInputJsonParser.javajson/async/NonBlockingJsonParserBase.javaJsonStreamContext.javaAdicionado getNestingDepth() que percorre a cadeia pai para calcular a profundidade absoluta:```java
public int getNestingDepth() {
int depth = 0;
JsonStreamContext curr = this;
while ((curr = curr.getParent()) != null) {
depth++;
}
return depth;
}
#### `json/JsonReadContext.java`
Assinaturas de `createChildArrayContext()` e `createChildObjectContext()` atualizadas para propagar
`throws IOException`.
#### `json/async/NonBlockingJsonParser.java` (apenas Sonatype-2022-6438)
Esta é a principal correção adicional descoberta além do escopo do PR #827 upstream. Seis
locais de conclusão de número que ignoravam `ParserBase.resetInt()` foram remediados individualmente:
| Método | Validação adicionada |
|--------|----------------------|
| `_startPositiveNumber()` — retorno de caminho rápido | `validateIntegerLength(_intLength)` |
| `_startNegativeNumber()` — retorno de caminho rápido | `validateIntegerLength(_intLength)` |
| `_finishNumberIntegralPart()` — retorno final | `validateIntegerLength(_intLength)` |
| `_finishToken()` caso `MINOR_NUMBER_INTEGER_DIGITS` | `validateIntegerLength(_intLength)` |
| `_startFloat()` / `_finishFloatFraction()` / `_finishFloatExponent()` retornos finais | `validateFPLength(_intLength + _fractLength + _expLength)` |
| `_finishToken()` casos `MINOR_NUMBER_FRACTION_DIGITS` / `MINOR_NUMBER_EXPONENT_DIGITS` | `validateFPLength(...)` |
Todos os locais são acessíveis: os métodos de caminho rápido lidam com o caso em que o número completo está
disponível em uma única chamada `feedInput()`; os casos de estado de retomada `MINOR_*` lidam com o caso
fragmentado onde os dígitos do número chegam em múltiplas chamadas. Ambos os caminhos devem ser protegidos.
---
## Cobertura de Testes
### CVE-2025-52999 — Testes de Profundidade de Aninhamento
| Arquivo de Teste | Método de Teste | Modos do Parser | Cobre |
|------------------|----------------|-----------------|-------|
| `read/ArrayParsingTest.java` | `testCVE_2025_52999` | stream | Travessia recursiva simulando uso real de aplicativo; confirma `StreamConstraintsException` na profundidade 1001 (remediado) e `StackOverflowError` na profundidade 20.000 (2.13.5) |
| `read/ArrayParsingTest.java` | `testCustomNestingDepthConstraint` | API direta | `StreamReadConstraints.builder().maxNestingDepth(5)` — acessor, no limite passa, acima do limite lança exceção |
| `read/ArrayParsingTest.java` | `testObjectNestingDepthLimit` | stream | Objetos exatamente no limite 1000 (passam), objetos de nível 1001 lançam exceção |
| `read/ArrayParsingTest.java` | `testDataInputParserDepthLimit` | `UTF8DataInputJsonParser` | `UTF8DataInputJsonParser` impõe limite de profundidade de 1001 níveis |
| `read/ArrayParsingTest.java` | `testNonBlockingParserDepthLimit` | non-blocking | `NonBlockingJsonParser` impõe limite de profundidade de 1001 níveis |
### Sonatype-2022-6438 — Testes de Comprimento Numérico
| Arquivo de Teste | Método de Teste | Modos do Parser | Cobre |
|------------------|----------------|-----------------|-------|
| `read/NumberOverflowTest.java` | `testSonatype_2022_6438` | `ALL_MODES` | Inteiro no limite (passa), inteiro acima do limite (falha), float no limite (passa), float acima do limite (falha), expoente de notação científica no limite (passa), expoente acima do limite (falha) — todos os quatro parsers |
| `read/NumberOverflowTest.java` | `testNonBlockingParserNumericLengthLimit` | non-blocking | `NonBlockingJsonParser` impõe comprimento numérico |
| `read/NumberOverflowTest.java` | `testNonBlockingParserExponentLengthLimit` | non-blocking | `NonBlockingJsonParser` impõe comprimento do expoente de notação científica via `validateFPLength` |
| `read/NumberOverflowTest.java` | `testCustomMaxNumberLengthConstraint` | API direta | `StreamReadConstraints.builder().maxNumberLength(5)` — acessor, no limite passa, acima do limite lança exceção para ambos `validateIntegerLength()` e `validateFPLength()`, formato da mensagem de erro |
> **Chave dos modos do parser:**
> - `ALL_STREAMING_MODES` = `UTF8StreamJsonParser` (stream), `UTF8StreamJsonParser` (throttled), `ReaderBasedJsonParser`
> - `ALL_MODES` = os três acima + `UTF8DataInputJsonParser`
> - `non-blocking` = `NonBlockingJsonParser` via `ByteArrayFeeder`
> - `stream` = `UTF8StreamJsonParser` (padrão `JsonFactory.createParser`)
### SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 — Testes de Comprimento de Documento
| Arquivo de Teste | Método de Teste | Modos do Parser | Cobre |
|------------------|----------------|-----------------|-------|
| `constraints/LargeDocReadTest.java` | `testInputStreamExceedsLimit` | `UTF8StreamJsonParser` | Parser InputStream impõe `maxDocumentLength` — documento de 20K rejeitado com limite de 10K |
| `constraints/LargeDocReadTest.java` | `testInputStreamUnderLimitSucceeds` | `UTF8StreamJsonParser` | Parser InputStream aceita documento dentro do limite |
| `constraints/LargeDocReadTest.java` | `testReaderExceedsLimit` | `ReaderBasedJsonParser` | Parser Reader impõe `maxDocumentLength` — documento de 20K rejeitado com limite de 10K |
| `constraints/LargeDocReadTest.java` | `testReaderUnderLimitSucceeds` | `ReaderBasedJsonParser` | Parser Reader aceita documento dentro do limite |
| `constraints/LargeDocReadTest.java` | `testAsyncExceedsLimit` | `NonBlockingJsonParser` | Parser assíncrono impõe `maxDocumentLength` — documento de 20K rejeitado em `feedInput()` |
| `constraints/LargeDocReadTest.java` | `testAsyncUnderLimitSucceeds` | `NonBlockingJsonParser` | Parser assíncrono aceita documento dentro do limite |
| `constraints/LargeDocReadTest.java` | `testDataInputWithDocLengthLimitFails` | `UTF8DataInputJsonParser` | Parser DataInput falha rapidamente quando `maxDocumentLength` está configurado |
| `constraints/LargeDocReadTest.java` | `testDataInputWithoutDocLengthLimitWorks` | `UTF8DataInputJsonParser` | Parser DataInput funciona normalmente sem `maxDocumentLength` configurado |
| `constraints/LargeDocReadTest.java` | `testDefaultFactoryNoLimit` | `UTF8StreamJsonParser` | Fábrica padrão (sem limite) aceita documentos grandes |
---
## Resultados Completos da Suíte de Testes```
Tests run: 957, Failures: 0, Errors: 0, Skipped: 0
Todos os 957 testes passam na compilação corrigida. Detalhamento dos novos testes de segurança adicionados:
Comando de compilação:```bash ./mvnw test
---
## Resultados de Validação
### Teste de Regressão Contra jackson-core 2.13.5
Os métodos de teste CVE foram projetados para **falhar** na base de código 2.13.5 e
**passar** na compilação corrigida.
**`NumberOverflowTest#testSonatype_2022_6438`** contra 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 contra 2.13.5:```
FAIL — CVE-2025-52999 VULNERABILITY PRESENT: StackOverflowError after 20000 nesting
levels — parser enforces no depth limit (2.13.5)
**Ambos os testes na versão corrigida: PASS.**
---
## Prova de Conceito
### CVE-2025-52999 — Nesting Depth 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();
}
Todas as quatro formas de token acionam validateFPLength / validateIntegerLength antes de qualquer tentativa de conversão 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(); } }
---
## Guia de Migração
### Opção 1: Atualizar para jackson-core 2.15.4+ (Recomendado)```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>
Com 2.15+, você também pode ajustar limites em tempo de execução se sua aplicação precisar legitimamente de aninhamento mais profundo ou números mais longos:```java JsonFactory factory = JsonFactory.builder() .streamReadConstraints(StreamReadConstraints.builder() .maxNestingDepth(2000) .maxNumberLength(10000) .maxDocumentLength(50_000_000L) .build()) .build();
### Opção 2: Aplicar Esta Remediação de Segurança```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
Em seguida, atualize a dependência do seu projeto para usar o artefato 2.13.5-SNAPSHOT instalado localmente, ou implante-o em seu repositório interno de artefatos.
Total: 16 arquivos alterados, 1.606 inserções (conforme reportado por git diff jackson-core-2.13.5 --stat).
StreamReadConstraints (javadoc 2.15): https://javadoc.io/doc/com.fasterxml.jackson.core/jackson-core/2.15.4/com/fasterxml/jackson/core/StreamReadConstraints.htmlQualquer aplicação que:
jackson-databind)está vulnerável a ambos os ataques.
Mesmo após a correção, considere:
maxRequestSize em contêineres Servlet)
para rejeitar corpos de requisição extremamente grandes antes que o parser seja invocado.StreamReadConstraints customizadas se sua aplicação exigir legitimamente documentos maiores ou mais
profundos — ajuste os limites ao mínimo necessário para seu caso de uso.| Dimensão | Requisito |
|---|---|
| JDK de build | Java 8 (JDK 1.8) ou posterior — este branch requer Java 8+ para build e teste |
| JRE mínima em execução | Java 8 ou posterior |
| Maven | 3.6.3 ou posterior (o wrapper incluso ./mvnw atende automaticamente) |
Nota: A versão original jackson-core 2.13.5 tinha como alvo Java 6 (
-source 1.6 -target 1.6). A partir deste branch de segurança, Java 8 ou posterior é necessário tanto para build quanto para execução. Nenhuma API pública do Jackson 2.13 foi alterada; apenas os runtimes JDK 6/7 perdem compatibilidade devido aos requisitos do Maven e JDK.
./mvnw test
./mvnw install -DskipTests
## Licença
Licenciado sob a [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.
Autor de Pesquisa e Remediação de Vulnerabilidades de Segurança : Jinwoo Hwang (https://JinwooHwang.com)
| ID | Tipo | Gravidade | CVSS | Correção Upstream |
|---|
| CVE-2025-52999 | Negação de Serviço — profundidade de aninhamento ilimitada | Alta | 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 | Negação de Serviço — comprimento de token numérico ilimitado | Alta | 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 | Alocação de Recursos sem Limites ou Aceleração | Alta | 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 ou superior. |
| SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 / GHSA-2m67-wjpj-xhg9 | Alocação de Recursos sem Limites ou Aceleração — violação da restrição de comprimento de documento | Alta | 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 ou superior. |
| Versão | CVE-2025-52999 | Sonatype-2022-6438 / SNYK-JAVA-COMFASTERXMLJACKSONCORE-7569538 | SNYK-JAVA-COMFASTERXMLJACKSONCORE-15365924 | SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 |
|---|
| 2.13.5 | Vulnerável | Vulnerável | Vulnerável | Vulnerável |
| 2.13.5-CVE-2025-52999-sonatype-2022-6438 | Corrigido | Corrigido | Corrigido | Vulnerável |
| 2.13.5-CVE-2025-52999-sonatype-2022-6438-GHSA-2m67-wjpj-xhg9 | Corrigido | Corrigido | Corrigido | Corrigido |
| 2.14.x | Vulnerável | Vulnerável | Vulnerável | Vulnerável |
| 2.15.x | Corrigido | Corrigido | Vulnerável | Vulnerável |
| 2.16.x | Corrigido | Corrigido | Vulnerável | Vulnerável |
| 2.17.x | Corrigido | Corrigido | Vulnerável | Vulnerável |
| 2.18.6+ | Corrigido | Corrigido | Corrigido | Vulnerável |
| 2.18.7+ | Corrigido | Corrigido | Corrigido | Corrigido |
| 2.21.1+ | Corrigido | Corrigido | Corrigido | Vulnerável |
| 2.21.2+ | Corrigido | Corrigido | Corrigido | Corrigido |
| Campo | Valor |
|---|
| ID CVE | CVE-2025-52999 |
| Publicado | 2025-06-25 |
| Última modificação | 2025-06-26 |
| Fonte (CNA) | GitHub, Inc. |
| Pontuação 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 — Estouro de Buffer baseado em Pilha |
| Aviso GitHub | GHSA-h46c-h94j-95f3 |
| PR de correção upstream | jackson-core#943 |
| 100.000 |
| Test File | New / Extended Methods |
|---|
read/ArrayParsingTest.java | testCVE_2025_52999, testCustomNestingDepthConstraint, testObjectNestingDepthLimit, testDataInputParserDepthLimit, testNonBlockingParserDepthLimit |
read/NumberOverflowTest.java | testSonatype_2022_6438 (extended to include exponent cases), testNonBlockingParserNumericLengthLimit, testCustomMaxNumberLengthConstraint, testNonBlockingParserExponentLengthLimit |
constraints/LargeDocReadTest.java | testInputStreamExceedsLimit, testInputStreamUnderLimitSucceeds, testReaderExceedsLimit, testReaderUnderLimitSucceeds, testAsyncExceedsLimit, testAsyncUnderLimitSucceeds, testDataInputWithDocLengthLimitFails, testDataInputWithoutDocLengthLimitWorks, testDefaultFactoryNoLimit |
| Arquivo | Tipo de Alteração | Linhas Alteradas | Descrição |
|---|
src/main/java/.../StreamReadConstraints.java | Novo | +356 | Configuração de restrições + 5 métodos de validação |
src/main/java/.../exc/StreamConstraintsException.java | Novo | +52 | Tipo de exceção para violações de restrições |
src/main/java/.../base/ParserBase.java | Modificado | +44 | Hooks de profundidade/comprimento em reset e helpers de contexto |
src/main/java/.../json/JsonReadContext.java | Modificado | +6 | Propagação de throws IOException |
src/main/java/.../json/ReaderBasedJsonParser.java | Modificado | +30 | Utiliza helpers _createChild* com verificação de profundidade |
src/main/java/.../json/UTF8StreamJsonParser.java | Modificado | +26 | Utiliza helpers _createChild* com verificação de profundidade |
src/main/java/.../json/UTF8DataInputJsonParser.java | Modificado | +26 | Utiliza helpers _createChild* com verificação de profundidade |
src/main/java/.../json/async/NonBlockingJsonParserBase.java | Modificado | +4 | Utiliza helpers _createChild* com verificação de profundidade |
src/main/java/.../json/async/NonBlockingJsonParser.java | Modificado | +9 | NOVO — validateIntegerLength / validateFPLength em 6 locais de conclusão de número; validateDocumentLength em feedInput() |
src/main/java/.../JsonStreamContext.java | Modificado | +20 | Adicionado getNestingDepth() |
src/main/java/.../TSFBuilder.java | Modificado | +15 | Adicionado campo _streamReadConstraints e setter streamReadConstraints() |
src/main/java/.../JsonFactory.java | Modificado | +12 | Conecta restrições do builder nos construtores; fail-fast no DataInput para maxDocumentLength |
src/test/java/.../read/NumberOverflowTest.java | Modificado | +241 | testSonatype_2022_6438 (incl. casos com expoente), testNonBlockingParserNumericLengthLimit, testNonBlockingParserExponentLengthLimit, testCustomMaxNumberLengthConstraint |
src/test/java/.../read/NumberParsingTest.java | Modificado | +39 | 3 locais de chamada verifyException atualizados |
src/test/java/.../read/ArrayParsingTest.java | Modificado | +143 | testCVE_2025_52999, testCustomNestingDepthConstraint |
src/test/java/.../constraints/LargeDocReadTest.java | Novo | +200 | 9 testes para aplicação de maxDocumentLength em todos os caminhos de parser |
| Componente | Versão |
|---|
| Tag base | jackson-core-2.13.5 |
| Branch | 2.13.5-CVE-2025-52999-sonatype-2022-6438-GHSA-2m67-wjpj-xhg9 |
| Ferramenta de build | Maven (wrapper: ./mvnw) |
| Framework de teste | JUnit 3 / estilo TestCase |
| Número de testes | 957 |