CVE-2026-65915
NLTK before 3.10.0 Arbitrary File Read via FileSystemPathPointer
NLTK versions before 3.10.0 contain a logic bug in FileSystemPathPointer.open() where the sandbox validation check compares a normalized path against itself, making the security check permanently inert. Attackers can pass file:// URLs to nltk.data.load() to read arbitrary files accessible to the process user, including credentials and configuration files.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
7.1
NLTK before 3.10.0 Arbitrary File Read via FileSystemPathPointer
NLTK versions before 3.10.0 contain a logic bug in FileSystemPathPointer.open() where the sandbox validation check compares a normalized path against itself, making the security check permanently inert. Attackers can pass file:// URLs to nltk.data.load() to read arbitrary files accessible to the process user, including credentials and configuration files.
- Record
CVE-2026-65915- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N - CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-284
- Updated
- 2026-11-02
EUVDEUVD-2026-64309
7.1
NLTK versions before 3.10.0 contain a logic bug in FileSystemPathPointer.open() where the sandbox validation check compares a normalized path against itself, making the security check permanently inert. Attackers can pass file:// URLs to nltk.data.load() to read arbitrary files accessible to the process user, including credentials and configuration files.
- Record
EUVD-2026-64309- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-72r2-7mfr-5xr9
6.5
NLTK: FileSystemPathPointer.open() sandbox check is dead code — arbitrary file read via file:// protocol
Summary
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:
def open(self, encoding=None):
path = os.path.normpath(self._path)
# Block raw absolute reads such as "/" "C:\\Windows" etc.
if os.path.isabs(path) and path != os.path.normpath(self._path):
raise ValueError(f"Direct absolute file access blocked: {path}")
stream = open(self._path, "rb")
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.
import nltk
from nltk.data import FileSystemPathPointer
# direct construction
ptr = FileSystemPathPointer("/etc/passwd")
with ptr.open() as f:
print(f.read(300))
# via load() using file:// URL
data = nltk.data.load("file:///etc/passwd", format="raw")
print(data[:300])
Both print file contents. No exception is raised.
Impact
Any app that lets users influence the string passed to nltk.data.load() or
nltk.data.find() is exposed — web APIs, notebook servers, multi-tenant
pipelines. An attacker can read any file the process user has access to:
/etc/passwd, .env files, private keys, ~/.aws/credentials, etc.
Suggested Fix
File: nltk/data.py — FileSystemPathPointer.open() (lines 378–390)
What's wrong
Line 387 compares normpath(self._path) against itself — always equal,
so the ValueError never fires. The check is dead code.
__init__ already calls abspath() on construction, so re-running
normpath inside open() changes nothing either.
Fix
Validate against the actual list of permitted data directories instead:
def open(self, encoding=None):
import nltk.data as _d
allowed = [os.path.abspath(p) for p in _d.path if p]
if allowed and not any(
os.path.commonpath([self._path, r]) == r for r in allowed
):
raise ValueError(
f"Access outside nltk_data blocked: {self._path!r}"
)
stream = open(self._path, "rb")
if encoding is not None:
stream = SeekableUnicodeStreamReader(stream, encoding)
return stream
Why commonpath not startswith
startswith is bypassable by a path that shares a prefix:
/tmp/nltk_data_evil".startswith("/tmp/nltk_data") → True ✗
commonpath(["/tmp/nltk_data_evil", "/tmp/nltk_data"]) → "/tmp" ✓
Diff
- path = os.path.normpath(self._path)
- if os.path.isabs(path) and path != os.path.normpath(self._path):
- raise ValueError(f"Direct absolute file access blocked: {path}")
-
+ import nltk.data as _d
+ allowed = [os.path.abspath(p) for p in _d.path if p]
+ if allowed and not any(
+ os.path.commonpath([self._path, r]) == r for r in allowed
+ ):
+ raise ValueError(f"Access outside nltk_data blocked: {self._path!r}")
stream = open(self._path, "rb")
- Record
GHSA-72r2-7mfr-5xr9- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-284
- Updated
- 2026-11-02
GitHub AdvisoriesGHSA-79ph-w9m5-4v5m
7.1
Duplicate Advisory: FileSystemPathPointer.open() sandbox check is dead code — arbitrary file read via file:// protocol
Duplicate Advisory
This advisory has been withdrawn because it is a duplicate of GHSA-72r2-7mfr-5xr9. This link is maintained to preserve external references.
Original Description
NLTK versions before 3.10.0 contain a logic bug in FileSystemPathPointer.open() where the sandbox validation check compares a normalized path against itself, making the security check permanently inert. Attackers can pass file:// URLs to nltk.data.load() to read arbitrary files accessible to the process user, including credentials and configuration files.
- Record
GHSA-79ph-w9m5-4v5m- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-284
- Updated
- 2026-11-02
NVD
7.1
NLTK versions before 3.10.0 contain a logic bug in FileSystemPathPointer.open() where the sandbox validation check compares a normalized path against itself, making the security check permanently inert. Attackers can pass file:// URLs to nltk.data.load() to read arbitrary files accessible to the process user, including credentials and configuration files.
- Record
CVE-2026-65915- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-284
- Updated
- 2026-11-02
OSV
7.1
NLTK before 3.10.0 Arbitrary File Read via FileSystemPathPointer
NLTK versions before 3.10.0 contain a logic bug in FileSystemPathPointer.open() where the sandbox validation check compares a normalized path against itself, making the security check permanently inert. Attackers can pass file:// URLs to nltk.data.load() to read arbitrary files accessible to the process user, including credentials and configuration files.
- Record
CVE-2026-65915- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-284
- Updated
- 2026-11-02
Show 5 moreShow less
OSVDEBIAN-CVE-2026-65915
7.1
NLTK versions before 3.10.0 contain a logic bug in FileSystemPathPointer.open() where the sandbox validation check compares a normalized path against itself, making the security check permanently inert. Attackers can pass file:// URLs to nltk.data.load() to read arbitrary files accessible to the process user, including credentials and configuration files.
- Record
DEBIAN-CVE-2026-65915- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - Updated
- 2026-11-02
OSVGHSA-72r2-7mfr-5xr9
6.5
NLTK: FileSystemPathPointer.open() sandbox check is dead code — arbitrary file read via file:// protocol
Summary
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:
def open(self, encoding=None):
path = os.path.normpath(self._path)
# Block raw absolute reads such as "/" "C:\\Windows" etc.
if os.path.isabs(path) and path != os.path.normpath(self._path):
raise ValueError(f"Direct absolute file access blocked: {path}")
stream = open(self._path, "rb")
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.
import nltk
from nltk.data import FileSystemPathPointer
# direct construction
ptr = FileSystemPathPointer("/etc/passwd")
with ptr.open() as f:
print(f.read(300))
# via load() using file:// URL
data = nltk.data.load("file:///etc/passwd", format="raw")
print(data[:300])
Both print file contents. No exception is raised.
Impact
Any app that lets users influence the string passed to nltk.data.load() or
nltk.data.find() is exposed — web APIs, notebook servers, multi-tenant
pipelines. An attacker can read any file the process user has access to:
/etc/passwd, .env files, private keys, ~/.aws/credentials, etc.
Suggested Fix
File: nltk/data.py — FileSystemPathPointer.open() (lines 378–390)
What's wrong
Line 387 compares normpath(self._path) against itself — always equal,
so the ValueError never fires. The check is dead code.
__init__ already calls abspath() on construction, so re-running
normpath inside open() changes nothing either.
Fix
Validate against the actual list of permitted data directories instead:
def open(self, encoding=None):
import nltk.data as _d
allowed = [os.path.abspath(p) for p in _d.path if p]
if allowed and not any(
os.path.commonpath([self._path, r]) == r for r in allowed
):
raise ValueError(
f"Access outside nltk_data blocked: {self._path!r}"
)
stream = open(self._path, "rb")
if encoding is not None:
stream = SeekableUnicodeStreamReader(stream, encoding)
return stream
Why commonpath not startswith
startswith is bypassable by a path that shares a prefix:
/tmp/nltk_data_evil".startswith("/tmp/nltk_data") → True ✗
commonpath(["/tmp/nltk_data_evil", "/tmp/nltk_data"]) → "/tmp" ✓
Diff
- path = os.path.normpath(self._path)
- if os.path.isabs(path) and path != os.path.normpath(self._path):
- raise ValueError(f"Direct absolute file access blocked: {path}")
-
+ import nltk.data as _d
+ allowed = [os.path.abspath(p) for p in _d.path if p]
+ if allowed and not any(
+ os.path.commonpath([self._path, r]) == r for r in allowed
+ ):
+ raise ValueError(f"Access outside nltk_data blocked: {self._path!r}")
stream = open(self._path, "rb")
- Record
GHSA-72r2-7mfr-5xr9- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-284
- Updated
- 2026-11-02
OSVGHSA-79ph-w9m5-4v5m
7.1
Duplicate Advisory: FileSystemPathPointer.open() sandbox check is dead code — arbitrary file read via file:// protocol
Duplicate Advisory
This advisory has been withdrawn because it is a duplicate of GHSA-72r2-7mfr-5xr9. This link is maintained to preserve external references.
Original Description
NLTK versions before 3.10.0 contain a logic bug in FileSystemPathPointer.open() where the sandbox validation check compares a normalized path against itself, making the security check permanently inert. Attackers can pass file:// URLs to nltk.data.load() to read arbitrary files accessible to the process user, including credentials and configuration files.
- Record
GHSA-79ph-w9m5-4v5m- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-284
- Updated
- 2026-11-02
OSVPYSEC-2026-3731
7.1
NLTK versions before 3.10.0 contain a logic bug in FileSystemPathPointer.open() where the sandbox validation check compares a normalized path against itself, making the security check permanently inert. Attackers can pass file:// URLs to nltk.data.load() to read arbitrary files accessible to the process user, including credentials and configuration files.
- Record
PYSEC-2026-3731- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-65915
7.1
NLTK versions before 3.10.0 contain a logic bug in FileSystemPathPointer.open() where the sandbox validation check compares a normalized path against itself, making the security check permanently inert. Attackers can pass file:// URLs to nltk.data.load() to read arbitrary files accessible to the process user, including credentials and configuration files.
- Record
UBUNTU-CVE-2026-65915- CVSS 4.0
- 7.1 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N - CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N - CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
nltkPyPI |
through 3.9.3 | 3.10.0 |
nltkDebian:12 |
All versions, plus listed versions 3.10.0-13.10.1-13.10.3-13.8-13.8.1-1 Show 4 moreShow less3.9.1-13.9.1-23.9.2-13.9.3-1 |
None known |
nltkDebian:13 |
All versions, plus listed versions 3.10.0-13.10.1-13.10.3-13.9.1-23.9.2-13.9.3-1 |
None known |
nltkDebian:14 |
before 3.10.0-1, plus listed versions 3.9.1-23.9.2-13.9.3-1 |
3.10.0-1 |
nltkPyPI |
before 3.10.0, plus listed versions 0.80.90.9.30.9.40.9.5 Show 45 moreShow less0.9.60.9.70.9.80.9.92.0.12.0.1rc12.0.1rc2-git2.0.1rc32.0.1rc42.0.22.0.32.0.42.0.52.0b42.0b52.0b62.0b72.0b82.0b93.0.03.0.0b13.0.0b23.0.13.0.23.0.33.0.43.0.53.13.23.2.13.2.23.2.33.2.43.2.53.33.43.4.13.4.23.4.33.4.43.4.53.53.5b13.63.6.1 |
3.10.0 |
nltkUbuntu:Pro:14.04:LTS |
All versions, plus listed versions 2.0~b9-0ubuntu42.0~b9-0ubuntu4.1~esm22.0~b9-0ubuntu4.1~esm42.0~b9-0ubuntu4.1~esm52.0~b9-0ubuntu4.1~esm6 |
None known |
nltkUbuntu:Pro:16.04:LTS |
All versions, plus listed versions 3.0.4-13.0.5-13.1-13.1-1ubuntu0.13.1-1ubuntu0.1+esm13.1-1ubuntu0.1+esm23.1-1ubuntu0.1+esm33.1-1ubuntu0.1+esm4 |
None known |
nltkUbuntu:Pro:18.04:LTS |
All versions, plus listed versions 3.2.4-13.2.5-13.2.5-1ubuntu0.13.2.5-1ubuntu0.1+esm13.2.5-1ubuntu0.1+esm23.2.5-1ubuntu0.1+esm33.2.5-1ubuntu0.1+esm4 |
None known |
nltkUbuntu:Pro:20.04:LTS |
All versions, plus listed versions 3.4.5-13.4.5-23.4.5-2ubuntu0.1~esm13.4.5-2ubuntu0.1~esm23.4.5-2ubuntu0.1~esm33.4.5-2ubuntu0.1~esm4 |
None known |
nltkUbuntu:Pro:22.04:LTS |
All versions, plus listed versions 3.5-13.6.5-13.6.7-13.7-13.7-1ubuntu0.1~esm13.7-1ubuntu0.1~esm2 |
None known |
nltkUbuntu:Pro:24.04:LTS |
All versions, plus listed versions 3.8.1-13.8.1-1ubuntu0.1~esm13.8.1-1ubuntu0.1~esm2 |
None known |
nltkUbuntu:26.10 |
All versions, plus listed versions 3.9.2-13.9.3-13.10.0-13.10.1-13.10.3-1 |
None known |
nltkUbuntu:Pro:26.04:LTS |
All versions, plus listed versions 3.9.1-23.9.1-2build13.9.2-13.9.2-1ubuntu0.1~esm13.9.2-1ubuntu0.1~esm2 |
None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected | ||
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-65915 is a high vulnerability (CVSS 7.1) in Nltk, published August 22, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.41%, the 35th percentile. Fixed in nltk 3.10.0.
References
github.com/nltk/nltk/security/advisories/GHSA-72r2-7mfr-5xr9
vulncheck.com/advisories/nltk-before-arbitrary-file-read-via-filesystempathpointer
nvd.nist.gov/vuln/detail/CVE-2026-65915
github.com/nltk/nltk/pull/3522
github.com/nltk/nltk/commit/69db9911fdba914ceeaca7aec6e892d1b14586a9
github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3731.yaml
Show 12 moreShow less
huntr.com/bounties/a510de7b-ffaf-4a83-9bf8-fa7e63f4bd2d
github.com/advisories/GHSA-72r2-7mfr-5xr9
github.com/advisories/GHSA-79ph-w9m5-4v5m
github.com/CVEProject/cvelistV5/tree/main/cves/2026/65xxx/CVE-2026-65915.json
security-tracker.debian.org/tracker/CVE-2026-65915
github.com/nltk/nltk
ubuntu.com/security/CVE-2026-65915
cve.org/CVERecord?id=CVE-2026-65915
github.com/nltk/nltk/security/advisories/ghsa-72r2-7mfr-5xr9
security-tracker.debian.org/tracker/cve-2026-65915
github.com/cveproject/cvelistv5/tree/main/cves/2026/65xxx/cve-2026-65915.json
nvd.nist.gov/vuln/detail/cve-2026-65915
Left out here: 15 versions. The JSON record has them.