|
20 | 20 |
|
21 | 21 |
|
22 | 22 | def replace_unicode(data): |
| 23 | + """Helper function to replace all valid unicode/hex escapes.""" |
23 | 24 |
|
24 | 25 | def replacer(match): |
25 | | - hex_code = match.group(1) |
26 | | - code_point = int(hex_code, 16) |
27 | | - # JS engines require UTF-8 encoded input. |
28 | | - return chr(code_point).encode('utf-8') |
29 | | - |
30 | | - pattern = rb'\\u([0-9a-fA-F]{4})' |
| 26 | + hex_code = match.group(1) or match.group(2) or match.group(3) |
| 27 | + if not hex_code: |
| 28 | + return match.group(0) |
| 29 | + |
| 30 | + try: |
| 31 | + code_point = int(hex_code, 16) |
| 32 | + # JS engines require UTF-8 encoded input. |
| 33 | + # This will fail for code_point > 0x10FFFF |
| 34 | + return chr(code_point).encode('utf-8') |
| 35 | + except (ValueError, OverflowError): |
| 36 | + # If it's an invalid code point, return the original escape. |
| 37 | + return match.group(0) |
| 38 | + |
| 39 | + pattern = rb'\\u\{([0-9a-fA-F]+)\}|\\u([0-9a-fA-F]{4})|\\x([0-9a-fA-F]{2})' |
31 | 40 | return re.sub(pattern, replacer, data) |
32 | 41 |
|
33 | 42 |
|
@@ -108,3 +117,74 @@ def test_minimize_not_buggy_unicode_escape_multi_unicode(self): |
108 | 117 |
|
109 | 118 | self.assertEqual( |
110 | 119 | result, b'text E text G some text H some other text I yet another text') |
| 120 | + |
| 121 | + def test_minimize_buggy_hex_escape(self): |
| 122 | + """Program crashes because of \\x41.""" |
| 123 | + data = b'text \\x41 text' # \x41 = 'A' |
| 124 | + self.crash_programs.append(data) |
| 125 | + self.no_crash_programs.append(replace_unicode(data)) # b'text A text' |
| 126 | + |
| 127 | + result = self._minimizer.minimize(data) |
| 128 | + self.assertEqual(result, b'text \\x41 text') |
| 129 | + |
| 130 | + def test_minimize_not_buggy_hex_escape(self): |
| 131 | + """Program crashes, but not because of \\x41.""" |
| 132 | + data = b'text \\x41 text' |
| 133 | + self.crash_programs.append(data) |
| 134 | + self.crash_programs.append(replace_unicode(data)) # b'text A text' |
| 135 | + |
| 136 | + result = self._minimizer.minimize(data) |
| 137 | + self.assertEqual(result, b'text A text') |
| 138 | + |
| 139 | + def test_minimize_buggy_extended_unicode_escape(self): |
| 140 | + """Program crashes because of \\u{42}.""" |
| 141 | + data = b'text \\u{42} text' # \u{42} = 'B' |
| 142 | + self.crash_programs.append(data) |
| 143 | + self.no_crash_programs.append(replace_unicode(data)) # b'text B text' |
| 144 | + |
| 145 | + result = self._minimizer.minimize(data) |
| 146 | + self.assertEqual(result, b'text \\u{42} text') |
| 147 | + |
| 148 | + def test_minimize_not_buggy_extended_unicode_escape(self): |
| 149 | + """Program crashes, but not because of \\u{42}.""" |
| 150 | + data = b'text \\u{42} text' |
| 151 | + self.crash_programs.append(data) |
| 152 | + self.crash_programs.append(replace_unicode(data)) # b'text B text' |
| 153 | + |
| 154 | + result = self._minimizer.minimize(data) |
| 155 | + self.assertEqual(result, b'text B text') |
| 156 | + |
| 157 | + def test_minimize_buggy_all_escapes(self): |
| 158 | + """Program crashes because of one of the mixed escapes.""" |
| 159 | + data = b'A \\u0041 B \\x42 C \\u{43} D' # A, B, C |
| 160 | + self.crash_programs.append(data) |
| 161 | + self.no_crash_programs.append(replace_unicode(data)) # b'A A B B C C D' |
| 162 | + |
| 163 | + result = self._minimizer.minimize(data) |
| 164 | + self.assertEqual(result, b'A \\u0041 B \\x42 C \\u{43} D') |
| 165 | + |
| 166 | + def test_minimize_not_buggy_all_escapes(self): |
| 167 | + """Program crashes, but not because of any of the mixed escapes.""" |
| 168 | + data = b'A \\u0041 B \\x42 C \\u{43} D' # A, B, C |
| 169 | + self.crash_programs.append(data) |
| 170 | + self.crash_programs.append(replace_unicode(data)) # b'A A B B C C D' |
| 171 | + |
| 172 | + result = self._minimizer.minimize(data) |
| 173 | + self.assertEqual(result, b'A A B B C C D') |
| 174 | + |
| 175 | + def test_minimize_invalid_unicode_escape(self): |
| 176 | + """Minimizer should not replace invalid escape sequences.""" |
| 177 | + # \\u{110000} is outside the valid Unicode range (max 0x10FFFF) |
| 178 | + data = b'text \\u{110000} text' |
| 179 | + self.crash_programs.append(data) |
| 180 | + |
| 181 | + # replace_unicode will fail to decode and return the original string |
| 182 | + replaced_data = replace_unicode(data) |
| 183 | + self.assertEqual(data, replaced_data) |
| 184 | + |
| 185 | + # Add the (un)replaced data to crash_programs |
| 186 | + self.crash_programs.append(replaced_data) |
| 187 | + |
| 188 | + result = self._minimizer.minimize(data) |
| 189 | + |
| 190 | + self.assertEqual(result, data) |
0 commit comments