Python: Port xml.sax tests

This commit is contained in:
Rasmus Wriedt Larsen
2022-03-03 20:57:04 +01:00
parent 5fb4c4d152
commit 0b12d91817
2 changed files with 47 additions and 104 deletions

View File

@@ -0,0 +1,47 @@
from io import StringIO
import xml.sax
x = "some xml"
class MainHandler(xml.sax.ContentHandler):
def __init__(self):
self._result = []
def characters(self, data):
self._result.append(data)
def parse(self, f):
xml.sax.parse(f, self) # $ MISSING: input=f vuln='Billion Laughs' vuln='Quadratic Blowup'
self._result
MainHandler().parse(StringIO(x))
parser = xml.sax.make_parser()
parser.parse(StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='Quadratic Blowup'
# You can make it vuln to both XXE and DTD retrieval by setting this flag
# see https://docs.python.org/3/library/xml.sax.handler.html#xml.sax.handler.feature_external_ges
parser = xml.sax.make_parser()
parser.setFeature(xml.sax.handler.feature_external_ges, True)
parser.parse(StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='DTD retrieval' vuln='Quadratic Blowup' vuln='XXE'
parser = xml.sax.make_parser()
parser.setFeature(xml.sax.handler.feature_external_ges, False)
parser.parse(StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='Quadratic Blowup'
# Forward Type Tracking test
def func(cond):
parser = xml.sax.make_parser()
if cond:
parser.setFeature(xml.sax.handler.feature_external_ges, True)
parser.parse(StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='DTD retrieval' vuln='Quadratic Blowup' vuln='XXE'
else:
parser.parse(StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='Quadratic Blowup'
# make it vuln, then making it safe
# a bit of an edge-case, but is nice to be able to handle.
parser = xml.sax.make_parser()
parser.setFeature(xml.sax.handler.feature_external_ges, True)
parser.setFeature(xml.sax.handler.feature_external_ges, False)
parser.parse(StringIO(x)) # $ input=StringIO(..) vuln='Billion Laughs' vuln='Quadratic Blowup'

View File

@@ -1,104 +0,0 @@
from flask import request, Flask
from io import StringIO
import xml.sax
app = Flask(__name__)
class MainHandler(xml.sax.ContentHandler):
def __init__(self):
self._result = []
def characters(self, data):
self._result.append(data)
def parse(self, f):
xml.sax.parse(f, self) # OK for XXE/DTD, NOT OK for billion laughs/quadratic
return self._result
# GOOD
@app.route("/MainHandler")
def mainHandler():
xml_content = request.args['xml_content']
return MainHandler().parse(StringIO(xml_content))
@app.route("/xml.sax.make_parser()+MainHandler")
def xml_makeparser_MainHandler():
xml_content = request.args['xml_content']
GoodHandler = MainHandler()
parser = xml.sax.make_parser()
parser.setContentHandler(GoodHandler)
parser.parse(StringIO(xml_content)) # OK for XXE/DTD, NOT OK for billion laughs/quadratic
return GoodHandler._result
@app.route("/xml.sax.make_parser()+MainHandler-xml.sax.handler.feature_external_ges_False")
def xml_makeparser_MainHandler_entitiesFalse():
xml_content = request.args['xml_content']
GoodHandler = MainHandler()
parser = xml.sax.make_parser()
parser.setContentHandler(GoodHandler)
# https://docs.python.org/3/library/xml.sax.handler.html#xml.sax.handler.feature_external_ges
parser.setFeature(xml.sax.handler.feature_external_ges, False)
parser.parse(StringIO(xml_content)) # # OK for XXE/DTD, NOT OK for billion laughs/quadratic
return GoodHandler._result
@app.route("not-user-controlled")
def not_user_controlled():
parser = xml.sax.make_parser()
parser.setFeature(xml.sax.handler.feature_external_ges, True)
parser.parse("/not-user-controlled/default_config.xml") # OK
return
# BAD
@app.route("/xml.sax.make_parser()+MainHandler-xml.sax.handler.feature_external_ges_True")
def xml_makeparser_MainHandler_entitiesTrue():
xml_content = request.args['xml_content']
BadHandler = MainHandler()
parser = xml.sax.make_parser()
parser.setContentHandler(BadHandler)
parser.setFeature(xml.sax.handler.feature_external_ges, True)
parser.parse(StringIO(xml_content)) # NOT OK for XXE/DTD, NOT OK for billion laughs/quadratic
return BadHandler._result
@app.route("/xml.sax.make_parser()+xml.dom.minidom.parse-xml.sax.handler.feature_external_ges_True")
def xml_makeparser_minidom_entitiesTrue():
xml_content = request.args['xml_content']
parser = xml.sax.make_parser()
parser.setFeature(xml.sax.handler.feature_external_ges, True)
doc = xml.dom.minidom.parse(StringIO(xml_content), parser=parser) # NOT OK for XXE/DTD, NOT OK for billion laughs/quadratic
return doc.documentElement.childNodes
# Forward Type Tracking test
@app.route("forward_tracking1")
def forward_tracking1(action):
xml_content = request.args['xml_content']
parser = xml.sax.make_parser()
if action == 'load-config':
parser.setFeature(xml.sax.handler.feature_external_ges, True)
parser.parse(StringIO(xml_content)) # NOT OK for XXE/DTD, NOT OK for billion laughs/quadratic
else:
parser.parse(StringIO(xml_content)) # OK for XXE/DTD, NOT OK for billion laughs/quadratic
return
@app.route("forward_tracking2")
def forward_tracking2(action):
xml_content = request.args['xml_content']
parser = xml.sax.make_parser()
parser.setFeature(xml.sax.handler.feature_external_ges, True)
parser.setFeature(xml.sax.handler.feature_external_ges, False)
parser.parse(StringIO(xml_content)) # OK for XXE/DTD, NOT OK for billion laughs/quadratic
return