77import re
88import sys
99import sysconfig
10+ import textwrap
1011import unittest
1112import traceback
1213from io import BytesIO
@@ -834,24 +835,42 @@ class AttackProtectionTestBase(abc.ABC):
834835 """
835836
836837 @staticmethod
837- def exponential_expansion_payload (ncols , nrows , text = '.' ):
838+ def exponential_expansion_payload (* , nrows , ncols , text = '.' ):
838839 """Create a billion laughs attack payload.
839840
840841 Be careful: the number of total items is pow(n, k), thereby
841842 requiring at least pow(ncols, nrows) * sizeof(text) memory!
842843 """
844+ template = textwrap .dedent (f"""\
845+ <?xml version="1.0"?>
846+ <!DOCTYPE doc [
847+ <!ENTITY row0 "{ text } ">
848+ <!ELEMENT doc (#PCDATA)>
849+ {{body}}
850+ ]>
851+ <doc>&row{ nrows } ;</doc>
852+ """ ).rstrip ()
853+
843854 body = '\n ' .join (
844855 f'<!ENTITY row{ i + 1 } "{ f"&row{ i } ;" * ncols } ">'
845856 for i in range (nrows )
846857 )
847- return f"""\
848- <?xml version="1.0"?>
849- <!DOCTYPE doc [
850- <!ENTITY row0 "{ text } ">
851- <!ELEMENT doc (#PCDATA)>
852- { body }
853- ]>
854- <doc>&row{ nrows } ;</doc>"""
858+ body = textwrap .indent (body , ' ' * 4 )
859+ return template .format (body = body )
860+
861+ def test_payload_generation (self ):
862+ # self-test for exponential_expansion_payload()
863+ payload = self .exponential_expansion_payload (nrows = 2 , ncols = 3 )
864+ self .assertEqual (payload , textwrap .dedent ("""\
865+ <?xml version="1.0"?>
866+ <!DOCTYPE doc [
867+ <!ENTITY row0 ".">
868+ <!ELEMENT doc (#PCDATA)>
869+ <!ENTITY row1 "&row0;&row0;&row0;">
870+ <!ENTITY row2 "&row1;&row1;&row1;">
871+ ]>
872+ <doc>&row2;</doc>
873+ """ ).rstrip ())
855874
856875 def assert_root_parser_failure (self , func , / , * args , ** kwargs ):
857876 """Check that func(*args, **kwargs) is invalid for a sub-parser."""
@@ -966,7 +985,7 @@ def test_set_activation_threshold__threshold_reached(self):
966985 # Check that the threshold is reached by choosing a small factor
967986 # and a payload whose peak amplification factor exceeds it.
968987 self .assertIsNone (self .set_maximum_amplification (parser , 1.0 ))
969- payload = self .exponential_expansion_payload (10 , 4 )
988+ payload = self .exponential_expansion_payload (ncols = 10 , nrows = 4 )
970989 self .assert_rejected (parser .Parse , payload , True )
971990
972991 def test_set_activation_threshold__threshold_not_reached (self ):
@@ -976,7 +995,7 @@ def test_set_activation_threshold__threshold_not_reached(self):
976995 # Check that the threshold is reached by choosing a small factor
977996 # and a payload whose peak amplification factor exceeds it.
978997 self .assertIsNone (self .set_maximum_amplification (parser , 1.0 ))
979- payload = self .exponential_expansion_payload (10 , 4 )
998+ payload = self .exponential_expansion_payload (ncols = 10 , nrows = 4 )
980999 self .assertIsNotNone (parser .Parse (payload , True ))
9811000
9821001 def test_set_maximum_amplification__amplification_exceeded (self ):
@@ -986,7 +1005,7 @@ def test_set_maximum_amplification__amplification_exceeded(self):
9861005 # Choose a max amplification factor expected to always be exceeded.
9871006 self .assertIsNone (self .set_maximum_amplification (parser , 1.0 ))
9881007 # Craft a payload for which the peak amplification factor is > 1.0.
989- payload = self .exponential_expansion_payload (1 , 2 )
1008+ payload = self .exponential_expansion_payload (ncols = 1 , nrows = 2 )
9901009 self .assert_rejected (parser .Parse , payload , True )
9911010
9921011 def test_set_maximum_amplification__amplification_not_exceeded (self ):
@@ -996,7 +1015,7 @@ def test_set_maximum_amplification__amplification_not_exceeded(self):
9961015 # Choose a max amplification factor expected to never be exceeded.
9971016 self .assertIsNone (self .set_maximum_amplification (parser , 1e4 ))
9981017 # Craft a payload for which the peak amplification factor is < 1e4.
999- payload = self .exponential_expansion_payload (1 , 2 )
1018+ payload = self .exponential_expansion_payload (ncols = 1 , nrows = 2 )
10001019 self .assertIsNotNone (parser .Parse (payload , True ))
10011020
10021021
0 commit comments