Avoid a redirect loop when the request URL is not normalized
[lhc/web/wiklou.git] / tests / phpunit / includes / MediaWikiTest.php
1 <?php
2
3 class MediaWikiTest extends MediaWikiTestCase {
4 private $oldServer, $oldGet, $oldPost;
5
6 protected function setUp() {
7 parent::setUp();
8
9 $this->setMwGlobals( [
10 'wgServer' => 'http://example.org',
11 'wgScriptPath' => '/w',
12 'wgScript' => '/w/index.php',
13 'wgArticlePath' => '/wiki/$1',
14 'wgActionPaths' => [],
15 ] );
16
17 // phpcs:disable MediaWiki.Usage.SuperGlobalsUsage.SuperGlobals
18 $this->oldServer = $_SERVER;
19 $this->oldGet = $_GET;
20 $this->oldPost = $_POST;
21 }
22
23 protected function tearDown() {
24 parent::tearDown();
25 $_SERVER = $this->oldServer;
26 $_GET = $this->oldGet;
27 $_POST = $this->oldPost;
28 }
29
30 public static function provideTryNormaliseRedirect() {
31 return [
32 [
33 // View: Canonical
34 'url' => 'http://example.org/wiki/Foo_Bar',
35 'query' => [],
36 'title' => 'Foo_Bar',
37 'redirect' => false,
38 ],
39 [
40 // View: Escaped title
41 'url' => 'http://example.org/wiki/Foo%20Bar',
42 'query' => [],
43 'title' => 'Foo_Bar',
44 'redirect' => 'http://example.org/wiki/Foo_Bar',
45 ],
46 [
47 // View: Script path
48 'url' => 'http://example.org/w/index.php?title=Foo_Bar',
49 'query' => [ 'title' => 'Foo_Bar' ],
50 'title' => 'Foo_Bar',
51 'redirect' => false,
52 ],
53 [
54 // View: Script path with implicit title from page id
55 'url' => 'http://example.org/w/index.php?curid=123',
56 'query' => [ 'curid' => '123' ],
57 'title' => 'Foo_Bar',
58 'redirect' => false,
59 ],
60 [
61 // View: Script path with implicit title from revision id
62 'url' => 'http://example.org/w/index.php?oldid=123',
63 'query' => [ 'oldid' => '123' ],
64 'title' => 'Foo_Bar',
65 'redirect' => false,
66 ],
67 [
68 // View: Script path without title
69 'url' => 'http://example.org/w/index.php',
70 'query' => [],
71 'title' => 'Main_Page',
72 'redirect' => 'http://example.org/wiki/Main_Page',
73 ],
74 [
75 // View: Script path with empty title
76 'url' => 'http://example.org/w/index.php?title=',
77 'query' => [ 'title' => '' ],
78 'title' => 'Main_Page',
79 'redirect' => 'http://example.org/wiki/Main_Page',
80 ],
81 [
82 // View: Index with escaped title
83 'url' => 'http://example.org/w/index.php?title=Foo%20Bar',
84 'query' => [ 'title' => 'Foo Bar' ],
85 'title' => 'Foo_Bar',
86 'redirect' => 'http://example.org/wiki/Foo_Bar',
87 ],
88 [
89 // View: Script path with escaped title
90 'url' => 'http://example.org/w/?title=Foo_Bar',
91 'query' => [ 'title' => 'Foo_Bar' ],
92 'title' => 'Foo_Bar',
93 'redirect' => false,
94 ],
95 [
96 // View: Root path with escaped title
97 'url' => 'http://example.org/?title=Foo_Bar',
98 'query' => [ 'title' => 'Foo_Bar' ],
99 'title' => 'Foo_Bar',
100 'redirect' => false,
101 ],
102 [
103 // View: Canonical with redundant query
104 'url' => 'http://example.org/wiki/Foo_Bar?action=view',
105 'query' => [ 'action' => 'view' ],
106 'title' => 'Foo_Bar',
107 'redirect' => false,
108 ],
109 [
110 // Edit: Canonical view url with action query
111 'url' => 'http://example.org/wiki/Foo_Bar?action=edit',
112 'query' => [ 'action' => 'edit' ],
113 'title' => 'Foo_Bar',
114 'redirect' => false,
115 ],
116 [
117 // View: Index with action query
118 'url' => 'http://example.org/w/index.php?title=Foo_Bar&action=view',
119 'query' => [ 'title' => 'Foo_Bar', 'action' => 'view' ],
120 'title' => 'Foo_Bar',
121 'redirect' => false,
122 ],
123 [
124 // Edit: Index with action query
125 'url' => 'http://example.org/w/index.php?title=Foo_Bar&action=edit',
126 'query' => [ 'title' => 'Foo_Bar', 'action' => 'edit' ],
127 'title' => 'Foo_Bar',
128 'redirect' => false,
129 ],
130 [
131 // Path with double slash prefix (T100782)
132 'url' => 'http://example.org//wiki/Double_slash',
133 'query' => [],
134 'title' => 'Double_slash',
135 'redirect' => false,
136 ],
137 ];
138 }
139
140 /**
141 * @dataProvider provideTryNormaliseRedirect
142 * @covers MediaWiki::tryNormaliseRedirect
143 */
144 public function testTryNormaliseRedirect( $url, $query, $title, $expectedRedirect = false ) {
145 // Set SERVER because interpolateTitle() doesn't use getRequestURL(),
146 // whereas tryNormaliseRedirect does(). Also, using WebRequest allows
147 // us to test some quirks in that class.
148 $_SERVER['REQUEST_URI'] = $url;
149 $_POST = [];
150 $_GET = $query;
151 $req = new WebRequest;
152
153 // This adds a virtual 'title' query parameter. Normally called from Setup.php
154 $req->interpolateTitle();
155
156 $titleObj = Title::newFromText( $title );
157
158 // Set global context since some involved code paths don't yet have context
159 $context = RequestContext::getMain();
160 $context->setRequest( $req );
161 $context->setTitle( $titleObj );
162
163 $mw = new MediaWiki( $context );
164
165 $method = new ReflectionMethod( $mw, 'tryNormaliseRedirect' );
166 $method->setAccessible( true );
167 $ret = $method->invoke( $mw, $titleObj );
168
169 $this->assertEquals(
170 $expectedRedirect !== false,
171 $ret,
172 'Return true only when redirecting'
173 );
174
175 $this->assertEquals(
176 $expectedRedirect ?: '',
177 $context->getOutput()->getRedirect()
178 );
179 }
180
181 /**
182 * Test a post-send job can not set cookies (T191537).
183 */
184 public function testPostSendJobDoesNotSetCookie() {
185 // Prevent updates from running
186 $this->setMwGlobals( 'wgCommandLineMode', false );
187
188 $response = new WebResponse;
189
190 // A job that attempts to set a cookie
191 $jobHasRun = false;
192 DeferredUpdates::addCallableUpdate( function () use ( $response, &$jobHasRun ) {
193 $jobHasRun = true;
194 $response->setCookie( 'JobCookie', 'yes' );
195 $response->header( 'Foo: baz' );
196 } );
197
198 $hookWasRun = false;
199 $this->setTemporaryHook( 'WebResponseSetCookie', function () use ( &$hookWasRun ) {
200 $hookWasRun = true;
201 return true;
202 } );
203
204 $logger = new TestLogger();
205 $logger->setCollect( true );
206 $this->setLogger( 'cookie', $logger );
207 $this->setLogger( 'header', $logger );
208
209 $mw = new MediaWiki();
210 $mw->doPostOutputShutdown();
211 // restInPeace() might have been registered to a callback of
212 // register_postsend_function() and thus can not be triggered from
213 // PHPUnit.
214 if ( $jobHasRun === false ) {
215 $mw->restInPeace();
216 }
217
218 $this->assertTrue( $jobHasRun, 'post-send job has run' );
219 $this->assertFalse( $hookWasRun,
220 'post-send job must not trigger WebResponseSetCookie hook' );
221 $this->assertEquals(
222 [
223 [ 'info', 'ignored post-send cookie {cookie}' ],
224 [ 'info', 'ignored post-send header {header}' ],
225 ],
226 $logger->getBuffer()
227 );
228 }
229 }