CVE-2026-33231
NLTK's WordNet Browser is vulnerable to unauthenticated remote shutdown.
- CVSS 7.5
- CWE-306
- Authentication, Authorization, and Session Management
- Remote
NLTK (Natural Language Toolkit) is a suite of open source Python modules, data sets, and tutorials supporting research and development in Natural Language Processing. In versions 3.9.3 and prior, `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. Commit bbaae83db86a0f49e00f5b0db44a7254c268de9b patches the issue.
- CWE
- CWE-306
- CVSS base score
- 7.5
- Published
- 2026-03-20
- OWASP
- A01 Broken Access Control
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Authentication, Authorization, and Session Management
- Subcategory
- Poorly Designed Access Controls
- Accessibility scope
- Remote
- Impact
- Denial of Service (DoS)
- Affected component
- NLTK
- Fixed by upgrading
- Yes
Solution
Upgrade NLTK to version 3.9.4 or later.
Vulnerable code sample
# Natural Language Toolkit: Wordnet Browser Application
#
# Copyright (C) 2001-2023 NLTK Project
# Author: Jussi Salmela <jtsalmela@users.sourceforge.net>
# Paul Bone <pbone@csse.unimelb.edu.au>
# Tom Aarsen
# URL: <https://www.nltk.org/>
# For license information, see LICENSE.TXT
import os
import re
import sys
import webbrowser
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import unquote_plus
from nltk.corpus import wordnet
from nltk.data import find
try:
from tkinter import (
Button,
Frame,
Label,
Menu,
Menubutton,
Tk,
Toplevel,
scrolledtext,
)
except ImportError:
from Tkinter import (
Button,
Frame,
Label,
Menu,
Menubutton,
Tk,
Toplevel,
scrolledtext,
)
try:
wordnet.ensure_loaded()
except LookupError as e:
from nltk.downloader import download
print(e)
print("This app requires the WordNet corpus.")
print("Please use a command like the following to download it:")
print("\n >>> import nltk")
print(" >>> nltk.download('wordnet')\n")
sys.exit(1)
######################################################################
# The WordNet Browser
######################################################################
class WordNetBrowser:
"""
A WordNet browser that can be used to navigate the WordNet database.
This browser is launched by `nltk.app.wordnet()`.
The browser consists of a main window, which is divided into four
main sections:
- A "word" section, containing a text entry box for words.
Pressing the return key in this box, or pressing the "Find
Word" button, will search for that word in WordNet.
- A "senses" section, which lists the senses for the most
recently searched-for word. This section is divided into
four sub-sections: one for each of the four wordnet parts of
speech (noun, verb, adjective, and adverb). Clicking on any
of the senses will cause its synset to be displayed in the
"synset" section of the main window.
- A "synset" section, which displays the information about the
most recently selected synset. This section is divided into
three sub-sections:
- A "lemmas" sub-section, which lists the lemmas for the
synset.
- A "relations" sub-section, which lists the relations for
the synset. Clicking on any of these relations will
cause the related synset to be displayed.
- A "gloss" sub-section, which displays the gloss for the
synset.
"""
def __init__(self, root, wn):
self._wn = wn
self._root = root
self._root.title("WordNet Browser")
self._find_word_box = FindWordBox(self, self._root, self._find_word)
self._senses_box = SensesBox(self, self._root)
self._synset_box = SynsetBox(self, self._root)
self._find_word_box.focus()
def mainloop(self):
self._root.mainloop()
# When the find word box gets a word, this is called.
def _find_word(self, word):
self._word = word
self._senses_box.update(word)
# When a sense is selected, this is called.
def select_sense(self, sense):
self._synset_box.update(sense)
# When a relation is selected, this is called.
def select_relation(self, relation):
self._synset_box.update(relation)
######################################################################
# Find Word Box
######################################################################
class FindWordBox(Frame):
def __init__(self, browser, parent, command):
Frame.__init__(self, parent, borderwidth=5)
self.pack(fill="x")
self._browser = browser
self._command = command
Label(self, text="Find word:").pack(side="left")
self._entry = Entry(self, background="white")
self._entry.pack(side="left", fill="x", expand=True)
self._entry.bind("<Return>", self._find)
self._button = Button(self, text="Find", command=self._find)
self._button.pack(side="right")
def _find(self, *e):
word = self._entry.get()
if not word:
return
self._command(word)
return "break"
def focus(self):
self._entry.focus()
######################################################################
# Synset Box
######################################################################
class SynsetBox(Frame):
def __init__(self, browser, parent):
Frame.__init__(self, parent, borderwidth=5)
self.pack(expand=True, fill="both")
self._browser = browser
# Create the top-level frames.
self._lemmas = Frame(self)
self._relations = Frame(self)
self._gloss = Frame(self)
self._lemmas.pack(fill="x", pady=2)
self._relations.pack(fill="both", expand=True)
self._gloss.pack(fill="x", pady=2)
# Create the widgets for the lemmas frame.
Label(self._lemmas, text="Lemmas:").pack(side="left")
self._lemma_list = FlowList(self._lemmas, self._browser._wn)
self._lemma_list.pack(side="left", expand=True, fill="x")
# Create the widgets for the relations frame.
Label(self._relations, text="Relations:", anchor="nw").pack(
side="top", anchor="nw"
)
self._relation_list = RelationList(
self._relations, self._browser, self._browser._wn
)
self._relation_list.pack(side="left", anchor="n", expand=True, fill="both")
# Create the widgets for the gloss frame.
self._gloss_label = Label(self._gloss, text="Gloss:", anchor="nw")
self._gloss_label.pack(side="top", anchor="nw")
self._gloss_text = scrolledtext.ScrolledText(
self._gloss,
height=5,
state="disabled",
wrap="word",
background="#dddddd",
relief="groove",
borderwidth=2,
)
self._gloss_text.pack(expand=True, fill="both")
def update(self, synset):
self._lemma_list.update(synset)
self._relation_list.update(synset)
self._gloss_text.config(state="normal")
self._gloss_text.delete("1.0", "end")
self._gloss_text.insert("end", synset.definition())
examples = synset.examples()
if examples:
self._gloss_text.insert("end", "\n\nExamples: \n- ")
self._gloss_text.insert("end", "\n- ".join(examples))
self._gloss_text.config(state="disabled")
######################################################################
# Senses Box
######################################################################
class SensesBox(Frame):
def __init__(self, browser, parent):
Frame.__init__(self, parent, borderwidth=5)
self.pack(expand=True, fill="both")
self._browser = browser
self._pos = {}
for (pos, name) in (
(wordnet.NOUN, "Nouns"),
(wordnet.VERB, "Verbs"),
(wordnet.ADJ, "Adjectives"),
(wordnet.ADV, "Adverbs"),
):
frame = Frame(self)
label = Label(frame, text=f"{name}:", width=12, anchor="n")
list = SynsetList(frame, self._browser, self._browser._wn)
label.pack(side="left", anchor="n")
list.pack(side="left", expand=True, fill="x", anchor="n")
self._pos[pos] = {"frame": frame, "list": list}
self._pos[wordnet.NOUN]["frame"].pack(expand=1, fill="x")
self._pos[wordnet.VERB]["frame"].pack(expand=1, fill="x")
self._pos[wordnet.ADJ]["frame"].pack(expand=1, fill="x")
self._pos[wordnet.ADV]["frame"].pack(expand=1, fill="x")
def update(self, word):
for pos in self._pos:
synsets = self._browser._wn.synsets(word, pos)
self._pos[pos]["list"].update(synsets)
######################################################################
# FlowList: a list of hyperlinked items that flows to fill the
# width of its container. Used to list the lemmas for a synset.
######################################################################
class FlowList(Frame):
"""
A frame that contains a list of 'items' (hyperlinked text
fragments). The items are arranged to flow from left to right,
and from top to bottom.
"""
def __init__(self, parent, wn, **kwargs):
Frame.__init__(self, parent)
self._wn = wn
self._items = []
def update(self, synset):
# Clear any old items.
for item in self._items:
item.destroy()
# Add the new items.
for i, lemma in enumerate(synset.lemmas()):
if i > 0:
item = Label(self, text=", ")
item.pack(side="left")
self._items.append(item)
item = HyperlinkedText(
self, lemma.name().replace("_", " "), self._select_lemma(lemma), "lemma"
)
item.pack(side="left")
self._items.append(item)
# This should find the word corresponding to this lemma.
def _select_lemma(self, lemma):
def thunk(e, l=lemma):
pass
return thunk
######################################################################
# SynsetList: A list of hyperlinked synsets. Used to list the
# senses for a word.
######################################################################
class SynsetList(Frame):
def __init__(self, parent, browser, wn):
Frame.__init__(self, parent)
self._browser = browser
self._wn = wn
self._items = []
def update(self, synsets):
# Clear any old items.
for item in self._items:
item.destroy()
# Add the new items.
for synset in synsets:
item = HyperlinkedText(
self, synset.definition(), self._select_synset(synset)
)
item.pack(anchor="w")
self._items.append(item)
def _select_synset(self, synset):
def thunk(e, s=synset):
self._browser.select_sense(s)
return thunk
######################################################################
# RelationList: A list of hyperlinked relations. Used to list the
# relations for a synset.
######################################################################
class RelationList(Frame):
# These are taken from the wordnet documentation
RELATIONS = {
# Noun relations
"hypernyms": "Hypernyms",
"instance_hypernyms": "Instance Hypernyms",
"hyponyms": "Hyponyms",
"instance_hyponyms": "Instance Hyponyms",
"member_holonyms": "Member Holonyms",
"substance_holonyms": "Substance Holonyms",
"part_holonyms": "Part Holonyms",
"member_meronyms": "Member Meronyms",
"substance_meronyms": "Substance Meronyms",
"part_meronyms": "Part Meronyms",
"attributes": "Attributes",
"entailments": "Entailments",
"causes": "Causes",
"also_sees": "Also Sees",
"verb_groups": "Verb Groups",
"similar_tos": "Similar Tos",
"pertainyms": "Pertainyms",
"derivationally_related_forms": "Derivationally Related Forms",
# Verb relations
"antonyms": "Antonyms",
}
# This is a list of relation types in an order that seems to make
# sense.
RELATION_ORDER = [
"hypernyms",
"instance_hypernyms",
"hyponyms",
"instance_hyponyms",
"member_holonyms",
"substance_holonyms",
"part_holonyms",
"member_meronyms",
"substance_meronyms",
"part_meronyms",
"entailments",
"causes",
"verb_groups",
"attributes",
"similar_tos",
"also_sees",
"pertainyms",
"derivationally_related_forms",
"antonyms",
]
def __init__(self, parent, browser, wn):
Frame.__init__(self, parent)
self._browser = browser
self._wn = wn
self._items = []
def update(self, synset):
# Clear any old items.
for item in self._items:
item.destroy()
# Add the new items.
for rel in self.RELATION_ORDER:
# Get the set of related synsets.
if hasattr(synset, rel):
related_synsets = getattr(synset, rel)()
elif hasattr(synset, rel.replace("_", "")):
related_synsets = getattr(synset, rel.replace("_", ""))()
else:
continue
if not related_synsets:
continue
# Add a label for the relation.
label = Label(self, text=f"{self.RELATIONS.get(rel, rel)}:")
label.pack(anchor="w")
self._items.append(label)
# Add the list of related synsets.
list = Frame(self)
for i, related_syn in enumerate(related_synsets):
if i > 0:
item = Label(list, text=", ")
item.pack(side="left")
self._items.append(item)
item = HyperlinkedText(
list,
related_syn.name(),
self._select_synset(related_syn),
"synset",
)
item.pack(side="left")
self._items.append(item)
list.pack(anchor="w", fill="x", expand=True)
self._items.append(list)
def _select_synset(self, synset):
def thunk(e, s=synset):
self._browser.select_relation(s)
return thunk
######################################################################
# Helper widgets
######################################################################
class HyperlinkedText(Label):
"""
A text label that acts as a hyperlink. When the user clicks on
it, a given command is called. When the mouse is over it, it is
underlined.
"""
def __init__(self, parent, text, command, type=None):
if type == "lemma":
font = "helvetica 12"
else:
font = "helvetica 12"
Label.__init__(self, parent, text=text, fg="blue", font=font)
self._command = command
self.bind("<Enter>", self._enter)
self.bind("<Leave>", self._leave)
self.bind("<ButtonPress-1>", self._click)
self._type = type
self._orig_font = self["font"]
if self._orig_font is None:
self._orig_font = "helvetica 12"
(family, size, style) = self._orig_font.split()
self._underline_font = f"{family} {size} {style} underline"
self._orig_cursor = self["cursor"]
def _enter(self, e):
self.config(font=self._underline_font, cursor="hand2")
def _leave(self, e):
self.config(font=self._orig_font, cursor=self._orig_cursor)
def _click(self, e):
self._command(e)
# The default Tkinter Entry widget doesn't let us bind to the
# return key without capturing all events, so we'll just subclass
# it.
try:
from tkinter import Entry
except ImportError:
from Tkinter import Entry
class WordNetApp:
def __init__(self, wordnet_corpus_reader=wordnet):
"""
Create a new WordNet browser.
"""
self._wn = wordnet_corpus_reader
self._root = Tk()
self._browser = WordNetBrowser(self._root, self._wn)
self._menubar = self._init_menubar()
self._root.config(menu=self._menubar)
def _init_menubar(self):
menubar = Menu(self._root)
# File menu
filemenu = Menu(menubar, tearoff=0)
filemenu.add_command(label="About", command=self._about)
filemenu.add_command(label="Quit", command=self._root.destroy)
menubar.add_cascade(label="File", menu=filemenu)
# Search menu
searchmenu = Menu(menubar, tearoff=0)
searchmenu.add_command(
label="Find Word", command=self._browser._find_word_box.focus
)
menubar.add_cascade(label="Search", menu=searchmenu)
return menubar
def _about(self, *e):
# Get the current version
try:
from nltk import __version__ as version
except ImportError:
version = "unknown"
# The about box.
about = Toplevel(self._root)
about.title("About the WordNet Browser")
about.bind("<KeyPress-q>", lambda e: about.destroy())
about.bind("<KeyPress-Q>", lambda e: about.destroy())
about.focus()
msg = [
"NLTK WordNet Browser",
"",
"A graphical interface for browsing the WordNet database.",
"",
"NLTK Version: %s" % version,
"Written by Jussi Salmela and Paul Bone",
"Ported to Tkinter by Tom Aarsen",
]
Label(
about,
text="\n".join(msg),
foreground="darkblue",
background="white",
justify="center",
font="helvetica 12",
padx=20,
pady=10,
).pack()
Button(about, text="Ok", command=about.destroy, padx=20).pack(pady=10)
def main(self):
"""
Start the WordNet browser. This method will not return until
the browser is closed.
"""
self._browser.mainloop()
try:
self._root.destroy()
except:
pass
######################################################################
# The WordNet Browser (HTTP)
######################################################################
# A simple web-browser interface for the wordnet browser.
class WordNetRequestHandler(BaseHTTPRequestHandler):
"""
A handler for WordNet lookup requests. This request handler can
be used to create a simple web interface for browsing WordNet. It
generates HTML pages to display information about synsets and
lemmas. The following paths are handled by this server:
- ``/``: The main page. This page contains a simple form
asking for a word. When the user enters a word, it will
take them to ``/lookup/``*word*.
- ``/style.css``: A css stylesheet.
- ``/lookup/``*word*: A page displaying all synsets for *word*.
- ``/synset/``*synset*: A page displaying the synset *synset*.
- ``/SHUTDOWN``: Shut down the server.
"""
def do_GET(self):
# Let's hope the user isn't using ie5
html = '<?xml version="1.0" encoding="utf-8"?>\n'
html += '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" '
html += '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n'
if self.path.endswith("/SHUTDOWN%20THE%20SERVER"):
self.send_response(200)
self.send_header("Content-type", "text/plain")
self.end_headers()
self.wfile.write(b"Shutting down the server.")
# This is a rather drastic way to kill the server.
# But it's the simplest thing that works.
os._exit(0)
return
if self.path == "/":
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
html += self._formpage()
self.wfile.write(html.encode("utf8"))
return
if self.path == "/style.css":
self.send_response(200)
self.send_header("Content-type", "text/css")
self.end_headers()
html = self._style()
self.wfile.write(html.encode("utf8"))
return
match = re.match(r"/lookup/(.*)", self.path)
if match:
word = unquote_plus(match.group(1))
synsets = wordnet.synsets(word)
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
html += self._wordpage(word, synsets)
self.wfile.write(html.encode("utf8"))
return
match = re.match(r"/synset/(.*)", self.path)
if match:
try:
synset = wordnet.synset(match.group(1))
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
html += self._synsetpage(synset)
self.wfile.write(html.encode("utf8"))
except:
self.send_error(404, f"Synset not found: {match.group(1)}")
return
self.send_error(404, f"File Not Found: {self.path}")
def _style(self):
return """
h1, h2, h3, h4, h5, h6 { margin-bottom: 0; }
body {
font-family: Arial, Helvetica, sans-serif;
font-size: 90%;
margin-left: 5%;
margin-right: 5%;
}
div.synset {
border: 1px solid #000;
padding: 1ex;
margin-top: 1em;
margin-bottom: 1em;
}
div.header {
background: #ddd;
padding: 1ex;
font-weight: bold;
}
div.relations {
padding: 1ex;
}
div.gloss {
padding: 1ex;
font-style: italic;
}
div.relation_lemmas {
padding-left: 2em;
}
div.examples {
padding-left: 2em;
font-style: normal;
}
a:link {
text-decoration: none;
color: #00c;
}
a:visited {
text-decoration: none;
color: #507;
}
a:hover {
text-decoration: underline;
color: #00c;
}
"""
def _formpage(self):
html = f"""
<html>
<head>
<title>WordNet Search</title>
<link rel="stylesheet" type="text/css" href="style.css" />
<script type="text/javascript">
function do_lookup() {{
var word = document.getElementById("word").value;
if (word != "") {{
parent.location="/lookup/"+word;
}}
}}
</script>
</head>
<body>
<h1>WordNet Search</h1>
<div class="synset">
<div class="header">Find a word</div>
<div class="relations">
Word:
<input type="text" name="word" id="word" />
<input type="button" value="Look up" onclick="do_lookup()"/>
</div>
</div>
<p>
<a href="https://wordnet.princeton.edu/">
About WordNet
</a>
<br/>
<a href="https://www.nltk.org/">
About NLTK
</a>
</p>
<script type="text/javascript">
document.getElementById('word').onkeypress =
function(e) {{
if (!e) e = window.event;
if (e.keyCode == '13') {{
do_lookup();
return false;
}}
}}
document.getElementById('word').focus()
</script>
</body>
</html>
"""
return html
def _wordpage(self, word, synsets):
html = f"""
<html>
<head>
<title>Synsets for {word}</title>
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
<body>
"""
html += '<h1>Synsets for "%s"</h1>' % word
for synset in synsets:
html += self._synsetdiv(synset)
if not synsets:
html += f"No synsets found for {word}."
html += """
<p><a href="/">New Search</a></p>
</body>
</html>
"""
return html
def _synsetpage(self, synset):
html = f"""
<html>
<head>
<title>Synset: {synset.name()}</title>
<link rel="stylesheet" type="text/css" href="/style.css" />
</head>
<body>
<h1>Synset: {synset.name()}</h1>
{self._synsetdiv(synset, verbose=True)}
<p><a href="/">New Search</a></p>
</body>
</html>
"""
return html
def _synsetdiv(self, synset, verbose=False):
html = ""
html += '<div class="synset">'
html += '<div class="header">'
html += ", ".join(
f'<a href="/lookup/{l.name()}">{l.name()}</a>'
for l in synset.lemmas()[:5]
)
if len(synset.lemmas()) > 5:
html += f", etc..."
html += "</div>"
html += '<div class="gloss">'
html += synset.definition()
if synset.examples():
html += '<div class="examples">'
for ex in synset.examples():
html += f"“{ex}” "
html += "</div>"
html += "</div>"
if verbose:
html += self._relationsdiv(synset)
else:
html += (
'<div align="right"><a href="/synset/'
+ synset.name()
+ '">more...</a></div>'
)
html += "</div>"
return html
def _relationsdiv(self, synset):
html = '<div class="relations">'
# A list of relations to check for. This is the same list
# that the graphical browser uses.
relation_order = RelationList.RELATION_ORDER
# A mapping from relation method name to display name.
relation_names = RelationList.RELATIONS
# Display the relations.
for rel in relation_order:
if hasattr(synset, rel):
related = getattr(synset, rel)()
elif hasattr(synset, rel.replace("_", "")):
related = getattr(synset, rel.replace("_", ""))()
else:
continue
if not related:
continue
html += f'<div class="relation_header">{relation_names.get(rel, rel)}</div>'
html += '<div class="relation_lemmas">'
html += ", ".join(
f'<a href="/synset/{s.name()}">{s.name()}</a>' for s in related
)
html += "</div>"
html += "</div>"
return html
def serve(wordnet_corpus_reader=wordnet, port=8000):
"""
Start a simple web server on a given port, that can be used to
browse WordNet.
"""
try:
if port == 0:
port = 8000
server = HTTPServer(("", port), WordNetRequestHandler)
except OSError as e:
print(f"Error: Port {port} is already in use.")
print("Please choose a different port.")
sys.exit(1)
print(f"WordNet browser server running on http://{server.server_name}:{server.server_port}")
print("Enter 'SHUTDOWN' to stop the server.")
print('Alternatively, you can send a GET request to "/SHUTDOWN THE SERVER"')
# Automatically open a web browser on the server's main page.
url = f"http://localhost:{port}"
try:
webbrowser.open(url)
print(f"Your web browser has been opened to {url}")
except:
print(f"Please open your web browser to {url}")
# The server's main loop. This will process requests until we
# tell it to stop.
try:
server.serve_forever()
except KeyboardInterrupt:
print("Shutting down the server.")
server.socket.close()
def demo(wordnet_corpus_reader=wordnet):
"""
Create and run a new WordNet browser.
"""
WordNetApp(wordnet_corpus_reader).main()
def main():
# If a command-line argument is given, then use it as the port
# for an HTTP server.
if len(sys.argv) > 1:
try:
port = int(sys.argv[1])
serve(port=port)
except ValueError:
print("Usage: wordnet_app.py [PORT]")
# Otherwise, run the graphical demo.
else:
# Check that the user has a screen.
if "DISPLAY" not in os.environ:
# Fall back on the web-based browser.
print("No DISPLAY environment variable specified.")
print("Falling back on the web-based browser.")
print("(Tkinter-based WordNet browser requires a display.)")
print()
serve()
# Otherwise, run the graphical browser.
else:
demo()
if __name__ == "__main__":
main()Patched code sample
import os
import re
import sys
from http.server import BaseHTTPRequestHandler, HTTPServer
from socketserver import ThreadingMixIn
from urllib.parse import parse_qs, urlparse
from nltk.corpus import wordnet
# This is a simplified representation of the application structure for clarity.
# The core change is the removal of the vulnerable 'if self.path == "/SHUTDOWN THE SERVER":'
# block from the do_GET method. The code below reflects the state *after* the fix.
_STYLE = """
... CSS styles would be here ...
"""
_SCRIPT = """
... JavaScript would be here ...
"""
_HELP = """
... HTML help content would be here ...
"""
class WordNetRequestHandler(BaseHTTPRequestHandler):
"""
The HTTP request handler for the WordNet browser.
"""
def do_GET(self):
"""
GET requests are used to serve the main page, and to handle
searches.
"""
# This is the fixed version. The following vulnerable block was removed:
#
# if self.path == "/SHUTDOWN THE SERVER":
# print("Server shutting down.")
# # a non-graceful shutdown is all that we need here
# os._exit(0)
if self.path == "/":
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(b"<html><head>")
self.wfile.write(b"<title>WordNet Browser</title>")
self.wfile.write(b'<link rel="stylesheet" type="text/css" ')
self.wfile.write(b'href="style.css" />')
self.wfile.write(b"</head><body>")
self.wfile.write(_HELP.encode("utf8"))
self.wfile.write(b"</body></html>")
elif self.path == "/style.css":
self.send_response(200)
self.send_header("Content-type", "text/css")
self.end_headers()
self.wfile.write(_STYLE.encode("utf8"))
elif self.path.startswith("/img/"):
img_path = os.path.join(os.path.dirname(__file__), self.path[1:])
if os.path.exists(img_path):
self.send_response(200)
if img_path.endswith(".png"):
self.send_header("Content-type", "image/png")
elif img_path.endswith(".gif"):
self.send_header("Content-type", "image/gif")
else:
self.send_header("Content-type", "image/jpeg")
self.end_headers()
with open(img_path, "rb") as f:
self.wfile.write(f.read())
else:
self.send_error(404, "Image not found")
elif self.path.startswith("/search"):
query = parse_qs(urlparse(self.path).query)
# ... The rest of the search handling logic would be here ...
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(b"<html><body>Search results would appear here.</body></html>")
else:
self.send_error(404, "File not found")Cite this entry
@misc{vaitp:cve202633231,
title = {{NLTK's WordNet Browser is vulnerable to unauthenticated remote shutdown.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-33231},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-33231/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
