Open actionable advisories and recorded vendor fixes. This public package view has no inventory attached and cannot establish whether your deployment is affected. A vendor fix never resolves your finding; verify your installed version and the source guidance.
Open advisories
53 actionable advisories, newest first. Withdrawn advisories and news are excluded. Coverage is partial.
`WekaClassifier` in NLTK passes a caller-controlled model file path to the weka Java VM without any `nltk.pathsec` validation. `WekaClassifier.train` puts the caller's `model_filename` verbatim into the weka argv `["-d", model_filename, ...]` (weka *writes* the trained model there), and `WekaClassifier._classify_many` puts `self._model` into `["-l", self._model, ...]` (weka *reads* it). Under `nltk.pathsec.ENFORCE=True` (the default on current HEAD), NLTK's stated invariant — established by advisory CVE-2026-81726 / GHSA-8mgp-746c-j5xp — is that caller-controlled model-artifact paths must be bounded to the allowed NLTK data roots before any native tool opens them. `WekaClassifier` is the one `java()`-based tool wrapper that skips this entirely, giving an untrusted workflow arbitrary-location file write (`-d`) and an arbitrary file read/parse oracle (`-l`), defeating pathsec containment. This is a standalone, unfixed variant of the same class as CVE-2026-81726, on a…
Recommended action: Upgrade to 3.10.4 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Pl196xCorpusReader still parses whole TEI blocks with multiple lazy regexes over attacker-controlled text. A malformed file with many opening tags and no matching closing tags forces repeated rescans and produces quadratic CPU growth in public reader APIs. Details - Vulnerability type: Regular-expression denial of service - Affected component: nltk.corpus.reader.pl196x.TEICorpusView.readblock and Pl196xCorpusReader public methods - Affected versions: Published 3.9.4 and current source v3.10.0-rc2 both reproduced. - Patched versions: Not yet patched - Root cause: Lazy .? whole-block regexes rescan untrusted XML-like blocks from each opening-tag position. The parser uses regexes for paragraphs, sentences, and word tags across the whole <text block. When the attacker supplies many unmatched opening tags, each attempt scans toward the end of the block and fails, then restarts from the next opening tag. There is near four-times runtime growth each time the number of malformed <p…
Recommended action: Upgrade to 3.10.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
The NLTK tgrep module accepts user-supplied regular expressions and passes them to the Python re engine without a timeout or validation, enabling catastrophic backtracking (ReDoS). Applications that expose the tgrep API to external input are vulnerable to a single-request denial of service that blocks the Python process indefinitely. Affected Code nltk/tgrep.py — tgrepnodeaction() (around line 320) When a tgrep pattern contains a /regex/ node, tgrepnodeaction compiles the embedded regex literal directly with no validation: The compiled regex is applied against every matching tree node label via r.search(...). A caller reaching this path via tgreppositions() or tgrepcompile() controls nodelit entirely. Proof of Concept Working Poc The following script uses increasing values of n (the number of repeated as in the tree root label) to measure the execution time of tgreppositions with the catastrophic regex /((a+)+)b/. On standard CPython with NLTK 3.10.2, the runtime grows…
Recommended action: Upgrade to 3.10.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
NLTK's Text.findall() and TokenSearcher.findall() methods accept user-supplied regular expressions and pass them to the Python re engine without timeout or validation, enabling catastrophic backtracking (ReDoS). This issue is isolated to the nltk.text module and was resolved in a prior commit. Affected Code nltk/text.py — TokenSearcher.findall() (line 255) / Text.findall() (line 620) TokenSearcher.init builds an internal string by wrapping each token in angle brackets. The findall() method preprocesses the caller-supplied regexp and runs it directly against this string with no timeout: The preprocessing transforms < and angle-bracket syntax but does not inspect or reject catastrophically backtracking patterns. Proof of Concept Impact Applications that expose Text.findall() to external input are vulnerable to a denial of service. An unauthenticated attacker can cause indefinite CPU saturation with one request, denying service to all other users of the Python process.…
Recommended action: Upgrade to 3.10.0 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
NLTK corpus-reader constructors can still reach outside-root file and database reads before the nltk.pathsec sandbox boundary is enforced. The PoC shows the safe path blocked by pathsec.open, then LinThesaurusCorpusReader and PanLexLiteCorpusReader succeeding in the same process. Affected Product - Product: NLTK - Asset / component: nltk.corpus.reader constructors - Version tested: 3.10.2 - Deployment / package / tag: commit 474af1f5a94b1b8d53fc2b6defec3a2ce7633b74 / PyPI nltk - Environment used for verification: Python 3.13.14 Vulnerability Details - Vulnerability class: path sandbox bypass / external control of file path - Required privileges: none beyond the ability to supply a corpus root path to a consumer call site - Entry point: LinThesaurusCorpusReader(root) and PanLexLiteCorpusReader(root) - Trust boundary crossed: NLTK data-root sandbox enforced by nltk.pathsec - Root affected functions: - CorpusReader.init - LinThesaurusCorpusReader.init -…
Recommended action: Upgrade to 3.10.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Several corpus readers still step outside NLTK's symlink-aware trusted-root model. They derive in-root paths from trusted corpus state, convert those paths back into plain strings, and reopen them with built-in open() rather than nltk.pathsec.open(). Details - Vulnerability type: Path traversal and symlink boundary bypass - Affected component: nltk.corpus.reader.ipipan, nltk.corpus.reader.crubadan, nltk.corpus.reader.lin - Affected versions: Published 3.9.4 and current source v3.10.0-rc2 both reproduced. - Patched versions: Not yet patched - Root cause: Root-derived paths are reopened with raw open() without preserving the trusted-root boundary. IPIPANCorpusReader opens header.xml derived from morph.xml, CrubadanCorpusReader opens table.txt directly, and LinThesaurusCorpusReader opens simN.lsp paths returned from its own root helpers. Under pathsec.ENFORCE=True, a symlink placed inside the trusted corpus root can point outside the root and still be parsed successfully. It was…
Recommended action: Upgrade to 3.10.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
The current source tree still allows arbitrary code execution during supposedly safer allowlisted pickle loading. The allowlist trusts whole module namespaces instead of exact safe globals, so crafted pickles can invoke dangerous in-namespace callables through pickle REDUCE. Details - Vulnerability type: Remote code execution via unsafe deserialization - Affected component: nltk.picklesec.allowlistedpickleload, nltk.tokenize.punkt.punktpickleload, nltk.parse.transitionparser.TransitionParser.parse - Affected versions: Current source v3.10.0-rc2; published 3.9.4 was not the claim target for this bypass. - Patched versions: Not yet patched - Root cause: Module-prefix allowlists include dangerous callables such as nltk.tokenize.repp.ReppTokenizer.execute and numpy.f2py.crackfortran.myeval. punktpickleload() allowlists both nltk.tokenize.punkt and the whole nltk.tokenize namespace, which exposes ReppTokenizer.execute() and its subprocess.Popen(...) sink during unpickling.…
Recommended action: Upgrade to 3.10.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Several XML parsing sites in NLTK still used xml.etree.ElementTree directly, which honours <!ENTITY declarations in a document's internal DTD subset. A crafted document a few hundred bytes long can expand to megabytes in memory (each nesting level multiplies by ten), a denial-of-service. Affected call sites (<= 3.10.2): - nltk.chunk.namedentity.loadacefile — parses ACE annotation XML - nltk.internals.ElementWrapper — converts any given string to an Element - nltk.downloader — Package.fromxml, Collection.fromxml, findcollections, findpackages libexpat 2.6.0 added an input-amplification cap, but it only engages above an activation threshold (~8 MiB output) and depends on whichever libexpat the interpreter links; builds against older libexpat have no cap at all. External entities are not resolved by ElementTree, so this is a memory-amplification DoS (CWE-776), not XXE/file disclosure. This completes the earlier defusedxml adoption that these sites were missed by. Fix routes all of them…
Recommended action: Upgrade to 3.10.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Current NLTK source reopens SSRF in proxied environments. pathsec.urlopen() validates the requested hostname locally, but once proxy inheritance is enabled the real fetch is performed by the proxy rather than by the validated direct-connect socket path. Details - Vulnerability type: Server-side request forgery - Affected component: nltk.pathsec.urlopen, nltk.data.load, nltk.downloader.Downloader.index, nltk.downloader.Downloader.download - Affected versions: Current source v3.10.0-rc2; published 3.9.4 was a negative control and did not reproduce. - Patched versions: Not yet patched - Root cause: Proxy-handler inheritance disables SafeHTTPHandler and SafeHTTPSHandler, so the validated hostname no longer matches the actual egress destination. The hardened direct path pins the validated numeric destination IP before opening the socket. The proxied branch instead copies ProxyHandler instances from the global opener, marks the request as proxied, and skips the pinned handlers. I…
Recommended action: Upgrade to 3.10.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
The NLTK library's TransitionParser.parse() method deserializes model files using pickleload() with the default restricted=False parameter, allowing arbitrary Python code execution when loading a malicious model file. The library provides a RestrictedUnpickler class for safe deserialization, but it is never used by production code paths, leaving the vulnerability unpatched. Root Cause File: nltk/parse/transitionparser.py (lines 542-557) The parse() method calls pickleload(f) without restricted=True, routing through WarningUnpickler which inherits from pickle.Unpickler and does NOT override findclass(). This allows arbitrary class/function resolution during unpickling, enabling RCE via standard pickle gadgets (e.g., os.system, subprocess.Popen). Vulnerability chain in nltk/picklesec.py: WarningUnpickler only emits a warning but does NOT block unsafe class loading — it calls super().load() which is standard pickle.Unpickler.load(). Why this is not by design: - NLTK…
Recommended action: Upgrade to 3.10.0 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
IPIPANCorpusReader (nltk/corpus/reader/ipipan.py) exposes public methods, channels(), domains(), categories(), and fileids(channels=...), that accept a caller supplied fileids list and read a file via a completely unprotected builtin open() call, with no nltk.pathsec involvement at all. A symlink placed inside the corpus root, with a name containing no separators or .., passes NLTK's existing traversal checks and is opened directly, reading a file from anywhere on the filesystem the process can access. Root cause All four methods route through gettag(): f arrives via listheaderfiles() / listmorphfilesby(), both of which call: on the result of self.abspath(...) or self.abspaths(...). FileSystemPathPointer subclasses str, so .replace() returns a plain Python string, silently discarding the PathPointer wrapper. That plain string is handed straight to builtin open(). This is a more severe variant of the same CWE-59 class already fixed elsewhere in this codebase…
Recommended action: Upgrade to 3.10.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
This is a new, distinct vulnerability: a bypass of the fix already published as GHSA-xh95-f55m-82fw ("Path traversal in NLTK FramenetCorpusReader.frame() allows arbitrary XML file read, bypassing the nltk.pathsec sandbox"), not a duplicate of it. Summary The original advisory was fixed (PR #3581) by adding rejectunsafepathcomponent(), which blocks literal /, \, .., and Windows drive prefixes in caller-/corpus-supplied names. It never resolves symlinks. All three call sites that use this guard still resolve the resulting path through self.abspath() (nltk/corpus/reader/api.py, self.root.join(fileid)), which is a plain lexical join, not the symlink-resolving, requiredroot-scoped check that CorpusReader.open() (and NKJPCorpusReader's own fix for its sibling advisory) correctly use elsewhere in this same codebase. A symlink placed inside the corpus's own subdirectory, with a name containing no separators at all, passes the guard cleanly and reads a file completely outside the corpus root.…
Recommended action: Upgrade to 3.10.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Published nltk==3.9.4 still contains several XML-reader entrypoints that build parser paths from caller-controlled selectors or trusted-looking index state without preserving the corpus-root boundary. Details - Vulnerability type: Path traversal and trusted-root bypass - Affected component: FramenetCorpusReader.framebyname, FramenetCorpusReader.doc, FramenetCorpusReader.lu, NKJPCorpusReader.header - Affected versions: Published 3.9.4 reproduced. Current source v3.10.0-rc2 acted as a negative control and blocked the same payloads. - Patched versions: Patched in version 3.10.0, which includes the path-safety rejections seen in the release candidate. - Root cause: Stable reader paths still construct raw XML filenames from unsafe selectors, poisoned index state, or unsafe file identifiers. I confirmed four public stable entrypoints return parsed outside-root content: a parent-segment traversal frame name, a poisoned fulltext index filename, a poisoned LU id, and an unsafe NKJP…
Recommended action: Upgrade to 3.10.0 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
NLTK's package downloader in nltk/downloader.py does not verify file integrity after download and before extraction. The download flow at lines 789-825: 1. File is downloaded to a temp path via HTTP 2. os.replace(tmp_filepath, filepath) moves it to the final location (line 799) 3. Extraction begins via _unzip_iter() (line 825) Between steps 2 and 3, there is no SHA-256 verification. The checksum logic exists in _pkg_status() (lines 982-1015) but it is only used BEFORE download as a status check ("is this package already installed and up-to-date?"). It is never called after download to verify the file that was actually received. Attack vectors: 1. MITM during HTTP download (NLTK downloads from http:// by default on some mirrors) 2. Race condition on shared filesystems (attacker replaces file between os.replace and _unzip_iter) 3. DNS poisoning redirecting to attacker-controlled server PoC: ```python import nltk import unittest.mock import zipfile import io import os Create a…
Recommended action: Upgrade to 3.9.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Setting nltk.pathsec.ENFORCE = True is documented to sandbox all file access to allowed NLTK data directories and raise PermissionError on unauthorized access. However, StreamBackedCorpusView opens files via builtins.open() directly, bypassing pathsec.validatepath() entirely. An attacker who can influence the fileid argument can read arbitrary local files regardless of the ENFORCE setting. Details nltk/pathsec.py:274 defines the enforcement point: StreamBackedCorpusView.open() in nltk/corpus/reader/util.py bypasses this entirely for string paths: Also affected: XMLCorpusView and any corpus reader subclass that passes a raw string fileid to StreamBackedCorpusView. PoC Trigger: Confirmed on latest stable NLTK. No privileges required. Impact - Type: Arbitrary Local File Read / Security Control Bypass - CWE: CWE-22, CWE-284 - OWASP: A01:2021 – Broken Access Control Affects web apps, REST APIs, and multi-tenant NLP pipelines where user input influences the fileid passed to NLTK…
Recommended action: Upgrade to 3.10.0 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
There's a logic bug in FileSystemPathPointer.open() inside nltk/data.py that makes the sandbox check permanently inert. The guard condition is always False — meaning any file the process can read is accessible by passing a file:// URL to nltk.data.load(). --- Details In nltk/data.py, FileSystemPathPointer.open() was patched at some point with a comment saying "SECURITY PATCH ENFORCING SANDBOX", but the check doesn't work: path is set to os.path.normpath(self.path) on line 1, then compared against os.path.normpath(self.path) again in the condition. They are always equal. The ValueError never fires. On top of that, init already calls os.path.abspath() before storing self.path, so it's normalized before open() is even called. Running normpath on it again changes nothing. The stream = open(self.path, "rb") line is always reached regardless of what path was passed in. --- PoC Tested on Python 3.11, NLTK 3.9.1, Ubuntu 22.04. Both print file contents. No exception is raised. ---…
Recommended action: Upgrade to 3.10.0 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
nltk.corpus.reader.api.CorpusReader.open() can be used to read files outside the intended corpus root via a symlink placed inside that root. Although NLTK blocks absolute paths and .. traversal, the current boundary check is only lexical and does not account for symlink resolution. This leads to an arbitrary local file read / filesystem sandbox bypass for applications that rely on CorpusReader or FileSystemPathPointer to restrict file access. Details The vulnerable flow is: - nltk/corpus/reader/api.py:222 - CorpusReader.open() blocks absolute paths and .., then calls self.root.join(file).open() - nltk/data.py:398 - FileSystemPathPointer.join() joins the requested file ID and checks whether the resulting path still appears to remain under the configured root The problem is that the check is based on the lexical path after os.path.normpath(), not on the resolved path after following symlinks. Current behavior: 1. CorpusReader.open() rejects: - absolute paths - .. path traversal…
Recommended action: Upgrade to 3.9.4 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
NLTK's pathsec.py security module defaults to ENFORCE=False (line 24), which means all 8 security validation functions only emit RuntimeWarning instead of raising exceptions when violations are detected. The pathsec module was introduced as the fix for CVE-2024-39705 (arbitrary code execution via pickle) and CVE-2026-0846 (path traversal). However, with ENFORCE=False as the default: 1. pathsec.open('/etc/passwd') succeeds (reads the file, emits warning) 2. pathsec.validatenetworkurl('http://169.254.169.254/...') succeeds (warning only) 3. pickle.loads() via nltk.data.load() proceeds despite unsafe source (warning only) Every security gate follows the same pattern: This means the security remediations for CVE-2024-39705 and CVE-2026-0846 are effectively disabled by default. Any user who installed NLTK 3.9.x expecting the security fixes to be active is still vulnerable unless they manually set NLTKPATHSECENFORCE=1. PoC: The correct default is fail-secure: ENFORCE should be True unless…
Recommended action: Upgrade to 3.10.0 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
There is an SSRF vulnerability in NLTK 3.9.4's network URL validation. The validatenetworkurl() function in nltk/pathsec.py fails open when DNS resolution returns an error. The resolvehostname() helper at lines 193-234 catches OSError and ValueError during socket.getaddrinfo() and returns an empty list []. When this happens, the validation loop in validatenetworkurl() iterates over nothing (for addr in resolved: ... never executes), with no else/fallback check. The function returns normally, and urlopen() proceeds to make the request without any IP validation. This means: 1. If DNS is temporarily unavailable, ALL SSRF protections are disabled 2. DNS rebinding attacks bypass the check after the LRU cache entry expires 3. In environments with unreliable resolvers, the protection is permanently bypassed PoC: The correct behavior is fail-closed: if DNS resolution fails, the URL should be REJECTED (not allowed). The function should raise an exception or return a failure status when…
Recommended action: Upgrade to 3.10.0 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
nltk.stem.PorterStemmer.stem() -- a ubiquitous public API applied to arbitrary, often untrusted, tokens -- runs in O(n^2) time on a token containing a long run of the letter 'y', letting a single ~20-50 KB token pin a CPU core (CWE-407). Root cause isconsonant(word, i) was made iterative (commit for #3633, GHSA/CWE-674) to fix an earlier unbounded-recursion RecursionError on 'y'10000. The iterative form walks backward over the whole run of 'y's on every call: measure() then calls isconsonant(stem, i) once for every position i of the stem. For a run of n 'y's that is sum{i} O(i) = O(n^2). The recursion fix therefore traded a CWE-674 RecursionError for a CWE-407 quadratic-time DoS. Proof of concept Measured (Python 3.13): stem('y'5000 + 'ness') = 2.6s, stem('y'10000 + 'ness') = 11.3s (2x input - ~4.3x time = quadratic), stem('y'20000 + 'ness') 20s. A pure run of 'y' with no matching suffix is fast because the stemmer rules that call measure do not fire; a real suffix such as 'ness'…
Recommended action: Upgrade to 3.10.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
NLTK's downloader now blocks symlink escapes during ZIP extraction, but it still treats pre-existing hardlinks inside the install tree as ordinary in-root files. A normal package install can therefore overwrite an outside-root inode through that hardlink. Details - Vulnerability type: Filesystem containment bypass - Affected component: nltk.downloader.Downloader.download, nltk.downloader.Downloader.incrdownload - Affected versions: Published 3.9.4 and current source v3.10.0-rc2 both reproduced for the extraction-stage overwrite. - Patched versions: 3.10.3 - Root cause: The downloader validates traversal and symlink conditions but does not reject pre-existing hardlink aliases inside the install tree. The install flow correctly rejects a pre-existing symlink at an extraction target, yet it accepts a pre-existing hardlink at the same path. When the package is installed, extracted member data is written through the hardlink and mutates the outside inode. PoC Preconditions - The…
Recommended action: Upgrade to 3.10.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Several model-artifact APIs still treat caller-controlled model paths as ordinary filenames even when NLTK path security is enforced. The same outside-root paths are rejected by guarded helpers, but these public read and write flows still use raw file APIs. Details - Vulnerability type: File sandbox bypass - Affected component: `TransitionParser.train`, `TransitionParser.parse`, `AveragedPerceptron.save`, `AveragedPerceptron.load`, `PerceptronTagger.save_to_json`, `save_maxent_params` - Affected versions: Published `3.9.4` and current source `v3.10.0-rc2` both reproduced. - Patched versions: Not yet patched - Root cause: Model import and export helpers use built-in `open()` on caller-controlled paths instead of pathsec-aware helpers. `TransitionParser.train()` writes outside allowed roots, `TransitionParser.parse()` reads outside allowed roots, `AveragedPerceptron` bypasses the sandbox in both directions, and adjacent read-side helpers in the same…
Recommended action: Upgrade to 3.10.4 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
XMLCorpusView.readxmlfragment() reads a corpus file in 1 KiB blocks, appending each block to a growing fragment string, then calls VALIDXMLRE.match(fragment) on the full accumulated buffer every iteration. Because each iteration rescans the entire accumulated fragment, the total amount of work grows quadratically with input size. Commit c9c332284 (CWE-1333) made each match() call linear. The quadratic behavior is separate: the loop calls match() once per 1 KiB block, each time on a longer buffer. On the test system, an 8 MiB malformed XML file consumed approximately 48 CPU-seconds through the public BNCCorpusReader.words() API with no source modification. Absolute timings vary by hardware. readxmlfragment() imposes no limit on fragment size or iteration count. Details File: nltk/corpus/reader/xmldocs.py Function: XMLCorpusView.readxmlfragment(), lines 261–308 The relevant loop: For a payload of b'<' + b'a' (N-1): - For this malformed input, VALIDXMLRE.match(fragment) does not…
Recommended action: Upgrade to 3.10.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
nltk.parse.RecursiveDescentParser (and SteppingRecursiveDescentParser) enumerate parses top-down with no bound on the number of recursive steps. A small, crafted context-free grammar makes a short input consume unbounded CPU (and/or exhaust the Python recursion stack), pinning a process indefinitely — a denial of service. Proof of concept Both of the following hang on a 24-token input (killed after 8s; growth is super-linear in input length), on NLTK develop: Impact An application that runs RecursiveDescentParser on a grammar (or an input) drawn from an untrusted source can be driven into an unbounded CPU / stack-exhaustion loop by a tiny payload. No confidentiality or integrity impact; single-process availability only. Sibling The RegexpTokenizer ReDoS reported alongside this (CVE-2026-12875) is a different class (caller-supplied regex) and is addressed under GHSA-w3v8-gmh9-3wv7.
Recommended action: Upgrade to 3.10.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
nltk.featstruct.FeatStructReader (used by FeatStruct(str) and by FeatureGrammar.fromstring()) parses feature-structure strings such as [a=1] with a recursive-descent parser that has no nesting-depth limit. A small, trivially-crafted input (~700 bytes) with deeply nested brackets drives the parser past Python's recursion limit and raises an unhandled RecursionError instead of the library's normal, catchable ValueError/LogicalExpressionException. Any application that parses user-supplied feature-structure or feature-grammar text (e.g. NLP teaching tools, grammar "playgrounds", unification-grammar-based NLU pipelines) can be crashed by an unauthenticated input with no special privileges. This is a Denial of Service issue (CWE-674, Uncontrolled Recursion), not a memory-safety or code-execution issue. This appears to be the same bug class as two issues already fixed elsewhere in the codebase — nltk/jsontags.py (JSONTaggedDecoder.decodeobj, guarded by MAXDECODEDEPTH = 200) and…
Recommended action: Upgrade to 3.10.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.