CVE-2026-62388: NLTK pathsec Insecure Default Allows Security Bypass
NLTK's security module defaults to a warn-only mode, meaning path traversal and pickle deserialization protections silently allow attacks through instead of blocking them.

The problem
The pathsec module was added to NLTK as the fix for prior CVEs covering path traversal and pickle deserialization (CVE-2024-39705, CVE-2026-0846). It centralizes all security checks: file path validation, network URL filtering, and zip-slip guards.
However, the module reads ENFORCE from the environment variable NLTK_PATHSEC_ENFORCE and defaults to False when that variable is absent. Every one of its eight validation functions follows the same pattern: on a violation, if ENFORCE is False, it emits a RuntimeWarning and lets execution continue.
The security gates are opt-in, not opt-out. Any NLTK 3.9.x deployment expecting the prior CVE fixes to be active is still fully exploitable unless an operator manually set the env var.
Proof of concept
A working proof-of-concept for CVE-2026-62388 in nltk, with the exact payload below.
import nltk.pathsec
import warnings
# ENFORCE is False by default -- no env var set
print(f'ENFORCE = {nltk.pathsec.ENFORCE}') # False
# pathsec.open() should block /etc/passwd, but only warns
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
f = nltk.pathsec.open('/etc/passwd', 'r')
print(f'Opened: {f.name}') # /etc/passwd -- attack succeeds
print(f'Warning: {w[0].category}') # RuntimeWarning, not an exception
print(f.read()) # file contents returned to callerThe root cause is CWE-1188 (Insecure Default Initialization of Resource). Every validation function in pathsec.py has a hard branch: if ENFORCE: raise SecurityError(...) else warnings.warn(..., RuntimeWarning). Because ENFORCE defaults to False, the raise branch is never reached in a standard install, and execution falls through to the dangerous operation unconditionally.
The patch in commit 155e40343c (PR #3593, released in 3.10.0) flips the default so ENFORCE=True unless the operator explicitly sets NLTK_PATHSEC_ENFORCE=0. This converts the module from opt-in enforcement to opt-out, which is the correct fail-secure posture for a security control.
The fix
Upgrade to nltk 3.10.0 or later. The default is now ENFORCE=True; all security violations raise PermissionError without any environment variable configuration. If you need the old warn-only behavior for a controlled migration, set NLTK_PATHSEC_ENFORCE=0 explicitly.
On affected versions (<=3.9.4) where upgrading is not yet possible, set NLTK_PATHSEC_ENFORCE=1 or add import nltk.pathsec; nltk.pathsec.ENFORCE = True at application startup.
Related research
- high · 5.9CVE-2026-80206: nltk tgrep ReDoS via User-Supplied Regex
- high · 7CVE-2026-81726CVE-2026-81726: NLTK Model-Artifact APIs Path Traversal Bypass (pathsec sandbox escape)
- high · 7.8CVE-2026-78680CVE-2026-78680: NLTK Uncontrolled Search Path via Graphviz dot Binary
- critical · 9.8CVE-2026-79675CVE-2026-79675: NLTK JVM Argument Injection via Per-Call Options (Stanford Wrappers)