We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
XML helper and incorrect HTML
XML crashes while trying to sanitize some sorts of incorrect html.

For example:

```
a = '</em></em>' # wrong html
b = XML(a, sanitize=True)
```

Result:
`<type 'exceptions.IndexError'> pop from empty list`


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `gluon/sanitizer.py`
Content:
```
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 """
4 | From http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496942
5 | Submitter: Josh Goldfoot (other recipes)
6 | Last Updated: 2006/08/05
7 | Version: 1.0
8 
9 Cross-site scripting (XSS) defense
10 -----------------------------------
11 """
12 
13 from ._compat import HTMLParser, urlparse, entitydefs, basestring
14 from cgi import escape
15 from formatter import AbstractFormatter
16 from xml.sax.saxutils import quoteattr
17 
18 __all__ = ['sanitize']
19 
20 
21 def xssescape(text):
22     """Gets rid of < and > and & and, for good measure, :"""
23 
24     return escape(text, quote=True).replace(':', '&#58;')
25 
26 
27 class XssCleaner(HTMLParser):
28 
29     def __init__(
30         self,
31         permitted_tags=[
32             'a',
33             'b',
34             'blockquote',
35             'br/',
36             'i',
37             'li',
38             'ol',
39             'ul',
40             'p',
41             'cite',
42             'code',
43             'pre',
44             'img/',
45         ],
46         allowed_attributes={'a': ['href', 'title'], 'img': ['src', 'alt'
47                                                             ], 'blockquote': ['type']},
48         strip_disallowed=False
49     ):
50 
51         HTMLParser.__init__(self)
52         self.result = ''
53         self.open_tags = []
54         self.permitted_tags = [i for i in permitted_tags if i[-1] != '/']
55         self.requires_no_close = [i[:-1] for i in permitted_tags
56                                   if i[-1] == '/']
57         self.permitted_tags += self.requires_no_close
58         self.allowed_attributes = allowed_attributes
59 
60         # The only schemes allowed in URLs (for href and src attributes).
61         # Adding "javascript" or "vbscript" to this list would not be smart.
62 
63         self.allowed_schemes = ['http', 'https', 'ftp', 'mailto']
64 
65         #to strip or escape disallowed tags?
66         self.strip_disallowed = strip_disallowed
67         # there might be data after final closing tag, that is to be ignored
68         self.in_disallowed = [False]
69 
70     def handle_data(self, data):
71         if data and not self.in_disallowed[-1]:
72             self.result += xssescape(data)
73 
74     def handle_charref(self, ref):
75         if self.in_disallowed[-1]:
76             return
77         elif len(ref) < 7 and (ref.isdigit() or ref == 'x27'): # x27 is a special case for apostrophe
78             self.result += '&#%s;' % ref
79         else:
80             self.result += xssescape('&#%s' % ref)
81 
82     def handle_entityref(self, ref):
83         if self.in_disallowed[-1]:
84             return
85         elif ref in entitydefs:
86             self.result += '&%s;' % ref
87         else:
88             self.result += xssescape('&%s' % ref)
89 
90     def handle_comment(self, comment):
91         if self.in_disallowed[-1]:
92             return
93         elif comment:
94             self.result += xssescape('<!--%s-->' % comment)
95 
96     def handle_starttag(
97         self,
98         tag,
99         attrs
100     ):
101         if tag not in self.permitted_tags:
102             self.in_disallowed.append(True)
103             if (not self.strip_disallowed):
104                 self.result += xssescape('<%s>' % tag)
105         else:
106             self.in_disallowed.append(False)
107             bt = '<' + tag
108             if tag in self.allowed_attributes:
109                 attrs = dict(attrs)
110                 self.allowed_attributes_here = [x for x in
111                                                 self.allowed_attributes[tag] if x in attrs
112                                                 and len(attrs[x]) > 0]
113                 for attribute in self.allowed_attributes_here:
114                     if attribute in ['href', 'src', 'background']:
115                         if self.url_is_acceptable(attrs[attribute]):
116                             bt += ' %s="%s"' % (attribute,
117                                                 attrs[attribute])
118                     else:
119                         bt += ' %s=%s' % (xssescape(attribute),
120                                           quoteattr(attrs[attribute]))
121             # deal with <a> without href and <img> without src
122             if bt == '<a' or bt == '<img':
123                 return
124             if tag in self.requires_no_close:
125                 bt += ' /'
126             bt += '>'
127             self.result += bt
128             if tag not in self.requires_no_close: self.open_tags.insert(0, tag)
129 
130     def handle_endtag(self, tag):
131         bracketed = '</%s>' % tag
132         self.in_disallowed.pop()
133         if tag not in self.permitted_tags:
134             if (not self.strip_disallowed):
135                 self.result += xssescape(bracketed)
136         elif tag in self.open_tags:
137             self.result += bracketed
138             self.open_tags.remove(tag)
139 
140     def url_is_acceptable(self, url):
141         """
142         Accepts relative, absolute, and mailto urls
143         """
144 
145         if url.startswith('#'):
146             return True
147         else:
148             parsed = urlparse(url)
149             return ((parsed[0] in self.allowed_schemes and '.' in parsed[1]) or
150                     (parsed[0] in self.allowed_schemes and '@' in parsed[2]) or
151                     (parsed[0] == '' and parsed[2].startswith('/')))
152 
153     def strip(self, rawstring, escape=True):
154         """
155         Returns the argument stripped of potentially harmful
156         HTML or Javascript code
157 
158         @type escape: boolean
159         @param escape: If True (default) it escapes the potentially harmful
160           content, otherwise remove it
161         """
162 
163         if not isinstance(rawstring, str):
164             return str(rawstring)
165         for tag in self.requires_no_close:
166             rawstring = rawstring.replace("<%s/>" % tag, "<%s />" % tag)
167         if not escape:
168             self.strip_disallowed = True
169         self.result = ''
170         self.feed(rawstring)
171         for endtag in self.open_tags:
172             if endtag not in self.requires_no_close:
173                 self.result += '</%s>' % endtag
174         return self.result
175 
176     def xtags(self):
177         """
178         Returns a printable string informing the user which tags are allowed
179         """
180 
181         tg = ''
182         for x in sorted(self.permitted_tags):
183             tg += '<' + x
184             if x in self.allowed_attributes:
185                 for y in self.allowed_attributes[x]:
186                     tg += ' %s=""' % y
187             tg += '> '
188         return xssescape(tg.strip())
189 
190 
191 def sanitize(text, permitted_tags=[
192         'a',
193         'b',
194         'blockquote',
195         'br/',
196         'i',
197         'li',
198         'ol',
199         'ul',
200         'p',
201         'cite',
202         'code',
203         'pre',
204         'img/',
205         'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
206         'table', 'tbody', 'thead', 'tfoot', 'tr', 'td', 'div',
207         'strong', 'span',
208 ],
209     allowed_attributes={
210         'a': ['href', 'title'],
211         'img': ['src', 'alt'],
212         'blockquote': ['type'],
213         'td': ['colspan'],
214     },
215         escape=True):
216     if not isinstance(text, basestring):
217         return str(text)
218     return XssCleaner(permitted_tags=permitted_tags,
219                       allowed_attributes=allowed_attributes).strip(text, escape)
220
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/gluon/sanitizer.py b/gluon/sanitizer.py
--- a/gluon/sanitizer.py
+++ b/gluon/sanitizer.py
@@ -129,7 +129,7 @@
 
     def handle_endtag(self, tag):
         bracketed = '</%s>' % tag
-        self.in_disallowed.pop()
+        self.in_disallowed and self.in_disallowed.pop()
         if tag not in self.permitted_tags:
             if (not self.strip_disallowed):
                 self.result += xssescape(bracketed)

{"golden_diff": "diff --git a/gluon/sanitizer.py b/gluon/sanitizer.py\n--- a/gluon/sanitizer.py\n+++ b/gluon/sanitizer.py\n@@ -129,7 +129,7 @@\n \n     def handle_endtag(self, tag):\n         bracketed = '</%s>' % tag\n-        self.in_disallowed.pop()\n+        self.in_disallowed and self.in_disallowed.pop()\n         if tag not in self.permitted_tags:\n             if (not self.strip_disallowed):\n                 self.result += xssescape(bracketed)\n", "issue": "XML helper and incorrect HTML\nXML crashes while trying to sanitize some sorts of incorrect html.\n\nFor example:\n\n```\na = '</em></em>' # wrong html\nb = XML(a, sanitize=True)\n```\n\nResult:\n`<type 'exceptions.IndexError'> pop from empty list`\n\n", "before_files": [{"content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\"\"\"\n| From http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496942\n| Submitter: Josh Goldfoot (other recipes)\n| Last Updated: 2006/08/05\n| Version: 1.0\n\nCross-site scripting (XSS) defense\n-----------------------------------\n\"\"\"\n\nfrom ._compat import HTMLParser, urlparse, entitydefs, basestring\nfrom cgi import escape\nfrom formatter import AbstractFormatter\nfrom xml.sax.saxutils import quoteattr\n\n__all__ = ['sanitize']\n\n\ndef xssescape(text):\n    \"\"\"Gets rid of < and > and & and, for good measure, :\"\"\"\n\n    return escape(text, quote=True).replace(':', '&#58;')\n\n\nclass XssCleaner(HTMLParser):\n\n    def __init__(\n        self,\n        permitted_tags=[\n            'a',\n            'b',\n            'blockquote',\n            'br/',\n            'i',\n            'li',\n            'ol',\n            'ul',\n            'p',\n            'cite',\n            'code',\n            'pre',\n            'img/',\n        ],\n        allowed_attributes={'a': ['href', 'title'], 'img': ['src', 'alt'\n                                                            ], 'blockquote': ['type']},\n        strip_disallowed=False\n    ):\n\n        HTMLParser.__init__(self)\n        self.result = ''\n        self.open_tags = []\n        self.permitted_tags = [i for i in permitted_tags if i[-1] != '/']\n        self.requires_no_close = [i[:-1] for i in permitted_tags\n                                  if i[-1] == '/']\n        self.permitted_tags += self.requires_no_close\n        self.allowed_attributes = allowed_attributes\n\n        # The only schemes allowed in URLs (for href and src attributes).\n        # Adding \"javascript\" or \"vbscript\" to this list would not be smart.\n\n        self.allowed_schemes = ['http', 'https', 'ftp', 'mailto']\n\n        #to strip or escape disallowed tags?\n        self.strip_disallowed = strip_disallowed\n        # there might be data after final closing tag, that is to be ignored\n        self.in_disallowed = [False]\n\n    def handle_data(self, data):\n        if data and not self.in_disallowed[-1]:\n            self.result += xssescape(data)\n\n    def handle_charref(self, ref):\n        if self.in_disallowed[-1]:\n            return\n        elif len(ref) < 7 and (ref.isdigit() or ref == 'x27'): # x27 is a special case for apostrophe\n            self.result += '&#%s;' % ref\n        else:\n            self.result += xssescape('&#%s' % ref)\n\n    def handle_entityref(self, ref):\n        if self.in_disallowed[-1]:\n            return\n        elif ref in entitydefs:\n            self.result += '&%s;' % ref\n        else:\n            self.result += xssescape('&%s' % ref)\n\n    def handle_comment(self, comment):\n        if self.in_disallowed[-1]:\n            return\n        elif comment:\n            self.result += xssescape('<!--%s-->' % comment)\n\n    def handle_starttag(\n        self,\n        tag,\n        attrs\n    ):\n        if tag not in self.permitted_tags:\n            self.in_disallowed.append(True)\n            if (not self.strip_disallowed):\n                self.result += xssescape('<%s>' % tag)\n        else:\n            self.in_disallowed.append(False)\n            bt = '<' + tag\n            if tag in self.allowed_attributes:\n                attrs = dict(attrs)\n                self.allowed_attributes_here = [x for x in\n                                                self.allowed_attributes[tag] if x in attrs\n                                                and len(attrs[x]) > 0]\n                for attribute in self.allowed_attributes_here:\n                    if attribute in ['href', 'src', 'background']:\n                        if self.url_is_acceptable(attrs[attribute]):\n                            bt += ' %s=\"%s\"' % (attribute,\n                                                attrs[attribute])\n                    else:\n                        bt += ' %s=%s' % (xssescape(attribute),\n                                          quoteattr(attrs[attribute]))\n            # deal with <a> without href and <img> without src\n            if bt == '<a' or bt == '<img':\n                return\n            if tag in self.requires_no_close:\n                bt += ' /'\n            bt += '>'\n            self.result += bt\n            if tag not in self.requires_no_close: self.open_tags.insert(0, tag)\n\n    def handle_endtag(self, tag):\n        bracketed = '</%s>' % tag\n        self.in_disallowed.pop()\n        if tag not in self.permitted_tags:\n            if (not self.strip_disallowed):\n                self.result += xssescape(bracketed)\n        elif tag in self.open_tags:\n            self.result += bracketed\n            self.open_tags.remove(tag)\n\n    def url_is_acceptable(self, url):\n        \"\"\"\n        Accepts relative, absolute, and mailto urls\n        \"\"\"\n\n        if url.startswith('#'):\n            return True\n        else:\n            parsed = urlparse(url)\n            return ((parsed[0] in self.allowed_schemes and '.' in parsed[1]) or\n                    (parsed[0] in self.allowed_schemes and '@' in parsed[2]) or\n                    (parsed[0] == '' and parsed[2].startswith('/')))\n\n    def strip(self, rawstring, escape=True):\n        \"\"\"\n        Returns the argument stripped of potentially harmful\n        HTML or Javascript code\n\n        @type escape: boolean\n        @param escape: If True (default) it escapes the potentially harmful\n          content, otherwise remove it\n        \"\"\"\n\n        if not isinstance(rawstring, str):\n            return str(rawstring)\n        for tag in self.requires_no_close:\n            rawstring = rawstring.replace(\"<%s/>\" % tag, \"<%s />\" % tag)\n        if not escape:\n            self.strip_disallowed = True\n        self.result = ''\n        self.feed(rawstring)\n        for endtag in self.open_tags:\n            if endtag not in self.requires_no_close:\n                self.result += '</%s>' % endtag\n        return self.result\n\n    def xtags(self):\n        \"\"\"\n        Returns a printable string informing the user which tags are allowed\n        \"\"\"\n\n        tg = ''\n        for x in sorted(self.permitted_tags):\n            tg += '<' + x\n            if x in self.allowed_attributes:\n                for y in self.allowed_attributes[x]:\n                    tg += ' %s=\"\"' % y\n            tg += '> '\n        return xssescape(tg.strip())\n\n\ndef sanitize(text, permitted_tags=[\n        'a',\n        'b',\n        'blockquote',\n        'br/',\n        'i',\n        'li',\n        'ol',\n        'ul',\n        'p',\n        'cite',\n        'code',\n        'pre',\n        'img/',\n        'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n        'table', 'tbody', 'thead', 'tfoot', 'tr', 'td', 'div',\n        'strong', 'span',\n],\n    allowed_attributes={\n        'a': ['href', 'title'],\n        'img': ['src', 'alt'],\n        'blockquote': ['type'],\n        'td': ['colspan'],\n    },\n        escape=True):\n    if not isinstance(text, basestring):\n        return str(text)\n    return XssCleaner(permitted_tags=permitted_tags,\n                      allowed_attributes=allowed_attributes).strip(text, escape)\n", "path": "gluon/sanitizer.py"}], "after_files": [{"content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\"\"\"\n| From http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496942\n| Submitter: Josh Goldfoot (other recipes)\n| Last Updated: 2006/08/05\n| Version: 1.0\n\nCross-site scripting (XSS) defense\n-----------------------------------\n\"\"\"\n\nfrom ._compat import HTMLParser, urlparse, entitydefs, basestring\nfrom cgi import escape\nfrom formatter import AbstractFormatter\nfrom xml.sax.saxutils import quoteattr\n\n__all__ = ['sanitize']\n\n\ndef xssescape(text):\n    \"\"\"Gets rid of < and > and & and, for good measure, :\"\"\"\n\n    return escape(text, quote=True).replace(':', '&#58;')\n\n\nclass XssCleaner(HTMLParser):\n\n    def __init__(\n        self,\n        permitted_tags=[\n            'a',\n            'b',\n            'blockquote',\n            'br/',\n            'i',\n            'li',\n            'ol',\n            'ul',\n            'p',\n            'cite',\n            'code',\n            'pre',\n            'img/',\n        ],\n        allowed_attributes={'a': ['href', 'title'], 'img': ['src', 'alt'\n                                                            ], 'blockquote': ['type']},\n        strip_disallowed=False\n    ):\n\n        HTMLParser.__init__(self)\n        self.result = ''\n        self.open_tags = []\n        self.permitted_tags = [i for i in permitted_tags if i[-1] != '/']\n        self.requires_no_close = [i[:-1] for i in permitted_tags\n                                  if i[-1] == '/']\n        self.permitted_tags += self.requires_no_close\n        self.allowed_attributes = allowed_attributes\n\n        # The only schemes allowed in URLs (for href and src attributes).\n        # Adding \"javascript\" or \"vbscript\" to this list would not be smart.\n\n        self.allowed_schemes = ['http', 'https', 'ftp', 'mailto']\n\n        #to strip or escape disallowed tags?\n        self.strip_disallowed = strip_disallowed\n        # there might be data after final closing tag, that is to be ignored\n        self.in_disallowed = [False]\n\n    def handle_data(self, data):\n        if data and not self.in_disallowed[-1]:\n            self.result += xssescape(data)\n\n    def handle_charref(self, ref):\n        if self.in_disallowed[-1]:\n            return\n        elif len(ref) < 7 and (ref.isdigit() or ref == 'x27'): # x27 is a special case for apostrophe\n            self.result += '&#%s;' % ref\n        else:\n            self.result += xssescape('&#%s' % ref)\n\n    def handle_entityref(self, ref):\n        if self.in_disallowed[-1]:\n            return\n        elif ref in entitydefs:\n            self.result += '&%s;' % ref\n        else:\n            self.result += xssescape('&%s' % ref)\n\n    def handle_comment(self, comment):\n        if self.in_disallowed[-1]:\n            return\n        elif comment:\n            self.result += xssescape('<!--%s-->' % comment)\n\n    def handle_starttag(\n        self,\n        tag,\n        attrs\n    ):\n        if tag not in self.permitted_tags:\n            self.in_disallowed.append(True)\n            if (not self.strip_disallowed):\n                self.result += xssescape('<%s>' % tag)\n        else:\n            self.in_disallowed.append(False)\n            bt = '<' + tag\n            if tag in self.allowed_attributes:\n                attrs = dict(attrs)\n                self.allowed_attributes_here = [x for x in\n                                                self.allowed_attributes[tag] if x in attrs\n                                                and len(attrs[x]) > 0]\n                for attribute in self.allowed_attributes_here:\n                    if attribute in ['href', 'src', 'background']:\n                        if self.url_is_acceptable(attrs[attribute]):\n                            bt += ' %s=\"%s\"' % (attribute,\n                                                attrs[attribute])\n                    else:\n                        bt += ' %s=%s' % (xssescape(attribute),\n                                          quoteattr(attrs[attribute]))\n            # deal with <a> without href and <img> without src\n            if bt == '<a' or bt == '<img':\n                return\n            if tag in self.requires_no_close:\n                bt += ' /'\n            bt += '>'\n            self.result += bt\n            if tag not in self.requires_no_close: self.open_tags.insert(0, tag)\n\n    def handle_endtag(self, tag):\n        bracketed = '</%s>' % tag\n        self.in_disallowed and self.in_disallowed.pop()\n        if tag not in self.permitted_tags:\n            if (not self.strip_disallowed):\n                self.result += xssescape(bracketed)\n        elif tag in self.open_tags:\n            self.result += bracketed\n            self.open_tags.remove(tag)\n\n    def url_is_acceptable(self, url):\n        \"\"\"\n        Accepts relative, absolute, and mailto urls\n        \"\"\"\n\n        if url.startswith('#'):\n            return True\n        else:\n            parsed = urlparse(url)\n            return ((parsed[0] in self.allowed_schemes and '.' in parsed[1]) or\n                    (parsed[0] in self.allowed_schemes and '@' in parsed[2]) or\n                    (parsed[0] == '' and parsed[2].startswith('/')))\n\n    def strip(self, rawstring, escape=True):\n        \"\"\"\n        Returns the argument stripped of potentially harmful\n        HTML or Javascript code\n\n        @type escape: boolean\n        @param escape: If True (default) it escapes the potentially harmful\n          content, otherwise remove it\n        \"\"\"\n\n        if not isinstance(rawstring, str):\n            return str(rawstring)\n        for tag in self.requires_no_close:\n            rawstring = rawstring.replace(\"<%s/>\" % tag, \"<%s />\" % tag)\n        if not escape:\n            self.strip_disallowed = True\n        self.result = ''\n        self.feed(rawstring)\n        for endtag in self.open_tags:\n            if endtag not in self.requires_no_close:\n                self.result += '</%s>' % endtag\n        return self.result\n\n    def xtags(self):\n        \"\"\"\n        Returns a printable string informing the user which tags are allowed\n        \"\"\"\n\n        tg = ''\n        for x in sorted(self.permitted_tags):\n            tg += '<' + x\n            if x in self.allowed_attributes:\n                for y in self.allowed_attributes[x]:\n                    tg += ' %s=\"\"' % y\n            tg += '> '\n        return xssescape(tg.strip())\n\n\ndef sanitize(text, permitted_tags=[\n        'a',\n        'b',\n        'blockquote',\n        'br/',\n        'i',\n        'li',\n        'ol',\n        'ul',\n        'p',\n        'cite',\n        'code',\n        'pre',\n        'img/',\n        'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n        'table', 'tbody', 'thead', 'tfoot', 'tr', 'td', 'div',\n        'strong', 'span',\n],\n    allowed_attributes={\n        'a': ['href', 'title'],\n        'img': ['src', 'alt'],\n        'blockquote': ['type'],\n        'td': ['colspan'],\n    },\n        escape=True):\n    if not isinstance(text, basestring):\n        return str(text)\n    return XssCleaner(permitted_tags=permitted_tags,\n                      allowed_attributes=allowed_attributes).strip(text, escape)\n", "path": "gluon/sanitizer.py"}]}