2
2
3
3
from dojo .models import Finding , Test
4
4
from dojo .tools .appcheck_web_application_scanner .engines .appcheck import AppCheckScanningEngineParser
5
- from dojo .tools .appcheck_web_application_scanner .engines .base import BaseEngineParser , strip_markup
5
+ from dojo .tools .appcheck_web_application_scanner .engines .base import (
6
+ BaseEngineParser ,
7
+ escape_non_printable ,
8
+ strip_markup ,
9
+ )
6
10
from dojo .tools .appcheck_web_application_scanner .engines .nmap import NmapScanningEngineParser
7
11
from dojo .tools .appcheck_web_application_scanner .parser import AppCheckWebApplicationScannerParser
8
12
@@ -217,6 +221,71 @@ def test_appcheck_web_application_scanner_parser_dupes(self):
217
221
# Test has 5 entries, but we should only return 3 findings.
218
222
self .assertEqual (3 , len (findings ))
219
223
224
+ def test_appcheck_web_application_scanner_parser_http2 (self ):
225
+ with open ("unittests/scans/appcheck_web_application_scanner/appcheck_web_application_scanner_http2.json" ) as testfile :
226
+ parser = AppCheckWebApplicationScannerParser ()
227
+ findings = parser .get_findings (testfile , Test ())
228
+ self .assertEqual (3 , len (findings ))
229
+
230
+ finding = findings [0 ]
231
+ self .assertEqual ("1c564bddf78f7642468474a49c9be6653f39e9df6b32d658" , finding .unique_id_from_tool )
232
+ self .assertEqual ("2024-08-06" , finding .date )
233
+ self .assertEqual ("HTTP/2 Supported" , finding .title )
234
+ self .assertEqual (1 , len (finding .unsaved_endpoints ))
235
+ self .assertTrue ("**Messages**" not in finding .description )
236
+ self .assertTrue ("\x00 " not in finding .description )
237
+ self .assertIsNotNone (finding .unsaved_request )
238
+ self .assertTrue (finding .unsaved_request .startswith (":method = GET" ))
239
+ self .assertIsNotNone (finding .unsaved_response )
240
+ self .assertTrue (finding .unsaved_response .startswith (":status: 200" ))
241
+ endpoint = finding .unsaved_endpoints [0 ]
242
+ endpoint .clean ()
243
+ self .assertEqual ("www.xzzvwy.com" , endpoint .host )
244
+ self .assertEqual (443 , endpoint .port )
245
+ self .assertEqual ("https" , endpoint .protocol )
246
+ self .assertEqual ("media/vzdldjmk/pingpong2.jpg" , endpoint .path )
247
+ self .assertEqual ("rmode=max&height=500" , endpoint .query )
248
+
249
+ finding = findings [1 ]
250
+ self .assertEqual ("4e7c0b570ff6083376b99e1897102a87907effe2199dc8d4" , finding .unique_id_from_tool )
251
+ self .assertEqual ("2024-08-06" , finding .date )
252
+ self .assertEqual ("HTTP/2 Protocol: Transfer-Encoding Header Accepted" , finding .title )
253
+ self .assertTrue ("**Messages**" not in finding .description )
254
+ self .assertTrue ("\x00 " not in finding .description )
255
+ self .assertTrue ("**HTTP2 Headers**" in finding .description )
256
+ self .assertIsNotNone (finding .unsaved_request )
257
+ self .assertTrue (finding .unsaved_request .startswith (":method = POST" ))
258
+ self .assertIsNotNone (finding .unsaved_response )
259
+ self .assertTrue (finding .unsaved_response .startswith (":status: 200" ))
260
+ self .assertEqual (1 , len (finding .unsaved_endpoints ))
261
+ endpoint = finding .unsaved_endpoints [0 ]
262
+ endpoint .clean ()
263
+ self .assertEqual ("www.xzzvwy.com" , endpoint .host )
264
+ self .assertEqual (443 , endpoint .port )
265
+ self .assertEqual ("https" , endpoint .protocol )
266
+ self .assertEqual ("media/mmzzvwy/pingpong2.jpg" , endpoint .path )
267
+ self .assertEqual ("rmode=max&height=500" , endpoint .query )
268
+
269
+ finding = findings [2 ]
270
+ self .assertEqual ("2f1fb384e6a866f9ee0c6f7550e3b607e8b1dd2b1ab0fd02" , finding .unique_id_from_tool )
271
+ self .assertEqual ("2024-08-06" , finding .date )
272
+ self .assertEqual ("HTTP/2 Protocol: Transfer-Encoding Header Accepted" , finding .title )
273
+ self .assertTrue ("**Messages**" not in finding .description )
274
+ self .assertTrue ("**HTTP2 Headers**" in finding .description )
275
+ self .assertTrue ("\x00 " not in finding .description )
276
+ self .assertIsNotNone (finding .unsaved_request )
277
+ self .assertTrue (finding .unsaved_request .startswith (":method = POST" ))
278
+ self .assertIsNotNone (finding .unsaved_response )
279
+ self .assertTrue (finding .unsaved_response .startswith (":status: 200" ))
280
+ self .assertEqual (1 , len (finding .unsaved_endpoints ))
281
+ endpoint = finding .unsaved_endpoints [0 ]
282
+ endpoint .clean ()
283
+ self .assertEqual ("www.zzvwy.com" , endpoint .host )
284
+ self .assertEqual (443 , endpoint .port )
285
+ self .assertEqual ("https" , endpoint .protocol )
286
+ self .assertEqual ("media/bnhfz2s2/transport-hubs.jpeg" , endpoint .path )
287
+ self .assertEqual ("width=768&height=505&mode=crop&format=webp&quality=60" , endpoint .query )
288
+
220
289
def test_appcheck_web_application_scanner_parser_base_engine_parser (self ):
221
290
engine = BaseEngineParser ()
222
291
@@ -411,6 +480,14 @@ def test_appcheck_web_application_scanner_parser_appcheck_engine_parser(self):
411
480
{"Messages" : "--->\n \n some stuff\n \n <--\n \n here" },
412
481
# Incorrect request starting-marker
413
482
{"Messages" : "-->\n \n some stuff here\n \n <---\n \n here" },
483
+ # Missing data
484
+ {"Messages" : "HTTP/2 Request Headers:\n \n \r \n HTTP/2 Response Headers:\n \n " },
485
+ {"Messages" : "HTTP/2 Request Headers:\n \n \r \n HTTP/2 Response Headers:\n \n Data" },
486
+ {"Messages" : "HTTP/2 Request Headers:\n \n Data\r \n HTTP/2 Response Headers:\n \n " },
487
+ # No response
488
+ {"Messages" : "HTTP/2 Request Headers:\n \n Data\r \n " },
489
+ # No request
490
+ {"Messages" : "\r \n HTTP/2 Response Headers:\n \n Data" },
414
491
]:
415
492
has_messages_entry = "Messages" in no_rr
416
493
engine .extract_request_response (f , no_rr )
@@ -420,15 +497,28 @@ def test_appcheck_web_application_scanner_parser_appcheck_engine_parser(self):
420
497
if has_messages_entry :
421
498
self .assertTrue ("Messages" in no_rr )
422
499
423
- for req , res in [
424
- ("some stuff" , "here" ), ("some stuff <---" , " here" ), ("s--->" , "here<---" ), (" s " , " h " ),
425
- ("some stuff... HERE\r \n \r \n " , "no, here\n \n " ),
426
- ]:
427
- rr = {"Messages" : f"--->\n \n { req } \n \n <---\n \n { res } " }
428
- engine .extract_request_response (f , rr )
429
- self .assertEqual (req .strip (), f .unsaved_request )
430
- self .assertEqual (res .strip (), f .unsaved_response )
431
- f .unsaved_request = f .unsaved_response = None
500
+ for template , test_data in {
501
+ # HTTP/1
502
+ "--->\n \n {req}\n \n <---\n \n {res}" : [
503
+ ("some stuff" , "here" ),
504
+ ("some stuff <---" , " here" ),
505
+ ("s--->" , "here<---" ),
506
+ (" s " , " h " ),
507
+ ("some stuff... HERE\r \n \r \n " , "no, here\n \n " ),
508
+ ],
509
+ # HTTP/2
510
+ "HTTP/2 Request Headers:\n \n {req}\r \n HTTP/2 Response Headers:\n \n {res}" : [
511
+ ("some stuff" , "here" ),
512
+ (" s---> " , " here<--- " ),
513
+ ("\x00 \x01 \u0004 \n \r \t data" , "\r \n \x00 \x01 \x0c \x0b data" ),
514
+ ],
515
+ }.items ():
516
+ for req , res in test_data :
517
+ rr = {"Messages" : template .format (req = req , res = res )}
518
+ engine .extract_request_response (f , rr )
519
+ self .assertEqual (req .strip (), f .unsaved_request )
520
+ self .assertEqual (res .strip (), f .unsaved_response )
521
+ f .unsaved_request = f .unsaved_response = None
432
522
433
523
def test_appcheck_web_application_scanner_parser_markup_stripper (self ):
434
524
for markup , expected in [
@@ -440,3 +530,33 @@ def test_appcheck_web_application_scanner_parser_markup_stripper(self):
440
530
("[[markup]] but with [[urlhere]]" , "but with urlhere" ),
441
531
]:
442
532
self .assertEqual (expected , strip_markup (markup ))
533
+
534
+ def test_appcheck_web_application_scanner_parser_non_printable_escape (self ):
535
+ for test_string , expected in [
536
+ ("" , "" ),
537
+ (
538
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\" #$%&'()*+,-./:;<=>?@[\\ ]^_`{|}~ \t \n \r \x0b \x0c " ,
539
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\" #$%&'()*+,-./:;<=>?@[\\ ]^_`{|}~ \t \n \r \\ x0b\\ x0c" ,
540
+ ),
541
+ ("'!Test String?'\" \" " , "'!Test String?'\" \" " ),
542
+ ("\r \n \t Test\r \n String\t \r \n " , "\r \n \t Test\r \n String\t \r \n " ),
543
+ ("\0 Test\r \n String\0 \n " , "\\ x00Test\r \n String\\ x00\n " ),
544
+ ("\0 \0 你好,\0 我不知道。对马好!\n " , "\\ x00\\ x00你好,\\ x00我不知道。对马好!\n " ),
545
+ ("\u0000 " , r"\x00" ),
546
+ ("\x00 " , r"\x00" ),
547
+ ("\u0000 \u0000 " , r"\x00\x00" ),
548
+ ("\r \n \t \t \u0000 \u0000 \n \n " , "\r \n \t \t \\ x00\\ x00\n \n " ),
549
+ (
550
+ "¡A qÙîçk ΛæzŸ ßrȯωñ Møøβe\n önce \u0000 \u202d \u200e Σister's ÞΕ 🜯 ¼ 50¢ «soda¬¿ υϖυ 🤪\u000b …" ,
551
+ "¡A qÙîçk ΛæzŸ ßrȯωñ Møøβe\n önce \\ x00\\ u202d\\ u200e Σister's ÞΕ 🜯 ¼ 50¢ «soda¬¿ υϖυ 🤪\\ x0b…" ,
552
+ ),
553
+ (
554
+ "Words: \u0000 \u0010 ABCD\u0000 \u0001 \u0001 `\u0000 jpeg\u0000 CC+\u0000 \b \u0000 \u0003 ;\u0001 \u0002 \u0000 2\u001c \u0000 @\u0000 i\u0004 \\ \u0000 . Done." ,
555
+ r"Words: \x00\x10ABCD\x00\x01\x01`\x00jpeg\x00CC+\x00\x08\x00\x03;\x01\x02\x002\x1c\x00@\x00i\x04\\x00. Done." ,
556
+ ),
557
+ (
558
+ "\u0016 \n o#bota\u0012 4&7\r \u0019 j9}\t \u0004 ef\u202e gh\u001c " ,
559
+ "\\ x16\n o#bota\\ x124&7\r \\ x19j9}\t \\ x04ef\\ u202egh\\ x1c" ,
560
+ ),
561
+ ]:
562
+ self .assertEqual (expected , escape_non_printable (test_string ))
0 commit comments