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)