Json.cs: Catastrophic regex backtracking in Regex_String causes extreme slowdown with JSON arrays

オープン 初心者向け
#45 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
74/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
静か
技術スタック
csharp
領域
tooling

調査の方向性

ColorCode.Core/Compilation/Languages/Json.cs を開き、Regex_String パターンと issue で説明されているキー照合ルールを調べます。文字列値を含む JSON 配列で低速化を再現し、空にマッチする繰り返しを指定された unrolled-loop パターンに置き換え、期待される JSON scopes を変更せずに例が速やかに色付けされることを確認します。

索引モデルが issue の本文から書いたものです。

説明

Description

The JSON language definition in the underlying ColorCode-Universal dependency (ColorCode.Core/Compilation/Languages/Json.cs) contains a catastrophic backtracking bug in its Regex_String pattern. This causes exponential processing time (O(2^n)) when colorizing JSON that contains arrays with string values, making the application appear completely frozen.

The pattern is:

"[^"\\]*(?:\\[^\r\n]|[^"\\]*)*"

The repeating group (?:\\[^\r\n]|[^"\\]*) has two alternatives:

  • \\[^\r\n] — backslash escape, always consumes 2 characters ✓
  • [^"\\]*can match an empty string

The key-matching rule [,{]\s*("...")\s*: attempts to match JSON keys (string followed by :). For every , inside a JSON array, the engine tries this rule, matches the string value (e.g. "John Doe"), but fails when no : follows. On failure, the engine backtracks into the string subpattern and tries all possible ways to partition the n non-special characters between the outer [^"\\]* and the inner (?:[^"\\]*) repetition — resulting in O(2^n) backtracking states. For a 16-character string value this is ~65,000 states; for longer strings the number grows exponentially.

The fix is to replace the pattern with Friedl's Unrolled Loop:

"[^"\\]*(?:\\.[^"\\]*)*"

Each iteration of the group now must consume at least 2 characters via \\., eliminating the empty alternative and all backtracking.

Expected Behaviour

JSON arrays with string values are colorized in milliseconds, identical to simple JSON objects without arrays.

Actual Behaviour

Colorizing JSON containing arrays with string values causes extreme slowdown. Processing time grows exponentially with the length of string values inside arrays. Simple JSON objects (no arrays) are not affected.

Affected Version

3.0.1

Steps to Reproduce

  1. Set up a Markdig pipeline with UseColorCode()
  2. Call Markdig.Markdown.ToHtml() on a Markdown document containing the following fenced code block:
```json
{
  "Project": "My Project",
  "Tags": [
    "first long string value",
    "another long string value"
  ]
}
```
  1. Observe that processing takes several seconds (or longer) instead of being near-instantaneous
  2. Note that removing the array (keeping only simple key-value pairs) restores normal performance

Workaround (until the underlying bug is fixed in ColorCode-Universal): pass a corrected JSON language definition via additionalLanguages:

var pipeline = new MarkdownPipelineBuilder()
    .UseAdvancedExtensions()
    .UseColorCode(additionalLanguages: new[] { new JsonLanguageFixed() })
    .Build();
// Friedl's Unrolled Loop – no backtracking, correct escape handling
private sealed class JsonLanguageFixed : ILanguage
{
    private const string RegexString = @"""[^""\\]*(?:\\.[^""\\]*)*""";
    private const string RegexNumber = @"-?(?:0|[1-9][0-9]*)(?:\.[0-9]*)?(?:[eE][-+]?[0-9]+)?";

    public string Id => LanguageId.Json;
    public string Name => "JSON";
    public string CssClassName => "json";
    public string FirstLinePattern => null;

    public IList<LanguageRule> Rules => new List<LanguageRule>
    {
        new LanguageRule($@"[,\{{]\s*({RegexString})\s*:", new Dictionary<int, string> { { 1, ScopeName.JsonKey } }),
        new LanguageRule(RegexString,                       new Dictionary<int, string> { { 0, ScopeName.JsonString } }),
        new LanguageRule(RegexNumber,                       new Dictionary<int, string> { { 0, ScopeName.JsonNumber } }),
        new LanguageRule(@"\b(true|false|null)\b",          new Dictionary<int, string> { { 1, ScopeName.JsonConst } }),
    };

    public bool HasAlias(string lang) => false;
}

Checklist

主要言語
C#
スター
267
フォーク
58
PR マージ指標
30日以内にマージされた PR はありません

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

CommunityToolkit/ColorCode-Universal のほかの issue

CommunityToolkit/ColorCode-Universal の issue をすべて見る

似ている issue

C# の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。