22
33from dojo .models import Finding , Test
44from 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+ )
610from dojo .tools .appcheck_web_application_scanner .engines .nmap import NmapScanningEngineParser
711from dojo .tools .appcheck_web_application_scanner .parser import AppCheckWebApplicationScannerParser
812
@@ -217,6 +221,71 @@ def test_appcheck_web_application_scanner_parser_dupes(self):
217221 # Test has 5 entries, but we should only return 3 findings.
218222 self .assertEqual (3 , len (findings ))
219223
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+
220289 def test_appcheck_web_application_scanner_parser_base_engine_parser (self ):
221290 engine = BaseEngineParser ()
222291
@@ -411,6 +480,14 @@ def test_appcheck_web_application_scanner_parser_appcheck_engine_parser(self):
411480 {"Messages" : "--->\n \n some stuff\n \n <--\n \n here" },
412481 # Incorrect request starting-marker
413482 {"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" },
414491 ]:
415492 has_messages_entry = "Messages" in no_rr
416493 engine .extract_request_response (f , no_rr )
@@ -420,15 +497,28 @@ def test_appcheck_web_application_scanner_parser_appcheck_engine_parser(self):
420497 if has_messages_entry :
421498 self .assertTrue ("Messages" in no_rr )
422499
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
432522
433523 def test_appcheck_web_application_scanner_parser_markup_stripper (self ):
434524 for markup , expected in [
@@ -440,3 +530,33 @@ def test_appcheck_web_application_scanner_parser_markup_stripper(self):
440530 ("[[markup]] but with [[urlhere]]" , "but with urlhere" ),
441531 ]:
442532 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