1<?php 2 3/** 4 * @group ajax 5 */ 6class ajax_requests_test extends DokuWikiTest { 7 8 /** 9 * DataProvider for the builtin Ajax calls 10 * 11 * @return array 12 */ 13 public function defaultCalls() { 14 return [ 15 // TODO: better logic and DOM walks 16 // Call | POST | regexp pattern to match 17 [ 'linkwiz', ['q' => ''], '/^<div class="odd type_d/' ], 18 [ 'suggestions', ['q' => ''], null ], 19 [ 'lock', ['id' => ''], null ], 20 [ 'draftdel', ['id' => ''], null ], 21 [ 'medians', ['ns' => 'some:ns'], null ], 22 [ 'medialist', ['ns' => '', 'recent' => '', 'do' => ''], null ], 23 [ 'mediadetails', ['image' => ''], null ], 24 [ 'mediadiff', ['image' => ''], null ], 25 [ 'mediaupload', ['mediaid' => '', 'qqfile' => '' ], null ], // $_FILES 26 [ 'index', ['idx' => ''], null ], 27 [ 'linkwiz', ['q' => ''], null ], 28 ]; 29 } 30 31 /** 32 * @dataProvider defaultCalls 33 * @param string $call 34 * @param array $post 35 * @param string $regexp 36 */ 37 public function test_defaultCallsExist($call, $post, $regexp) { 38 39 $request = new TestRequest(); 40 $response = $request->post(['call'=> $call]+$post, '/lib/exe/ajax.php'); 41 $this->assertNotEquals("AJAX call '$call' unknown!\n", $response->getContent()); 42 43 if (!empty($regexp)) { 44 $this->assertMatchesRegularExpression($regexp, $response->getContent()); 45 } 46 } 47 48 /** 49 * callMediaupload must normalize the namespace with cleanID() before it is used. 50 * 51 * regression test for XSS reflection and passing unclened data to the ACL check 52 */ 53 public function test_mediaupload_reflects_cleaned_namespace() { 54 $request = new TestRequest(); 55 $response = $request->post( 56 ['call' => 'mediaupload', 'ns' => 'Foo"><script>x</script>'], 57 '/lib/exe/ajax.php' 58 ); 59 60 $result = json_decode($response->getContent(), true); 61 $this->assertIsArray($result); 62 $this->assertSame( 63 'foo_script_x_script', 64 $result['ns'], 65 'the raw namespace must be cleaned before it is used' 66 ); 67 } 68 69 public function test_CallNotProvided() { 70 $request = new TestRequest(); 71 $response = $request->post([], '/lib/exe/ajax.php'); 72 $this->assertEquals('', $response->getContent()); 73 } 74 75 public function test_UnknownCall() { 76 $call = 'unknownCALL'; 77 $request = new TestRequest(); 78 $response = $request->post(['call'=> $call], '/lib/exe/ajax.php'); 79 $this->assertEquals("AJAX call '$call' unknown!\n", $response->getContent()); 80 } 81 82 83 public function test_EventOnUnknownCall() { 84 global $EVENT_HANDLER; 85 $call = 'unknownCALL'; 86 $request = new TestRequest(); 87 88 // referenced data from event hook 89 $hookTriggered = false; 90 $eventDataTriggered = ''; 91 $dataTriggered = ''; 92 $postTriggered = ''; 93 94 $hookTriggered_AFTER = false; 95 $eventDataTriggered_AFTER = ''; 96 $dataTriggered_AFTER = ''; 97 $postTriggered_AFTER = ''; 98 99 $EVENT_HANDLER->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', null, 100 function($event, $data) use (&$hookTriggered, &$dataTriggered, &$eventDataTriggered, &$postTriggered) { 101 /** @var Doku_Event $event */ 102 $hookTriggered = true; 103 $dataTriggered = $data; 104 $eventDataTriggered = $event->data; 105 $postTriggered = $GLOBALS['INPUT']->post->str('q'); 106 $event->preventDefault(); 107 $event->stopPropagation(); 108 echo "captured event BEFORE\n"; 109 }, 'some passed data' 110 ); 111 112 $EVENT_HANDLER->register_hook('AJAX_CALL_UNKNOWN', 'AFTER', null, 113 function($event, $data) use (&$hookTriggered_AFTER , &$dataTriggered_AFTER , &$eventDataTriggered_AFTER , &$postTriggered_AFTER ) { 114 /** @var Doku_Event $event */ 115 $hookTriggered_AFTER = true; 116 $dataTriggered_AFTER = $data; 117 $eventDataTriggered_AFTER = $event->data; 118 $postTriggered_AFTER = $GLOBALS['INPUT']->post->str('q'); 119 $event->preventDefault(); 120 $event->stopPropagation(); 121 echo "captured event AFTER"; 122 }, 'some passed data AFTER' 123 ); 124 125 126 $response = $request->post(['call'=> $call, 'q' => 'some-post-param'], '/lib/exe/ajax.php'); 127 128 // BEFORE 129 $this->assertEquals(true, $hookTriggered, 'Testing plugin did not trigger!'); 130 $this->assertEquals('some passed data', $dataTriggered); 131 $this->assertEquals($call, $eventDataTriggered, 'Must pass call name as event data'); 132 $this->assertEquals('some-post-param', $postTriggered); 133 134 // AFTER 135 $this->assertEquals(true, $hookTriggered_AFTER, 'Testing plugin did not trigger!'); 136 $this->assertEquals('some passed data AFTER', $dataTriggered_AFTER); 137 $this->assertEquals($call, $eventDataTriggered_AFTER, 'Must pass call name as event data'); 138 $this->assertEquals('some-post-param', $postTriggered_AFTER); 139 140 //output 141 $this->assertEquals("captured event BEFORE\ncaptured event AFTER", $response->getContent()); 142 143 } 144} 145