MediaWiki UI: Use em's for the button width
[lhc/web/wiklou.git] / tests / phpunit / includes / upload / UploadBaseTest.php
1 <?php
2
3 /**
4 * @group Upload
5 */
6 class UploadBaseTest extends MediaWikiTestCase {
7
8 /** @var UploadTestHandler */
9 protected $upload;
10
11 protected function setUp() {
12 global $wgHooks;
13 parent::setUp();
14
15 $this->upload = new UploadTestHandler;
16 $this->hooks = $wgHooks;
17 $wgHooks['InterwikiLoadPrefix'][] = function ( $prefix, &$data ) {
18 return false;
19 };
20 }
21
22 protected function tearDown() {
23 global $wgHooks;
24 $wgHooks = $this->hooks;
25
26 parent::tearDown();
27 }
28
29 /**
30 * First checks the return code
31 * of UploadBase::getTitle() and then the actual returned title
32 *
33 * @dataProvider provideTestTitleValidation
34 * @covers UploadBase::getTitle
35 */
36 public function testTitleValidation( $srcFilename, $dstFilename, $code, $msg ) {
37 /* Check the result code */
38 $this->assertEquals( $code,
39 $this->upload->testTitleValidation( $srcFilename ),
40 "$msg code" );
41
42 /* If we expect a valid title, check the title itself. */
43 if ( $code == UploadBase::OK ) {
44 $this->assertEquals( $dstFilename,
45 $this->upload->getTitle()->getText(),
46 "$msg text" );
47 }
48 }
49
50 /**
51 * Test various forms of valid and invalid titles that can be supplied.
52 */
53 public static function provideTestTitleValidation() {
54 return array(
55 /* Test a valid title */
56 array( 'ValidTitle.jpg', 'ValidTitle.jpg', UploadBase::OK,
57 'upload valid title' ),
58 /* A title with a slash */
59 array( 'A/B.jpg', 'B.jpg', UploadBase::OK,
60 'upload title with slash' ),
61 /* A title with illegal char */
62 array( 'A:B.jpg', 'A-B.jpg', UploadBase::OK,
63 'upload title with colon' ),
64 /* Stripping leading File: prefix */
65 array( 'File:C.jpg', 'C.jpg', UploadBase::OK,
66 'upload title with File prefix' ),
67 /* Test illegal suggested title (r94601) */
68 array( '%281%29.JPG', null, UploadBase::ILLEGAL_FILENAME,
69 'illegal title for upload' ),
70 /* A title without extension */
71 array( 'A', null, UploadBase::FILETYPE_MISSING,
72 'upload title without extension' ),
73 /* A title with no basename */
74 array( '.jpg', null, UploadBase::MIN_LENGTH_PARTNAME,
75 'upload title without basename' ),
76 /* A title that is longer than 255 bytes */
77 array( str_repeat( 'a', 255 ) . '.jpg', null, UploadBase::FILENAME_TOO_LONG,
78 'upload title longer than 255 bytes' ),
79 /* A title that is longer than 240 bytes */
80 array( str_repeat( 'a', 240 ) . '.jpg', null, UploadBase::FILENAME_TOO_LONG,
81 'upload title longer than 240 bytes' ),
82 );
83 }
84
85 /**
86 * Test the upload verification functions
87 * @covers UploadBase::verifyUpload
88 */
89 public function testVerifyUpload() {
90 /* Setup with zero file size */
91 $this->upload->initializePathInfo( '', '', 0 );
92 $result = $this->upload->verifyUpload();
93 $this->assertEquals( UploadBase::EMPTY_FILE,
94 $result['status'],
95 'upload empty file' );
96 }
97
98 // Helper used to create an empty file of size $size.
99 private function createFileOfSize( $size ) {
100 $filename = tempnam( wfTempDir(), "mwuploadtest" );
101
102 $fh = fopen( $filename, 'w' );
103 ftruncate( $fh, $size );
104 fclose( $fh );
105
106 return $filename;
107 }
108
109 /**
110 * test uploading a 100 bytes file with $wgMaxUploadSize = 100
111 *
112 * This method should be abstracted so we can test different settings.
113 */
114 public function testMaxUploadSize() {
115 global $wgMaxUploadSize;
116 $savedGlobal = $wgMaxUploadSize; // save global
117 global $wgFileExtensions;
118 $wgFileExtensions[] = 'txt';
119
120 $wgMaxUploadSize = 100;
121
122 $filename = $this->createFileOfSize( $wgMaxUploadSize );
123 $this->upload->initializePathInfo( basename( $filename ) . '.txt', $filename, 100 );
124 $result = $this->upload->verifyUpload();
125 unlink( $filename );
126
127 $this->assertEquals(
128 array( 'status' => UploadBase::OK ), $result );
129
130 $wgMaxUploadSize = $savedGlobal; // restore global
131 }
132
133
134 /**
135 * @dataProvider provideCheckSvgScriptCallback
136 */
137 public function testCheckSvgScriptCallback( $svg, $wellFormed, $filterMatch, $message ) {
138 list( $formed, $match ) = $this->upload->checkSvgString( $svg );
139 $this->assertSame( $wellFormed, $formed, $message );
140 $this->assertSame( $filterMatch, $match, $message );
141 }
142
143 public static function provideCheckSvgScriptCallback() {
144 return array(
145 // html5sec SVG vectors
146 array(
147 '<svg xmlns="http://www.w3.org/2000/svg"><script>alert(1)</script></svg>',
148 true,
149 true,
150 'Script tag in svg (http://html5sec.org/#47)'
151 ),
152 array(
153 '<svg xmlns="http://www.w3.org/2000/svg"><g onload="javascript:alert(1)"></g></svg>',
154 true,
155 true,
156 'SVG with onload property (http://html5sec.org/#11)'
157 ),
158 array(
159 '<svg onload="javascript:alert(1)" xmlns="http://www.w3.org/2000/svg"></svg>',
160 true,
161 true,
162 'SVG with onload property (http://html5sec.org/#65)'
163 ),
164 array(
165 '<svg xmlns="http://www.w3.org/2000/svg"> <a xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="javascript:alert(1)"><rect width="1000" height="1000" fill="white"/></a> </svg>',
166 true,
167 true,
168 'SVG with javascript xlink (http://html5sec.org/#87)'
169 ),
170 array(
171 '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <animation xlink:href="javascript:alert(1)"/> </svg>',
172 true,
173 true,
174 'SVG with Opera animation xlink (http://html5sec.org/#88 - a)'
175 ),
176 array(
177 '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <animation xlink:href="data:text/xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' onload=\'alert(1)\'%3E%3C/svg%3E"/> </svg>',
178 true,
179 true,
180 'SVG with Opera animation xlink (http://html5sec.org/#88 - b)'
181 ),
182 array(
183 '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <image xlink:href="data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' onload=\'alert(1)\'%3E%3C/svg%3E"/> </svg>',
184 true,
185 true,
186 'SVG with Opera image xlink (http://html5sec.org/#88 - c)'
187 ),
188 array(
189 '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <foreignObject xlink:href="javascript:alert(1)"/> </svg>',
190 true,
191 true,
192 'SVG with Opera foreignObject xlink (http://html5sec.org/#88 - d)'
193 ),
194 array(
195 '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <foreignObject xlink:href="data:text/xml,%3Cscript xmlns=\'http://www.w3.org/1999/xhtml\'%3Ealert(1)%3C/script%3E"/> </svg>',
196 true,
197 true,
198 'SVG with Opera foreignObject xlink (http://html5sec.org/#88 - e)'
199 ),
200 array(
201 '<svg xmlns="http://www.w3.org/2000/svg"> <set attributeName="onmouseover" to="alert(1)"/> </svg>',
202 true,
203 true,
204 'SVG with event handler set (http://html5sec.org/#89 - a)'
205 ),
206 array(
207 '<svg xmlns="http://www.w3.org/2000/svg"> <animate attributeName="onunload" to="alert(1)"/> </svg>',
208 true,
209 true,
210 'SVG with event handler animate (http://html5sec.org/#89 - a)'
211 ),
212 array(
213 '<svg xmlns="http://www.w3.org/2000/svg"> <handler xmlns:ev="http://www.w3.org/2001/xml-events" ev:event="load">alert(1)</handler> </svg>',
214 true,
215 true,
216 'SVG with element handler (http://html5sec.org/#94)'
217 ),
218 array(
219 '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <feImage> <set attributeName="xlink:href" to="data:image/svg+xml;charset=utf-8;base64, PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxzY3JpcHQ%2BYWxlcnQoMSk8L3NjcmlwdD48L3N2Zz4NCg%3D%3D"/> </feImage> </svg>',
220 true,
221 true,
222 'SVG with href to data: url (http://html5sec.org/#95)'
223 ),
224 array(
225 '<svg xmlns="http://www.w3.org/2000/svg" id="foo"> <x xmlns="http://www.w3.org/2001/xml-events" event="load" observer="foo" handler="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%3Chandler%20xml%3Aid%3D%22bar%22%20type%3D%22application%2Fecmascript%22%3E alert(1) %3C%2Fhandler%3E%0A%3C%2Fsvg%3E%0A#bar"/> </svg>',
226 true,
227 true,
228 'SVG with Tiny handler (http://html5sec.org/#104)'
229 ),
230 array(
231 '<svg xmlns="http://www.w3.org/2000/svg"> <a id="x"><rect fill="white" width="1000" height="1000"/></a> <rect fill="white" style="clip-path:url(test3.svg#a);fill:url(#b);filter:url(#c);marker:url(#d);mask:url(#e);stroke:url(#f);"/> </svg>',
232 true,
233 true,
234 'SVG with new CSS styles properties (http://html5sec.org/#109)'
235 ),
236 array(
237 '<svg xmlns="http://www.w3.org/2000/svg"> <a id="x"><rect fill="white" width="1000" height="1000"/></a> <rect clip-path="url(test3.svg#a)" /> </svg>',
238 true,
239 true,
240 'SVG with new CSS styles properties as attributes'
241 ),
242 array(
243 '<svg xmlns="http://www.w3.org/2000/svg"> <a id="x"> <rect fill="white" width="1000" height="1000"/> </a> <rect fill="url(http://html5sec.org/test3.svg#a)" /> </svg>',
244 true,
245 true,
246 'SVG with new CSS styles properties as attributes (2)'
247 ),
248 array(
249 '<svg xmlns="http://www.w3.org/2000/svg"> <path d="M0,0" style="marker-start:url(test4.svg#a)"/> </svg>',
250 true,
251 true,
252 'SVG with path marker-start (http://html5sec.org/#110)'
253 ),
254 array(
255 '<?xml version="1.0"?> <?xml-stylesheet type="text/xml" href="#stylesheet"?> <!DOCTYPE doc [ <!ATTLIST xsl:stylesheet id ID #REQUIRED>]> <svg xmlns="http://www.w3.org/2000/svg"> <xsl:stylesheet id="stylesheet" version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <iframe xmlns="http://www.w3.org/1999/xhtml" src="javascript:alert(1)"></iframe> </xsl:template> </xsl:stylesheet> <circle fill="red" r="40"></circle> </svg>',
256 true,
257 true,
258 'SVG with embedded stylesheet (http://html5sec.org/#125)'
259 ),
260 array(
261 '<svg xmlns="http://www.w3.org/2000/svg" id="x"> <listener event="load" handler="#y" xmlns="http://www.w3.org/2001/xml-events" observer="x"/> <handler id="y">alert(1)</handler> </svg>',
262 true,
263 true,
264 'SVG with handler attribute (http://html5sec.org/#127)'
265 ),
266 array(
267 // Haven't found a browser that accepts this particular example, but we
268 // don't want to allow embeded svgs, ever
269 '<svg> <image style=\'filter:url("data:image/svg+xml;charset=utf-8;base64, PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxzY3JpcHQ/YWxlcnQoMSk8L3NjcmlwdD48L3N2Zz4NCg==")\' /> </svg>',
270 true,
271 true,
272 'SVG with image filter via style (http://html5sec.org/#129)'
273 ),
274 array(
275 // This doesn't seem possible without embedding the svg, but just in case
276 '<svg> <a xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="?"> <circle r="400"></circle> <animate attributeName="xlink:href" begin="0" from="javascript:alert(1)" to="" /> </a></svg>',
277 true,
278 true,
279 'SVG with animate from (http://html5sec.org/#137)'
280 ),
281
282 // Other hostile SVG's
283 array(
284 '<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns:xlink="http://www.w3.org/1999/xlink"> <image xlink:href="https://upload.wikimedia.org/wikipedia/commons/3/34/Bahnstrecke_Zeitz-Camburg_1930.png" /> </svg>',
285 true,
286 true,
287 'SVG with non-local image href (bug 65839)'
288 ),
289 array(
290 '<?xml version="1.0" ?> <?xml-stylesheet type="text/xsl" href="/w/index.php?title=User:Jeeves/test.xsl&amp;action=raw&amp;format=xml" ?> <svg> <height>50</height> <width>100</width> </svg>',
291 true,
292 true,
293 'SVG with remote stylesheet (bug 57550)'
294 ),
295 array(
296 '<svg xmlns="http://www.w3.org/2000/svg" viewbox="-1 -1 15 15"> <rect y="0" height="13" width="12" stroke="#179" rx="1" fill="#2ac"/> <text x="1.5" y="11" font-family="courier" stroke="white" font-size="16"><![CDATA[B]]></text> <iframe xmlns="http://www.w3.org/1999/xhtml" srcdoc="&#x3C;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3E;&#x61;&#x6C;&#x65;&#x72;&#x74;&#x28;&#x27;&#x58;&#x53;&#x53;&#x45;&#x44;&#x20;&#x3D;&#x3E;&#x20;&#x44;&#x6F;&#x6D;&#x61;&#x69;&#x6E;&#x28;&#x27;&#x2B;&#x74;&#x6F;&#x70;&#x2E;&#x64;&#x6F;&#x63;&#x75;&#x6D;&#x65;&#x6E;&#x74;&#x2E;&#x64;&#x6F;&#x6D;&#x61;&#x69;&#x6E;&#x2B;&#x27;&#x29;&#x27;&#x29;&#x3B;&#x3C;&#x2F;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3E;"></iframe> </svg>',
297 true,
298 true,
299 'SVG with rembeded iframe (bug 60771)'
300 ),
301 array(
302 '<svg xmlns="http://www.w3.org/2000/svg" viewBox="6 3 177 153" xmlns:xlink="http://www.w3.org/1999/xlink"> <style>@import url("https://fonts.googleapis.com/css?family=Bitter:700&amp;text=WebPlatform.org");</style> <g transform="translate(-.5,-.5)"> <text fill="#474747" x="95" y="150" text-anchor="middle" font-family="Bitter" font-size="20" font-weight="bold">WebPlatform.org</text> </g> </svg>',
303 true,
304 true,
305 'SVG with @import in style element (bug 69008)'
306 ),
307 array(
308 '<svg xmlns="http://www.w3.org/2000/svg" viewBox="6 3 177 153" xmlns:xlink="http://www.w3.org/1999/xlink"> <style>@import url("https://fonts.googleapis.com/css?family=Bitter:700&amp;text=WebPlatform.org");<foo/></style> <g transform="translate(-.5,-.5)"> <text fill="#474747" x="95" y="150" text-anchor="middle" font-family="Bitter" font-size="20" font-weight="bold">WebPlatform.org</text> </g> </svg>',
309 true,
310 true,
311 'SVG with @import in style element and child element (bug 69008#c11)'
312 ),
313 array(
314 '<svg xmlns="http://www.w3.org/2000/svg"> <rect width="100" height="100" style="background-image:url(https://www.google.com/images/srpr/logo11w.png)"/> </svg>',
315 true,
316 true,
317 'SVG with remote background image (bug 69008)'
318 ),
319 array(
320 '<svg xmlns="http://www.w3.org/2000/svg"> <rect width="100" height="100" style="background-image:\55rl(https://www.google.com/images/srpr/logo11w.png)"/> </svg>',
321 true,
322 true,
323 'SVG with remote background image, encoded (bug 69008)'
324 ),
325 array(
326 '<svg xmlns="http://www.w3.org/2000/svg"> <style> #a { background-image:\55rl(\'https://www.google.com/images/srpr/logo11w.png\'); } </style> <rect width="100" height="100" id="a"/> </svg>',
327 true,
328 true,
329 'SVG with remote background image, in style element (bug 69008)'
330 ),
331 array(
332 // This currently doesn't seem to work in any browsers, but in case
333 // http://www.w3.org/TR/css3-images/ is implemented for SVG files
334 '<svg xmlns="http://www.w3.org/2000/svg"> <rect width="100" height="100" style="background-image:image(\'sprites.svg#xywh=40,0,20,20\')"/> </svg>',
335 true,
336 true,
337 'SVG with remote background image using image() (bug 69008)'
338 ),
339
340 // Test good, but strange files that we want to allow
341 array(
342 '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <g> <a xlink:href="http://en.wikipedia.org/wiki/Main_Page"> <path transform="translate(0,496)" id="path6706" d="m 112.09375,107.6875 -5.0625,3.625 -4.3125,5.03125 -0.46875,0.5 -4.09375,3.34375 -9.125,5.28125 -8.625,-3.375 z" style="fill:#cccccc;fill-opacity:1;stroke:#6e6e6e;stroke-width:0.69999999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;display:inline" /> </a> </g> </svg>',
343 true,
344 false,
345 'SVG with <a> link to a remote site'
346 ),
347 array(
348 '<svg> <defs> <filter id="filter6226" x="-0.93243687" width="2.8648737" y="-0.24250539" height="1.4850108"> <feGaussianBlur stdDeviation="3.2344681" id="feGaussianBlur6228" /> </filter> <clipPath id="clipPath2436"> <path d="M 0,0 L 0,0 L 0,0 L 0,0 z" id="path2438" /> </clipPath> </defs> <g clip-path="url(#clipPath2436)" id="g2460"> <text id="text2466"> <tspan>12345</tspan> </text> </g> <path style="fill:#346733;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:bevel;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:1, 1;stroke-dashoffset:0;filter:url(\'#filter6226\');fill-opacity:1;opacity:0.79807692" d="M 236.82371,332.63732 C 236.92217,332.63732 z" id="path5618" /> </svg>',
349 true,
350 false,
351 'SVG with local urls, including filter: in style'
352 ),
353
354 );
355 }
356 }
357
358 class UploadTestHandler extends UploadBase {
359 public function initializeFromRequest( &$request ) {
360 }
361
362 public function testTitleValidation( $name ) {
363 $this->mTitle = false;
364 $this->mDesiredDestName = $name;
365 $this->mTitleError = UploadBase::OK;
366 $this->getTitle();
367
368 return $this->mTitleError;
369 }
370
371 /**
372 * Almost the same as UploadBase::detectScriptInSvg, except it's
373 * public, works on an xml string instead of filename, and returns
374 * the result instead of interpreting them.
375 */
376 public function checkSvgString( $svg ) {
377 $check = new XmlTypeCheck(
378 $svg,
379 array( $this, 'checkSvgScriptCallback' ),
380 false,
381 array( 'processing_instruction_handler' => 'UploadBase::checkSvgPICallback' )
382 );
383 return array( $check->wellFormed, $check->filterMatch );
384 }
385 }