
このブランチ (2.13.5-CVE-2025-52999-sonatype-2022-6438-GHSA-2m67-wjpj-xhg9) には、jackson-core 2.13.5 を対象とした、サービス拒否 (DoS) および無制限またはスロットリングなしのリソース割り当ての脆弱性に対する包括的なセキュリティ修正が含まれています。jackson-core 2.15.0 で導入された API に合わせて設計されつつ、より広範なパーサー対応と追加の攻撃ベクトル保護を備えた StreamReadConstraints API を導入し、ネスト深度の枯渇攻撃 (CVE-2025-52999)、無制限またはスロットリングなしのリソース割り当て (SNYK-JAVA-COMFASTERXMLJACKSONCORE-15365924)、ドキュメント長制約のバイパス (SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 / GHSA-2m67-wjpj-xhg9)、数値トークン長の枯渇攻撃 (Sonatype-2022-6438) に対処しつつ、jackson-core バージョン 2.13.5 の公開 API サーフェスとの互換性を維持しています。
| ブランチ | 対処された脆弱性 |
|---|---|
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) は 3 つの脆弱性を修正しており、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 も、JSON 入力の解析に jackson-core を使用するため、この変更の恩恵を受けます。回避策として、ユーザーは信頼できないソースからの入力ファイルの解析を避ける必要があります。
回避策: 修正済みバージョンがデプロイされるまで、信頼できないソースからの JSON 入力の解析を避けてください。
根本原因: 2.15.0 より前のバージョンでは、JsonParser は JSON ドキュメントのネスト深度に制限を課していませんでした。配列の [ またはオブジェクトの { トークンごとに、JsonReadContext.createChildArrayContext() / createChildObjectContext() がヒープ上に新しいコンテキストノードを割り当て、参照チェーンを増加させていました。攻撃者は数万レベルのネストを含むドキュメントを細工し、Java 仮想マシンのスレッドスタックまたはヒープメモリを使い果たさせることができます。
脆弱なコードパス:
同じ欠落チェックが、4 つのパーサー実装のそれぞれを通じて到達されます。``` 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
4つの `JsonParser` 実装はすべてこの欠陥を共有しています。攻撃は、入力が `InputStream`、`Reader`、`DataInput`、または非同期のノンブロッキングフィーダー API のいずれを介して到着する場合でも、そのすべてを通じて同様に悪用可能です。
**攻撃ベクター:**
| # | 戦略 | ペイロード例 | 深さの増分 | 影響を受けるパーサー |
|---|----------|----------------|-----------------|------------------|
| 1 | 配列のネスト | `[[[…]]]` — 1,001個の連続する `[` トークン | `[` ごとに +1 | 全4種 |
| 2 | オブジェクトのネスト | `{"k":{"k":{…}}}` — 1,001個の連続する `{` トークン | `{` ごとに +1 | 全4種 |
| 3 | 交互のネスト | `[{"k":[{"k":…}]}]` — 1,001個の `[`/`{` 混在トークン | `[` または `{` ごとに +1 | 全4種 |
主な観察点:
- **配列のネスト — 最小限のオーバーヘッド:** 必要なのは `[` と `]` トークンだけです。キー、値、空白は不要です。1,001組の角括弧からなる2,002バイトのペイロードで、デフォルトの制限である1,000を超えるのに十分です。
- **オブジェクトのネスト — ヒープ圧力の増幅:** 各 `{` は深さチェーンのノードに加えて `JsonReadContext` のキースロットを追加で割り当てるため、極端な深さではメモリ消費がさらに悪化します。
- **交互のネスト — Web Application Firewall (WAF) の回避:** 繰り返しの `[[[` や `{{{` シーケンスを検出するパターンマッチング防御は、トークンが交互に現れるネストを認識できません。パーサーの深さカウンターはトークンの種類に関係なく同じように増加します。
- **ノンブロッキングフィーダー — 同じペイロード、異なる配信面:** 3つのネスト戦略はすべて、`NonBlockingJsonParser` と `ByteArrayFeeder` API を介して同等に悪用可能です。ドキュメントは任意に小さなチャンクで配信できます。`NonBlockingJsonParserBase._startArrayScope()` / `_startObjectScope()` は、バイトの到着方法に関係なく、コンテキストを開くトークンごとに深さカウンターを増加させ、複数の `feedInput()` 呼び出しをまたいで深さを蓄積します。
3つのネスト戦略はすべて、4つの `JsonParser` 実装のいずれからも到達可能です。2.13.5では解析は静かに成功しますが、この修正により、3つのベクターすべてが `StreamConstraintsException: Depth (1001) exceeds the maximum allowed nesting depth (1000)` をスローします。
---
### Sonatype-2022-6438 — 無制限の数値トークン長
**セキュリティ詳細**
| 項目 | 値 |
|-------|-------|
| Sonatype ID | [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 HIGH** — `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` をインスタンス化しようとする際に、無制限のヒープ割り当てと CPU 枯渇が発生します。
---
**根本原因:** 2.15.0より前では、`JsonParser` は整数、科学的表記法、単純浮動小数点、複合浮動小数点の各トークンのバイト長に制限を課していませんでした。パーサーが数字を蓄積するために内部の `_textBuffer` を割り当てると、攻撃者は数百万桁の数字を提供でき、バッファが無制限に成長して最終的にヒープメモリを枯渇させます。
**脆弱なコードパス:**
構造的に異なる2つの脆弱なパスがあります — 1つは3つの同期パーサーが共有し、もう1つは非同期パーサーを通る独立したパスです。
**パス A — 同期パーサー** (3つの実装、1つの共有シンク):```
JsonParser.nextToken() // common entry point
│
├─ ReaderBasedJsonParser ─┐
├─ UTF8StreamJsonParser ├─→ ParserBase.resetInt() / resetFloat()
└─ UTF8DataInputJsonParser ─┘ │
▼
_textBuffer.contentsAsString()
⚠ no length check — buffer grows without bound
Path 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()` を
一切経由せずに直接設定します。つまり、`ParserBase` にのみ検証を追加したアップストリームの PR #827 の修正は、
**`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` 値を生成し、同じしきい値で制約をトリガーします。
- **小数 — 短い整数、無制限の小数部:** 整数部は1桁(0)でも、小数部は無制限に増加します。小数点自体はカウントから除外されます。
- **科学表記 — コンパクトでありながら壊滅的:** 約1,004バイトで最小の有効ペイロードです。スケールが ±10^1001 の `BigDecimal` のインスタンス化を強制し、トークンが小さいにもかかわらず無制限の中間ヒープ割り当てを要求します。
- **複合浮動小数点数 — 分割制限の回避:** `fractLen = 500` と `expLen = 500` の場合、どちらの成分も個別には1,000桁のしきい値に達しません。統合チェック `validateFPLength(intLen + fractLen + expLen)` のみがこのギャップを塞ぐ防御です。
- **非ブロッキングパーサー — 独立したバイパス:** 上記の4つのトークン形状はすべて、`ByteArrayFeeder` を介した `NonBlockingJsonParser` を通じて独立に悪用可能です。同期パーサーとは異なり、`NonBlockingJsonParser` は `_intLength` / `_fractLength` / `_expLength` を直接書き込むプライベートループ(`_startPositiveNumber`、`_finishNumberIntegralPart`、`_startFloat`、`_finishFloatFraction`、`_finishFloatExponent`)で数字を蓄積し、`ParserBase.resetInt()` と `resetFloat()` を完全にバイパスします。したがって、`ParserBase` のみを変更したアップストリームの PR #827 はこのパーサーを完全に無防備なまま残し、この修正では6箇所の独立した呼び出しサイトパッチが必要でした。
4つのトークン形状すべてと非ブロッキングバイパスは、`BigDecimal` または `BigInteger` が構築される前のトークン化時点で、
`ParserBase`(同期パーサー)の `validateIntegerLength` と `validateFPLength`、および
`NonBlockingJsonParser`(非同期パーサー)の6箇所の専用呼び出しサイトによって拒否されます。2.13.5 では4つのトークン形状すべてが黙って受け入れられますが、この修正により、すべてのパーサーは
`StreamConstraintsException: Number length (N) exceeds the maximum length (1000)` をスローします。
> **大きなペイロードを含む `UTF8DataInputJsonParser` に関する注意:** DataInput パーサーには
> 65 536バイトの既存の内部バッファ制限があります([jackson-core#493](https://github.com/FasterXML/jackson-core/issues/493))。
> このサイズを超えるドキュメント(例: 199,999桁のPoCペイロード ≈ 200 KB)は、
> 長さ制約が発動する前に `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 HIGH** |
| 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` がまったく存在せず、ドキュメントサイズを
制限する方法がありませんでした。この修正では、`StreamReadConstraints` に
`maxDocumentLength` フィールドを導入し、すべてのパーサーパスでそれを強制します。
**脆弱なパーサーパス:**
| パーサー | パス | 強制ポイント |
|--------|------|-------------------|
| `UTF8StreamJsonParser` | `InputStream` → `_loadMore()` | バッファ再充填後およびEOF時に `_currInputProcessed + count` を検証 |
| `ReaderBasedJsonParser` | `Reader` → `_loadMore()` | `UTF8StreamJsonParser` と同じパターン |
| `NonBlockingJsonParser` | `feedInput()` | 入力チャンクごとに `_currInputProcessed + _origBufferLen` を検証 |
| `UTF8DataInputJsonParser` | `DataInput` | フェイルファスト: `maxDocumentLength` が設定されている場合に `StreamConstraintsException` をスロー |
**攻撃シナリオ:** 攻撃者は、有効だが過大なJSONドキュメント(例: 数ギガバイトに及ぶ深いネスト
または高反復構造)を、`maxDocumentLength` を設定してリソース枯渇を防いでいるサービスに送信します。
強制がない場合、パーサーは設定された制限に関係なくドキュメント全体を処理し、
無制限のメモリとCPUを消費します。
---
## セキュリティへの影響
4つの脆弱性はすべて、認証不要でリモートから悪用可能です:
- `JsonParser` を介して攻撃者が制御するJSONを解析するサービス(直接、または
`jackson-core` をラップする Jackson Databind 経由)はすべてリスクがあります。
- 攻撃は簡単に構築可能です — 数百バイトのJSONで無制限のリソース消費を
トリガーするのに十分です。
- 機密性や完全性への影響はありません。可用性(DoS)のみが影響クラスです。
---
## 修正の詳細
### 公式アップグレード(推奨)
**jackson-core 2.15.4** 以降の安定版リリースにアップグレードしてください。2.15.x および 2.16+ のすべてのリリースには、
安全なデフォルト値を備えた `StreamReadConstraints` API が含まれています。```xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.15.4</version>
</dependency>
If upgrading to a later release is not immediately feasible, this branch applies a comprehensive
fix to the 2.13.5 codebase. It introduces StreamReadConstraints and StreamConstraintsException
with an API compatible with 2.15.x, hooks the constraints into all four JsonParser implementations
— including the non-blocking parser, which was not covered by the upstream fix — and enforces the
following default limits:
| Constraint | Default Limit |
|---|---|
| Maximum nesting depth | 1,000 |
| Maximum numeric token length | 1,000 digits |
| Maximum string token length | 1,000,000 characters |
Maximum BigDecimal scale magnitude | 100,000 |
StreamReadConstraints (356 lines)Immutable value object holding per-parser stream-read limits, constructed via a 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;
例外メッセージの形式:
"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);
}
4つのパーサーはすべて、_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 の範囲を超えて発見された主な追加修正です。6つの
`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` | ストリーム | 実際のアプリケーション利用をシミュレートする再帰的トラバーサル。深さ1001で `StreamConstraintsException`(修正済み)、深さ20,000で `StackOverflowError`(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` | 上限の整数(成功)、上限超過の整数(失敗)、上限の浮動小数点(成功)、上限超過の浮動小数点(失敗)、科学表記の指数が上限(成功)、指数が上限超過(失敗) — 全4パーサー |
| `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` = 上記3つ + `UTF8DataInputJsonParser`
> - `non-blocking` = `ByteArrayFeeder` 経由の `NonBlockingJsonParser`
> - `stream` = `UTF8StreamJsonParser`(デフォルトの `JsonFactory.createParser`)
### SNYK-JAVA-COMFASTERXMLJACKSONCORE-15907551 — ドキュメント長テスト
| テストファイル | テストメソッド | パーサーモード | 対象 |
|-----------|-------------|-------------|--------|
| `constraints/LargeDocReadTest.java` | `testInputStreamExceedsLimit` | `UTF8StreamJsonParser` | InputStream パーサーが `maxDocumentLength` を強制 — 10K制限で20Kドキュメントが拒否 |
| `constraints/LargeDocReadTest.java` | `testInputStreamUnderLimitSucceeds` | `UTF8StreamJsonParser` | InputStream パーサーが制限内のドキュメントを受け入れる |
| `constraints/LargeDocReadTest.java` | `testReaderExceedsLimit` | `ReaderBasedJsonParser` | Reader パーサーが `maxDocumentLength` を強制 — 10K制限で20Kドキュメントが拒否 |
| `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件のテストがすべて成功します。新たに追加されたセキュリティテストの内訳:
ビルドコマンド:```bash ./mvnw test
---
## 検証結果
### jackson-core 2.13.5 に対するリグレッションテスト
CVE テストメソッドは、2.13.5 コードベースでは **失敗** し、修正後ビルドでは **成功** するように設計されています。
2.13.5 に対する **`NumberOverflowTest#testSonatype_2022_6438`**:```
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)
**修正済みビルドでの両テスト: PASS。**
---
## 概念実証
### 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();
}
4つのトークン形状はすべて、BigDecimal / BigInteger への変換が試行される前に、
validateFPLength / validateIntegerLength をトリガーします。```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 以降(同梱のラッパー ./mvnw がこれを自動的に満たします) |
注記: 元の jackson-core 2.13.5 リリースは Java 6 をターゲットにしていました(
-source 1.6 -target 1.6)。 このセキュリティブランチでは、ビルドとランタイムの両方に Java 8 以降が必要です。 Jackson 2.13 の公開 API サーフェスは変更されていません。Maven と JDK の要件により、JDK 6/7 ランタイムのみ互換性が失われます。
./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 |
| テストファイル | 新規/拡張メソッド |
|---|
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 | 新規 — 6つの数値完了箇所での validateIntegerLength / validateFPLength。feedInput() 内の validateDocumentLength |
src/main/java/.../JsonStreamContext.java | 変更 | +20 | getNestingDepth() を追加 |
src/main/java/.../TSFBuilder.java | 変更 | +15 | _streamReadConstraints フィールドと streamReadConstraints() セッターを追加 |
src/main/java/.../JsonFactory.java | 変更 | +12 | ビルダーの制約をコンストラクタに配線。maxDocumentLength に対する DataInput のフェイルファスト |
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 | 全パーサーパスにおける maxDocumentLength 強制の9テスト |
| コンポーネント | バージョン |
|---|
| ベースタグ | jackson-core-2.13.5 |
| ブランチ | 2.13.5-CVE-2025-52999-sonatype-2022-6438-GHSA-2m67-wjpj-xhg9 |
| ビルドツール | Maven (ラッパー: ./mvnw) |
| テストフレームワーク | JUnit 3 / TestCase スタイル |
| テスト数 | 957 |