Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐍
🐍 PyPI
Not in CISA KEV
MEDIUM severity

GHSA-72r2-7mfr-5xr9 — nltk

MEDIUMFix: nltk/nltk#3522

GHSA-72r2-7mfr-5xr9 is a medium-severity (CVSS 6.5) CWE-284 vulnerability in nltk. A fix is available for nltk — see the affected versions and patch details below.

NLTK: FileSystemPathPointer.open() sandbox check is dead code — arbitrary file read via file:// protocol

Also known asCVE-2026-65915PYSEC-2026-3731
Published
Updated
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Oct 5, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.

Exploitation and automatability from CISA’s SSVC triage for GHSA-72r2-7mfr-5xr9.

EPSS Exploitation Probability

via FIRST.org ↗
0.4%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs33th percentile — riskier than 33% of all scored CVEsHighest risk

Probability of exploitation in the next 30 days, from FIRST.org EPSS.

How urgent is this, really

GHSA-72r2-7mfr-5xr9 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.

Where this sits among everything scored

Of 383,485 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.

Real-World Exposure

1 pkg affected
🐍nltk

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.

Description

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")

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐍PyPInltkall versions3.10.0pip install --upgrade 'nltk==3.10.0'

Affected Products

1 product · 1 configurations
Application
nltknltk
< 3.10.0
range

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for nltk, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update nltk to 3.10.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-72r2-7mfr-5xr9 is resolved across your whole dependency graph.

  3. Workarounds

    Put an independent control in front of the weakness: restrict the affected endpoint or interface to trusted networks, require an additional authentication factor or proxy-level check, and invalidate existing sessions and credentials in case the flaw has already been used.

Fixing This On Your OS

If you run this on a Linux distribution, patch through your package manager against the distro's own security advisory below — it tracks the exact backported fix for your release, which can ship on a different timeline (and sometimes a different severity) than the upstream project.

Red HatModerate

This vulnerability in NLTK allows an authenticated attacker to read arbitrary files accessible to the process by supplying specially crafted `file://` URLs to `nltk.data.load()`. This could lead to information disclosure, including sensitive credentials or configuration data, within affected Red Hat products such as…

Workaround published by Red Hat
Mitigation for this issue involves ensuring that applications utilizing the NLTK library do not process untrusted `file://` URLs through `nltk.data.load()`. Implement input validation and sanitization for any data passed to NLTK functions that could resolve file paths. Additionally, running applications that use NLTK with the principle of least privilege can limit the impact of successful exploitation by restricting access to sensitive files.
Source: Red Hat security advisory for GHSA-72r2-7mfr-5xr9 (CC BY 4.0)

Frequently Asked Questions

### 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: ```python def open(self, encoding=None): path = os.path.normpath(self._path) # Block raw absolute reads such as "/" "C:\\Windows" e
O3 Security · Impact-Aware SCA

Is GHSA-72r2-7mfr-5xr9 in your dependencies?

Find it across PyPI, including transitive dependencies.

GHSA-72r2-7mfr-5xr9: nltk — Fixed in 3.10.0 | O3 Security