GCVE Workshop - 22 September 2026 (14:00-18:00), Luxembourg Before The Vulnopticon Conference - Registration
brew-acronym-cve-2026-66393
Vulnerability from osv_homebrew
Published
2026-08-23 20:29
Modified
2026-09-09 23:40
Summary
Natural Language Toolkit (NLTK) has unbounded recursion in JSONTaggedDecoder.decode_obj() may cause DoS
Details

Summary

JSONTaggedDecoder.decode_obj() in nltk/jsontags.py calls itself recursively without any depth limit. A deeply nested JSON structure exceeding sys.getrecursionlimit() (default: 1000) will raise an unhandled RecursionError, crashing the Python process.

Affected code

File: nltk/jsontags.py, lines 47–52

@classmethod
def decode_obj(cls, obj):
    if isinstance(obj, dict):
        obj = {key: cls.decode_obj(val) for (key, val) in obj.items()}
    elif isinstance(obj, list):
        obj = list(cls.decode_obj(val) for val in obj)

Proof of Concept

import sys, json
from nltk.jsontags import JSONTaggedDecoder

depth = sys.getrecursionlimit() + 50  # e.g. 1050
payload = '{"x":' * depth + "null" + "}" * depth

# Raises RecursionError, crashing the process
json.loads(payload, cls=JSONTaggedDecoder)

Impact

Any code path that passes externally-supplied JSON to JSONTaggedDecoder is vulnerable to denial of service. The severity depends on whether such a path exists in the calling code (e.g. nltk/data.py).

Suggested Fix

Add a depth parameter with a hard limit:

@classmethod
def decode_obj(cls, obj, _depth=0):
    if _depth > 100:
        raise ValueError("JSON nesting too deep")
    if isinstance(obj, dict):
        obj = {key: cls.decode_obj(val, _depth + 1) 
               for (key, val) in obj.items()}
    elif isinstance(obj, list):
        obj = list(cls.decode_obj(val, _depth + 1) for val in obj)

{
  "affected": [
    {
      "ecosystem_specific": {
        "fix": "bump",
        "range_state": "fixed",
        "resource": "nltk",
        "resource_purl": "pkg:pypi/nltk@3.10.3",
        "upstream_fixed_in": "3.9.4"
      },
      "package": {
        "ecosystem": "Homebrew",
        "name": "acronym",
        "purl": "pkg:brew/acronym"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.0.0_3"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "database_specific": {
    "confidence": "high",
    "source": "matched",
    "strategy": "registry",
    "upstream_evidence": [
      {
        "ecosystem": "PyPI",
        "key": "pkg:pypi/nltk@3.10.3",
        "name": "nltk",
        "resource": "nltk",
        "strategy": "registry",
        "subject_version": "3.10.3"
      }
    ]
  },
  "details": "### Summary\n`JSONTaggedDecoder.decode_obj()` in `nltk/jsontags.py` calls itself \nrecursively without any depth limit. A deeply nested JSON structure \nexceeding `sys.getrecursionlimit()` (default: 1000) will raise an \nunhandled `RecursionError`, crashing the Python process.\n\n### Affected code\nFile: `nltk/jsontags.py`, lines 47\u201352\n```python\n@classmethod\ndef decode_obj(cls, obj):\n    if isinstance(obj, dict):\n        obj = {key: cls.decode_obj(val) for (key, val) in obj.items()}\n    elif isinstance(obj, list):\n        obj = list(cls.decode_obj(val) for val in obj)\n```\n\n### Proof of Concept\n```python\nimport sys, json\nfrom nltk.jsontags import JSONTaggedDecoder\n\ndepth = sys.getrecursionlimit() + 50  # e.g. 1050\npayload = \u0027{\"x\":\u0027 * depth + \"null\" + \"}\" * depth\n\n# Raises RecursionError, crashing the process\njson.loads(payload, cls=JSONTaggedDecoder)\n```\n\n### Impact\nAny code path that passes externally-supplied JSON to \n`JSONTaggedDecoder` is vulnerable to denial of service.\nThe severity depends on whether such a path exists in the \ncalling code (e.g. `nltk/data.py`).\n\n### Suggested Fix\nAdd a depth parameter with a hard limit:\n```python\n@classmethod\ndef decode_obj(cls, obj, _depth=0):\n    if _depth \u003e 100:\n        raise ValueError(\"JSON nesting too deep\")\n    if isinstance(obj, dict):\n        obj = {key: cls.decode_obj(val, _depth + 1) \n               for (key, val) in obj.items()}\n    elif isinstance(obj, list):\n        obj = list(cls.decode_obj(val, _depth + 1) for val in obj)\n```",
  "id": "BREW-acronym-CVE-2026-66393",
  "modified": "2026-09-09T23:40:40Z",
  "published": "2026-08-23T20:29:34Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/nltk/nltk/security/advisories/GHSA-rf74-v2fm-23pw"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-66393"
    },
    {
      "type": "WEB",
      "url": "https://github.com/nltk/nltk/commit/00cdcd392142e6c745e7120c8d50a24127df5fad"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/nltk/nltk"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3724.yaml"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/nltk-before-denial-of-service-via-jsontaggeddecoder"
    }
  ],
  "schema_version": "1.7.3",
  "severity": [
    {
      "score": "CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Natural Language Toolkit (NLTK) has unbounded recursion in JSONTaggedDecoder.decode_obj() may cause DoS",
  "upstream": [
    "GHSA-rf74-v2fm-23pw",
    "CVE-2026-66393",
    "PYSEC-2026-3724"
  ]
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…