diff --git a/bandit/plugins/xml.py b/bandit/plugins/xml.py
new file mode 100644
index 00000000..5f581944
--- /dev/null
+++ b/bandit/plugins/xml.py
@@ -0,0 +1,279 @@
+# -*- coding:utf-8 -*-
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# Most of this file is based off of Christian Heimes' work on defusedxml:
+# https://pypi.python.org/pypi/defusedxml/#defusedxml-sax
+
+import bandit
+from bandit.core.test_properties import *
+
+
+###############################################################################
+# check function calls
+###############################################################################
+@checks('Call')
+def etree_celement_function_calls(context):
+ if type(context.call_function_name_qual) == str:
+ qlist = context.call_function_name_qual.split('.')
+ qual = '.'.join(qlist[:-1])
+ func = qlist[-1]
+ blacklist = ['parse', 'iterparse', 'fromstring', 'XMLParser']
+ if 'xml.etree.cElementTree' == qual and func in blacklist:
+ s = ("Using xml.etree.cElementTree.%s to parse untrusted XML data"
+ " is known to be vulnerable to XML attacks. Replace "
+ " xml.etree.cElementTree.%s with defusedxml.cElementTree.%s"
+ " function.")
+ return bandit.Issue(
+ severity=bandit.HIGH,
+ confidence=bandit.MEDIUM,
+ text=s % (func, func, func))
+
+
+@checks('Call')
+def etree_element_function_calls(context):
+ if type(context.call_function_name_qual) == str:
+ qlist = context.call_function_name_qual.split('.')
+ qual = '.'.join(qlist[:-1])
+ func = qlist[-1]
+ blacklist = ['parse', 'iterparse', 'fromstring', 'XMLParser']
+ if 'xml.etree.ElementTree' == qual and func in blacklist:
+ s = ("Using xml.etree.ElementTree.%s to parse untrusted XML data"
+ " is known to be vulnerable to XML attacks. Replace"
+ " xml.etree.ElementTree.%s with defusedxml.ElementTree.%s"
+ " function.")
+ return bandit.Issue(
+ severity=bandit.HIGH,
+ confidence=bandit.MEDIUM,
+ text=s % (func, func, func))
+
+
+@checks('Call')
+def expatreader_function_calls(context):
+ if type(context.call_function_name_qual) == str:
+ qlist = context.call_function_name_qual.split('.')
+ qual = '.'.join(qlist[:-1])
+ func = qlist[-1]
+ blacklist = ['create_parser']
+ if 'xml.sax.expatreader' == qual and func in blacklist:
+ s = ("Using xml.sax.expatreader.%s to parse untrusted XML data is"
+ " known to be vulnerable to XML attacks. Replace"
+ " xml.sax.expatreader.%s with defusedxml.expatreader.%s"
+ " function.")
+ return bandit.Issue(
+ severity=bandit.HIGH,
+ confidence=bandit.MEDIUM,
+ text=s % (func, func, func))
+
+
+@checks('Call')
+def expatbuilder_function_calls(context):
+ if type(context.call_function_name_qual) == str:
+ qlist = context.call_function_name_qual.split('.')
+ qual = '.'.join(qlist[:-1])
+ func = qlist[-1]
+ blacklist = ['parse', 'parseString']
+ if 'xml.dom.expatbuilder' == qual and func in blacklist:
+ s = ("Using xml.dom.expatbuilder.%s to parse untrusted XML data"
+ " is known to be vulnerable to XML attacks. Replace"
+ " xml.dom.expatbuilder.%s with defusedxml.expatbuilder.%s"
+ " function.")
+ return bandit.Issue(
+ severity=bandit.HIGH,
+ confidence=bandit.MEDIUM,
+ text=s % (func, func, func))
+
+
+@checks('Call')
+def sax_function_calls(context):
+ if type(context.call_function_name_qual) == str:
+ qlist = context.call_function_name_qual.split('.')
+ qual = '.'.join(qlist[:-1])
+ func = qlist[-1]
+ blacklist = ['parse', 'parseString', 'make_parser']
+ if 'xml.sax' == qual and func in blacklist:
+ s = ("Using xml.sax.%s to parse untrusted XML data is known to"
+ " be vulnerable to XML attacks. Replace xml.sax.%s with"
+ " defusedxml.sax.%s function.")
+ return bandit.Issue(
+ severity=bandit.HIGH,
+ confidence=bandit.MEDIUM,
+ text=s % (func, func, func))
+
+
+@checks('Call')
+def minidom_function_calls(context):
+ if type(context.call_function_name_qual) == str:
+ qlist = context.call_function_name_qual.split('.')
+ qual = '.'.join(qlist[:-1])
+ func = qlist[-1]
+ blacklist = ['parse', 'parseString']
+ if 'xml.dom.minidom' == qual and func in blacklist:
+ s = ("Using xml.dom.minidom.%s to parse untrusted XML data is"
+ " known to be vulnerable to XML attacks. Replace"
+ " xml.dom.minidom.%s with defusedxml.minidom.%s"
+ " function.")
+ return bandit.Issue(
+ severity=bandit.HIGH,
+ confidence=bandit.MEDIUM,
+ text=s % (func, func, func))
+
+
+@checks('Call')
+def pulldom_function_calls(context):
+ if type(context.call_function_name_qual) == str:
+ qlist = context.call_function_name_qual.split('.')
+ qual = '.'.join(qlist[:-1])
+ func = qlist[-1]
+ blacklist = ['parse', 'parseString']
+ if 'xml.dom.pulldom' == qual and func in blacklist:
+ s = ("Using xml.dom.pulldom.%s to parse untrusted XML data is"
+ " known to be vulnerable to XML attacks. Replace"
+ " xml.dom.pulldom.%s with defusedxml.pulldom.%s function.")
+ return bandit.Issue(
+ severity=bandit.HIGH,
+ confidence=bandit.MEDIUM,
+ text=s % (func, func, func))
+
+
+@checks('Call')
+def lxml_function_calls(context):
+ if type(context.call_function_name_qual) == str:
+ qlist = context.call_function_name_qual.split('.')
+ qual = '.'.join(qlist[:-1])
+ func = qlist[-1]
+ blacklist = ['parse', 'fromstring', 'RestrictedElement',
+ 'GlobalParserTLS', 'getDefaultParser', 'check_docinfo']
+ if 'lxml.etree' == qual and func in blacklist:
+ s = ("Using lxml.etree.%s to parse untrusted XML data is"
+ " known to be vulnerable to XML attacks. Replace"
+ " lxml.etree.%s with defused.lxml.%s function.")
+ return bandit.Issue(
+ severity=bandit.HIGH,
+ confidence=bandit.MEDIUM,
+ text=s % (func, func, func))
+
+
+###############################################################################
+# check imports
+###############################################################################
+@checks('Import', 'ImportFrom')
+def etree_celement_import(context):
+ if context.is_module_being_imported('xml.etree.cElementTree'):
+ s = ("Using xml.etree.cElementTree to parse untrusted XML data is"
+ " known to be vulnerable to XML attacks. Replace"
+ " xml.etree.cElementTree with defusedxml.cElementTree package.")
+ return bandit.Issue(
+ severity=bandit.LOW,
+ confidence=bandit.HIGH,
+ text=s)
+
+
+@checks('Import', 'ImportFrom')
+def etree_element_import(context):
+ if context.is_module_being_imported('xml.etree.ElementTree'):
+ s = ("Using xml.etree.ElementTree to parse untrusted XML data is"
+ " known to be vulnerable to XML attacks. Replace"
+ " xml.etree.ElementTree with defusedxml.ElementTree package.")
+ return bandit.Issue(
+ severity=bandit.LOW,
+ confidence=bandit.HIGH,
+ text=s)
+
+
+@checks('Import', 'ImportFrom')
+def expatreader_import(context):
+ if context.is_module_being_imported('xml.sax.expatreader'):
+ s = ("Using xml.sax.expatreader to parse untrusted XML data is known"
+ " to be vulnerable to XML attacks. Replace xml.sax.expatreader"
+ " with defusedxml.expatreader package.")
+ return bandit.Issue(
+ severity=bandit.LOW,
+ confidence=bandit.HIGH,
+ text=s)
+
+
+@checks('Import', 'ImportFrom')
+def sax_import(context):
+ if context.is_module_being_imported('xml.sax'):
+ s = ("Using xml.sax to parse untrusted XML data is known to be"
+ " vulnerable to XML attacks. Replace xml.sax with defusedxml.sax"
+ " package.")
+ return bandit.Issue(
+ severity=bandit.LOW,
+ confidence=bandit.HIGH,
+ text=s)
+
+
+@checks('Import', 'ImportFrom')
+def expatbuilder_import(context):
+ if context.is_module_being_imported('xml.dom.expatbuilder'):
+ s = ("Using xml.dom.expatbuilder to parse untrusted XML data is known"
+ " to be vulnerable to XML attacks. Replace xml.dom.expatbuilder"
+ " with defusedxml.expatbuilder package.")
+ return bandit.Issue(
+ severity=bandit.LOW,
+ confidence=bandit.HIGH,
+ text=s)
+
+
+@checks('Import', 'ImportFrom')
+def minidom_import(context):
+ if context.is_module_being_imported('xml.dom.minidom'):
+ s = ("Using xml.dom.minidom to parse untrusted XML data is known to be"
+ " vulnerable to XML attacks. Replace xml.dom.minidom with"
+ " defusedxml.minidom package.")
+ return bandit.Issue(
+ severity=bandit.LOW,
+ confidence=bandit.HIGH,
+ text=s)
+
+
+@checks('Import', 'ImportFrom')
+def pulldom_import(context):
+ if context.is_module_being_imported('xml.dom.pulldom'):
+ s = ("Using xml.dom.pulldom to parse untrusted XML data is known to be"
+ " vulnerable to XML attacks. Replace xml.dom.pulldom with"
+ " defusedxml.pulldom package.")
+ return bandit.Issue(
+ severity=bandit.LOW,
+ confidence=bandit.HIGH,
+ text=s)
+
+
+# this one is 'HIGH' instead of 'LOW' because we know the entire package has
+# to be monkeypatched via defusedxml.xmlrpc.monkey_patch()
+@checks('Import', 'ImportFrom')
+def xmlrpclib_import(context):
+ if context.is_module_being_imported('xmlrpclib'):
+ s = ("Using xmlrpclib to parse untrusted XML data is known to be"
+ " vulnerable to XML attacks. Use defused.xmlrpc.monkey_patch()"
+ " function to monkey-patch xmlrpclib and mitigate XML"
+ " vulnerabilities.")
+ return bandit.Issue(
+ severity=bandit.HIGH,
+ confidence=bandit.HIGH,
+ text=s)
+
+
+@checks('Import', 'ImportFrom')
+def lxml_import(context):
+ if(context.is_module_being_imported('lxml.etree') or
+ context.is_module_being_imported('lxml')):
+ s = ("Using lxml.etree to parse untrusted XML data is known to be"
+ " vulnerable to XML attacks. Replace lxml.etree with"
+ " defused.lxml package.")
+ return bandit.Issue(
+ severity=bandit.LOW,
+ confidence=bandit.HIGH,
+ text=s)
diff --git a/docs/xml.md b/docs/xml.md
new file mode 100644
index 00000000..1768094a
--- /dev/null
+++ b/docs/xml.md
@@ -0,0 +1,219 @@
+Use safe XML libraries to avoid XML vulnerabilities
+=====================
+XML vulnerabilities are known and well studied. The [defusedxml](https://pypi.python.org/pypi/defusedxml/) library provides a great synposis of XML vulnerabilities, how they're exploited, and which Python libraries are vulnerable to which attacks.
+
+Most XML vulnerabilities essentially amount to Denial of Service attacks but as [previous blackhat presentations](https://media.blackhat.com/eu-13/briefings/Osipov/bh-eu-13-XML-data-osipov-slides.pdf) have shown, XML vulnerabilities can lead to local file reading, intranet access, and some times remote code execution.
+
+We don't attempt to rehash the details of each vulnerability class and instead recommend those interested read [defuxedxml](https://pypi.python.org/pypi/defusedxml/)'s page, including references.
+
+### Incorrect
+Currently, the following Python XML libraries are vulnerable to some form of XML attack:
+* [xml.sax](https://docs.python.org/2/library/xml.sax.html)
+ - vulnerable to: billion laughs, quadratic blowup, external entity expansion, DTD retrieval
+* [xml.etree.ElementTree](https://docs.python.org/2/library/xml.etree.elementtree.html)
+ - vulnerable to: billion laughs, quadratic blowup
+* [xml.dom.minidom](https://docs.python.org/2/library/xml.dom.minidom.html)
+ - vulnerable to: billion laughs, quadratic blowup
+* [xml.dom.pulldom](https://docs.python.org/2/library/xml.dom.pulldom.html)
+ - vulnerable to: billion laughs, quadratic blowup, external entity expansion, DTD retrieval
+* [xmlrpclib](https://docs.python.org/2/library/xmlrpclib.html)
+ - vulnerable to: billion laughs, quadratic blowup, decompression bomb
+
+[Python's XML library page](https://docs.python.org/2/library/xml.html#xml-vulnerabilities) indicates that [defusedxml](https://pypi.python.org/pypi/defusedxml/) is the correct choice for XML libraries.
+
+### Correct
+#### xml.sax
+Replace all xml.sax parsers with defusedxml parsers:
+* ```xml.sax.parser()``` -> ```defusedxml.sax.parser()```
+* ```xml.sax.parseString()``` -> ```defusedxml.sax.parseString()```
+* ```xml.sax.create_parser()``` -> ```defusedxml.sax.parseString()```
+
+Intead of this:
+```python
+ import xml.sax
+
+ class ExampleContentHandler(xml.sax.ContentHandler):
+ def __init__(self):
+ xml.sax.ContentHandler.__init__(self)
+
+ def startElement(self, name, attrs):
+ print 'start:', name
+
+ def endElement(self, name):
+ print 'end:', name
+
+ def characters(self, content):
+ print 'chars:', content
+
+ def main():
+ xml.sax.parse(open('input.xml'), ExampleContentHandler())
+
+ if __name__ == "__main__":
+ main()
+```
+
+Do this:
+```python
+ import xml.sax
+ import defusedxml.sax
+
+ class ExampleContentHandler(xml.sax.ContentHandler):
+ def __init__(self):
+ xml.sax.ContentHandler.__init__(self)
+
+ def startElement(self, name, attrs):
+ print 'start:', name
+
+ def endElement(self, name):
+ print 'end:', name
+
+ def characters(self, content):
+ print 'chars:', content
+
+ def main():
+ defusedxml.sax.parse(open('input.xml'), ExampleContentHandler())
+
+ if __name__ == "__main__":
+ main()
+```
+
+#### xml.etree.ElementTree
+Replace the following instances of xml.etree.ElementTree functions with the corresponding defusedxml functions:
+* ```xml.etree.ElementTree.parse()``` -> ```defusedxml.ElementTree.parse()```
+* ```xml.etree.ElementTree.iterparse()``` -> ```defusedxml.ElementTree.iterparse()```
+* ```xml.etree.ElementTree.fromstring()``` -> ```defusedxml.ElementTree.fromstring()```
+* ```xml.etree.ElementTree.XMLParser``` -> ```defusedxml.ElementTree.XMLParser```
+
+Intead of this:
+```python
+ import xml.etree.ElementTree as ET
+ tree = ET.parse("input.xml")
+ root = tree.getroot()
+```
+
+Do this:
+```python
+ import defusedxml.ElementTree as ET
+ tree = ET.parse("input.xml")
+ root = tree.getroot()
+```
+
+#### xml.etree.cElementTree
+Replace the following instances of xml.etree.cElementTree functions with the corresponding defusedxml functions:
+* ```xml.etree.cElementTree.parse()``` -> ```defusedxml.cElementTree.parse()```
+* ```xml.etree.cElementTree.iterparse()``` -> ```defusedxml.cElementTree.iterparse()```
+* ```xml.etree.cElementTree.fromstring()``` -> ```defusedxml.cElementTree.fromstring()```
+* ```xml.etree.cElementTree.XMLParser``` -> ```defusedxml.cElementTree.XMLParser```
+
+Intead of this:
+```python
+ import xml.etree.cElementTree as ET
+ tree = ET.parse("input.xml")
+ root = tree.getroot()
+```
+
+Do this:
+```python
+ import defusedxml.cElementTree as ET
+ tree = ET.parse("input.xml")
+ root = tree.getroot()
+```
+
+#### xml.dom.minidom
+Replace the following instances of xml.dom.minidom functions with the corresponding defusedxml functions:
+* ```xml.dom.minidom.parse()``` -> ```defusedxml.minidom.parse()```
+* ```xml.dom.minidom.parseString()``` -> ```defusedxml.minidom.parseString()```
+
+Intead of this:
+```python
+ from xml.dom.minidom import parseString
+ parseString('Some data some more data')
+```
+
+Do this:
+```python
+ from defusedxml.minidom import parseString
+ parseString('Some data some more data')
+```
+
+#### xml.dom.pulldom
+Replace the following instances of xml.dom.pulldom functions with the corresponding defusedxml functions:
+* ```xml.dom.pulldom.parse()``` -> ```defusedxml.pulldom.parse()```
+* ```xml.dom.pulldom.parseString()``` -> ```defusedxml.pulldom.parseString()```
+
+Intead of this:
+```python
+ from xml.dom.pulldom import parseString
+ parseString('Some data some more data')
+```
+
+Do this:
+```python
+ from defusedxml.pulldom import parseString
+ parseString('Some data some more data')
+```
+
+#### xmlrpclib
+Taken directly from the defusedxml page:
+"The function monkey_patch() enables the fixes, unmonkey_patch() removes the patch and puts the code in its former state."
+
+Intead of this:
+```python
+ from xmlrpclib import ServerProxy, Error
+
+ server = ServerProxy("http://betty.userland.com")
+
+ print server
+
+ try:
+ print server.examples.getStateName(41)
+ except Error as v:
+ print "ERROR", v
+```
+
+Do this:
+```python
+ from xmlrpclib import ServerProxy, Error
+ import defusedxml.xmlrpc
+
+ defusedxml.xmlrpc.monkey_patch()
+
+ server = ServerProxy("http://betty.userland.com")
+
+ print server
+
+ try:
+ print server.examples.getStateName(41)
+ except Error as v:
+ print "ERROR", v
+```
+
+#### lxml.etree
+Replace the following instances of lxml functions with the corresponding defusedxml functions:
+* ```lxml.etree.parse()``` -> ```defusedxml.lxml.parse```
+* ```lxml.etree.fromstring()``` -> ```defusedxml.lxml.fromstring()```
+* ```lxml.etree.RestrictedElement()``` -> ```defusedxml.lxml.RestrictedElement()```
+* ```lxml.etree.getDefaultParser()``` -> ```defusedxml.lxml.getDefaultParser()```
+* ```lxml.etree.check_docinfo()``` -> ```defusedxml.lxml.check_docinfo()```
+
+Intead of this:
+```python
+ from lxml import etree
+ root = etree.parse('input.xml')
+```
+
+Do this:
+```python
+ from defusedxml.lxml import parse
+ root = parse('input.xml')
+
+```
+## References
+* https://pypi.python.org/pypi/defusedxml/
+* https://media.blackhat.com/eu-13/briefings/Osipov/bh-eu-13-XML-data-osipov-slides.pdf
+* https://docs.python.org/2/library/xml.sax.html
+* https://docs.python.org/2/library/xml.etree.elementtree.html
+* https://docs.python.org/2/library/xml.dom.minidom.html
+* https://docs.python.org/2/library/xml.dom.pulldom.html
+* https://docs.python.org/2/library/xmlrpclib.html
+* https://docs.python.org/2/library/xml.html#xml-vulnerabilities
diff --git a/examples/xml_etree_celementtree.py b/examples/xml_etree_celementtree.py
new file mode 100644
index 00000000..48fb4e4d
--- /dev/null
+++ b/examples/xml_etree_celementtree.py
@@ -0,0 +1,18 @@
+import xml.etree.cElementTree as badET
+import defusedxml.cElementTree as goodET
+
+xmlString = "\nTove\nJani\nReminder\nDon't forget me this weekend!\n"
+
+# unsafe
+tree = badET.fromstring(xmlString)
+print tree
+badET.parse('filethatdoesntexist.xml')
+badET.iterparse('filethatdoesntexist.xml')
+a = badET.XMLParser()
+
+# safe
+tree = goodET.fromstring(xmlString)
+print tree
+goodET.parse('filethatdoesntexist.xml')
+goodET.iterparse('filethatdoesntexist.xml')
+a = goodET.XMLParser()
diff --git a/examples/xml_etree_elementtree.py b/examples/xml_etree_elementtree.py
new file mode 100644
index 00000000..930790ed
--- /dev/null
+++ b/examples/xml_etree_elementtree.py
@@ -0,0 +1,18 @@
+import xml.etree.ElementTree as badET
+import defusedxml.ElementTree as goodET
+
+xmlString = "\nTove\nJani\nReminder\nDon't forget me this weekend!\n"
+
+# unsafe
+tree = badET.fromstring(xmlString)
+print tree
+badET.parse('filethatdoesntexist.xml')
+badET.iterparse('filethatdoesntexist.xml')
+a = badET.XMLParser()
+
+# safe
+tree = goodET.fromstring(xmlString)
+print tree
+goodET.parse('filethatdoesntexist.xml')
+goodET.iterparse('filethatdoesntexist.xml')
+a = goodET.XMLParser()
diff --git a/examples/xml_expatbuilder.py b/examples/xml_expatbuilder.py
new file mode 100644
index 00000000..1deb8575
--- /dev/null
+++ b/examples/xml_expatbuilder.py
@@ -0,0 +1,10 @@
+import xml.dom.expatbuilder as bad
+import defusedxml.expatbuilder as good
+
+bad.parse('filethatdoesntexist.xml')
+good.parse('filethatdoesntexist.xml')
+
+xmlString = "\nTove\nJani\nReminder\nDon't forget me this weekend!\n"
+
+bad.parseString(xmlString)
+good.parseString(xmlString)
diff --git a/examples/xml_expatreader.py b/examples/xml_expatreader.py
new file mode 100644
index 00000000..12e6f965
--- /dev/null
+++ b/examples/xml_expatreader.py
@@ -0,0 +1,5 @@
+import xml.sax.expatreader as bad
+import defusedxml.expatreader as good
+
+p = bad.create_parser()
+b = good.create_parser()
diff --git a/examples/xml_lxml.py b/examples/xml_lxml.py
new file mode 100644
index 00000000..dd12e538
--- /dev/null
+++ b/examples/xml_lxml.py
@@ -0,0 +1,9 @@
+import lxml.etree
+import lxml
+from lxml import etree
+from defusedxml.lxml import fromstring
+from defuxedxml import lxml as potatoe
+
+xmlString = "\nTove\nJani\nReminder\nDon't forget me this weekend!\n"
+root = lxml.etree.fromstring(xmlString)
+root = fromstring(xmlString)
diff --git a/examples/xml_minidom.py b/examples/xml_minidom.py
new file mode 100644
index 00000000..48fd6cc3
--- /dev/null
+++ b/examples/xml_minidom.py
@@ -0,0 +1,14 @@
+from xml.dom.minidom import parseString as badParseString
+from defusedxml.minidom import parseString as goodParseString
+a = badParseString("Some data some more data")
+print a
+b = goodParseString("Some data some more data")
+print b
+
+
+from xml.dom.minidom import parse as badParse
+from defusedxml.minidom import parse as goodParse
+a = badParse("somfilethatdoesntexist.xml")
+print a
+b = goodParse("somefilethatdoesntexist.xml")
+print b
diff --git a/examples/xml_pulldom.py b/examples/xml_pulldom.py
new file mode 100644
index 00000000..c0638590
--- /dev/null
+++ b/examples/xml_pulldom.py
@@ -0,0 +1,14 @@
+from xml.dom.pulldom import parseString as badParseString
+from defusedxml.pulldom import parseString as goodParseString
+a = badParseString("Some data some more data")
+print a
+b = goodParseString("Some data some more data")
+print b
+
+
+from xml.dom.pulldom import parse as badParse
+from defusedxml.pulldom import parse as goodParse
+a = badParse("somfilethatdoesntexist.xml")
+print a
+b = goodParse("somefilethatdoesntexist.xml")
+print b
diff --git a/examples/xml_sax.py b/examples/xml_sax.py
new file mode 100644
index 00000000..b2508022
--- /dev/null
+++ b/examples/xml_sax.py
@@ -0,0 +1,37 @@
+import xml.sax
+from xml import sax
+import defusedxml.sax
+
+class ExampleContentHandler(xml.sax.ContentHandler):
+ def __init__(self):
+ xml.sax.ContentHandler.__init__(self)
+
+ def startElement(self, name, attrs):
+ print 'start:', name
+
+ def endElement(self, name):
+ print 'end:', name
+
+ def characters(self, content):
+ print 'chars:', content
+
+def main():
+ xmlString = "\nTove\nJani\nReminder\nDon't forget me this weekend!\n"
+ # bad
+ xml.sax.parseString(xmlString, ExampleContentHandler())
+ xml.sax.parse('notaxmlfilethatexists.xml', ExampleContentHandler())
+ sax.parseString(xmlString, ExampleContentHandler())
+ sax.parse('notaxmlfilethatexists.xml', ExampleContentHandler)
+
+ # good
+ defusedxml.sax.parseString(xmlString, ExampleContentHandler())
+
+ # bad
+ xml.sax.make_parser()
+ sax.make_parser()
+ print 'nothing'
+ # good
+ defusedxml.sax.make_parser()
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/xml_xmlrpc.py b/examples/xml_xmlrpc.py
new file mode 100644
index 00000000..594ad67f
--- /dev/null
+++ b/examples/xml_xmlrpc.py
@@ -0,0 +1,10 @@
+import xmlrpclib
+from SimpleXMLRPCServer import SimpleXMLRPCServer
+
+def is_even(n):
+ return n%2 == 0
+
+server = SimpleXMLRPCServer(("localhost", 8000))
+print "Listening on port 8000..."
+server.register_function(is_even, "is_even")
+server.serve_forever()
diff --git a/tests/test_functional.py b/tests/test_functional.py
index 4a4a7432..9f6773e7 100644
--- a/tests/test_functional.py
+++ b/tests/test_functional.py
@@ -68,6 +68,8 @@ class FunctionalTests(unittest.TestCase):
:param example_script: Filename of an example script to test
:param expect: dict with expected counts of issue types
'''
+ # reset scores for subsequent calls to check_example
+ self.b_mgr.scores = []
self.run_example(example_script)
expected = 0
result = 0
@@ -302,3 +304,41 @@ class FunctionalTests(unittest.TestCase):
'''Test Mako templates for XSS.'''
expect = {'SEVERITY': {'MEDIUM': 3}, 'CONFIDENCE': {'HIGH': 3}}
self.check_example('mako_templating.py', expect)
+
+ def test_xml(self):
+ '''Test xml vulnerabilities.'''
+ expect = {'SEVERITY': {'LOW': 1, 'HIGH': 4},
+ 'CONFIDENCE': {'HIGH': 1, 'MEDIUM': 4}}
+ self.check_example('xml_etree_celementtree.py', expect)
+
+ expect = {'SEVERITY': {'LOW': 1, 'HIGH': 2},
+ 'CONFIDENCE': {'HIGH': 1, 'MEDIUM': 2}}
+ self.check_example('xml_expatbuilder.py', expect)
+
+ expect = {'SEVERITY': {'LOW': 3, 'HIGH': 1},
+ 'CONFIDENCE': {'HIGH': 3, 'MEDIUM': 1}}
+ self.check_example('xml_lxml.py', expect)
+
+ expect = {'SEVERITY': {'LOW': 2, 'HIGH': 2},
+ 'CONFIDENCE': {'HIGH': 2, 'MEDIUM': 2}}
+ self.check_example('xml_pulldom.py', expect)
+
+ expect = {'SEVERITY': {'HIGH': 1},
+ 'CONFIDENCE': {'HIGH': 1}}
+ self.check_example('xml_xmlrpc.py', expect)
+
+ expect = {'SEVERITY': {'LOW': 1, 'HIGH': 4},
+ 'CONFIDENCE': {'HIGH': 1, 'MEDIUM': 4}}
+ self.check_example('xml_etree_elementtree.py', expect)
+
+ expect = {'SEVERITY': {'LOW': 1, 'HIGH': 1},
+ 'CONFIDENCE': {'HIGH': 1, 'MEDIUM': 1}}
+ self.check_example('xml_expatreader.py', expect)
+
+ expect = {'SEVERITY': {'LOW': 2, 'HIGH': 2},
+ 'CONFIDENCE': {'HIGH': 2, 'MEDIUM': 2}}
+ self.check_example('xml_minidom.py', expect)
+
+ expect = {'SEVERITY': {'LOW': 1, 'HIGH': 6},
+ 'CONFIDENCE': {'HIGH': 1, 'MEDIUM': 6}}
+ self.check_example('xml_sax.py', expect)