{"id":"GHSA-gpwc-27cw-rh9r","kind":"vulnerability","aliases":["GHSA-gpwc-27cw-rh9r"],"related":[],"title":"NLTK: Quadratic CPU exhaustion in LegalitySyllableTokenizer.tokenize via unguarded onset reversal loop","description":"## Summary\n\n`LegalitySyllableTokenizer.tokenize` in the NLTK tokenizer package contains an unguarded O(n^2) processing loop. When the method is called on a single token that consists of a long run of vowels (for example `\"a\" * n`), it consumes CPU time proportional to the square of the token length. An attacker who can supply an untrusted token to this public tokenizer API can force a single request to saturate one CPU core for a prolonged period, degrading availability of any service that syllabifies attacker-controlled text. No authentication, special configuration, or user interaction is required.\n\n## Root Cause\n\nThe method walks the token in reverse and, on every iteration after the first vowel is seen, evaluates the membership test `char_lower + current_onset[::-1] in self.legal_onsets`. The set `legal_onsets` is built by `onset()`, which returns only the characters up to the first vowel, so every member is vowel-free. For an all-vowel token the first branch can therefore never match, `onset` never becomes `True`, and the reset branch that would clear `current_onset` is never reached. As a result `current_onset` grows by one character on every iteration. Building `current_onset[::-1]` and hashing the resulting string costs O(k) on iteration k, and this is repeated for k from 1 to n, giving total cost O(n^2). The security invariant that is violated is bounded per-token work: there is no length cap on this code path, so untrusted token length translates directly into quadratic CPU consumption.\n\n```text\nAffected function: LegalitySyllableTokenizer.tokenize\nSource: nltk/tokenize/legality_principle.py (membership-test sink at line 133; loop over token[::-1] beginning at line 126)\nHelper establishing the vowel-free onset set: LegalitySyllableTokenizer.onset (nltk/tokenize/legality_principle.py, returns prefix up to first vowel)\n```\n\n## Impact\n\nAn attacker who controls a token passed to `LegalitySyllableTokenizer.tokenize` can trigger quadratic CPU work. In the reviewed release a token of roughly 64,000 vowel characters takes about 1.8 seconds on a single core, and the cost grows with the square of the length, so a token in the hundreds of kilobytes to megabyte range consumes a core for many seconds to minutes. Memory use is linear. The demonstrated capability is single-core CPU exhaustion leading to denial of service or latency degradation in any network-reachable component that syllabifies untrusted input. There is no confidentiality or integrity impact; the effect is availability degradation of the affected process. The plausible operational consequence, where multiple concurrent requests are used, is exhaustion of the available worker pool; this is inferred from the per-request cost and was not itself executed against a live deployment.\n\n## Proof of Concept\n\n`Requires only the released nltk package; no corpus download is needed because the quadratic behavior is driven entirely by the attacker token, not by the size of the legal-onset set.`\n\n1. Construct a tokenizer with any small onset source.\n2. Call `tokenize` with an all-vowel token of increasing length and time each call.\n3. Compare against a consonant-alternating control token of equal length.\n\n```python\nimport time\nfrom nltk.tokenize import LegalitySyllableTokenizer\n\ntok = LegalitySyllableTokenizer(\"tr c d\".split())  # tiny 3-word onset source\nfor n in (8000, 16000, 32000):\n    payload = \"a\" * n\n    start = time.perf_counter()\n    tok.tokenize(payload)\n    print(\"vowel-run\", n, round(time.perf_counter() - start, 3))\n\ncontrol = \"ba\" * 32000  # equal-length linear control\nstart = time.perf_counter()\ntok.tokenize(control)\nprint(\"control\", 64000, round(time.perf_counter() - start, 3))\n```\n\n```text\nvowel-run 8000 0.031\nvowel-run 16000 0.120\nvowel-run 32000 0.468   (about 4x per doubling => O(n^2))\ncontrol 64000 0.004     (linear, roughly 130x faster at larger size)\n```\n\n## Attack Chain\n\n1. **Exposure:** `LegalitySyllableTokenizer` is exported from `nltk.tokenize` and `tokenize` is a public, documented method reachable by any service that syllabifies untrusted text.\n2. **Control:** The attacker fully controls the `token` argument, including its length and character content; a run of vowels such as `\"a\" * n` survives with no normalization or length reduction.\n3. **Path:** The token flows into the reverse walk at `nltk/tokenize/legality_principle.py:126` and reaches the membership-test at line 133 on every iteration past the first vowel.\n4. **Guard:** There is no length cap on this path. The sibling `SyllableTokenizer` received a `MAX_TOKEN_LEN` cap during the project hardening work, but `legality_principle.py` received no such guard.\n5. **Primitive:** Repeated O(k) construction and hashing of `current_onset[::-1]` inside the loop yields cumulative O(n^2) CPU work.\n6. **Result:** A single request with a large vowel-run token exhausts one CPU core for a sustained period, degrading service availability.\n\n## Bypass Evidence\n\nNo guard blocks this path. Membership-set analysis shows the first branch at line 133 cannot fire for a vowel character because `legal_onsets` is provably vowel-free (it is derived from `onset()`, which stops at the first vowel), so `onset` never becomes `True` and `current_onset` is never reset. The strongest refutations were attempted and failed: (1) the hypothesis that the quadratic depends on a large or specific onset corpus was disproven by reproducing the same O(n^2) growth with a three-word onset source, showing the cost is driven solely by the attacker token; (2) the hypothesis that an upstream length cap neutralizes long tokens was disproven by direct source inspection, which found no `MAX_TOKEN_LEN` or equivalent clamp on this class and confirmed the default word-tokenization path does not call this class; (3) the hypothesis that this is covered by existing hardening was disproven, since the project quadratic-DoS test suite exercises the sibling `SyllableTokenizer` but contains no test for `LegalitySyllableTokenizer`, and no commit added a cap to this file. This is a fix-missed sibling of the accepted algorithmic-complexity class, not a previously remediated issue.\n\n## Affected Versions\n\n- `Ecosystem: pip`\n- `Package: nltk`\n- `Confirmed affected range: >= 3.6, <= 3.10.3`\n- `Latest release checked: 3.10.3 (PyPI)`\n- `Fix status: not fixed`\n\nThe vulnerable loop is present and unguarded in the current released artifact 3.10.3, verified by direct source inspection and by reproducing the quadratic curve. The lower bound of 3.6 reflects when the affected module first shipped; the disclosure-relevant fact is that the latest published release is affected and no fix has been released.\n\n## Suggested Fix\n\nEnforce a bound on per-token work in `LegalitySyllableTokenizer.tokenize`, consistent with the `MAX_TOKEN_LEN` guard already applied to the sibling `SyllableTokenizer`. Options include rejecting or truncating tokens above a configurable maximum length before the reverse walk, or restructuring the onset accumulation so the loop does not repeatedly build and hash a growing reversed string (for example by maintaining the onset in its final orientation and comparing without an O(k) reversal each iteration). The remediation should cover any caller that passes untrusted tokens, not just the default word-tokenization path. As an interim mitigation, deployments that expose syllabification of untrusted input can cap request and token sizes at the application boundary to bound the worst-case token length.\n\nReported by zx (GitHub: @manus-pi).\n\n## Fix status (2026-09-28)\n\nPR: #3895 \"[WIP] Bound algorithmic-DoS and uncontrolled-recursion vectors (CWE-400/407/674/1333)\", open, head b22ebe144.\n\nFix commit: e20c40e26 \"Bound tokenizer/parser algorithmic DoS (ffr9/gpwc/8fx7)\" (LegalitySyllableTokenizer.tokenize refuses a token longer than MAX_TOKEN_LEN=4096 with ValueError naming CWE-407).\n\nNo probe. The advisory PoC re-run: on the PR head the 32k vowel token is refused and under the cap the tokenizer scales 0.8x over a 4x input (1k vs 4k); on develop 2fa3f48fe it scales 14.2x over a 4x input (8k 0.087 s, 32k 1.245 s), so develop is still VULNERABLE. The related merged PR #3911 (25beee962) bounds the SyllableTokenizer vowel accumulation, a different tokenizer, and does not cover this sink.","summary":"NLTK's LegalitySyllableTokenizer.tokenize does quadratic work on a single token made of a long run of vowels, so one crafted input can pin a CPU core. An attacker who can submit text to a service that syllabifies it can cause denial of service, with no confidentiality or integrity impact.","severity":"medium","cvss":5.3,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"nltk","fixed":["3.10.4"],"affected":">= 3.6, <= 3.10.3","ecosystem":"pypi"}],"ecosystems":["pypi"],"tags":["cwe-400","cwe-407","nlp","nltk","package","python","text-processing"],"not_affected_if":["default-word-tokenizer","no-legality-syllable-tokenizer","trusted-input-only"],"how_to_check":"- Check the installed version with `pip show nltk` and whether it falls in `>= 3.6, <= 3.10.3`.\n- Search your code for `LegalitySyllableTokenizer` to see whether untrusted input reaches it.\n- The default word-tokenization path does not call this class, per the advisory.","fix":"- `nltk` `3.10.4` is listed as the fixed version in the product data.\n- A fix commit (e20c40e26) caps tokens at 4096 characters in the referenced patch work, but the fix status note says the PR is still open and develop remains vulnerable, so confirm the released version rather than relying on the patch.","status":"open","published_at":"2026-11-13T21:16:34+00:00","first_seen_at":"2026-11-13T21:19:52+00:00","triaged_at":"2026-11-13T21:21:11+00:00","updated_at":"2026-11-13T21:21:11+00:00","category":"package","confidence":0.75,"triage":{"at":"2026-11-13T21:21:11Z","model":"claude-haiku-5-5","output":{"fix":"- `nltk` `3.10.4` is listed as the fixed version in the product data.\n- A fix commit (e20c40e26) caps tokens at 4096 characters in the referenced patch work, but the fix status note says the PR is still open and develop remains vulnerable, so confirm the released version rather than relying on the patch.","reach":"network","title":"NLTK: long vowel-run token makes syllabifier use a core for minutes","impact":"low","summary":"NLTK's LegalitySyllableTokenizer.tokenize does quadratic work on a single token made of a long run of vowels, so one crafted input can pin a CPU core. An attacker who can submit text to a service that syllabifies it can cause denial of service, with no confidentiality or integrity impact.","audience":"developers","category":"package","nicknames":[],"who_cares":["nltk","python","nlp","text-processing"],"confidence":0.75,"fix_status":"available","needs_auth":"no","worst_case":"A single request exhausts one CPU core for a sustained period","analyst_note":"Confirm the released fixed version: the sources give 3.10.4 but the fix-status note says PR #3895 is still open and develop remains vulnerable; check the NLTK release page and the GHSA advisory for the patched release.","how_to_check":"- Check the installed version with `pip show nltk` and whether it falls in `>= 3.6, <= 3.10.3`.\n- Search your code for `LegalitySyllableTokenizer` to see whether untrusted input reaches it.\n- The default word-tokenization path does not call this class, per the advisory.","plain_action":"","product_name":"","needs_analyst":true,"not_affected_if":["trusted-input-only","default-word-tokenizer","no-legality-syllable-tokenizer"],"who_is_affected":"Only services that pass attacker-controlled text to LegalitySyllableTokenizer.tokenize are exposed, because a single long vowel-run token drives the quadratic loop. Applications that tokenize only trusted text, or that use only the default word tokenizer, are not exposed.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"no","severity_if_unknown":"unknown"},"cost_usd":0.001133,"input_tokens":7630,"output_tokens":739,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-1':205C,278C,353C '-400':24B '-407':26B '0.004':680C '0.031':660C '0.120':665C '0.468':670C '000':398C '1':297C,555C,690C '1.8':403C '126':357C '130x':683C '133':349C '16000':614C,664C '2':95C,305C,564C,677C '27cw':15A,20A '3':580C,606C,633C,654C '32000':615C,636C,669C '4x':672C '64':397C '64000':649C,679C '8000':613C,659C 'affect':338C,483C 'all-vowel':236C,569C 'altern':586C 'api':143C 'attack':61B,132C,166C,376C,543C,688C 'attacker-control':165C 'authent':170C 'avail':159C,479C,499C 'ba':635C 'becom':249C 'begin':354C 'behavior':537C 'bound':313C 'branch':242C,254C 'build':275C 'built':214C 'c':602C 'call':102C,565C,579C 'cap':322C 'capabl':444C 'caus':73B,179C 'chain':689C 'char':201C 'charact':221C,271C,400C 'clear':257C 'code':325C 'compar':581C 'compon':464C 'concept':522C 'concurr':491C 'confidenti':79B,472C 'configur':172C 'consequ':488C 'consist':108C 'conson':585C 'consonant-altern':584C 'construct':556C 'consum':120C,430C 'consumpt':336C 'contain':90C 'control':167C,378C,587C,634C,641C,646C,648C,678C 'core':59B,153C,408C,432C,448C 'corpus':530C 'cost':284C,302C,411C,510C 'counter':621C,631C,644C,652C 'cpu':3A,58B,121C,152C,335C,387C,449C 'craft':53B 'current':203C,258C,266C,276C 'cwe':23B,25B 'd':603C 'degrad':158C,458C,480C 'demonstr':443C 'denial':74B,453C 'deploy':519C 'direct':332C 'doubl':674C 'download':531C 'driven':539C 'effect':477C 'entir':540C 'equal':590C,638C 'equal-length':637C 'establish':359C 'evalu':197C 'everi':189C,228C,273C 'exampl':116C 'execut':515C 'exhaust':4A,450C,496C 'export':694C 'exposur':691C 'faster':684C 'first':193C,225C,241C,372C 'forc':145C 'free':233C,363C 'function':339C 'ghsa':13A,18A 'ghsa-gpwc-27cw-rh9r':12A,17A 'give':300C 'gpwc':14A,19A 'grow':268C,412C 'hash':280C 'helper':358C 'hundr':424C 'impact':82B,374C,475C 'import':593C,597C 'increas':574C 'infer':504C 'input':54B,468C 'integr':81B,474C 'interact':175C 'invari':308C 'iter':190C,274C,288C 'k':286C,289C,295C 'kilobyt':426C 'larger':686C 'latenc':457C 'lead':451C 'legal':211C,552C 'legal-onset':551C 'legalitysyllabletoken':598C,600C,692C 'legalitysyllabletokenizer.onset':366C 'legalitysyllabletokenizer.tokenize':6A,36B,84C,340C,383C 'length':130C,321C,330C,418C,575C,591C,639C 'line':348C,356C 'linear':441C,640C,681C 'live':518C 'long':47B,111C 'loop':11A,97C,350C 'lower':202C 'made':44B 'mani':434C 'match':246C 'megabyt':428C 'member':229C 'membership':199C,344C 'membership-test':343C 'memori':438C 'method':100C,181C 'minut':437C 'multipl':490C 'n':94C,118C,299C,304C,611C,618C,628C,676C 'need':533C 'network':462C 'network-reach':461C 'never':245C,248C,261C 'nlp':27B 'nltk':1A,22A,28B,34B,87C,527C 'nltk.tokenize':596C,696C 'nltk/tokenize/legality_principle.py':342C,367C 'o':93C,285C,303C,675C 'one':52B,151C,270C 'onset':9A,204C,208C,212C,216C,247C,259C,267C,277C,364C,553C,562C,608C 'oper':487C 'packag':29B,89C,528C 'pass':381C 'path':326C 'payload':616C,623C 'per':315C,508C,673C 'per-request':507C 'per-token':314C 'period':157C 'pin':56B 'plausibl':486C 'pool':501C 'prefix':369C 'print':624C,647C 'process':33B,96C,484C 'prolong':156C 'proof':520C 'proport':123C 'public':141C 'python':30B,592C 'quadrat':2A,38B,334C,386C,536C 'rang':429C 'reach':262C 'reachabl':463C 'releas':392C,526C 'repeat':293C 'request':148C,492C,509C 'requir':177C,523C 'reset':253C 'result':265C,282C 'return':218C,368C 'revers':10A,186C 'review':391C 'rh9r':16A,21A 'root':178C 'rough':396C,682C 'round':629C,650C 'run':48B,112C,627C,658C,663C,668C 'satur':150C 'second':404C,435C 'secur':307C 'seen':196C 'self.legal':207C 'servic':68B,76B,162C,455C 'set':210C,365C,554C 'singl':42B,105C,147C,407C,447C 'single-cor':446C 'sink':346C 'size':548C,687C 'small':561C 'sourc':341C,563C,609C 'special':171C 'split':604C 'squar':126C,415C 'start':619C,632C,642C,653C 'string':283C 'submit':64B 'summari':83C 'suppli':135C 'syllabifi':70B,164C,466C 'take':401C 'test':200C,345C 'text':32B,65B,168C,337C,655C 'text-process':31B 'therefor':244C 'time':122C,577C,594C 'time.perf':620C,630C,643C,651C 'tini':605C 'tok':599C 'tok.tokenize':622C,645C 'token':43B,88C,106C,129C,138C,142C,184C,239C,316C,329C,352C,380C,394C,421C,544C,558C,566C,572C,588C,698C 'total':301C 'tr':601C 'translat':331C 'trigger':385C 'true':250C 'unguard':8A,92C 'untrust':137C,328C,467C 'use':439C,494C 'user':174C 'via':7A 'violat':311C 'vowel':50B,114C,194C,226C,232C,238C,362C,373C,399C,571C,626C,657C,662C,667C 'vowel-fre':231C,361C 'vowel-run':625C,656C,661C,666C 'walk':182C 'word':607C 'work':39B,317C,388C 'worker':500C 'would':256C","nicknames":[],"mitigations":[],"who_affected":"Only services that pass attacker-controlled text to LegalitySyllableTokenizer.tokenize are exposed, because a single long vowel-run token drives the quadratic loop. Applications that tokenize only trusted text, or that use only the default word tokenizer, are not exposed.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["nltk"],"ptokens":["nltk"],"labels":["cwe400","cwe407","medium","nlp","nltk","package","pypi","python","textprocessing"],"campaign_type":null,"search_a":"'27cw':15A,20A 'cpu':3A 'exhaust':4A 'ghsa':13A,18A 'ghsa-gpwc-27cw-rh9r':12A,17A 'gpwc':14A,19A 'legalitysyllabletokenizer.tokenize':6A 'loop':11A 'nltk':1A,22A 'onset':9A 'quadrat':2A 'revers':10A 'rh9r':16A,21A 'unguard':8A 'via':7A","search_b":"'-400':24B '-407':26B 'attack':61B 'caus':73B 'confidenti':79B 'core':59B 'cpu':58B 'craft':53B 'cwe':23B,25B 'denial':74B 'impact':82B 'input':54B 'integr':81B 'legalitysyllabletokenizer.tokenize':36B 'long':47B 'made':44B 'nlp':27B 'nltk':28B,34B 'one':52B 'packag':29B 'pin':56B 'process':33B 'python':30B 'quadrat':38B 'run':48B 'servic':68B,76B 'singl':42B 'submit':64B 'syllabifi':70B 'text':32B,65B 'text-process':31B 'token':43B 'vowel':50B 'work':39B","search_c":"'-1':205C,278C,353C '0.004':680C '0.031':660C '0.120':665C '0.468':670C '000':398C '1':297C,555C,690C '1.8':403C '126':357C '130x':683C '133':349C '16000':614C,664C '2':95C,305C,564C,677C '3':580C,606C,633C,654C '32000':615C,636C,669C '4x':672C '64':397C '64000':649C,679C '8000':613C,659C 'affect':338C,483C 'all-vowel':236C,569C 'altern':586C 'api':143C 'attack':132C,166C,376C,543C,688C 'attacker-control':165C 'authent':170C 'avail':159C,479C,499C 'ba':635C 'becom':249C 'begin':354C 'behavior':537C 'bound':313C 'branch':242C,254C 'build':275C 'built':214C 'c':602C 'call':102C,565C,579C 'cap':322C 'capabl':444C 'caus':179C 'chain':689C 'char':201C 'charact':221C,271C,400C 'clear':257C 'code':325C 'compar':581C 'compon':464C 'concept':522C 'concurr':491C 'confidenti':472C 'configur':172C 'consequ':488C 'consist':108C 'conson':585C 'consonant-altern':584C 'construct':556C 'consum':120C,430C 'consumpt':336C 'contain':90C 'control':167C,378C,587C,634C,641C,646C,648C,678C 'core':153C,408C,432C,448C 'corpus':530C 'cost':284C,302C,411C,510C 'counter':621C,631C,644C,652C 'cpu':121C,152C,335C,387C,449C 'current':203C,258C,266C,276C 'd':603C 'degrad':158C,458C,480C 'demonstr':443C 'denial':453C 'deploy':519C 'direct':332C 'doubl':674C 'download':531C 'driven':539C 'effect':477C 'entir':540C 'equal':590C,638C 'equal-length':637C 'establish':359C 'evalu':197C 'everi':189C,228C,273C 'exampl':116C 'execut':515C 'exhaust':450C,496C 'export':694C 'exposur':691C 'faster':684C 'first':193C,225C,241C,372C 'forc':145C 'free':233C,363C 'function':339C 'give':300C 'grow':268C,412C 'hash':280C 'helper':358C 'hundr':424C 'impact':374C,475C 'import':593C,597C 'increas':574C 'infer':504C 'input':468C 'integr':474C 'interact':175C 'invari':308C 'iter':190C,274C,288C 'k':286C,289C,295C 'kilobyt':426C 'larger':686C 'latenc':457C 'lead':451C 'legal':211C,552C 'legal-onset':551C 'legalitysyllabletoken':598C,600C,692C 'legalitysyllabletokenizer.onset':366C 'legalitysyllabletokenizer.tokenize':84C,340C,383C 'length':130C,321C,330C,418C,575C,591C,639C 'line':348C,356C 'linear':441C,640C,681C 'live':518C 'long':111C 'loop':97C,350C 'lower':202C 'mani':434C 'match':246C 'megabyt':428C 'member':229C 'membership':199C,344C 'membership-test':343C 'memori':438C 'method':100C,181C 'minut':437C 'multipl':490C 'n':94C,118C,299C,304C,611C,618C,628C,676C 'need':533C 'network':462C 'network-reach':461C 'never':245C,248C,261C 'nltk':87C,527C 'nltk.tokenize':596C,696C 'nltk/tokenize/legality_principle.py':342C,367C 'o':93C,285C,303C,675C 'one':151C,270C 'onset':204C,208C,212C,216C,247C,259C,267C,277C,364C,553C,562C,608C 'oper':487C 'packag':89C,528C 'pass':381C 'path':326C 'payload':616C,623C 'per':315C,508C,673C 'per-request':507C 'per-token':314C 'period':157C 'plausibl':486C 'pool':501C 'prefix':369C 'print':624C,647C 'process':96C,484C 'prolong':156C 'proof':520C 'proport':123C 'public':141C 'python':592C 'quadrat':334C,386C,536C 'rang':429C 'reach':262C 'reachabl':463C 'releas':392C,526C 'repeat':293C 'request':148C,492C,509C 'requir':177C,523C 'reset':253C 'result':265C,282C 'return':218C,368C 'revers':186C 'review':391C 'root':178C 'rough':396C,682C 'round':629C,650C 'run':112C,627C,658C,663C,668C 'satur':150C 'second':404C,435C 'secur':307C 'seen':196C 'self.legal':207C 'servic':162C,455C 'set':210C,365C,554C 'singl':105C,147C,407C,447C 'single-cor':446C 'sink':346C 'size':548C,687C 'small':561C 'sourc':341C,563C,609C 'special':171C 'split':604C 'squar':126C,415C 'start':619C,632C,642C,653C 'string':283C 'summari':83C 'suppli':135C 'syllabifi':164C,466C 'take':401C 'test':200C,345C 'text':168C,337C,655C 'therefor':244C 'time':122C,577C,594C 'time.perf':620C,630C,643C,651C 'tini':605C 'tok':599C 'tok.tokenize':622C,645C 'token':88C,106C,129C,138C,142C,184C,239C,316C,329C,352C,380C,394C,421C,544C,558C,566C,572C,588C,698C 'total':301C 'tr':601C 'translat':331C 'trigger':385C 'true':250C 'unguard':92C 'untrust':137C,328C,467C 'use':439C,494C 'user':174C 'violat':311C 'vowel':114C,194C,226C,232C,238C,362C,373C,399C,571C,626C,657C,662C,667C 'vowel-fre':231C,361C 'vowel-run':625C,656C,661C,666C 'walk':182C 'word':607C 'work':317C,388C 'worker':500C 'would':256C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-10-26T14:11:58+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-gpwc-27cw-rh9r","url":"https://github.com/advisories/GHSA-gpwc-27cw-rh9r","severity":"medium","published_at":"2026-11-13T21:16:34+00:00","modified_at":"2026-11-13T21:16:39+00:00","first_seen_at":"2026-11-13T21:19:52+00:00","fetched_at":"2026-11-13T21:45:25+00:00","note":null}],"documents":[{"id":149691,"url":"https://api.github.com/repos/nltk/nltk/security-advisories/GHSA-gpwc-27cw-rh9r","title":"Maintainer's advisory: api.github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: api.github.com"},{"id":144931,"url":"https://github.com/nltk/nltk/security/advisories/GHSA-gpwc-27cw-rh9r","title":"Vendor advisory: github.com","kind":"vendor","source":"nvd","publisher":"vendor","text":null,"published_at":"2026-10-26T14:11:58+00:00","relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"},{"id":149667,"url":"https://github.com/nltk/nltk/pull/3895","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":149694,"url":"https://github.com/nltk/nltk/commit/78781ba4575d536fb1540897f27baca0fb40617c","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":149668,"url":"https://github.com/nltk/nltk/commit/e20c40e26ec9306de885703f6e53250695f1f49b","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":149657,"url":"https://github.com/nltk/nltk/releases/tag/v3.10.4","title":"Release: github.com","kind":"release","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Release: github.com"}],"disclosures":[],"exploit_sources":[],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}