Security Tracking: NLTK Vulnerability Mitigation Roadmap
This issue tracks the 20 remaining unfixed NLTK security advisories from Huntr.com, categorized by shared implementation logic and common code-fixing patterns to maximize code reuse and streamline pull request reviews.
These reports where filed near the end of June 2026, so the corresponding CVEs are scheduled for publication in the last week of September.
Cluster 1: Downloader & Archive Integrity (nltk/downloader.py)
Shared Code/Fix Logic: Input stream bounds checking, strict archive member path validation, and cryptographic signature verification replacing MD5.
| Component / Vulnerability | CVE / ID | Status |
|---|---|---|
nltk.downloader.Downloader (Zip-Slip / hard-link overwrite) |
Unknown | PR #3804 |
nltk.downloader.Downloader (MD5 trust & colliding ZIP substitution) |
Unknown | Pending PR |
nltk/downloader.py (Unbounded response size / disk exhaustion) |
Unknown (CWE-400) | Pending PR |
Cluster 2: Data Loading & Deserialization (nltk/data.py)
Shared Code/Fix Logic: Resource loading safety checks, disabling insecure pickle over HTTP, and preventing arbitrary local module shadowing (json.py/yaml.py).
| Component / Vulnerability | CVE / ID | Status |
|---|---|---|
nltk.data.load() (ReDoS via local json.py / yaml.py import injection) |
CVE-2026-12914 | PR #3798 |
nltk.data.load() (Insecure deserialization via pickle.load() on HTTP sources) |
CVE-2026-14937 | PR #3794 |
nltk.data.path (Namespace traversal / stale cache entries) |
CVE-2026-14707 | Addressed upstream / PR #3753 |
Cluster 3: Newline & Protocol Sanitization (nltk.tag & nltk.sem)
Shared Code/Fix Logic: A common input sanitization helper to strip or reject newline characters (\n, \r) from text inputs before they are written to line-oriented subprocess protocols or parser control records.
| Component / Vulnerability | CVE / ID | Status |
|---|---|---|
nltk.tag.hunpos.HunposTagger.tag() (Newline protocol poisoning) |
CVE-2026-14710 | Pending PR |
nltk.sem.boxer.Boxer (Newline sentence text META control-record injection) |
CVE-2026-14715 | Pending PR |
WORKDIR/runner.py / CoreNLPParser (Batch-boundary injection) |
CVE-2026-14728 | Addressed upstream / PR #3788 |
WORK/runner.py / StanfordParser (Batch-boundary injection) |
Unknown (CWE-501) | Addressed upstream / PR #3782 / #3788 |
Cluster 4: Corpus Readers (nltk.corpus.reader)
Shared Code/Fix Logic: XML parsing safety, tree builder sanitization, and state isolation in lazy iterators.
| Component / Vulnerability | CVE / ID | Status |
|---|---|---|
nltk.corpus.reader.mte.MTECorpusReader (Shared lazy tag state corruption) |
CVE-2026-14944 | Fixed in PR #3792 |
nltk.corpus.reader.toolbox.ToolboxCorpusReader.xml() (XML/XSS marker injection) |
CVE-2026-14718 | Fixed in PR #3800 |
Cluster 5: Isolated Individual Fixes (Unique Code Paths)
Modules requiring distinct, non-overlapping code changes:
| Component / Vulnerability | CVE / ID | Status |
|---|---|---|
nltk.parse.malt.MaltParser.parse_tagged_sents() (CoNLL row injection via unescaped tokens) |
CVE-2026-14720 | Pending PR |
nltk.decorators.new_wrapper() (Dynamic eval() signature metadata execution) |
CVE-2026-14727 | Pending PR |
nltk.twitter.twitterclient.TweetViewer (Terminal control sequence emission) |
CVE-2026-14724 | Pending PR |
nltk.tokenize.repp.ReppTokenizer.tokenize_sents() (Unbounded plaintext temporary files) |
Unknown (CWE-459) | Pending PR |
nltk.__version__ (config_java() / java()) (Concurrent JVM option injection) |
Unknown (CWE-567) | Pending PR |
nltk.tag.stanford.StanfordTagger.tag_sents() (Shared _input_file_path state exposure) |
CVE-2026-14712 | Addressed upstream / PR #3781 / #3787 |
nltk.pathsec / corenlp.py (SSRF network reconnaissance) |
CVE-2026-14945 | Addressed upstream / PR #3753 / #3787 |
nltk.inference.prover9.Prover9Parent.prover9_input() (Delimiter injection end_of_list) |
CVE-2026-14709 | Pending PR |
Source: nltk/nltk