GHSA-x5ph-mj9p-rfr8: Path Traversal

Published Sep 8, 2026
·
Updated

Summary Setting nltk.pathsec.ENFORCE = True is documented to sandbox all file access to allowed NLTK data directories and raise PermissionError on unauthorized access. However, StreamBackedCorpusView opens files via builtins.open() directly, bypassing pathsec.validatepath() entirely. An attacker who can influence the fileid argument can read arbitrary local files regardless of the ENFORCE setting.

Details nltk/pathsec.py:274 defines the enforcement point: python def open(file, mode="r", kwargs): validatepath(file, context="pathsec.open") return builtins.open(file, mode=mode, kwargs)

StreamBackedCorpusView.open() in nltk/corpus/reader/util.py bypasses this entirely for string paths:

python line 171 — no validatepath() call self.eofpos = os.stat(self.fileid).stsize

line 208 — calls builtins.open directly self.stream = open(self.fileid, "rb")

Also affected: XMLCorpusView and any corpus reader subclass that passes a raw string fileid to StreamBackedCorpusView.

PoC python pocserver.py — StreamBackedCorpusView pathsec.ENFORCE bypass from flask import Flask, request, jsonify import nltk.pathsec as ps from nltk.corpus.reader.util import StreamBackedCorpusView, readlineblock

Strict mode enabled — expected to sandbox all file access ps.ENFORCE = True

app = Flask(name)

@app.post("/read") def readfile(): fname = request.json.get("file") # fileid is user-controlled, passed directly to StreamBackedCorpusView # pathsec.ENFORCE = True is ignored — builtins.open() called internally view = StreamBackedCorpusView(fname, readlineblock, encoding="utf8") return jsonify({"file": fname, "content": view[0]})

app.run(host="0.0.0.0", port=8000) Trigger: curl -s -X POST http://localhost:8000/read \ -H "Content-Type: application/json" \ -d '{"file": "/etc/passwd"}' Confirmed on latest stable NLTK. No privileges required.

Impact - Type: Arbitrary Local File Read / Security Control Bypass - CWE: CWE-22, CWE-284 - OWASP: A01:2021 – Broken Access Control

Affects web apps, REST APIs, and multi-tenant NLP pipelines where user input influences the fileid passed to NLTK corpus readers. Sensitive targets include /etc/passwd, /proc/self/environ (may contain AWSSECRETACCESSKEY, DATABASEURL, etc.), and application config files.

The core issue is that operators who explicitly set ENFORCE = True to harden production deployments are left with a false security guarantee.

Suggested fix: Replace builtins.open() and os.stat() in the string-path branch with nltk.pathsec.open() and nltk.pathsec.validatepath().

Affected Software

1 affected componentFixes available
pip/nltk<=3.9.4
3.10.0

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade pip/nltk to a version that resolves this vulnerability.

    Fixed in 3.10.0
  2. Configuration

    Do not rely on path sandboxing via nltk.pathsec.ENFORCE alone for string-path corpus reads; the bypass occurs in StreamBackedCorpusView when it calls builtins.open/os.stat internally for raw string fileids.

    NLTK pathsec (nltk.pathsec) ENFORCE = True
  3. Configuration

    Fix the string-path branch in StreamBackedCorpusView._open() by replacing direct calls to builtins.open() and os.stat() with nltk.pathsec.open() and adding/ensuring nltk.pathsec.validate_path() (the material notes line 171 has no validate_path() call and line 208 calls builtins.open() directly; pathsec.open/validate_path should be invoked before opening/stat’ing).

    NLTK StreamBackedCorpusView (nltk/corpus/reader/util.py) File open/stat path validation = Use nltk.pathsec.open + nltk.pathsec.validate_path

Event History

Sep 8, 2026
Advisory Published
via GitHub·03:27 PM
Data Sourced
via GitHub·03:27 PM
DescriptionWeaknessAffected Software

Frequently Asked Questions

1

Who is exposed to this issue?

Applications are exposed when they enable nltk.pathsec.ENFORCE and allow an attacker to influence a raw string fileid passed to StreamBackedCorpusView. XMLCorpusView and corpus reader subclasses that pass raw string fileid values to StreamBackedCorpusView are also affected.

2

What does an attacker need to exploit the bypass?

The attacker needs influence over the fileid argument used by an affected corpus view. They can then cause the view to open arbitrary local files despite pathsec.ENFORCE being set to True.

3

Does enabling pathsec enforcement prevent this attack?

No. StreamBackedCorpusView performs os.stat on the supplied path and opens it through builtins.open() directly, without calling pathsec.validate_path().

4

How can I identify potentially affected code?

Review uses of StreamBackedCorpusView, XMLCorpusView, and their subclasses for raw string fileid values that can be derived from untrusted input. In particular, look for application code that treats ENFORCE as the control restricting corpus file access.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203