{"id":"CVE-2026-62383","aliases":["DEBIAN-CVE-2026-62383","EUVD-2026-64339","GHSA-343m-9fqq-97c7","GHSA-3hhw-38pf-pxj6","MGASA-2026-0340","PYSEC-2026-3726","UBUNTU-CVE-2026-62383"],"status":"published","title":"nltk IPIPANCorpusReader Symlink Arbitrary File Read","description":"nltk versions before 3.10.2 contain a symlink-based arbitrary file read vulnerability in IPIPANCorpusReader methods that bypass nltk.pathsec validation entirely. Attackers can place a symlink in the corpus root directory and read arbitrary files accessible to the process by calling channels(), domains(), categories(), or fileids() methods with the symlink filename.","severity":{"version":"4.0","score":6.8,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-09","score":0.00183,"percentile":0.07895,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-22","CWE-59"],"published":"2026-08-22T14:12:36Z","modified":"2026-10-28T02:30:32Z","fixes":[{"ecosystem":"PyPI","package":"nltk","fixed_in":["3.10.2"],"sources":["ghsa","osv"]},{"ecosystem":"Debian:14","package":"nltk","fixed_in":["3.10.3-1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"nltk","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.10.0"},{"fixed":"3.10.2"}]}]},{"source":"osv","ecosystem":"Debian:12","package":"nltk","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.10.0-1","3.10.1-1","3.10.3-1","3.8-1","3.8.1-1","3.9.1-1","3.9.1-2","3.9.2-1","3.9.3-1"]},{"source":"osv","ecosystem":"Debian:13","package":"nltk","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.10.0-1","3.10.1-1","3.10.3-1","3.9.1-2","3.9.2-1","3.9.3-1"]},{"source":"osv","ecosystem":"Debian:14","package":"nltk","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.10.3-1"}]}],"versions":["3.10.0-1","3.10.1-1","3.9.1-2","3.9.2-1","3.9.3-1"]},{"source":"osv","ecosystem":"PyPI","package":"nltk","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.10.0"},{"fixed":"3.10.2"}]}],"versions":["3.10.0","3.10.1"]},{"source":"osv","ecosystem":"Mageia:10","package":"python-nltk","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.10.3-1.mga10"}]}],"bundled":18},{"source":"osv","ecosystem":"Mageia:9","package":"python-nltk","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.10.3-1.mga9"}]}],"bundled":18},{"source":"osv","ecosystem":"PyPI","package":"nltk","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.10.0"},{"fixed":"3.10.2"}]}],"versions":["3.10.0","3.10.1"]},{"source":"osv","ecosystem":"Ubuntu:Pro:14.04:LTS","package":"nltk","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.0~b9-0ubuntu4","2.0~b9-0ubuntu4.1~esm2","2.0~b9-0ubuntu4.1~esm4","2.0~b9-0ubuntu4.1~esm5","2.0~b9-0ubuntu4.1~esm6"]},{"source":"osv","ecosystem":"Ubuntu:Pro:16.04:LTS","package":"nltk","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.0.4-1","3.0.5-1","3.1-1","3.1-1ubuntu0.1","3.1-1ubuntu0.1+esm1","3.1-1ubuntu0.1+esm2","3.1-1ubuntu0.1+esm3","3.1-1ubuntu0.1+esm4"]},{"source":"osv","ecosystem":"Ubuntu:Pro:18.04:LTS","package":"nltk","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.2.4-1","3.2.5-1","3.2.5-1ubuntu0.1","3.2.5-1ubuntu0.1+esm1","3.2.5-1ubuntu0.1+esm2","3.2.5-1ubuntu0.1+esm3","3.2.5-1ubuntu0.1+esm4"]},{"source":"osv","ecosystem":"Ubuntu:Pro:20.04:LTS","package":"nltk","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.4.5-1","3.4.5-2","3.4.5-2ubuntu0.1~esm1","3.4.5-2ubuntu0.1~esm2","3.4.5-2ubuntu0.1~esm3","3.4.5-2ubuntu0.1~esm4"]},{"source":"osv","ecosystem":"Ubuntu:Pro:22.04:LTS","package":"nltk","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.5-1","3.6.5-1","3.6.7-1","3.7-1","3.7-1ubuntu0.1~esm1","3.7-1ubuntu0.1~esm2"]},{"source":"osv","ecosystem":"Ubuntu:Pro:24.04:LTS","package":"nltk","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.8.1-1","3.8.1-1ubuntu0.1~esm1","3.8.1-1ubuntu0.1~esm2"]},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"nltk","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.9.2-1","3.9.3-1","3.10.0-1","3.10.1-1","3.10.3-1"]},{"source":"osv","ecosystem":"Ubuntu:Pro:26.04:LTS","package":"nltk","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.9.1-2","3.9.1-2build1","3.9.2-1","3.9.2-1ubuntu0.1~esm1","3.9.2-1ubuntu0.1~esm2"]}],"products":[{"vendor":"nltk","product":"nltk"}],"references":[{"url":"https://github.com/nltk/nltk/security/advisories/GHSA-3hhw-38pf-pxj6","tags":["vendor-advisory","Exploit","Mitigation","Vendor Advisory","advisory","web","evidence"]},{"url":"https://www.vulncheck.com/advisories/nltk-ipipancorpusreader-symlink-arbitrary-file-read","tags":["third-party-advisory","Third Party Advisory","advisory","web","report"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-62383","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-343m-9fqq-97c7"},{"url":"https://github.com/nltk/nltk/pull/3727","tags":["web"]},{"url":"https://github.com/nltk/nltk/commit/ee1a42e51982c4dce6ad3ee77ff1ac43894288ab","tags":["web"]},{"url":"https://github.com/nltk/nltk/releases/tag/v3.10.2","tags":["web"]},{"url":"https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3726.yaml","tags":["web"]},{"url":"https://github.com/advisories/GHSA-3hhw-38pf-pxj6"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/62xxx/CVE-2026-62383.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-62383","tags":["advisory"]},{"url":"https://github.com/nltk/nltk","tags":["package"]},{"url":"https://advisories.mageia.org/MGASA-2026-0340.html","tags":["advisory"]},{"url":"https://bugs.mageia.org/show_bug.cgi?id=36186","tags":["report"]},{"url":"https://github.com/nltk/nltk/releases#release-v3.10.0","tags":["web"]},{"url":"https://github.com/nltk/nltk/releases#release-v3.10.2","tags":["web"]},{"url":"https://github.com/nltk/nltk/releases#release-v3.10.3","tags":["web"]},{"url":"https://ubuntu.com/security/CVE-2026-62383","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-62383","tags":["report"]},{"url":"https://github.com/nltk/nltk/security/advisories/ghsa-3hhw-38pf-pxj6","tags":["report"]},{"url":"https://security-tracker.debian.org/tracker/cve-2026-62383","tags":["report"]},{"url":"https://github.com/cveproject/cvelistv5/tree/main/cves/2026/62xxx/cve-2026-62383.json","tags":["report"]},{"url":"https://nvd.nist.gov/vuln/detail/cve-2026-62383","tags":["report"]}],"sources":{"cve":[{"id":"CVE-2026-62383","title":"nltk IPIPANCorpusReader Symlink Arbitrary File Read","description":"nltk versions before 3.10.2 contain a symlink-based arbitrary file read vulnerability in IPIPANCorpusReader methods that bypass nltk.pathsec validation entirely. Attackers can place a symlink in the corpus root directory and read arbitrary files accessible to the process by calling channels(), domains(), categories(), or fileids() methods with the symlink filename.","status":"published","published":"2026-08-22T14:12:36.606Z","modified":"2026-08-26T16:16:35.172Z","cvss":[{"version":"4.0","score":6.8,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium","assessor":"cna"},{"version":"3.1","score":5.5,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:20:14.665567Z"}],"euvd":[{"id":"EUVD-2026-64339","description":"nltk versions before 3.10.2 contain a symlink-based arbitrary file read vulnerability in IPIPANCorpusReader methods that bypass nltk.pathsec validation entirely. Attackers can place a symlink in the corpus root directory and read arbitrary files accessible to the process by calling channels(), domains(), categories(), or fileids() methods with the symlink filename.","status":"published","published":"2026-08-22T14:12:36Z","modified":"2026-08-26T16:16:35Z","cvss":[{"version":"4.0","score":6.8,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"updated_at":"2026-11-02T18:01:30.542287Z"}],"ghsa":[{"id":"GHSA-343m-9fqq-97c7","title":"Duplicate Advisory: NLTK: Symlink-based arbitrary file read in IPIPANCorpusReader, bypasses nltk.pathsec entirely","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-3hhw-38pf-pxj6. This link is maintained to preserve external references.\n\n## Original Description\nnltk versions before 3.10.2 contain a symlink-based arbitrary file read vulnerability in IPIPANCorpusReader methods that bypass nltk.pathsec validation entirely. Attackers can place a symlink in the corpus root directory and read arbitrary files accessible to the process by calling channels(), domains(), categories(), or fileids() methods with the symlink filename.","status":"withdrawn","published":"2026-08-22T15:31:03Z","modified":"2026-09-02T14:47:36Z","cvss":[{"version":"3.1","score":5.5,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium"},{"version":"4.0","score":6.8,"vector":"CVSS:4.0/AV:L/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","level":"medium"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:41:10.121131Z"},{"id":"GHSA-3hhw-38pf-pxj6","title":"NLTK: Symlink-based arbitrary file read in IPIPANCorpusReader, bypasses nltk.pathsec entirely","description":"## Summary\n\n`IPIPANCorpusReader` (`nltk/corpus/reader/ipipan.py`) exposes public methods, `channels()`, `domains()`, `categories()`, and `fileids(channels=...)`, that accept a caller supplied `fileids` list and read a file via a completely unprotected builtin `open()` call, with no `nltk.pathsec` involvement at all. A symlink placed inside the corpus root, with a name containing no separators or `..`, passes NLTK's existing traversal checks and is opened directly, reading a file from anywhere on the filesystem the process can access.\n\n## Root cause\n\nAll four methods route through `_get_tag()`:\n\n```python\ndef _get_tag(self, f, tag):\n    tags = []\n    with open(f) as infile:   # builtin open(), no pathsec involvement\n        header = infile.read()\n    ...\n```\n\n`f` arrives via `_list_header_files()` / `_list_morph_files_by()`, both of which call:\n\n```python\nf.replace(\"morph.xml\", \"header.xml\")\n```\n\non the result of `self.abspath(...)` or `self.abspaths(...)`. `FileSystemPathPointer` subclasses `str`, so `.replace()` returns a plain Python string, silently discarding the `PathPointer` wrapper. That plain string is handed straight to builtin `open()`.\n\nThis is a more severe variant of the same CWE-59 class already fixed elsewhere in this codebase (`CorpusReader.open()`, `NKJPCorpusReader.add_root()`, and the recent `FramenetCorpusReader` fix): those route file access through `nltk.pathsec.validate_path()`, at minimum the global, non-scoped check, before opening. Here, converting the `PathPointer` to a plain string before calling `open()` skips `pathsec` completely, not just the corpus-root-scoped check, so the symlink target does not even need to land under a registered `nltk.data.path` root.\n\nPlain literal `../` traversal in the fileid is still blocked by `FileSystemPathPointer.join()`, so this is specifically the symlink variant, not a regression of the older, simpler traversal class.\n\n## Proof of concept\n\nConstructed the normal, documented way, `fileids` as a regex over file paths, so the reader auto-discovers whatever `.xml` files exist in its root with no special knowledge of the planted symlink.\n\n```python\nimport os\nimport tempfile\n\nfrom nltk.corpus.reader.ipipan import IPIPANCorpusReader\n\nroot = tempfile.mkdtemp()\ncorpus_root = os.path.join(root, \"ipipan\")\nos.makedirs(corpus_root)\n\nwith open(os.path.join(corpus_root, \"real_morph.xml\"), \"w\") as f:\n    f.write(\"\u003cchannel\u003elegit\u003c/channel\u003e\")\n\nsecret_dir = os.path.join(root, \"outside_ipipan_root\")\nos.makedirs(secret_dir)\nsecret_path = os.path.join(secret_dir, \"stolen.xml\")\nwith open(secret_path, \"w\") as f:\n    f.write(\"\u003cchannel\u003eTOP-SECRET-CHANNEL-DATA-FROM-OUTSIDE-CORPUS-ROOT\u003c/channel\u003e\")\n\nos.symlink(secret_path, os.path.join(corpus_root, \"evil_link.xml\"))\n\nreader = IPIPANCorpusReader(corpus_root, r\".*\\.xml\")\nprint(\"Auto-discovered fileids:\", sorted(reader.fileids()))\n\nresult = reader.channels(fileids=[\"evil_link.xml\"])\nprint(result)\n```\n\nActual output when run against current `develop`:\n\n```\nAuto-discovered fileids: ['evil_link.xml', 'real_morph.xml']\n['TOP-SECRET-CHANNEL-DATA-FROM-OUTSIDE-CORPUS-ROOT']\n```\n\nThat content was read from `secret_path`, a file entirely outside `corpus_root`. No exception raised anywhere. The planted symlink even surfaces naturally in the reader's own `fileids()` listing, exactly as a real file would.\n\nVerified separately that literal `../` traversal in the fileid is still rejected (`ValueError: Traversal blocked`), confirming this is specifically the symlink gap, not a broader regression.\n\n## Why this is in scope\n\n- No malicious file for a victim to open, no special user interaction. Just a tampered or shared corpus directory (`SECURITY.md` names \"shared environments... multi-tenant pipelines\" as the project's own stated threat model) plus a completely normal API call.\n- Core corpus-reader code, reached through plain `import nltk` and documented, programmatic usage (`words()`, `sents()`, `channels()`, etc.), not a demo or GUI tool.\n- Same reader category, and same CWE-59 mechanism, already treated as CVE-worthy twice in this codebase for `FramenetCorpusReader` and `NKJPCorpusReader`.\n- Not a bypass of a claimed fix. `ipipan.py` has never had security hardening applied, and has no dedicated test coverage at all.\n\n## CVSS v3.1\n\n- **AV:L, AC:L**: exploitation is local filesystem symlink placement, then immediate and deterministic once triggered.\n- **PR:L**: the attacker needs some pre-existing ability to plant a symlink somewhere reachable, not zero privilege, but not elevated either.\n- **UI:N**: fires during routine, automated corpus processing, no separate victim action.\n- **S:U**: stays within the same process's existing privileges.\n- **C:H, I:N, A:N**: arbitrary file read only, no write, no crash.\n\n## Suggested fix\n\nRoute `_get_tag()` through `nltk.pathsec.validate_path()` with the corpus root as `required_root`, or through `CorpusReader.open()`, instead of converting the `PathPointer` to a plain string and calling builtin `open()` directly. The same fix pattern already applied to `FramenetCorpusReader` and `NKJPCorpusReader` applies directly here.","status":"published","published":"2026-09-08T16:40:38Z","modified":"2026-09-08T16:40:39Z","cvss":[{"version":"4.0","score":6.8,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium"},{"version":"3.1","score":5.5,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"cwes":["CWE-22","CWE-59"],"updated_at":"2026-11-02T14:41:13.477974Z"}],"nvd":[{"id":"CVE-2026-62383","description":"nltk versions before 3.10.2 contain a symlink-based arbitrary file read vulnerability in IPIPANCorpusReader methods that bypass nltk.pathsec validation entirely. Attackers can place a symlink in the corpus root directory and read arbitrary files accessible to the process by calling channels(), domains(), categories(), or fileids() methods with the symlink filename.","status":"published","published":"2026-08-22T15:16:17.883Z","modified":"2026-10-14T13:10:00.187Z","cvss":[{"version":"4.0","score":6.8,"vector":"CVSS:4.0/AV:L/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","level":"medium","assessor":"secondary"},{"version":"3.1","score":5.5,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:12:40.665005Z"}],"osv":[{"id":"CVE-2026-62383","title":"nltk IPIPANCorpusReader Symlink Arbitrary File Read","description":"nltk versions before 3.10.2 contain a symlink-based arbitrary file read vulnerability in IPIPANCorpusReader methods that bypass nltk.pathsec validation entirely. Attackers can place a symlink in the corpus root directory and read arbitrary files accessible to the process by calling channels(), domains(), categories(), or fileids() methods with the symlink filename.","status":"published","published":"2026-08-22T14:12:36.606Z","modified":"2026-10-28T02:30:32.138172411Z","cvss":[{"version":"4.0","score":6.8,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T15:45:13.306117Z"},{"id":"DEBIAN-CVE-2026-62383","description":"nltk versions before 3.10.2 contain a symlink-based arbitrary file read vulnerability in IPIPANCorpusReader methods that bypass nltk.pathsec validation entirely. Attackers can place a symlink in the corpus root directory and read arbitrary files accessible to the process by calling channels(), domains(), categories(), or fileids() methods with the symlink filename.","status":"published","published":"2026-08-22T15:16:17.883Z","modified":"2026-10-15T09:47:29.526542689Z","cvss":[{"version":"4.0","score":6.8,"vector":"CVSS:4.0/AV:L/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","level":"medium"}],"updated_at":"2026-11-02T15:45:57.932517Z"},{"id":"GHSA-343m-9fqq-97c7","title":"Duplicate Advisory: NLTK: Symlink-based arbitrary file read in IPIPANCorpusReader, bypasses nltk.pathsec entirely","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-3hhw-38pf-pxj6. This link is maintained to preserve external references.\n\n## Original Description\nnltk versions before 3.10.2 contain a symlink-based arbitrary file read vulnerability in IPIPANCorpusReader methods that bypass nltk.pathsec validation entirely. Attackers can place a symlink in the corpus root directory and read arbitrary files accessible to the process by calling channels(), domains(), categories(), or fileids() methods with the symlink filename.","status":"withdrawn","published":"2026-08-22T15:31:03Z","modified":"2026-09-02T15:00:10.479446644Z","cvss":[{"version":"3.1","score":5.5,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium"},{"version":"4.0","score":6.8,"vector":"CVSS:4.0/AV:L/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","level":"medium"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T15:46:35.050232Z"},{"id":"GHSA-3hhw-38pf-pxj6","title":"NLTK: Symlink-based arbitrary file read in IPIPANCorpusReader, bypasses nltk.pathsec entirely","description":"## Summary\n\n`IPIPANCorpusReader` (`nltk/corpus/reader/ipipan.py`) exposes public methods, `channels()`, `domains()`, `categories()`, and `fileids(channels=...)`, that accept a caller supplied `fileids` list and read a file via a completely unprotected builtin `open()` call, with no `nltk.pathsec` involvement at all. A symlink placed inside the corpus root, with a name containing no separators or `..`, passes NLTK's existing traversal checks and is opened directly, reading a file from anywhere on the filesystem the process can access.\n\n## Root cause\n\nAll four methods route through `_get_tag()`:\n\n```python\ndef _get_tag(self, f, tag):\n    tags = []\n    with open(f) as infile:   # builtin open(), no pathsec involvement\n        header = infile.read()\n    ...\n```\n\n`f` arrives via `_list_header_files()` / `_list_morph_files_by()`, both of which call:\n\n```python\nf.replace(\"morph.xml\", \"header.xml\")\n```\n\non the result of `self.abspath(...)` or `self.abspaths(...)`. `FileSystemPathPointer` subclasses `str`, so `.replace()` returns a plain Python string, silently discarding the `PathPointer` wrapper. That plain string is handed straight to builtin `open()`.\n\nThis is a more severe variant of the same CWE-59 class already fixed elsewhere in this codebase (`CorpusReader.open()`, `NKJPCorpusReader.add_root()`, and the recent `FramenetCorpusReader` fix): those route file access through `nltk.pathsec.validate_path()`, at minimum the global, non-scoped check, before opening. Here, converting the `PathPointer` to a plain string before calling `open()` skips `pathsec` completely, not just the corpus-root-scoped check, so the symlink target does not even need to land under a registered `nltk.data.path` root.\n\nPlain literal `../` traversal in the fileid is still blocked by `FileSystemPathPointer.join()`, so this is specifically the symlink variant, not a regression of the older, simpler traversal class.\n\n## Proof of concept\n\nConstructed the normal, documented way, `fileids` as a regex over file paths, so the reader auto-discovers whatever `.xml` files exist in its root with no special knowledge of the planted symlink.\n\n```python\nimport os\nimport tempfile\n\nfrom nltk.corpus.reader.ipipan import IPIPANCorpusReader\n\nroot = tempfile.mkdtemp()\ncorpus_root = os.path.join(root, \"ipipan\")\nos.makedirs(corpus_root)\n\nwith open(os.path.join(corpus_root, \"real_morph.xml\"), \"w\") as f:\n    f.write(\"\u003cchannel\u003elegit\u003c/channel\u003e\")\n\nsecret_dir = os.path.join(root, \"outside_ipipan_root\")\nos.makedirs(secret_dir)\nsecret_path = os.path.join(secret_dir, \"stolen.xml\")\nwith open(secret_path, \"w\") as f:\n    f.write(\"\u003cchannel\u003eTOP-SECRET-CHANNEL-DATA-FROM-OUTSIDE-CORPUS-ROOT\u003c/channel\u003e\")\n\nos.symlink(secret_path, os.path.join(corpus_root, \"evil_link.xml\"))\n\nreader = IPIPANCorpusReader(corpus_root, r\".*\\.xml\")\nprint(\"Auto-discovered fileids:\", sorted(reader.fileids()))\n\nresult = reader.channels(fileids=[\"evil_link.xml\"])\nprint(result)\n```\n\nActual output when run against current `develop`:\n\n```\nAuto-discovered fileids: ['evil_link.xml', 'real_morph.xml']\n['TOP-SECRET-CHANNEL-DATA-FROM-OUTSIDE-CORPUS-ROOT']\n```\n\nThat content was read from `secret_path`, a file entirely outside `corpus_root`. No exception raised anywhere. The planted symlink even surfaces naturally in the reader's own `fileids()` listing, exactly as a real file would.\n\nVerified separately that literal `../` traversal in the fileid is still rejected (`ValueError: Traversal blocked`), confirming this is specifically the symlink gap, not a broader regression.\n\n## Why this is in scope\n\n- No malicious file for a victim to open, no special user interaction. Just a tampered or shared corpus directory (`SECURITY.md` names \"shared environments... multi-tenant pipelines\" as the project's own stated threat model) plus a completely normal API call.\n- Core corpus-reader code, reached through plain `import nltk` and documented, programmatic usage (`words()`, `sents()`, `channels()`, etc.), not a demo or GUI tool.\n- Same reader category, and same CWE-59 mechanism, already treated as CVE-worthy twice in this codebase for `FramenetCorpusReader` and `NKJPCorpusReader`.\n- Not a bypass of a claimed fix. `ipipan.py` has never had security hardening applied, and has no dedicated test coverage at all.\n\n## CVSS v3.1\n\n- **AV:L, AC:L**: exploitation is local filesystem symlink placement, then immediate and deterministic once triggered.\n- **PR:L**: the attacker needs some pre-existing ability to plant a symlink somewhere reachable, not zero privilege, but not elevated either.\n- **UI:N**: fires during routine, automated corpus processing, no separate victim action.\n- **S:U**: stays within the same process's existing privileges.\n- **C:H, I:N, A:N**: arbitrary file read only, no write, no crash.\n\n## Suggested fix\n\nRoute `_get_tag()` through `nltk.pathsec.validate_path()` with the corpus root as `required_root`, or through `CorpusReader.open()`, instead of converting the `PathPointer` to a plain string and calling builtin `open()` directly. The same fix pattern already applied to `FramenetCorpusReader` and `NKJPCorpusReader` applies directly here.","status":"published","published":"2026-09-08T16:40:38Z","modified":"2026-09-08T16:45:03.60243227Z","cvss":[{"version":"3.1","score":5.5,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium"},{"version":"4.0","score":6.8,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-22","CWE-59"],"updated_at":"2026-11-02T15:46:35.747385Z"},{"id":"PYSEC-2026-3726","description":"nltk versions before 3.10.2 contain a symlink-based arbitrary file read vulnerability in IPIPANCorpusReader methods that bypass nltk.pathsec validation entirely. Attackers can place a symlink in the corpus root directory and read arbitrary files accessible to the process by calling channels(), domains(), categories(), or fileids() methods with the symlink filename.","status":"published","published":"2026-08-22T15:16:17.883Z","modified":"2026-08-28T20:30:02.66265698Z","cvss":[{"version":"4.0","score":6.8,"vector":"CVSS:4.0/AV:L/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","level":"medium"}],"updated_at":"2026-11-02T15:52:57.255153Z"},{"id":"UBUNTU-CVE-2026-62383","description":"nltk versions before 3.10.2 contain a symlink-based arbitrary file read vulnerability in IPIPANCorpusReader methods that bypass nltk.pathsec validation entirely. Attackers can place a symlink in the corpus root directory and read arbitrary files accessible to the process by calling channels(), domains(), categories(), or fileids() methods with the symlink filename.","status":"published","published":"2026-08-22T15:16:00Z","modified":"2026-10-17T13:00:31.09244349Z","cvss":[{"version":"4.0","score":6.8,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium"},{"version":"3.1","score":5.5,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium"},{"version":"4.0","score":6.8,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium"},{"version":"3.1","score":5.5,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","level":"medium"}],"updated_at":"2026-11-02T16:56:43.630667Z"},{"id":"MGASA-2026-0340","title":"Updated python-nltk packages fix security vulnerabilities","description":"Updated packages fix security vulnerabilities. Please see the links.\n","status":"published","published":"2026-08-29T15:47:31Z","modified":"2026-08-29T16:00:02.787972151Z","updated_at":"2026-11-02T15:49:43.795591Z"}]}}
