HIGHCVSS v3.1 7.5Vulnerability

CVE-2026-126304

## Summary `YCOECorpusReader` builds two nested corpus sub-readers whose roots are derived from `self.root.join("psd")` and `self.root.join("pos")` (nltk/corpus/reader/ycoe.py). Because `FileSystemPathPointer.join()` (nltk/data.py) performs a purely lexical containment check and never resolves symlinks, and because `CorpusReader.__init__` (nltk/corpus/reader/api.py) validates a sub-reader root only against the global NLTK data sandbox and not against the enclosing corpus root, a directory symlink named `psd` or `pos` planted inside a YCOE corpus lets the reader anchor its nested sub-readers outside the YCOE corpus root. An attacker who controls the contents of a YCOE corpus loaded under `pathsec.ENFORCE=True` can make `parsed_sents()`, `tagged_words()`, and related methods return file content from other locations inside the allowed data sandbox, defeating the per-corpus-root containment boundary the pathsec controls are meant to enforce. ## Root Cause NLTK's pathsec controls are designed to keep each corpus reader confined to its own corpus root. The scoped containment is enforced through `validate_path(..., required_root=self._root)` at read time. However, the containment invariant is violated at reader-construction time for nested readers: - `FileSystemPathPointer.join()` accepts a component that is a directory symlink because it only checks the joined string lexically (`os.path.normpath` plus a `startswith` prefix test) and never calls `resolve()`. - `CorpusReader.__init__` validates the resulting sub-reader root string, but calls `validate_path` without `required_root`, so only the global-sandbox layer runs; the scoped per-corpus-root layer is skipped. The symlink is resolved by that global check, its target is found to be inside the allowed sandbox, and the sub-reader is accepted with its root pointing at the escaped target. From that point every read the sub-reader performs is scoped to the already-escaped root, so no later guard re-anchors it to the YCOE

Properties

Severity
high
Summary
NLTK: Symlink escape in YCOECorpusReader lets a psd/pos directory symlink read files outside the corpus root within the data sandbox
Nvd fetched at
2026-11-14T00:40:25.638091+00:00
Epss score
0.00522
Cvss severity
HIGH
Cvss score
7.5
Retrieved at
2026-11-14T00:51:03+00:00
Ghsa published
2026-11-13T20:55:16Z
Source url
https://github.com/advisories/GHSA-qg35-44qx-7vrr
Ghsa updated
2026-11-13T20:55:19Z
Ghsa id
GHSA-qg35-44qx-7vrr
Last source
FIRST EPSS
Score
7.5
Cvss version
3.1
Cve id
CVE-2026-126304
Cvss vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
First seen by Signal
2026-11-13T17:31:47+00:00
Is ghsa only
false
Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
Published at
2026-11-13T10:17:35.637

Related Entities (3)

AFFECTS (1)

→[Software]pip/nltk

HAS_WEAKNESS (1)

VULNERABLE_TO (1)

←[Software]pip/nltk

Related research

Explore deeper with Ninja Signal's threat intelligence graph

CVE-2026-126304 (CVSS v3.1 7.5) — Ninja Signal Threat Intelligence | Ninja Signal