ca15d74ccbb5b0575bc720ae3ef2517665fef9c9
[lhc/web/wiklou.git] / maintenance / fuzz-tester.php
1 <?php
2 /**
3 * Performs fuzz-style testing of MediaWiki's parser and forms.
4 *
5 * Copyright © 2006 Nick Jenkins
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * http://www.gnu.org/copyleft/gpl.html
21 *
22 * @file
23 * @ingroup Maintenance
24 * @author Nick Jenkins ( http://nickj.org/ ).
25
26
27 Started: 18 May 2006.
28
29 Description:
30 Performs fuzz-style testing of MediaWiki's parser and forms.
31
32 How:
33 - Generate lots of nasty wiki text.
34 - Ask the Parser to render that wiki text to HTML, or ask MediaWiki's forms
35 to deal with that wiki text.
36 - Check MediaWiki's output for problems.
37 - Repeat.
38
39 Why:
40 - To help find bugs.
41 - To help find security issues, or potential security issues.
42
43 What type of problems are being checked for:
44 - Unclosed tags.
45 - Errors or interesting warnings from Tidy.
46 - PHP errors / warnings / notices.
47 - MediaWiki internal errors.
48 - Very slow responses.
49 - No response from apache.
50 - Optionally checking for malformed HTML using the W3C validator.
51
52 Background:
53 Many of the wikiFuzz class methods are a modified PHP port,
54 of a "shameless" Python port, of LCAMTUF'S MANGELME:
55 - http://www.securiteam.com/tools/6Z00N1PBFK.html
56 - http://www.securityfocus.com/archive/1/378632/2004-10-15/2004-10-21/0
57
58 Video:
59 There's an XviD video discussing this fuzz tester. You can get it from:
60 http://files.nickj.org/MediaWiki/Fuzz-Testing-MediaWiki-xvid.avi
61
62 Requirements:
63 To run this, you will need:
64 - Command-line PHP5, with PHP-curl enabled (not all installations have this
65 enabled - try "apt-get install php5-curl" if you're on Debian to install).
66 - the Tidy standalone executable. ("apt-get install tidy").
67
68 Optional:
69 - If you want to run the curl scripts, you'll need standalone curl installed
70 ("apt-get install curl")
71 - For viewing the W3C validator output on a command line, the "html2text"
72 program may be useful ("apt-get install html2text")
73
74 Saving tests and test results:
75 Any of the fuzz tests which find problems are saved for later review.
76 In order to help track down problems, tests are saved in a number of
77 different formats. The default filename extensions and their meanings are:
78 - ".test.php" : PHP script that reproduces just that one problem using PHP-Curl.
79 - ".curl.sh" : Shell script that reproduces that problem using standalone curl.
80 - ".data.bin" : The serialized PHP data so that this script can re-run the test.
81 - ".info.txt" : A human-readable text file with details of the field contents.
82
83 Wiki configuration for testing:
84 You should make some additions to LocalSettings.php in order to catch the most
85 errors. Note this configuration is for **TESTING PURPOSES ONLY**, and is IN NO
86 WAY, SHAPE, OR FORM suitable for deployment on a hostile network. That said,
87 personally I find these additions to be the most helpful for testing purposes:
88
89 // --------- Start ---------
90 // Everyone can do everything. Very useful for testing, yet useless for deployment.
91 $wgGroupPermissions['*']['autoconfirmed'] = true;
92 $wgGroupPermissions['*']['block'] = true;
93 $wgGroupPermissions['*']['bot'] = true;
94 $wgGroupPermissions['*']['delete'] = true;
95 $wgGroupPermissions['*']['deletedhistory'] = true;
96 $wgGroupPermissions['*']['deleterevision'] = true;
97 $wgGroupPermissions['*']['editinterface'] = true;
98 $wgGroupPermissions['*']['hiderevision'] = true;
99 $wgGroupPermissions['*']['import'] = true;
100 $wgGroupPermissions['*']['importupload'] = true;
101 $wgGroupPermissions['*']['minoredit'] = true;
102 $wgGroupPermissions['*']['move'] = true;
103 $wgGroupPermissions['*']['patrol'] = true;
104 $wgGroupPermissions['*']['protect'] = true;
105 $wgGroupPermissions['*']['proxyunbannable'] = true;
106 $wgGroupPermissions['*']['renameuser'] = true;
107 $wgGroupPermissions['*']['reupload'] = true;
108 $wgGroupPermissions['*']['reupload-shared'] = true;
109 $wgGroupPermissions['*']['rollback'] = true;
110 $wgGroupPermissions['*']['siteadmin'] = true;
111 $wgGroupPermissions['*']['unwatchedpages'] = true;
112 $wgGroupPermissions['*']['upload'] = true;
113 $wgGroupPermissions['*']['userrights'] = true;
114 $wgGroupPermissions['*']['renameuser'] = true;
115 $wgGroupPermissions['*']['makebot'] = true;
116 $wgGroupPermissions['*']['makesysop'] = true;
117
118 // Enable weird and wonderful options:
119 // Increase default error reporting level.
120 error_reporting (E_ALL); // At a later date could be increased to E_ALL | E_STRICT
121 $wgBlockOpenProxies = true; // Some block pages require this to be true in order to test.
122 $wgEnableUploads = true; // enable uploads.
123 $wgDBerrorLog = "/root/mediawiki-db-error-log.txt"; // log DB errors, replace with suitable path.
124 $wgShowSQLErrors = true; // Show SQL errors (instead of saying the query was hidden).
125 $wgShowExceptionDetails = true; // want backtraces.
126 $wgEnableAPI = true; // enable API.
127 $wgEnableWriteAPI = true; // enable API.
128
129 // Install & enable Parser Hook extensions to increase code coverage. E.g.:
130 require_once("extensions/ParserFunctions/ParserFunctions.php");
131 require_once("extensions/Cite/Cite.php");
132 require_once("extensions/inputbox/inputbox.php");
133 require_once("extensions/Sort/Sort.php");
134 require_once("extensions/wikihiero/wikihiero.php");
135 require_once("extensions/CharInsert/CharInsert.php");
136 require_once("extensions/FixedImage/FixedImage.php");
137
138 // Install & enable Special Page extensions to increase code coverage. E.g.:
139 require_once("extensions/Cite/SpecialCite.php");
140 require_once("extensions/Renameuser/SpecialRenameuser.php");
141 // --------- End ---------
142
143 If you want to try E_STRICT error logging, add this to the above:
144 // --------- Start ---------
145 error_reporting (E_ALL | E_STRICT);
146 set_error_handler( 'error_handler' );
147 function error_handler ($type, $message, $file=__FILE__, $line=__LINE__) {
148 if ($message == "var: Deprecated. Please use the public/private/protected modifiers") return;
149 print "<br />\n<b>Strict Standards:</b> Type: <b>$type</b>: $message in <b>$file</b> on line <b>$line</b><br />\n";
150 }
151 // --------- End ---------
152
153 Also add/change this in LocalSettings.php:
154 // --------- Start ---------
155 $wgEnableProfileInfo = true;
156 $wgDBserver = "localhost"; // replace with DB server hostname
157 // --------- End ---------
158
159 Usage:
160 Run with "php fuzz-tester.php".
161 To see the various command-line options, run "php fuzz-tester.php --help".
162 To stop the script, press Ctrl-C.
163
164 Console output:
165 - If requested, first any previously failed tests will be rerun.
166 - Then new tests will be generated and run. Any tests that fail will be saved,
167 and a brief message about why they failed will be printed on the console.
168 - The console will show the number of tests run, time run, number of tests
169 failed, number of tests being done per minute, and the name of the current test.
170
171 TODO:
172 Some known things that could improve this script:
173 - Logging in with cookie jar storage needed for some tests (as there are some
174 pages that cannot be tested without being logged in, and which are currently
175 untested - e.g. Special:Emailuser, Special:Preferences, adding to Watchist).
176 - Testing of Timeline extension (I cannot test as ploticus has/had issues on
177 my architecture).
178
179 */
180
181 // ///////////////////////// COMMAND LINE HELP ////////////////////////////////////
182
183 // This is a command line script, load MediaWiki env (gives command line options);
184 require_once( __DIR__ . '/commandLine.inc' );
185
186 // if the user asked for an explanation of command line options.
187 if ( isset( $options["help"] ) ) {
188 print <<<ENDS
189 MediaWiki $wgVersion fuzz tester
190 Usage: php {$_SERVER["SCRIPT_NAME"]} [--quiet] [--base-url=<url-to-test-wiki>]
191 [--directory=<failed-test-path>] [--include-binary]
192 [--w3c-validate] [--delete-passed-retests] [--help]
193 [--user=<username>] [--password=<password>]
194 [--rerun-failed-tests] [--max-errors=<int>]
195 [--max-runtime=<num-minutes>]
196 [--specific-test=<test-name>]
197
198 Options:
199 --quiet : Hides passed tests, shows only failed tests.
200 --base-url : URL to a wiki on which to run the tests.
201 The "http://" is optional and can be omitted.
202 --directory : Full path to directory for storing failed tests.
203 Will be created if it does not exist.
204 --include-binary : Includes non-alphanumeric characters in the tests.
205 --w3c-validate : Validates pages using the W3C's web validator.
206 Slow. Currently many pages fail validation.
207 --user : Login name of a valid user on your test wiki.
208 --password : Password for the valid user on your test wiki.
209 --delete-passed-retests : Will delete retests that now pass.
210 Requires --rerun-failed-tests to be meaningful.
211 --rerun-failed-tests : Whether to rerun any previously failed tests.
212 --max-errors : Maximum number of errors to report before exiting.
213 Does not include errors from --rerun-failed-tests
214 --max-runtime : Maximum runtime, in minutes, to run before exiting.
215 Only applies to new tests, not --rerun-failed-tests
216 --specific-test : Runs only the specified fuzz test.
217 Only applies to new tests, not --rerun-failed-tests
218 --keep-passed-tests : Saves all test files, even those that pass.
219 --help : Show this help message.
220
221 Example:
222 If you wanted to fuzz test a nightly MediaWiki checkout using cron for 1 hour,
223 and only wanted to be informed of errors, and did not want to redo previously
224 failed tests, and wanted a maximum of 100 errors, then you could do:
225 php {$_SERVER["SCRIPT_NAME"]} --quiet --max-errors=100 --max-runtime=60
226
227
228 ENDS;
229
230 exit( 0 );
231 }
232
233
234 // if we got command line options, check they look valid.
235 $validOptions = array ( "quiet", "base-url", "directory", "include-binary",
236 "w3c-validate", "user", "password", "delete-passed-retests",
237 "rerun-failed-tests", "max-errors",
238 "max-runtime", "specific-test", "keep-passed-tests", "help" );
239 if ( !empty( $options ) ) {
240 $unknownArgs = array_diff ( array_keys( $options ), $validOptions );
241 foreach ( $unknownArgs as $invalidArg ) {
242 print "Ignoring invalid command-line option: --$invalidArg\n";
243 }
244 }
245
246
247 // /////////////////////////// CONFIGURATION ////////////////////////////////////
248
249 // URL to some wiki on which we can run our tests.
250 if ( !empty( $options["base-url"] ) ) {
251 define( "WIKI_BASE_URL", $options["base-url"] );
252 } else {
253 define( "WIKI_BASE_URL", $wgServer . $wgScriptPath . '/' );
254 }
255
256 // The directory name where we store the output.
257 // Example for Windows: "c:\\temp\\wiki-fuzz"
258 if ( !empty( $options["directory"] ) ) {
259 define( "DIRECTORY", $options["directory"] );
260 } else {
261 define( "DIRECTORY", "{$wgUploadDirectory}/fuzz-tests" );
262 }
263
264 // Should our test fuzz data include binary strings?
265 define( "INCLUDE_BINARY", isset( $options["include-binary"] ) );
266
267 // Whether we want to validate HTML output on the web.
268 // At the moment very few generated pages will validate, so not recommended.
269 define( "VALIDATE_ON_WEB", isset( $options["w3c-validate"] ) );
270 // URL to use to validate our output:
271 define( "VALIDATOR_URL", "http://validator.w3.org/check" );
272
273 // Location of Tidy standalone executable.
274 define( "PATH_TO_TIDY", "/usr/bin/tidy" );
275
276 // The name of a user who has edited on your wiki. Used
277 // when testing the Special:Contributions and Special:Userlogin page.
278 if ( !empty( $options["user"] ) ) {
279 define( "USER_ON_WIKI", $options["user"] );
280 } else {
281 define( "USER_ON_WIKI", "nickj" );
282 }
283
284 // The password of the above user. Used when testing the login page,
285 // and to do this we sometimes need to login successfully.
286 if ( !empty( $options["password"] ) ) {
287 define( "USER_PASSWORD", $options["password"] );
288 } else {
289 // And no, this is not a valid password on any public wiki.
290 define( "USER_PASSWORD", "nickj" );
291 }
292
293 // If we have a test that failed, and then we run it again, and it passes,
294 // do you want to delete it or keep it?
295 define( "DELETE_PASSED_RETESTS", isset( $options["delete-passed-retests"] ) );
296
297 // Do we want to rerun old saved tests at script startup?
298 // Set to true to help catch regressions, or false if you only want new stuff.
299 define( "RERUN_OLD_TESTS", isset( $options["rerun-failed-tests"] ) );
300
301 // File where the database errors are logged. Should be defined in LocalSettings.php.
302 define( "DB_ERROR_LOG_FILE", $wgDBerrorLog );
303
304 // Run in chatty mode (all output, default), or run in quiet mode (only prints out details of failed tests)?
305 define( "QUIET", isset( $options["quiet"] ) );
306
307 // Keep all test files, even those that pass. Potentially useful to tracking input that causes something
308 // unusual to happen, if you don't know what "unusual" is until later.
309 define( "KEEP_PASSED_TESTS", isset( $options["keep-passed-tests"] ) );
310
311 // The maximum runtime, if specified.
312 if ( !empty( $options["max-runtime"] ) && intval( $options["max-runtime"] ) > 0 ) {
313 define( "MAX_RUNTIME", intval( $options["max-runtime"] ) );
314 }
315
316 // The maximum number of problems to find, if specified. Excludes retest errors.
317 if ( !empty( $options["max-errors"] ) && intval( $options["max-errors"] ) > 0 ) {
318 define( "MAX_ERRORS", intval( $options["max-errors"] ) );
319 }
320
321 // if the user has requested a specific test (instead of all tests), and the test they asked for looks valid.
322 if ( !empty( $options["specific-test"] ) ) {
323 if ( class_exists( $options["specific-test"] ) && get_parent_class( $options["specific-test"] ) == "pageTest" ) {
324 define( "SPECIFIC_TEST", $options["specific-test"] );
325 }
326 else {
327 print "Ignoring invalid --specific-test\n";
328 }
329 }
330
331 // Define the file extensions we'll use:
332 define( "PHP_TEST" , ".test.php" );
333 define( "CURL_TEST", ".curl.sh" );
334 define( "DATA_FILE", ".data.bin" );
335 define( "INFO_FILE", ".info.txt" );
336 define( "HTML_FILE", ".wiki_preview.html" );
337
338 // If it goes wrong, we want to know about it.
339 error_reporting( E_ALL | E_STRICT );
340
341 // ////////////// A CLASS THAT GENERATES RANDOM NASTY WIKI & HTML STRINGS //////////////////////
342
343 class wikiFuzz {
344
345 // Only some HTML tags are understood with params by MediaWiki, the rest are ignored.
346 // List the tags that accept params below, as well as what those params are.
347 public static $data = array(
348 "B" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
349 "CAPTION" => array( "CLASS", "ID", "STYLE", "align", "lang", "dir", "title" ),
350 "CENTER" => array( "CLASS", "STYLE", "ID", "lang", "dir", "title" ),
351 "DIV" => array( "CLASS", "STYLE", "ID", "align", "lang", "dir", "title" ),
352 "FONT" => array( "CLASS", "STYLE", "ID", "lang", "dir", "title", "face", "size", "color" ),
353 "H1" => array( "STYLE", "CLASS", "ID", "align", "lang", "dir", "title" ),
354 "H2" => array( "STYLE", "CLASS", "ID", "align", "lang", "dir", "title" ),
355 "HR" => array( "STYLE", "CLASS", "ID", "WIDTH", "lang", "dir", "title", "size", "noshade" ),
356 "LI" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "type", "value" ),
357 "TABLE" => array( "STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "BORDER", "CELLPADDING",
358 "CELLSPACING", "lang", "dir", "title", "summary", "frame", "rules" ),
359 "TD" => array( "STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "COLSPAN", "ROWSPAN",
360 "VALIGN", "abbr", "axis", "headers", "scope", "nowrap", "height", "lang",
361 "dir", "title", "char", "charoff" ),
362 "TH" => array( "STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "COLSPAN", "ROWSPAN",
363 "VALIGN", "abbr", "axis", "headers", "scope", "nowrap", "height", "lang",
364 "dir", "title", "char", "charoff" ),
365 "TR" => array( "CLASS", "STYLE", "ID", "BGCOLOR", "ALIGN", "VALIGN", "lang", "dir", "title", "char", "charoff" ),
366 "UL" => array( "CLASS", "STYLE", "ID", "lang", "dir", "title", "type" ),
367 "P" => array( "style", "class", "id", "align", "lang", "dir", "title" ),
368 "blockquote" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "cite" ),
369 "span" => array( "CLASS", "ID", "STYLE", "align", "lang", "dir", "title" ),
370 "code" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
371 "tt" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
372 "small" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
373 "big" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
374 "s" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
375 "u" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
376 "del" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "datetime", "cite" ),
377 "ins" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "datetime", "cite" ),
378 "sub" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
379 "sup" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
380 "ol" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "type", "start" ),
381 "br" => array( "CLASS", "ID", "STYLE", "title", "clear" ),
382 "cite" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
383 "var" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
384 "ruby" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
385 "rt" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
386 "rp" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
387 "dt" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
388 "dl" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
389 "em" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
390 "strong" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
391 "i" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
392 "thead" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign' ),
393 "tfoot" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign' ),
394 "tbody" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign' ),
395 "colgroup" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign', 'span', 'width' ),
396 "col" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign', 'span', 'width' ),
397 "pre" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "width" ),
398
399 // extension tags that accept parameters:
400 "sort" => array( "order", "class" ),
401 "ref" => array( "name" ),
402 "categorytree" => array( "hideroot", "mode", "style" ),
403 "chemform" => array( "link", "wikilink", "query" ),
404 "section" => array( "begin", "new" ),
405
406 // older MW transclusion.
407 "transclude" => array( "page" ),
408 );
409
410 // The types of the HTML that we will be testing were defined above
411 // Note: this needs to be initialized later to be equal to: array_keys(wikiFuzz::$data);
412 // as such, it also needs to also be publicly modifiable.
413 public static $types;
414
415
416 // Some attribute values.
417 static private $other = array( "&", "=", ":", "?", "\"", "\n", "%n%n%n%n%n%n%n%n%n%n%n%n", "\\" );
418 static private $ints = array(
419 // various numbers
420 "0", "-1", "127", "-7897", "89000", "808080", "90928345",
421 "0xfffffff", "ffff",
422
423 // Different ways of saying: '
424 "&#0000039;", // Long UTF-8 Unicode encoding
425 "&#39;", // dec version.
426 "&#x27;", // hex version.
427 "&#xA7;", // malformed hex variant, MSB not zero.
428
429 // Different ways of saying: "
430 "&#0000034;", // Long UTF-8 Unicode encoding
431 "&#34;",
432 "&#x22;", // hex version.
433 "&#xA2;", // malformed hex variant, MSB not zero.
434
435 // Different ways of saying: <
436 "<",
437 "&#0000060", // Long UTF-8 Unicode encoding without semicolon (Mediawiki wants the colon)
438 "&#0000060;", // Long UTF-8 Unicode encoding with semicolon
439 "&#60;",
440 "&#x3C;", // hex version.
441 "&#xBC;", // malformed hex variant, MSB not zero.
442 "&#x0003C;", // mid-length hex version
443 "&#X00003C;", // slightly longer hex version, with capital "X"
444
445 // Different ways of saying: >
446 ">",
447 "&#0000062;", // Long UTF-8 Unicode encoding
448 "&#62;",
449 "&#x3E;", // hex version.
450 "&#xBE;", // malformed variant, MSB not zero.
451
452 // Different ways of saying: [
453 "&#0000091;", // Long UTF-8 Unicode encoding
454 "&#91;",
455 "&#x5B;", // hex version.
456
457 // Different ways of saying: {{
458 "&#0000123;&#0000123;", // Long UTF-8 Unicode encoding
459 "&#123;&#123;",
460 "&#x7B;&#x7B;", // hex version.
461
462 // Different ways of saying: |
463 "&#0000124;", // Long UTF-8 Unicode encoding
464 "&#124;",
465 "&#x7C;", // hex version.
466 "&#xFC;", // malformed hex variant, MSB not zero.
467
468 // a "lignature" - http://www.robinlionheart.com/stds/html4/spchars#ligature
469 // &#8204; == &zwnj;
470 "&#8204;"
471 );
472
473 // Defines various wiki-related bits of syntax, that can potentially cause
474 // MediaWiki to do something other than just print that literal text.
475 static private $ext = array(
476 // links, templates, parameters.
477 "[[", "]]", "{{", "}}", "|", "[", "]", "{{{", "}}}", "|]]",
478
479 // wiki tables.
480 "\n{|", "\n|}",
481 "!",
482 "\n!",
483 "!!",
484 "||",
485 "\n|-", "| ", "\n|",
486
487 // section headings.
488 "=", "==", "===", "====", "=====", "======",
489
490 // lists (ordered and unordered) and indentation.
491 "\n*", "*", "\n:", ":",
492 "\n#", "#",
493
494 // definition lists (dl, dt, dd), newline, and newline with pre, and a tab.
495 "\n;", ";", "\n ",
496
497 // Whitespace: newline, tab, space.
498 "\n", "\t", " ",
499
500 // Some XSS attack vectors from http://ha.ckers.org/xss.html
501 "&#x09;", // tab
502 "&#x0A;", // newline
503 "&#x0D;", // carriage return
504 "\0", // null character
505 " &#14; ", // spaces and meta characters
506 "'';!--\"<XSS>=&{()}", // compact injection of XSS & SQL tester
507
508 // various NULL fields
509 "%00",
510 "&#00;",
511 "\0",
512
513 // horizontal rule.
514 "-----", "\n-----",
515
516 // signature, redirect, bold, italics.
517 "~~~~", "#REDIRECT [[", "'''", "''",
518
519 // comments.
520 "<!--", "-->",
521
522 // quotes.
523 "\"", "'",
524
525 // tag start and tag end.
526 "<", ">",
527
528 // implicit link creation on URIs.
529 "http://",
530 "https://",
531 "ftp://",
532 "irc://",
533 "news:",
534 'gopher://',
535 'telnet://',
536 'nntp://',
537 'worldwind://',
538 'mailto:',
539
540 // images.
541 "[[image:",
542 ".gif",
543 ".png",
544 ".jpg",
545 ".jpeg",
546 'thumbnail=',
547 'thumbnail',
548 'thumb=',
549 'thumb',
550 'right',
551 'none',
552 'left',
553 'framed',
554 'frame',
555 'enframed',
556 'centre',
557 'center',
558 "Image:",
559 "[[:Image",
560 'px',
561 'upright=',
562 'border',
563
564 // misc stuff to throw at the Parser.
565 '%08X',
566 '/',
567 ":x{|",
568 "\n|+",
569 "<noinclude>",
570 "</noinclude>",
571 " \302\273",
572 " :",
573 " !",
574 " ;",
575 "\302\253",
576 "[[category:",
577 "?=",
578 "(",
579 ")",
580 "]]]",
581 "../",
582 "{{{{",
583 "}}}}",
584 "[[Special:",
585 "<includeonly>",
586 "</includeonly>",
587 "<!--MWTEMPLATESECTION=",
588 '<!--MWTOC-->',
589
590 // implicit link creation on booknum, RFC, and PubMed ID usage (both with and without IDs)
591 "ISBN 2",
592 "RFC 000",
593 "PMID 000",
594 "ISBN ",
595 "RFC ",
596 "PMID ",
597
598 // magic words:
599 '__NOTOC__',
600 '__FORCETOC__',
601 '__NOEDITSECTION__',
602 '__START__',
603 '__NOTITLECONVERT__',
604 '__NOCONTENTCONVERT__',
605 '__END__',
606 '__TOC__',
607 '__NOTC__',
608 '__NOCC__',
609 "__FORCETOC__",
610 "__NEWSECTIONLINK__",
611 "__NOGALLERY__",
612
613 // more magic words / internal templates.
614 '{{PAGENAME}}',
615 '{{PAGENAMEE}}',
616 '{{NAMESPACE}}',
617 "{{MSG:",
618 "}}",
619 "{{MSGNW:",
620 "}}",
621 "{{INT:",
622 "}}",
623 '{{SITENAME}}',
624 "{{NS:",
625 "}}",
626 "{{LOCALURL:",
627 "}}",
628 "{{LOCALURLE:",
629 "}}",
630 "{{SCRIPTPATH}}",
631 "{{GRAMMAR:gentiv|",
632 "}}",
633 "{{REVISIONID}}",
634 "{{SUBPAGENAME}}",
635 "{{SUBPAGENAMEE}}",
636 "{{ns:0}}",
637 "{{fullurle:",
638 "}}",
639 "{{subst::",
640 "}}",
641 "{{UCFIRST:",
642 "}}",
643 "{{UC:",
644 '{{SERVERNAME}}',
645 '{{SERVER}}',
646 "{{RAW:",
647 "}}",
648 "{{PLURAL:",
649 "}}",
650 "{{LCFIRST:",
651 "}}",
652 "{{LC:",
653 "}}",
654 '{{CURRENTWEEK}}',
655 '{{CURRENTDOW}}',
656 "{{INT:{{LC:contribs-showhideminor}}|",
657 "}}",
658 "{{INT:googlesearch|",
659 "}}",
660 "{{ROOTPAGENAME}}",
661 "{{BASEPAGENAME}}",
662 "{{CONTENTLANGUAGE}}",
663 "{{PAGESINNAMESPACE:}}",
664 "{{#language:",
665 "}}",
666 "{{#special:",
667 "}}",
668 "{{#special:emailuser",
669 "}}",
670
671 // Some raw link for magic words.
672 "{{NUMBEROFPAGES:R",
673 "}}",
674 "{{NUMBEROFUSERS:R",
675 "}}",
676 "{{NUMBEROFARTICLES:R",
677 "}}",
678 "{{NUMBEROFFILES:R",
679 "}}",
680 "{{NUMBEROFADMINS:R",
681 "}}",
682 "{{padleft:",
683 "}}",
684 "{{padright:",
685 "}}",
686 "{{DEFAULTSORT:",
687 "}}",
688
689 // internal Math "extension":
690 "<math>",
691 "</math>",
692
693 // Parser extension functions:
694 "{{#expr:",
695 "{{#if:",
696 "{{#ifeq:",
697 "{{#ifexist:",
698 "{{#ifexpr:",
699 "{{#switch:",
700 "{{#time:",
701 "}}",
702
703 // references table for the Cite extension.
704 "<references/>",
705
706 // Internal Parser tokens - try inserting some of these.
707 "UNIQ25f46b0524f13e67NOPARSE",
708 "UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002",
709 "\x07UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002-QINU",
710
711 // Inputbox extension:
712 "<inputbox>\ntype=search\nsearchbuttonlabel=\n",
713 "</inputbox>",
714
715 // charInsert extension:
716 "<charInsert>",
717 "</charInsert>",
718
719 // wikiHiero extension:
720 "<hiero>",
721 "</hiero>",
722
723 // Image gallery:
724 "<gallery>",
725 "</gallery>",
726
727 // FixedImage extension.
728 "<fundraising/>",
729
730 // Timeline extension: currently untested.
731
732 // Nowiki:
733 "<nOwIkI>",
734 "</nowiki>",
735
736 // an external image to test the external image displaying code
737 "http://debian.org/Pics/debian.png",
738
739 // LabeledSectionTransclusion extension.
740 "{{#lstx:",
741 "}}",
742 "{{#lst:",
743 "}}",
744 "{{#lst:Main Page|",
745 "}}"
746 );
747
748 /**
749 ** Randomly returns one element of the input array.
750 */
751 public static function chooseInput( array $input ) {
752 $randindex = wikiFuzz::randnum( count( $input ) - 1 );
753 return $input[$randindex];
754 }
755
756 // Max number of parameters for HTML attributes.
757 static private $maxparams = 10;
758
759 /**
760 * Returns random number between finish and start.
761 * @param $finish
762 * @param $start int
763 * @return int
764 */
765 public static function randnum( $finish, $start = 0 ) {
766 return mt_rand( $start, $finish );
767 }
768
769 /**
770 * Returns a mix of random text and random wiki syntax.
771 * @return string
772 */
773 private static function randstring() {
774 $thestring = "";
775
776 for ( $i = 0; $i < 40; $i++ ) {
777 $what = wikiFuzz::randnum( 1 );
778
779 if ( $what == 0 ) { // include some random wiki syntax
780 $which = wikiFuzz::randnum( count( wikiFuzz::$ext ) - 1 );
781 $thestring .= wikiFuzz::$ext[$which];
782 }
783 else { // include some random text
784 $char = INCLUDE_BINARY
785 // Decimal version:
786 // "&#" . wikiFuzz::randnum(255) . ";"
787 // Hex version:
788 ? "&#x" . str_pad( dechex( wikiFuzz::randnum( 255 ) ), wikiFuzz::randnum( 2, 7 ), "0", STR_PAD_LEFT ) . ";"
789 // A truly binary version:
790 // ? chr(wikiFuzz::randnum(0,255))
791 : chr( wikiFuzz::randnum( 126, 32 ) );
792
793 $length = wikiFuzz::randnum( 8 );
794 $thestring .= str_repeat ( $char, $length );
795 }
796 }
797 return $thestring;
798 }
799
800 /**
801 * Returns either random text, or random wiki syntax, or random data from "ints",
802 * or random data from "other".
803 * @return string
804 */
805 private static function makestring() {
806 $what = wikiFuzz::randnum( 2 );
807 if ( $what == 0 ) {
808 return wikiFuzz::randstring();
809 } elseif ( $what == 1 ) {
810 return wikiFuzz::$ints[wikiFuzz::randnum( count( wikiFuzz::$ints ) - 1 )];
811 } else {
812 return wikiFuzz::$other[wikiFuzz::randnum( count( wikiFuzz::$other ) - 1 )];
813 }
814 }
815
816 /**
817 * Returns the matched character slash-escaped as in a C string
818 * Helper for makeTitleSafe callback
819 * @param $matches
820 * @return string
821 */
822 private static function stringEscape( $matches ) {
823 return sprintf( "\\x%02x", ord( $matches[1] ) );
824 }
825
826 /**
827 ** Strips out the stuff that Mediawiki balks at in a page's title.
828 ** Implementation copied/pasted from cleanupTable.inc & cleanupImages.php
829 * @param $str string
830 * @return string
831 */
832 public static function makeTitleSafe( $str ) {
833 $legalTitleChars = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF";
834 return preg_replace_callback(
835 "/([^$legalTitleChars])/", 'wikiFuzz::stringEscape',
836 $str );
837 }
838
839 /**
840 ** Returns a string of fuzz text.
841 * @return string
842 */
843 private static function loop() {
844 switch ( wikiFuzz::randnum( 3 ) ) {
845 case 1: // an opening tag, with parameters.
846 $string = "";
847 $i = wikiFuzz::randnum( count( wikiFuzz::$types ) - 1 );
848 $t = wikiFuzz::$types[$i];
849 $arr = wikiFuzz::$data[$t];
850 $string .= "<" . $t . " ";
851 $num_params = min( wikiFuzz::$maxparams, count( $arr ) );
852 for ( $z = 0; $z < $num_params; $z++ ) {
853 $badparam = $arr[wikiFuzz::randnum( count( $arr ) - 1 )];
854 $badstring = wikiFuzz::makestring();
855 $string .= $badparam . "=" . wikiFuzz::getRandQuote() . $badstring . wikiFuzz::getRandQuote() . " ";
856 }
857 $string .= ">\n";
858 return $string;
859 case 2: // a closing tag.
860 $i = wikiFuzz::randnum( count( wikiFuzz::$types ) - 1 );
861 return "</" . wikiFuzz::$types[$i] . ">";
862 case 3: // a random string, between tags.
863 return wikiFuzz::makeString();
864 }
865 return ""; // catch-all, should never be called.
866 }
867
868 /**
869 * Returns one of the three styles of random quote: ', ", and nothing.
870 * @return string
871 */
872 private static function getRandQuote() {
873 switch ( wikiFuzz::randnum( 3 ) ) {
874 case 1 : return "'";
875 case 2 : return "\"";
876 default: return "";
877 }
878 }
879
880 /**
881 ** Returns fuzz text, with the parameter indicating approximately how many lines of text you want.
882 * @param $maxtypes int
883 * @return string
884 */
885 public static function makeFuzz( $maxtypes = 2 ) {
886 $page = "";
887 for ( $k = 0; $k < $maxtypes; $k++ ) {
888 $page .= wikiFuzz::loop();
889 }
890 return $page;
891 }
892 }
893
894
895 // ////// MEDIAWIKI PAGES TO TEST, AND HOW TO TEST THEM ///////
896
897 /**
898 ** A page test has just these things:
899 ** 1) Form parameters.
900 ** 2) the URL we are going to test those parameters on.
901 ** 3) Any cookies required for the test.
902 ** 4) Whether Tidy should validate the page. Defaults to true, but can be turned off.
903 ** Declared abstract because it should be extended by a class
904 ** that supplies these parameters.
905 */
906 abstract class pageTest {
907 protected $params;
908 protected $pagePath;
909 protected $cookie = "";
910 protected $tidyValidate = true;
911
912 public function getParams() {
913 return $this->params;
914 }
915
916 public function getPagePath() {
917 return $this->pagePath;
918 }
919
920 public function getCookie() {
921 return $this->cookie;
922 }
923
924 public function tidyValidate() {
925 return $this->tidyValidate;
926 }
927 }
928
929
930 /**
931 ** a page test for the "Edit" page. Tests Parser.php and Sanitizer.php.
932 */
933 class editPageTest extends pageTest {
934 function __construct() {
935 $this->pagePath = "index.php?title=WIKIFUZZ";
936
937 $this->params = array (
938 "action" => "submit",
939 "wpMinoredit" => wikiFuzz::makeFuzz( 2 ),
940 "wpPreview" => wikiFuzz::makeFuzz( 2 ),
941 "wpSection" => wikiFuzz::makeFuzz( 2 ),
942 "wpEdittime" => wikiFuzz::makeFuzz( 2 ),
943 "wpSummary" => wikiFuzz::makeFuzz( 2 ),
944 "wpScrolltop" => wikiFuzz::makeFuzz( 2 ),
945 "wpStarttime" => wikiFuzz::makeFuzz( 2 ),
946 "wpAutoSummary" => wikiFuzz::makeFuzz( 2 ),
947 "wpTextbox1" => wikiFuzz::makeFuzz( 40 ) // the main wiki text, need lots of this.
948 );
949
950 // sometimes we don't want to specify certain parameters.
951 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpSection"] );
952 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpEdittime"] );
953 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpSummary"] );
954 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpScrolltop"] );
955 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpStarttime"] );
956 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpAutoSummary"] );
957 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpTextbox1"] );
958 }
959 }
960
961
962 /**
963 ** a page test for "Special:Listusers".
964 */
965 class listusersTest extends pageTest {
966 function __construct() {
967 $this->pagePath = "index.php?title=Special:Listusers";
968
969 $this->params = array (
970 "title" => wikiFuzz::makeFuzz( 2 ),
971 "group" => wikiFuzz::makeFuzz( 2 ),
972 "username" => wikiFuzz::makeFuzz( 2 ),
973 "Go" => wikiFuzz::makeFuzz( 2 ),
974 "limit" => wikiFuzz::chooseInput( array( "0", "-1", "---'----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
975 "offset" => wikiFuzz::chooseInput( array( "0", "-1", "--------'-----0", "+1", "81343242346234234", wikiFuzz::makeFuzz( 2 ) ) )
976 );
977 }
978 }
979
980
981 /**
982 ** a page test for "Special:Search".
983 */
984 class searchTest extends pageTest {
985 function __construct() {
986 $this->pagePath = "index.php?title=Special:Search";
987
988 $this->params = array (
989 "action" => "index.php?title=Special:Search",
990 "ns0" => wikiFuzz::makeFuzz( 2 ),
991 "ns1" => wikiFuzz::makeFuzz( 2 ),
992 "ns2" => wikiFuzz::makeFuzz( 2 ),
993 "ns3" => wikiFuzz::makeFuzz( 2 ),
994 "ns4" => wikiFuzz::makeFuzz( 2 ),
995 "ns5" => wikiFuzz::makeFuzz( 2 ),
996 "ns6" => wikiFuzz::makeFuzz( 2 ),
997 "ns7" => wikiFuzz::makeFuzz( 2 ),
998 "ns8" => wikiFuzz::makeFuzz( 2 ),
999 "ns9" => wikiFuzz::makeFuzz( 2 ),
1000 "ns10" => wikiFuzz::makeFuzz( 2 ),
1001 "ns11" => wikiFuzz::makeFuzz( 2 ),
1002 "ns12" => wikiFuzz::makeFuzz( 2 ),
1003 "ns13" => wikiFuzz::makeFuzz( 2 ),
1004 "ns14" => wikiFuzz::makeFuzz( 2 ),
1005 "ns15" => wikiFuzz::makeFuzz( 2 ),
1006 "redirs" => wikiFuzz::makeFuzz( 2 ),
1007 "search" => wikiFuzz::makeFuzz( 2 ),
1008 "offset" => wikiFuzz::chooseInput( array( "", "0", "-1", "--------'-----0", "+1", "81343242346234234", wikiFuzz::makeFuzz( 2 ) ) ),
1009 "fulltext" => wikiFuzz::chooseInput( array( "", "0", "1", "--------'-----0", "+1", wikiFuzz::makeFuzz( 2 ) ) ),
1010 "searchx" => wikiFuzz::chooseInput( array( "", "0", "1", "--------'-----0", "+1", wikiFuzz::makeFuzz( 2 ) ) )
1011 );
1012 }
1013 }
1014
1015
1016 /**
1017 ** a page test for "Special:Recentchanges".
1018 */
1019 class recentchangesTest extends pageTest {
1020 function __construct() {
1021 $this->pagePath = "index.php?title=Special:Recentchanges";
1022
1023 $this->params = array (
1024 "action" => wikiFuzz::makeFuzz( 2 ),
1025 "title" => wikiFuzz::makeFuzz( 2 ),
1026 "namespace" => wikiFuzz::chooseInput( range( -1, 15 ) ),
1027 "Go" => wikiFuzz::makeFuzz( 2 ),
1028 "invert" => wikiFuzz::chooseInput( array( "-1", "---'----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1029 "hideanons" => wikiFuzz::chooseInput( array( "-1", "------'-------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1030 'limit' => wikiFuzz::chooseInput( array( "0", "-1", "---------'----0", "+1", "81340909772349234", wikiFuzz::makeFuzz( 2 ) ) ),
1031 "days" => wikiFuzz::chooseInput( array( "-1", "----------'---0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1032 "hideminor" => wikiFuzz::chooseInput( array( "-1", "-----------'--0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1033 "hidebots" => wikiFuzz::chooseInput( array( "-1", "---------'----0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1034 "hideliu" => wikiFuzz::chooseInput( array( "-1", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1035 "hidepatrolled" => wikiFuzz::chooseInput( array( "-1", "-----'--------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1036 "hidemyself" => wikiFuzz::chooseInput( array( "-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1037 'categories_any' => wikiFuzz::chooseInput( array( "-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1038 'categories' => wikiFuzz::chooseInput( array( "-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1039 'feed' => wikiFuzz::chooseInput( array( "-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) )
1040 );
1041 }
1042 }
1043
1044
1045 /**
1046 ** a page test for "Special:Prefixindex".
1047 */
1048 class prefixindexTest extends pageTest {
1049 function __construct() {
1050 $this->pagePath = "index.php?title=Special:Prefixindex";
1051
1052 $this->params = array (
1053 "title" => "Special:Prefixindex",
1054 "namespace" => wikiFuzz::randnum( 101, -10 ),
1055 "Go" => wikiFuzz::makeFuzz( 2 )
1056 );
1057
1058 // sometimes we want 'prefix', sometimes we want 'from', and sometimes we want nothing.
1059 if ( wikiFuzz::randnum( 3 ) == 0 ) {
1060 $this->params["prefix"] = wikiFuzz::chooseInput( array( "-1", "-----'--------0", "+++--+1",
1061 wikiFuzz::randnum( 8134, -10 ), wikiFuzz::makeFuzz( 2 ) ) );
1062 }
1063 if ( wikiFuzz::randnum( 3 ) == 0 ) {
1064 $this->params["from"] = wikiFuzz::chooseInput( array( "-1", "-----'--------0", "+++--+1",
1065 wikiFuzz::randnum( 8134, -10 ), wikiFuzz::makeFuzz( 2 ) ) );
1066 }
1067 }
1068 }
1069
1070
1071 /**
1072 ** a page test for "Special:MIMEsearch".
1073 */
1074 class mimeSearchTest extends pageTest {
1075 function __construct() {
1076 $this->pagePath = "index.php?title=Special:MIMEsearch";
1077
1078 $this->params = array (
1079 "action" => "index.php?title=Special:MIMEsearch",
1080 "mime" => wikiFuzz::makeFuzz( 3 ),
1081 'limit' => wikiFuzz::chooseInput( array( "0", "-1", "-------'------0", "+1", "81342321351235325", wikiFuzz::makeFuzz( 2 ) ) ),
1082 'offset' => wikiFuzz::chooseInput( array( "0", "-1", "-----'--------0", "+1", "81341231235365252234324", wikiFuzz::makeFuzz( 2 ) ) )
1083 );
1084 }
1085 }
1086
1087
1088 /**
1089 ** a page test for "Special:Log".
1090 */
1091 class specialLogTest extends pageTest {
1092 function __construct() {
1093 $this->pagePath = "index.php?title=Special:Log";
1094
1095 $this->params = array (
1096 "type" => wikiFuzz::chooseInput( array( "", wikiFuzz::makeFuzz( 2 ) ) ),
1097 "par" => wikiFuzz::makeFuzz( 2 ),
1098 "user" => wikiFuzz::makeFuzz( 2 ),
1099 "page" => wikiFuzz::makeFuzz( 2 ),
1100 "from" => wikiFuzz::makeFuzz( 2 ),
1101 "until" => wikiFuzz::makeFuzz( 2 ),
1102 "title" => wikiFuzz::makeFuzz( 2 )
1103 );
1104 }
1105 }
1106
1107
1108 /**
1109 ** a page test for "Special:Userlogin", with a successful login.
1110 */
1111 class successfulUserLoginTest extends pageTest {
1112 function __construct() {
1113 $this->pagePath = "index.php?title=Special:Userlogin&action=submitlogin&type=login&returnto=" . wikiFuzz::makeFuzz( 2 );
1114
1115 $this->params = array (
1116 "wpName" => USER_ON_WIKI,
1117 // sometimes real password, sometimes not:
1118 'wpPassword' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), USER_PASSWORD ) ),
1119 'wpRemember' => wikiFuzz::makeFuzz( 2 )
1120 );
1121
1122 $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array( "1" , wikiFuzz::makeFuzz( 2 ) ) );
1123 }
1124 }
1125
1126
1127 /**
1128 ** a page test for "Special:Userlogin".
1129 */
1130 class userLoginTest extends pageTest {
1131 function __construct() {
1132
1133 $this->pagePath = "index.php?title=Special:Userlogin";
1134
1135 $this->params = array (
1136 'wpRetype' => wikiFuzz::makeFuzz( 2 ),
1137 'wpRemember' => wikiFuzz::makeFuzz( 2 ),
1138 'wpRealName' => wikiFuzz::makeFuzz( 2 ),
1139 'wpPassword' => wikiFuzz::makeFuzz( 2 ),
1140 'wpName' => wikiFuzz::makeFuzz( 2 ),
1141 'wpMailmypassword' => wikiFuzz::makeFuzz( 2 ),
1142 'wpLoginattempt' => wikiFuzz::makeFuzz( 2 ),
1143 'wpEmail' => wikiFuzz::makeFuzz( 2 ),
1144 'wpDomain' => wikiFuzz::chooseInput( array( "", "local", wikiFuzz::makeFuzz( 2 ) ) ),
1145 'wpCreateaccountMail' => wikiFuzz::chooseInput( array( "", wikiFuzz::makeFuzz( 2 ) ) ),
1146 'wpCreateaccount' => wikiFuzz::chooseInput( array( "", wikiFuzz::makeFuzz( 2 ) ) ),
1147 'wpCookieCheck' => wikiFuzz::chooseInput( array( "", wikiFuzz::makeFuzz( 2 ) ) ),
1148 'type' => wikiFuzz::chooseInput( array( "signup", "login", "", wikiFuzz::makeFuzz( 2 ) ) ),
1149 'returnto' => wikiFuzz::makeFuzz( 2 ),
1150 'action' => wikiFuzz::chooseInput( array( "", "submitlogin", wikiFuzz::makeFuzz( 2 ) ) )
1151 );
1152
1153 $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array( "1" , wikiFuzz::makeFuzz( 2 ) ) );
1154 }
1155 }
1156
1157
1158 /**
1159 ** a page test for "Special:Ipblocklist" (also includes unblocking)
1160 */
1161 class ipblocklistTest extends pageTest {
1162 function __construct() {
1163 $this->pagePath = "index.php?title=Special:Ipblocklist";
1164
1165 $this->params = array (
1166 'wpUnblockAddress' => wikiFuzz::makeFuzz( 2 ),
1167 'ip' => wikiFuzz::chooseInput( array( "20398702394", "", "Nickj2", wikiFuzz::makeFuzz( 2 ),
1168 // something like an IP address, sometimes invalid:
1169 ( wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) . "."
1170 . wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) ) ) ),
1171 'id' => wikiFuzz::makeFuzz( 2 ),
1172 'wpUnblockReason' => wikiFuzz::makeFuzz( 2 ),
1173 'action' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), "success", "submit", "unblock" ) ),
1174 'wpEditToken' => wikiFuzz::makeFuzz( 2 ),
1175 'wpBlock' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), "" ) ),
1176 'limit' => wikiFuzz::chooseInput( array( "0", "-1", "--------'-----0", "+1",
1177 "09700982312351132098234", wikiFuzz::makeFuzz( 2 ) ) ),
1178 'offset' => wikiFuzz::chooseInput( array( "0", "-1", "------'-------0", "+1",
1179 "09700980982341535324234234", wikiFuzz::makeFuzz( 2 ) ) )
1180 );
1181
1182 // sometimes we don't want to specify certain parameters.
1183 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["action"] );
1184 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["ip"] );
1185 if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["id"] );
1186 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["wpUnblockAddress"] );
1187 }
1188 }
1189
1190
1191 /**
1192 ** a page test for "Special:Newimages".
1193 */
1194 class newImagesTest extends pageTest {
1195 function __construct() {
1196 $this->pagePath = "index.php?title=Special:Newimages";
1197
1198 $this->params = array (
1199 'hidebots' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), "1", "", "-1" ) ),
1200 'wpIlMatch' => wikiFuzz::makeFuzz( 2 ),
1201 'until' => wikiFuzz::makeFuzz( 2 ),
1202 'from' => wikiFuzz::makeFuzz( 2 )
1203 );
1204
1205 // sometimes we don't want to specify certain parameters.
1206 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["until"] );
1207 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["from"] );
1208 }
1209 }
1210
1211
1212 /**
1213 ** a page test for the "Special:Imagelist" page.
1214 */
1215 class imagelistTest extends pageTest {
1216 function __construct() {
1217 $this->pagePath = "index.php?title=Special:Imagelist";
1218
1219 $this->params = array (
1220 'sort' => wikiFuzz::chooseInput( array( "bysize", "byname" , "bydate", wikiFuzz::makeFuzz( 2 ) ) ),
1221 'limit' => wikiFuzz::chooseInput( array( "0", "-1", "--------'-----0", "+1", "09700982312351132098234", wikiFuzz::makeFuzz( 2 ) ) ),
1222 'offset' => wikiFuzz::chooseInput( array( "0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz( 2 ) ) ),
1223 'wpIlMatch' => wikiFuzz::makeFuzz( 2 )
1224 );
1225 }
1226 }
1227
1228
1229 /**
1230 ** a page test for "Special:Export".
1231 */
1232 class specialExportTest extends pageTest {
1233 function __construct() {
1234 $this->pagePath = "index.php?title=Special:Export";
1235
1236 $this->params = array (
1237 'action' => wikiFuzz::chooseInput( array( "submit", "", wikiFuzz::makeFuzz( 2 ) ) ),
1238 'pages' => wikiFuzz::makeFuzz( 2 ),
1239 'curonly' => wikiFuzz::chooseInput( array( "", "0", "-1", wikiFuzz::makeFuzz( 2 ) ) ),
1240 'listauthors' => wikiFuzz::chooseInput( array( "", "0", "-1", wikiFuzz::makeFuzz( 2 ) ) ),
1241 'history' => wikiFuzz::chooseInput( array( "0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz( 2 ) ) ),
1242
1243 );
1244
1245 // For the time being, need to disable "submit" action as Tidy barfs on MediaWiki's XML export.
1246 if ( $this->params['action'] == 'submit' ) $this->params['action'] = '';
1247
1248 // Sometimes remove the history field.
1249 if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["history"] );
1250
1251 // page does not produce HTML.
1252 $this->tidyValidate = false;
1253 }
1254 }
1255
1256
1257 /**
1258 ** a page test for "Special:Booksources".
1259 */
1260 class specialBooksourcesTest extends pageTest {
1261 function __construct() {
1262 $this->pagePath = "index.php?title=Special:Booksources";
1263
1264 $this->params = array (
1265 'go' => wikiFuzz::makeFuzz( 2 ),
1266 // ISBN codes have to contain some semi-numeric stuff or will be ignored:
1267 'isbn' => "0X0" . wikiFuzz::makeFuzz( 2 )
1268 );
1269 }
1270 }
1271
1272
1273 /**
1274 ** a page test for "Special:Allpages".
1275 */
1276 class specialAllpagesTest extends pageTest {
1277 function __construct() {
1278 $this->pagePath = "index.php?title=Special%3AAllpages";
1279
1280 $this->params = array (
1281 'from' => wikiFuzz::makeFuzz( 2 ),
1282 'namespace' => wikiFuzz::chooseInput( range( -1, 15 ) ),
1283 'go' => wikiFuzz::makeFuzz( 2 )
1284 );
1285 }
1286 }
1287
1288
1289 /**
1290 ** a page test for the page History.
1291 */
1292 class pageHistoryTest extends pageTest {
1293 function __construct() {
1294 $this->pagePath = "index.php?title=Main_Page&action=history";
1295
1296 $this->params = array (
1297 'limit' => wikiFuzz::chooseInput( array( "-1", "0", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1298 'offset' => wikiFuzz::chooseInput( array( "-1", "0", "------'-------0", "+1", "9823412312312412435", wikiFuzz::makeFuzz( 2 ) ) ),
1299 "go" => wikiFuzz::chooseInput( array( "first", "last", wikiFuzz::makeFuzz( 2 ) ) ),
1300 "dir" => wikiFuzz::chooseInput( array( "prev", "next", wikiFuzz::makeFuzz( 2 ) ) ),
1301 "diff" => wikiFuzz::chooseInput( array( "-1", "--------'-----0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1302 "oldid" => wikiFuzz::chooseInput( array( "prev", "-1", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1303 "feed" => wikiFuzz::makeFuzz( 2 )
1304 );
1305 }
1306 }
1307
1308
1309 /**
1310 ** a page test for the Special:Contributions".
1311 */
1312 class contributionsTest extends pageTest {
1313 function __construct() {
1314 $this->pagePath = "index.php?title=Special:Contributions/" . USER_ON_WIKI;
1315
1316 $this->params = array (
1317 'target' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), "newbies", USER_ON_WIKI ) ),
1318 'namespace' => wikiFuzz::chooseInput( array( -1, 15, 1, wikiFuzz::makeFuzz( 2 ) ) ),
1319 'offset' => wikiFuzz::chooseInput( array( "0", "-1", "------'-------0", "+1", "982342131232131231241", wikiFuzz::makeFuzz( 2 ) ) ),
1320 'bot' => wikiFuzz::chooseInput( array( "", "-1", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
1321 'go' => wikiFuzz::chooseInput( array( "-1", 'prev', 'next', wikiFuzz::makeFuzz( 2 ) ) )
1322 );
1323 }
1324 }
1325
1326
1327 /**
1328 ** a page test for viewing a normal page, whilst posting various params.
1329 */
1330 class viewPageTest extends pageTest {
1331 function __construct() {
1332 $this->pagePath = "index.php?title=Main_Page";
1333
1334 $this->params = array (
1335 "useskin" => wikiFuzz::chooseInput( array( "chick", "cologneblue", "myskin",
1336 "nostalgia", "simple", "standard", wikiFuzz::makeFuzz( 2 ) ) ),
1337 "uselang" => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ),
1338 "ab", "af", "an", "ar", "arc", "as", "ast", "av", "ay", "az", "ba",
1339 "bat-smg", "be", "bg", "bm", "bn", "bo", "bpy", "br", "bs", "ca",
1340 "ce", "cs", "csb", "cv", "cy", "da", "de", "dv", "dz", "el", "en",
1341 "eo", "es", "et", "eu", "fa", "fi", "fo", "fr", "fur", "fy", "ga",
1342 "gn", "gsw", "gu", "he", "hi", "hr", "hu", "ia", "id", "ii", "is",
1343 "it", "ja", "jv", "ka", "km", "kn", "ko", "ks", "ku", "kv", "la",
1344 "li", "lo", "lt", "lv", "mk", "ml", "ms", "nah", "nap", "nds",
1345 "nds-nl", "nl", "nn", "no", "non", "nv", "oc", "or", "os", "pa",
1346 "pl", "pms", "ps", "pt", "pt-br", "qu", "rmy", "ro", "ru", "sc",
1347 "sd", "sk", "sl", "sq", "sr", "sr-ec", "sr-el",
1348 "su", "sv", "ta", "te", "th", "tr", "tt", "ty", "tyv", "udm",
1349 "ug", "uk", "ur", "utf8", "vec", "vi", "wa", "xal", "yi", "za",
1350 "zh", "zh-cn", "zh-hk", "zh-sg", "zh-tw", "zh-tw" ) ),
1351 "returnto" => wikiFuzz::makeFuzz( 2 ),
1352 "feed" => wikiFuzz::chooseInput( array( "atom", "rss", wikiFuzz::makeFuzz( 2 ) ) ),
1353 "rcid" => wikiFuzz::makeFuzz( 2 ),
1354 "action" => wikiFuzz::chooseInput( array( "view", "raw", "render", wikiFuzz::makeFuzz( 2 ), "markpatrolled" ) ),
1355 "printable" => wikiFuzz::makeFuzz( 2 ),
1356 "oldid" => wikiFuzz::makeFuzz( 2 ),
1357 "redirect" => wikiFuzz::makeFuzz( 2 ),
1358 "diff" => wikiFuzz::makeFuzz( 2 ),
1359 "search" => wikiFuzz::makeFuzz( 2 ),
1360 "rdfrom" => wikiFuzz::makeFuzz( 2 ), // things from Article.php from here on:
1361 "token" => wikiFuzz::makeFuzz( 2 ),
1362 "tbid" => wikiFuzz::makeFuzz( 2 ),
1363 // @todo FIXME: Duplicate array key.
1364 "action" => wikiFuzz::chooseInput( array( "purge", wikiFuzz::makeFuzz( 2 ) ) ),
1365 "wpReason" => wikiFuzz::makeFuzz( 2 ),
1366 "wpEditToken" => wikiFuzz::makeFuzz( 2 ),
1367 "from" => wikiFuzz::makeFuzz( 2 ),
1368 "bot" => wikiFuzz::makeFuzz( 2 ),
1369 "summary" => wikiFuzz::makeFuzz( 2 ),
1370 "direction" => wikiFuzz::chooseInput( array( "next", "prev", wikiFuzz::makeFuzz( 2 ) ) ),
1371 "section" => wikiFuzz::makeFuzz( 2 ),
1372 "preload" => wikiFuzz::makeFuzz( 2 ),
1373
1374 );
1375
1376 // Tidy does not know how to valid atom or rss, so exclude from testing for the time being.
1377 if ( $this->params["feed"] == "atom" ) { unset( $this->params["feed"] ); }
1378 elseif ( $this->params["feed"] == "rss" ) { unset( $this->params["feed"] ); }
1379
1380 // Raw pages cannot really be validated
1381 if ( $this->params["action"] == "raw" ) unset( $this->params["action"] );
1382
1383 // sometimes we don't want to specify certain parameters.
1384 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["rcid"] );
1385 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["diff"] );
1386 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["rdfrom"] );
1387 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["oldid"] );
1388
1389 // usually don't want action == purge.
1390 if ( wikiFuzz::randnum( 6 ) > 1 ) unset( $this->params["action"] );
1391 }
1392 }
1393
1394
1395 /**
1396 ** a page test for "Special:Allmessages".
1397 */
1398 class specialAllmessagesTest extends pageTest {
1399 function __construct() {
1400 $this->pagePath = "index.php?title=Special:Allmessages";
1401
1402 // only really has one parameter
1403 $this->params = array (
1404 "ot" => wikiFuzz::chooseInput( array( "php", "html", wikiFuzz::makeFuzz( 2 ) ) )
1405 );
1406 }
1407 }
1408
1409 /**
1410 ** a page test for "Special:Newpages".
1411 */
1412 class specialNewpagesPageTest extends pageTest {
1413 function __construct() {
1414 $this->pagePath = "index.php?title=Special:Newpages";
1415
1416 $this->params = array (
1417 "namespace" => wikiFuzz::chooseInput( range( -1, 15 ) ),
1418 "feed" => wikiFuzz::chooseInput( array( "atom", "rss", wikiFuzz::makeFuzz( 2 ) ) ),
1419 'limit' => wikiFuzz::chooseInput( array( "-1", "0", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1420 'offset' => wikiFuzz::chooseInput( array( "-1", "0", "------'-------0", "+1", "9823412312312412435", wikiFuzz::makeFuzz( 2 ) ) )
1421 );
1422
1423 // Tidy does not know how to valid atom or rss, so exclude from testing for the time being.
1424 if ( $this->params["feed"] == "atom" ) { unset( $this->params["feed"] ); }
1425 elseif ( $this->params["feed"] == "rss" ) { unset( $this->params["feed"] ); }
1426 }
1427 }
1428
1429 /**
1430 ** a page test for "redirect.php"
1431 */
1432 class redirectTest extends pageTest {
1433 function __construct() {
1434 $this->pagePath = "redirect.php";
1435
1436 $this->params = array (
1437 "wpDropdown" => wikiFuzz::makeFuzz( 2 )
1438 );
1439
1440 // sometimes we don't want to specify certain parameters.
1441 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpDropdown"] );
1442 }
1443 }
1444
1445
1446 /**
1447 ** a page test for "Special:Confirmemail"
1448 */
1449 class confirmEmail extends pageTest {
1450 function __construct() {
1451 // sometimes we send a bogus confirmation code, and sometimes we don't.
1452 $this->pagePath = "index.php?title=Special:Confirmemail" . wikiFuzz::chooseInput( array( "", "/" . wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 1 ) ) ) );
1453
1454 $this->params = array (
1455 "token" => wikiFuzz::makeFuzz( 2 )
1456 );
1457 }
1458 }
1459
1460
1461 /**
1462 ** a page test for "Special:Watchlist"
1463 ** Note: this test would be better if we were logged in.
1464 */
1465 class watchlistTest extends pageTest {
1466 function __construct() {
1467 $this->pagePath = "index.php?title=Special:Watchlist";
1468
1469 $this->params = array (
1470 "remove" => wikiFuzz::chooseInput( array( "Remove checked items from watchlist", wikiFuzz::makeFuzz( 2 ) ) ),
1471 'days' => wikiFuzz::chooseInput( array( 0, -1, -230, "--", 3, 9, wikiFuzz::makeFuzz( 2 ) ) ),
1472 'hideOwn' => wikiFuzz::chooseInput( array( "", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
1473 'hideBots' => wikiFuzz::chooseInput( array( "", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
1474 'namespace' => wikiFuzz::chooseInput( array( "", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
1475 'action' => wikiFuzz::chooseInput( array( "submit", "clear", wikiFuzz::makeFuzz( 2 ) ) ),
1476 'id[]' => wikiFuzz::makeFuzz( 2 ),
1477 'edit' => wikiFuzz::makeFuzz( 2 ),
1478 'token' => wikiFuzz::chooseInput( array( "", "1243213", wikiFuzz::makeFuzz( 2 ) ) )
1479 );
1480
1481 // sometimes we specifiy "reset", and sometimes we don't.
1482 if ( wikiFuzz::randnum( 3 ) == 0 ) $this->params["reset"] = wikiFuzz::chooseInput( array( "", "0", "1", wikiFuzz::makeFuzz( 2 ) ) );
1483 }
1484 }
1485
1486
1487 /**
1488 ** a page test for "Special:Blockme"
1489 */
1490 class specialBlockmeTest extends pageTest {
1491 function __construct() {
1492 $this->pagePath = "index.php?title=Special:Blockme";
1493
1494 $this->params = array ();
1495
1496 // sometimes we specify "ip", and sometimes we don't.
1497 if ( wikiFuzz::randnum( 1 ) == 0 ) {
1498 $this->params["ip"] = wikiFuzz::chooseInput( array( "10.12.41.213", wikiFuzz::randnum( 8134, -10 ), wikiFuzz::makeFuzz( 2 ) ) );
1499 }
1500 }
1501 }
1502
1503
1504 /**
1505 ** a page test for "Special:Movepage"
1506 */
1507 class specialMovePage extends pageTest {
1508 function __construct() {
1509 $this->pagePath = "index.php?title=Special:Movepage";
1510
1511 $this->params = array (
1512 "action" => wikiFuzz::chooseInput( array( "success", "submit", "", wikiFuzz::makeFuzz( 2 ) ) ),
1513 'wpEditToken' => wikiFuzz::chooseInput( array( '', 0, 34987987, wikiFuzz::makeFuzz( 2 ) ) ),
1514 'target' => wikiFuzz::chooseInput( array( "x", wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 2 ) ) ) ),
1515 'wpOldTitle' => wikiFuzz::chooseInput( array( "z", wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 2 ) ), wikiFuzz::makeFuzz( 2 ) ) ),
1516 'wpNewTitle' => wikiFuzz::chooseInput( array( "y", wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 2 ) ), wikiFuzz::makeFuzz( 2 ) ) ),
1517 'wpReason' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ) ) ),
1518 'wpMovetalk' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1519 'wpDeleteAndMove' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1520 'wpConfirm' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1521 'talkmoved' => wikiFuzz::chooseInput( array( "1", wikiFuzz::makeFuzz( 2 ), "articleexists", 'notalkpage' ) ),
1522 'oldtitle' => wikiFuzz::makeFuzz( 2 ),
1523 'newtitle' => wikiFuzz::makeFuzz( 2 ),
1524 'wpMovetalk' => wikiFuzz::chooseInput( array( "1", "0", wikiFuzz::makeFuzz( 2 ) ) )
1525 );
1526
1527 // sometimes we don't want to specify certain parameters.
1528 if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["wpEditToken"] );
1529 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["target"] );
1530 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["wpNewTitle"] );
1531 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpReason"] );
1532 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpOldTitle"] );
1533 }
1534 }
1535
1536
1537 /**
1538 ** a page test for "Special:Undelete"
1539 */
1540 class specialUndeletePageTest extends pageTest {
1541 function __construct() {
1542 $this->pagePath = "index.php?title=Special:Undelete";
1543
1544 $this->params = array (
1545 "action" => wikiFuzz::chooseInput( array( "submit", "", wikiFuzz::makeFuzz( 2 ) ) ),
1546 'wpEditToken' => wikiFuzz::chooseInput( array( '', 0, 34987987, wikiFuzz::makeFuzz( 2 ) ) ),
1547 'target' => wikiFuzz::chooseInput( array( "x", wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 2 ) ) ) ),
1548 'timestamp' => wikiFuzz::chooseInput( array( "125223", wikiFuzz::makeFuzz( 2 ) ) ),
1549 'file' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1550 'restore' => wikiFuzz::chooseInput( array( "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
1551 'preview' => wikiFuzz::chooseInput( array( "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
1552 'wpComment' => wikiFuzz::makeFuzz( 2 )
1553 );
1554
1555 // sometimes we don't want to specify certain parameters.
1556 if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["wpEditToken"] );
1557 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["target"] );
1558 if ( wikiFuzz::randnum( 1 ) == 0 ) unset( $this->params["restore"] );
1559 if ( wikiFuzz::randnum( 1 ) == 0 ) unset( $this->params["preview"] );
1560 }
1561 }
1562
1563
1564 /**
1565 ** a page test for "Special:Unlockdb"
1566 */
1567 class specialUnlockdbPageTest extends pageTest {
1568 function __construct() {
1569 $this->pagePath = "index.php?title=Special:Unlockdb";
1570
1571 $this->params = array (
1572 "action" => wikiFuzz::chooseInput( array( "submit", "success", "", wikiFuzz::makeFuzz( 2 ) ) ),
1573 'wpEditToken' => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1574 'wpLockConfirm' => wikiFuzz::chooseInput( array( "0", "1", wikiFuzz::makeFuzz( 2 ) ) )
1575 );
1576
1577 // sometimes we don't want to specify certain parameters.
1578 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpEditToken"] );
1579 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["action"] );
1580 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpLockConfirm"] );
1581 }
1582 }
1583
1584
1585 /**
1586 ** a page test for "Special:Lockdb"
1587 */
1588 class specialLockdbPageTest extends pageTest {
1589 function __construct() {
1590 $this->pagePath = "index.php?title=Special:Lockdb";
1591
1592 $this->params = array (
1593 "action" => wikiFuzz::chooseInput( array( "submit", "success", "", wikiFuzz::makeFuzz( 2 ) ) ),
1594 'wpEditToken' => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1595 'wpLockReason' => wikiFuzz::makeFuzz( 2 ),
1596 'wpLockConfirm' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) )
1597 );
1598
1599 // sometimes we don't want to specify certain parameters.
1600 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpEditToken"] );
1601 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["action"] );
1602 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpLockConfirm"] );
1603 }
1604 }
1605
1606
1607 /**
1608 ** a page test for "Special:Userrights"
1609 */
1610 class specialUserrights extends pageTest {
1611 function __construct() {
1612 $this->pagePath = "index.php?title=Special:Userrights";
1613
1614 $this->params = array (
1615 'wpEditToken' => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1616 'user-editname' => wikiFuzz::chooseInput( array( "Nickj2", "Nickj2\n<xyz>", wikiFuzz::makeFuzz( 2 ) ) ),
1617 'ssearchuser' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1618 'saveusergroups' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ), "Save User Groups" ),
1619 'member[]' => wikiFuzz::chooseInput( array( "0", "bot", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1620 "available[]" => wikiFuzz::chooseInput( array( "0", "sysop", "bureaucrat", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) )
1621 );
1622
1623 // sometimes we don't want to specify certain parameters.
1624 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params['ssearchuser'] );
1625 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params['saveusergroups'] );
1626 }
1627 }
1628
1629
1630 /**
1631 ** a test for page protection and unprotection.
1632 */
1633 class pageProtectionForm extends pageTest {
1634 function __construct() {
1635 $this->pagePath = "index.php?title=Main_Page";
1636
1637 $this->params = array (
1638 "action" => "protect",
1639 'wpEditToken' => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1640 "mwProtect-level-edit" => wikiFuzz::chooseInput( array( '', 'autoconfirmed', 'sysop', wikiFuzz::makeFuzz( 2 ) ) ),
1641 "mwProtect-level-move" => wikiFuzz::chooseInput( array( '', 'autoconfirmed', 'sysop', wikiFuzz::makeFuzz( 2 ) ) ),
1642 "mwProtectUnchained" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1643 'mwProtect-reason' => wikiFuzz::chooseInput( array( "because it was there", wikiFuzz::makeFuzz( 2 ) ) )
1644 );
1645
1646
1647 // sometimes we don't want to specify certain parameters.
1648 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["mwProtectUnchained"] );
1649 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params['mwProtect-reason'] );
1650 }
1651 }
1652
1653
1654 /**
1655 ** a page test for "Special:Blockip".
1656 */
1657 class specialBlockip extends pageTest {
1658 function __construct() {
1659 $this->pagePath = "index.php?title=Special:Blockip";
1660
1661 $this->params = array (
1662 "action" => wikiFuzz::chooseInput( array( "submit", "", wikiFuzz::makeFuzz( 2 ) ) ),
1663 'wpEditToken' => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1664 "wpBlockAddress" => wikiFuzz::chooseInput( array( "20398702394", "", "Nickj2", wikiFuzz::makeFuzz( 2 ),
1665 // something like an IP address, sometimes invalid:
1666 ( wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) . "."
1667 . wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) ) ) ),
1668 "ip" => wikiFuzz::chooseInput( array( "20398702394", "", "Nickj2", wikiFuzz::makeFuzz( 2 ),
1669 // something like an IP address, sometimes invalid:
1670 ( wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) . "."
1671 . wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) ) ) ),
1672 "wpBlockOther" => wikiFuzz::chooseInput( array( '', 'Nickj2', wikiFuzz::makeFuzz( 2 ) ) ),
1673 "wpBlockExpiry" => wikiFuzz::chooseInput( array( "other", "2 hours", "1 day", "3 days", "1 week", "2 weeks",
1674 "1 month", "3 months", "6 months", "1 year", "infinite", wikiFuzz::makeFuzz( 2 ) ) ),
1675 "wpBlockReason" => wikiFuzz::chooseInput( array( "because it was there", wikiFuzz::makeFuzz( 2 ) ) ),
1676 "wpAnonOnly" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1677 "wpCreateAccount" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1678 "wpBlock" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) )
1679 );
1680
1681 // sometimes we don't want to specify certain parameters.
1682 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpBlockOther"] );
1683 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpBlockExpiry"] );
1684 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpBlockReason"] );
1685 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpAnonOnly"] );
1686 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpCreateAccount"] );
1687 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpBlockAddress"] );
1688 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["ip"] );
1689 }
1690 }
1691
1692
1693 /**
1694 ** a test for the imagepage.
1695 */
1696 class imagepageTest extends pageTest {
1697 function __construct() {
1698 $this->pagePath = "index.php?title=Image:Small-email.png";
1699
1700 $this->params = array (
1701 "image" => wikiFuzz::chooseInput( array( "Small-email.png", wikiFuzz::makeFuzz( 2 ) ) ),
1702 "wpReason" => wikiFuzz::makeFuzz( 2 ),
1703 "oldimage" => wikiFuzz::chooseInput( array( "Small-email.png", wikiFuzz::makeFuzz( 2 ) ) ),
1704 "wpEditToken" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1705 );
1706
1707 // sometimes we don't want to specify certain parameters.
1708 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["image"] );
1709 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpReason"] );
1710 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["oldimage"] );
1711 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpEditToken"] );
1712 }
1713 }
1714
1715
1716 /**
1717 ** a test for page deletion form.
1718 */
1719 class pageDeletion extends pageTest {
1720 function __construct() {
1721 $this->pagePath = "index.php?title=Main_Page&action=delete";
1722
1723 $this->params = array (
1724 "wpEditToken" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1725 "wpReason" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1726 "wpConfirm" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1727 );
1728
1729 // sometimes we don't want to specify certain parameters.
1730 if ( wikiFuzz::randnum( 5 ) == 0 ) unset( $this->params["wpReason"] );
1731 if ( wikiFuzz::randnum( 5 ) == 0 ) unset( $this->params["wpEditToken"] );
1732 if ( wikiFuzz::randnum( 5 ) == 0 ) unset( $this->params["wpConfirm"] );
1733 }
1734 }
1735
1736
1737
1738 /**
1739 ** a test for Revision Deletion.
1740 */
1741 class specialRevisionDeletePageTest extends pageTest {
1742 function __construct() {
1743 $this->pagePath = "index.php?title=Special:Revisiondelete";
1744
1745 $this->params = array (
1746 "target" => wikiFuzz::chooseInput( array( "Main Page", wikiFuzz::makeFuzz( 2 ) ) ),
1747 "oldid" => wikiFuzz::makeFuzz( 2 ),
1748 "oldid[]" => wikiFuzz::makeFuzz( 2 ),
1749 "wpReason" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1750 "revdelete-hide-text" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1751 "revdelete-hide-comment" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1752 "revdelete-hide-user" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1753 "revdelete-hide-restricted" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1754 );
1755
1756 // sometimes we don't want to specify certain parameters.
1757 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["target"] );
1758 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["oldid"] );
1759 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["oldid[]"] );
1760 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpReason"] );
1761 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["revdelete-hide-text"] );
1762 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["revdelete-hide-comment"] );
1763 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["revdelete-hide-user"] );
1764 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["revdelete-hide-restricted"] );
1765 }
1766 }
1767
1768
1769 /**
1770 ** a test for Special:Import.
1771 */
1772 class specialImportPageTest extends pageTest {
1773 function __construct() {
1774 $this->pagePath = "index.php?title=Special:Import";
1775
1776 $this->params = array (
1777 "action" => "submit",
1778 "source" => wikiFuzz::chooseInput( array( "upload", "interwiki", wikiFuzz::makeFuzz( 2 ) ) ),
1779 "MAX_FILE_SIZE" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1780 "xmlimport" => wikiFuzz::chooseInput( array( "/var/www/hosts/mediawiki/wiki/AdminSettings.php", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1781 "namespace" => wikiFuzz::chooseInput( array( wikiFuzz::randnum( 30, -6 ), wikiFuzz::makeFuzz( 2 ) ) ),
1782 "interwiki" => wikiFuzz::makeFuzz( 2 ),
1783 "interwikiHistory" => wikiFuzz::makeFuzz( 2 ),
1784 "frompage" => wikiFuzz::makeFuzz( 2 ),
1785 );
1786
1787 // sometimes we don't want to specify certain parameters.
1788 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["action"] );
1789 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["source"] );
1790 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["MAX_FILE_SIZE"] );
1791 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["xmlimport"] );
1792 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["interwiki"] );
1793 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["interwikiHistory"] );
1794 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["frompage"] );
1795
1796 // Note: Need to do a file upload to fully test this Special page.
1797 }
1798 }
1799
1800
1801 /**
1802 ** a test for thumb.php
1803 */
1804 class thumbTest extends pageTest {
1805 function __construct() {
1806 $this->pagePath = "thumb.php";
1807
1808 $this->params = array (
1809 "f" => wikiFuzz::chooseInput( array( "..", "\\", "small-email.png", wikiFuzz::makeFuzz( 2 ) ) ),
1810 "w" => wikiFuzz::chooseInput( array( "80", wikiFuzz::randnum( 6000, -200 ), wikiFuzz::makeFuzz( 2 ) ) ),
1811 "r" => wikiFuzz::chooseInput( array( "0", wikiFuzz::makeFuzz( 2 ) ) ),
1812 );
1813
1814 // sometimes we don't want to specify certain parameters.
1815 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["f"] );
1816 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["w"] );
1817 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["r"] );
1818 }
1819 }
1820
1821 /**
1822 ** a test for profileinfo.php
1823 */
1824 class profileInfo extends pageTest {
1825 function __construct() {
1826 $this->pagePath = "profileinfo.php";
1827
1828 $this->params = array (
1829 "expand" => wikiFuzz::makeFuzz( 2 ),
1830 "sort" => wikiFuzz::chooseInput( array( "time", "count", "name", wikiFuzz::makeFuzz( 2 ) ) ),
1831 "filter" => wikiFuzz::chooseInput( array( "Main Page", wikiFuzz::makeFuzz( 2 ) ) ),
1832 );
1833
1834 // sometimes we don't want to specify certain parameters.
1835 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["sort"] );
1836 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["filter"] );
1837 }
1838 }
1839
1840
1841 /**
1842 ** a test for Special:Cite (extension Special page).
1843 */
1844 class specialCitePageTest extends pageTest {
1845 function __construct() {
1846 $this->pagePath = "index.php?title=Special:Cite";
1847
1848 $this->params = array (
1849 "page" => wikiFuzz::chooseInput( array( "\" onmouseover=\"alert(1);\"", "Main Page", wikiFuzz::makeFuzz( 2 ) ) ),
1850 "id" => wikiFuzz::chooseInput( array( "-1", "0", "------'-------0", "+1", "-9823412312312412435", wikiFuzz::makeFuzz( 2 ) ) ),
1851 );
1852
1853 // sometimes we don't want to specify certain parameters.
1854 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["page"] );
1855 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["id"] );
1856 }
1857 }
1858
1859
1860 /**
1861 ** a test for Special:Filepath (extension Special page).
1862 */
1863 class specialFilepathPageTest extends pageTest {
1864 function __construct() {
1865 $this->pagePath = "index.php?title=Special:Filepath";
1866
1867 $this->params = array (
1868 "file" => wikiFuzz::chooseInput( array( "Small-email.png", "Small-email.png" . wikiFuzz::makeFuzz( 1 ), wikiFuzz::makeFuzz( 2 ) ) ),
1869 );
1870 }
1871 }
1872
1873
1874 /**
1875 ** a test for Special:Renameuser (extension Special page).
1876 */
1877 class specialRenameuserPageTest extends pageTest {
1878 function __construct() {
1879 $this->pagePath = "index.php?title=Special:Renameuser";
1880
1881 $this->params = array (
1882 "oldusername" => wikiFuzz::chooseInput( array( "Nickj2", "192.168.0.2", wikiFuzz::makeFuzz( 1 ) ) ),
1883 "newusername" => wikiFuzz::chooseInput( array( "Nickj2", "192.168.0.2", wikiFuzz::makeFuzz( 1 ) ) ),
1884 "token" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1885 );
1886 }
1887 }
1888
1889
1890 /**
1891 ** a test for Special:Linksearch (extension Special page).
1892 */
1893 class specialLinksearch extends pageTest {
1894 function __construct() {
1895 $this->pagePath = "index.php?title=Special%3ALinksearch";
1896
1897 $this->params = array (
1898 "target" => wikiFuzz::makeFuzz( 2 ),
1899 );
1900
1901 // sometimes we don't want to specify certain parameters.
1902 if ( wikiFuzz::randnum( 10 ) == 0 ) unset( $this->params["target"] );
1903 }
1904 }
1905
1906
1907 /**
1908 ** a test for Special:CategoryTree (extension Special page).
1909 */
1910 class specialCategoryTree extends pageTest {
1911 function __construct() {
1912 $this->pagePath = "index.php?title=Special:CategoryTree";
1913
1914 $this->params = array (
1915 "target" => wikiFuzz::makeFuzz( 2 ),
1916 "from" => wikiFuzz::makeFuzz( 2 ),
1917 "until" => wikiFuzz::makeFuzz( 2 ),
1918 "showas" => wikiFuzz::makeFuzz( 2 ),
1919 "mode" => wikiFuzz::chooseInput( array( "pages", "categories", "all", wikiFuzz::makeFuzz( 2 ) ) ),
1920 );
1921
1922 // sometimes we do want to specify certain parameters.
1923 if ( wikiFuzz::randnum( 5 ) == 0 ) $this->params["notree"] = wikiFuzz::chooseInput( array( "1", 0, "", wikiFuzz::makeFuzz( 2 ) ) );
1924 }
1925 }
1926
1927
1928 /**
1929 ** a test for "Special:Chemicalsources" (extension Special page).
1930 */
1931 class specialChemicalsourcesTest extends pageTest {
1932 function __construct() {
1933 $this->pagePath = "index.php?title=Special:Chemicalsources";
1934
1935 // choose an input format to use.
1936 $format = wikiFuzz::chooseInput(
1937 array( 'go',
1938 'CAS',
1939 'EINECS',
1940 'CHEBI',
1941 'PubChem',
1942 'SMILES',
1943 'InChI',
1944 'ATCCode',
1945 'KEGG',
1946 'RTECS',
1947 'ECNumber',
1948 'DrugBank',
1949 'Formula',
1950 'Name'
1951 )
1952 );
1953
1954 // values for different formats usually start with either letters or numbers.
1955 switch ( $format ) {
1956 case 'Name' : $value = "A"; break;
1957 case 'InChI' :
1958 case 'SMILES' :
1959 case 'Formula': $value = "C"; break;
1960 default : $value = "0"; break;
1961 }
1962
1963 // and then we append the fuzz input.
1964 $this->params = array ( $format => $value . wikiFuzz::makeFuzz( 2 ) );
1965 }
1966 }
1967
1968
1969 /**
1970 ** A test for api.php (programmatic interface to MediaWiki in XML/JSON/RSS/etc formats).
1971 ** Quite involved to test because there are lots of options/parameters, and because
1972 ** for a lot of the functionality if all the parameters don't make sense then it just
1973 ** returns the help screen - so currently a lot of the tests aren't actually doing much
1974 ** because something wasn't right in the query.
1975 **
1976 ** @todo Incomplete / unfinished; Runs too fast (suggests not much testing going on).
1977 */
1978 class api extends pageTest {
1979
1980 // API login mode.
1981 private static function loginMode() {
1982 $arr = array ( "lgname" => wikiFuzz::makeFuzz( 2 ),
1983 "lgpassword" => wikiFuzz::makeFuzz( 2 ),
1984 );
1985 // sometimes we want to specify the extra "lgdomain" parameter.
1986 if ( wikiFuzz::randnum( 3 ) == 0 ) {
1987 $arr["lgdomain"] = wikiFuzz::chooseInput( array( "1", 0, "", wikiFuzz::makeFuzz( 2 ) ) );
1988 }
1989
1990 return $arr;
1991 }
1992
1993 // API OpenSearch mode.
1994 private static function opensearchMode() {
1995 return array ( "search" => wikiFuzz::makeFuzz( 2 ) );
1996 }
1997
1998 // API watchlist feed mode.
1999 private static function feedwatchlistMode() {
2000 // @todo FIXME: Add "wikiFuzz::makeFuzz(2)" as possible value below?
2001 return array ( "feedformat" => wikiFuzz::chooseInput( array( "rss", "atom" ) ) );
2002 }
2003
2004 // API query mode.
2005 private static function queryMode() {
2006 // @todo FIXME: Add "wikiFuzz::makeFuzz(2)" as possible params for the elements below?
2007 // Suspect this will stuff up the tests more, but need to check.
2008 $params = array (
2009 // @todo FIXME: More titles.
2010 "titles" => wikiFuzz::chooseInput( array( "Main Page" ) ),
2011 // @todo FIXME: More pageids.
2012 "pageids" => 1,
2013 "prop" => wikiFuzz::chooseInput( array( "info", "revisions", "watchlist" ) ),
2014 "list" => wikiFuzz::chooseInput( array( "allpages", "logevents", "watchlist", "usercontribs", "recentchanges", "backlinks", "embeddedin", "imagelinks" ) ),
2015 "meta" => wikiFuzz::chooseInput( array( "siteinfo" ) ),
2016 "generator" => wikiFuzz::chooseInput( array( "allpages", "logevents", "watchlist", "info", "revisions" ) ),
2017 "siprop" => wikiFuzz::chooseInput( array( "general", "namespaces", "general|namespaces" ) ),
2018 );
2019
2020 // Add extra parameters based on what list choice we got.
2021 switch ( $params["list"] ) {
2022 case "usercontribs" : self::addListParams ( $params, "uc", array( "limit", "start", "end", "user", "dir" ) ); break;
2023 case "allpages" : self::addListParams ( $params, "ap", array( "from", "prefix", "namespace", "filterredir", "limit" ) ); break;
2024 case "watchlist" : self::addListParams ( $params, "wl", array( "allrev", "start", "end", "namespace", "dir", "limit", "prop" ) ); break;
2025 case "logevents" : self::addListParams ( $params, "le", array( "limit", "type", "start", "end", "user", "dir" ) ); break;
2026 case "recentchanges": self::addListParams ( $params, "rc", array( "limit", "prop", "show", "namespace", "start", "end", "dir" ) ); break;
2027 case "backlinks" : self::addListParams ( $params, "bl", array( "continue", "namespace", "redirect", "limit" ) ); break;
2028 case "embeddedin" : self::addListParams ( $params, "ei", array( "continue", "namespace", "redirect", "limit" ) ); break;
2029 case "imagelinks" : self::addListParams ( $params, "il", array( "continue", "namespace", "redirect", "limit" ) ); break;
2030 }
2031
2032 if ( $params["prop"] == "revisions" ) {
2033 self::addListParams ( $params, "rv", array( "prop", "limit", "startid", "endid", "end", "dir" ) );
2034 }
2035
2036 // Sometimes we want redirects, sometimes we don't.
2037 if ( wikiFuzz::randnum( 3 ) == 0 ) {
2038 $params["redirects"] = wikiFuzz::chooseInput( array( "1", 0, "", wikiFuzz::makeFuzz( 2 ) ) );
2039 }
2040
2041 return $params;
2042 }
2043
2044 // Adds all the elements to the array, using the specified prefix.
2045 private static function addListParams( &$array, $prefix, $elements ) {
2046 foreach ( $elements as $element ) {
2047 $array[$prefix . $element] = self::getParamDetails( $element );
2048 }
2049 }
2050
2051 // For a given element name, returns the data for that element.
2052 private static function getParamDetails( $element ) {
2053 switch ( $element ) {
2054 case 'startid' :
2055 case 'endid' :
2056 case 'start' :
2057 case 'end' :
2058 case 'limit' : return wikiFuzz::chooseInput( array( "0", "-1", "---'----------0", "+1", "8134", "320742734234235", "20060230121212", wikiFuzz::randnum( 9000, -100 ), wikiFuzz::makeFuzz( 2 ) ) );
2059 case 'dir' : return wikiFuzz::chooseInput( array( "newer", "older", wikiFuzz::makeFuzz( 2 ) ) );
2060 case 'user' : return wikiFuzz::chooseInput( array( USER_ON_WIKI, wikiFuzz::makeFuzz( 2 ) ) );
2061 case 'namespace' : return wikiFuzz::chooseInput( array( -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 200000, wikiFuzz::makeFuzz( 2 ) ) );
2062 case 'filterredir': return wikiFuzz::chooseInput( array( "all", "redirects", "nonredirectsallpages", wikiFuzz::makeFuzz( 2 ) ) );
2063 case 'allrev' : return wikiFuzz::chooseInput( array( "1", 0, "", wikiFuzz::makeFuzz( 2 ) ) );
2064 case 'prop' : return wikiFuzz::chooseInput( array( "user", "comment", "timestamp", "patrol", "flags", "user|user|comment|flags", wikiFuzz::makeFuzz( 2 ) ) );
2065 case 'type' : return wikiFuzz::chooseInput( array( "block", "protect", "rights", "delete", "upload", "move", "import", "renameuser", "newusers", "makebot", wikiFuzz::makeFuzz( 2 ) ) );
2066 case 'hide' : return wikiFuzz::chooseInput( array( "minor", "bots", "anons", "liu", "liu|bots|", wikiFuzz::makeFuzz( 2 ) ) );
2067 case 'show' : return wikiFuzz::chooseInput( array( 'minor', '!minor', 'bot', '!bot', 'anon', '!anon', wikiFuzz::makeFuzz( 2 ) ) );
2068 default : return wikiFuzz::makeFuzz( 2 );
2069 }
2070 }
2071
2072 // Entry point.
2073 function __construct() {
2074 $this->pagePath = "api.php";
2075
2076 $modes = array ( "help",
2077 "login",
2078 "opensearch",
2079 "feedwatchlist",
2080 "query" );
2081 $action = wikiFuzz::chooseInput( array_merge ( $modes, array( wikiFuzz::makeFuzz( 2 ) ) ) );
2082
2083 switch ( $action ) {
2084 case "login" : $this->params = self::loginMode();
2085 break;
2086 case "opensearch" : $this->params = self::opensearchMode();
2087 break;
2088 case "feedwatchlist" : $this->params = self::feedwatchlistMode();
2089 break;
2090 case "query" : $this->params = self::queryMode();
2091 break;
2092 case "help" :
2093 default : // Do something random - "Crazy Ivan" mode.
2094 $random_mode = wikiFuzz::chooseInput( $modes ) . "Mode";
2095 // There is no "helpMode".
2096 if ( $random_mode == "helpMode" ) $random_mode = "queryMode";
2097 $this->params = self::$random_mode();
2098 break;
2099 }
2100
2101 // Save the selected action.
2102 $this->params["action"] = $action;
2103
2104 // Set the cookie:
2105 // @todo FIXME: Need to get this cookie dynamically set, rather than hard-coded.
2106 $this->cookie = "wikidbUserID=10001; wikidbUserName=Test; wikidb_session=178df0fe68c75834643af65dec9ec98a; wikidbToken=1adc6753d62c44aec950c024d7ae0540";
2107
2108 // Output format
2109 $this->params["format"] = wikiFuzz::chooseInput( array( "json", "jsonfm", "php", "phpfm",
2110 "wddx", "wddxfm", "xml", "xmlfm",
2111 "yaml", "yamlfm", "raw", "rawfm",
2112 wikiFuzz::makeFuzz( 2 ) ) );
2113
2114 // Page does not produce HTML (sometimes).
2115 $this->tidyValidate = false;
2116 }
2117 }
2118
2119
2120 /**
2121 ** a page test for the GeSHi extension.
2122 */
2123 class GeSHi_Test extends pageTest {
2124
2125 private function getGeSHiContent() {
2126 return "<source lang=\"" . $this->getLang() . "\" "
2127 . ( wikiFuzz::randnum( 2 ) == 0 ? "line " : "" )
2128 . ( wikiFuzz::randnum( 2 ) == 0 ? "strict " : "" )
2129 . "start=" . wikiFuzz::chooseInput( array( wikiFuzz::randnum( 6000, -6000 ), wikiFuzz::makeFuzz( 2 ) ) )
2130 . ">"
2131 . wikiFuzz::makeFuzz( 2 )
2132 . "</source>";
2133 }
2134
2135 private function getLang() {
2136 return wikiFuzz::chooseInput( array( "actionscript", "ada", "apache", "applescript", "asm", "asp", "autoit", "bash", "blitzbasic", "bnf", "c", "c_mac", "caddcl", "cadlisp",
2137 "cfdg", "cfm", "cpp", "cpp-qt", "csharp", "css", "d", "delphi", "diff", "div", "dos", "eiffel", "fortran", "freebasic", "gml", "groovy", "html4strict", "idl",
2138 "ini", "inno", "io", "java", "java5", "javascript", "latex", "lisp", "lua", "matlab", "mirc", "mpasm", "mysql", "nsis", "objc", "ocaml", "ocaml-brief", "oobas",
2139 "oracle8", "pascal", "perl", "php", "php-brief", "plsql", "python", "qbasic", "rails", "reg", "robots", "ruby", "sas", "scheme", "sdlbasic", "smalltalk", "smarty",
2140 "sql", "tcl", "text", "thinbasic", "tsql", "vb", "vbnet", "vhdl", "visualfoxpro", "winbatch", "xml", "xpp", "z80", wikiFuzz::makeFuzz( 1 ) ) );
2141 }
2142
2143 function __construct() {
2144 $this->pagePath = "index.php?title=WIKIFUZZ";
2145
2146 $this->params = array (
2147 "action" => "submit",
2148 "wpMinoredit" => "test",
2149 "wpPreview" => "test",
2150 "wpSection" => "test",
2151 "wpEdittime" => "test",
2152 "wpSummary" => "test",
2153 "wpScrolltop" => "test",
2154 "wpStarttime" => "test",
2155 "wpAutoSummary" => "test",
2156 "wpTextbox1" => $this->getGeSHiContent() // the main wiki text, contains fake GeSHi content.
2157 );
2158 }
2159 }
2160
2161 /**
2162 ** selects a page test to run.
2163 * @param $count
2164 * @return \api|\confirmEmail|\contributionsTest|\editPageTest|\imagelistTest|\imagepageTest|\ipblocklistTest|\listusersTest|\mimeSearchTest|\newImagesTest|\pageDeletion|\pageHistoryTest|\pageProtectionForm|\prefixindexTest|\profileInfo|\recentchangesTest|\redirectTest|\searchTest|\specialAllmessagesTest|\specialAllpagesTest|\specialBlockip|\specialBlockmeTest|\specialBooksourcesTest|\specialCategoryTree|\specialChemicalsourcesTest|\specialCitePageTest|\specialExportTest|\specialFilepathPageTest|\specialImportPageTest|\specialLinksearch|\specialLockdbPageTest|\specialLogTest|\specialMovePage|\specialNewpagesPageTest|\specialRenameuserPageTest|\specialRevisionDeletePageTest|\specialUndeletePageTest|\specialUnlockdbPageTest|\specialUserrights|\successfulUserLoginTest|\thumbTest|\userLoginTest|\viewPageTest|\watchlistTest
2165 */
2166 function selectPageTest( $count ) {
2167
2168 // if the user only wants a specific test, then only ever give them that.
2169 if ( defined( "SPECIFIC_TEST" ) ) {
2170 $testType = SPECIFIC_TEST;
2171 return new $testType ();
2172 }
2173
2174 // Some of the time we test Special pages, the remaining
2175 // time we test using the standard edit page.
2176 switch ( $count % 100 ) {
2177 case 0 : return new successfulUserLoginTest();
2178 case 1 : return new listusersTest();
2179 case 2 : return new searchTest();
2180 case 3 : return new recentchangesTest();
2181 case 4 : return new prefixindexTest();
2182 case 5 : return new mimeSearchTest();
2183 case 6 : return new specialLogTest();
2184 case 7 : return new userLoginTest();
2185 case 8 : return new ipblocklistTest();
2186 case 9 : return new newImagesTest();
2187 case 10: return new imagelistTest();
2188 case 11: return new specialExportTest();
2189 case 12: return new specialBooksourcesTest();
2190 case 13: return new specialAllpagesTest();
2191 case 14: return new pageHistoryTest();
2192 case 15: return new contributionsTest();
2193 case 16: return new viewPageTest();
2194 case 17: return new specialAllmessagesTest();
2195 case 18: return new specialNewpagesPageTest();
2196 case 19: return new searchTest();
2197 case 20: return new redirectTest();
2198 case 21: return new confirmEmail();
2199 case 22: return new watchlistTest();
2200 case 23: return new specialBlockmeTest();
2201 case 24: return new specialUndeletePageTest();
2202 case 25: return new specialMovePage();
2203 case 26: return new specialUnlockdbPageTest();
2204 case 27: return new specialLockdbPageTest();
2205 case 28: return new specialUserrights();
2206 case 29: return new pageProtectionForm();
2207 case 30: return new specialBlockip();
2208 case 31: return new imagepageTest();
2209 case 32: return new pageDeletion();
2210 case 33: return new specialRevisionDeletePageTest();
2211 case 34: return new specialImportPageTest();
2212 case 35: return new thumbTest();
2213 case 37: return new profileInfo();
2214 case 38: return new specialCitePageTest();
2215 case 39: return new specialFilepathPageTest();
2216 case 40: return new specialRenameuserPageTest();
2217 case 41: return new specialLinksearch();
2218 case 42: return new specialCategoryTree();
2219 case 43: return new api();
2220 case 44: return new specialChemicalsourcesTest();
2221 default: return new editPageTest();
2222 }
2223 }
2224
2225
2226 // ///////////////////// SAVING OUTPUT /////////////////////////
2227
2228 /**
2229 ** Utility function for saving a file. Currently has no error checking.
2230 */
2231 function saveFile( $data, $name ) {
2232 file_put_contents( $name, $data );
2233 }
2234
2235 /**
2236 ** Returns a test as an experimental GET-to-POST URL.
2237 ** This doesn't seem to always work though, and sometimes the output is too long
2238 ** to be a valid GET URL, so we also save in other formats.
2239 * @param $test pageTest
2240 * @return string
2241 */
2242 function getAsURL( pageTest $test ) {
2243 $used_question_mark = ( strpos( $test->getPagePath(), "?" ) !== false );
2244 $retval = "http://get-to-post.nickj.org/?" . WIKI_BASE_URL . $test->getPagePath();
2245 foreach ( $test->getParams() as $param => $value ) {
2246 if ( !$used_question_mark ) {
2247 $retval .= "?";
2248 $used_question_mark = true;
2249 }
2250 else {
2251 $retval .= "&";
2252 }
2253 $retval .= $param . "=" . urlencode( $value );
2254 }
2255 return $retval;
2256 }
2257
2258
2259 /**
2260 ** Saves a plain-text human-readable version of a test.
2261 */
2262 function saveTestAsText( pageTest $test, $filename ) {
2263 $str = "Test: " . $test->getPagePath();
2264 foreach ( $test->getParams() as $param => $value ) {
2265 $str .= "\n$param: $value";
2266 }
2267 $str .= "\nGet-to-post URL: " . getAsURL( $test ) . "\n";
2268 saveFile( $str, $filename );
2269 }
2270
2271
2272 /**
2273 ** Saves a test as a standalone basic PHP script that shows this one problem.
2274 ** Resulting script requires PHP-Curl be installed in order to work.
2275 */
2276 function saveTestAsPHP( pageTest $test, $filename ) {
2277 $str = "<?php\n"
2278 . "\$params = " . var_export( escapeForCurl( $test->getParams() ), true ) . ";\n"
2279 . "\$ch = curl_init();\n"
2280 . "curl_setopt(\$ch, CURLOPT_POST, 1);\n"
2281 . "curl_setopt(\$ch, CURLOPT_POSTFIELDS, \$params );\n"
2282 . "curl_setopt(\$ch, CURLOPT_URL, " . var_export( WIKI_BASE_URL . $test->getPagePath(), true ) . ");\n"
2283 . "curl_setopt(\$ch, CURLOPT_RETURNTRANSFER,1);\n"
2284 . ( $test->getCookie() ? "curl_setopt(\$ch, CURLOPT_COOKIE, " . var_export( $test->getCookie(), true ) . ");\n" : "" )
2285 . "\$result=curl_exec(\$ch);\n"
2286 . "curl_close (\$ch);\n"
2287 . "print \$result;\n"
2288 . "\n";
2289 saveFile( $str, $filename );
2290 }
2291
2292 /**
2293 * Escapes a value so that it can be used on the command line by Curl.
2294 * Specifically, "<" and "@" need to be escaped if they are the first character,
2295 * otherwise curl interprets these as meaning that we want to insert a file.
2296 * @param $input_params array
2297 * @return array
2298 */
2299 function escapeForCurl( array $input_params ) {
2300 $output_params = array();
2301 foreach ( $input_params as $param => $value ) {
2302 if ( strlen( $value ) > 0 && ( $value[0] == "@" || $value[0] == "<" ) ) {
2303 $value = "\\" . $value;
2304 }
2305 $output_params[$param] = $value;
2306 }
2307 return $output_params;
2308 }
2309
2310
2311 /**
2312 ** Saves a test as a standalone CURL shell script that shows this one problem.
2313 ** Resulting script requires standalone Curl be installed in order to work.
2314 */
2315 function saveTestAsCurl( pageTest $test, $filename ) {
2316 $str = "#!/bin/bash\n"
2317 . "curl --silent --include --globoff \\\n"
2318 . ( $test->getCookie() ? " --cookie " . escapeshellarg( $test->getCookie() ) . " \\\n" : "" );
2319 foreach ( escapeForCurl( $test->getParams() ) as $param => $value ) {
2320 $str .= " -F " . escapeshellarg( $param ) . "=" . escapeshellarg( $value ) . " \\\n";
2321 }
2322 $str .= " " . escapeshellarg( WIKI_BASE_URL . $test->getPagePath() ); // beginning space matters.
2323 $str .= "\n";
2324 saveFile( $str, $filename );
2325 chmod( $filename, 0755 ); // make executable
2326 }
2327
2328
2329 /**
2330 ** Saves the internal data structure to file.
2331 */
2332 function saveTestData ( pageTest $test, $filename ) {
2333 saveFile( serialize( $test ), $filename );
2334 }
2335
2336
2337 /**
2338 ** saves a test in the various formats.
2339 */
2340 function saveTest( pageTest $test, $testname ) {
2341 $base_name = DIRECTORY . "/" . $testname;
2342 saveTestAsText( $test, $base_name . INFO_FILE );
2343 saveTestAsPHP ( $test, $base_name . PHP_TEST );
2344 saveTestAsCurl( $test, $base_name . CURL_TEST );
2345 saveTestData ( $test, $base_name . DATA_FILE );
2346 }
2347
2348 // ////////////////// MEDIAWIKI OUTPUT /////////////////////////
2349
2350 /**
2351 * Asks MediaWiki for the HTML output of a test.
2352 * @param $test pageTest
2353 * @return string
2354 */
2355 function wikiTestOutput( pageTest $test ) {
2356
2357 $ch = curl_init();
2358
2359 // specify the cookie, if required.
2360 if ( $test->getCookie() ) {
2361 curl_setopt( $ch, CURLOPT_COOKIE, $test->getCookie() );
2362 }
2363 curl_setopt( $ch, CURLOPT_POST, 1 ); // save form using a POST
2364
2365 $params = escapeForCurl( $test->getParams() );
2366 curl_setopt( $ch, CURLOPT_POSTFIELDS, $params ); // load the POST variables
2367
2368 curl_setopt( $ch, CURLOPT_URL, WIKI_BASE_URL . $test->getPagePath() ); // set url to post to
2369 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); // return into a variable
2370
2371 $result = curl_exec ( $ch );
2372
2373 // if we encountered an error, then say so, and return an empty string.
2374 if ( curl_error( $ch ) ) {
2375 print "\nCurl error #: " . curl_errno( $ch ) . " - " . curl_error ( $ch );
2376 $result = "";
2377 }
2378
2379 curl_close ( $ch );
2380
2381 return $result;
2382 }
2383
2384
2385 // ////////////////// HTML VALIDATION /////////////////////////
2386
2387 /**
2388 * Asks the validator whether this is valid HTML, or not.
2389 * @param $text string
2390 * @return array
2391 */
2392 function validateHTML( $text ) {
2393
2394 $params = array ( "fragment" => $text );
2395
2396 $ch = curl_init();
2397
2398 curl_setopt( $ch, CURLOPT_POST, 1 ); // save form using a POST
2399 curl_setopt( $ch, CURLOPT_POSTFIELDS, $params ); // load the POST variables
2400 curl_setopt( $ch, CURLOPT_URL, VALIDATOR_URL ); // set url to post to
2401 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); // return into a variable
2402
2403 $result = curl_exec ( $ch );
2404
2405 // if we encountered an error, then log it, and exit.
2406 if ( curl_error( $ch ) ) {
2407 trigger_error( "Curl error #: " . curl_errno( $ch ) . " - " . curl_error ( $ch ) );
2408 print "Curl error #: " . curl_errno( $ch ) . " - " . curl_error ( $ch ) . " - exiting.\n";
2409 exit( 1 );
2410 }
2411
2412 curl_close ( $ch );
2413
2414 $valid = ( strpos( $result, "Failed validation" ) === false );
2415
2416 return array( $valid, $result );
2417 }
2418
2419 /**
2420 * Get tidy to check for no HTML errors in the output file (e.g. unescaped strings).
2421 * @param $name
2422 * @return bool
2423 */
2424 function tidyCheckFile( $name ) {
2425 $file = DIRECTORY . "/" . $name;
2426 $command = PATH_TO_TIDY . " -output /tmp/out.html -quiet $file 2>&1";
2427 $x = `$command`;
2428
2429 // Look for the most interesting Tidy errors and warnings.
2430 if ( strpos( $x, "end of file while parsing attributes" ) !== false
2431 || strpos( $x, "attribute with missing trailing quote mark" ) !== false
2432 || strpos( $x, "missing '>' for end of tag" ) !== false
2433 || strpos( $x, "Error:" ) !== false ) {
2434 print "\nTidy found something - view details with: $command";
2435 return false;
2436 } else {
2437 return true;
2438 }
2439 }
2440
2441 /**
2442 ** Returns whether or not an database error log file has changed in size since
2443 ** the last time this was run. This is used to tell if a test caused a DB error.
2444 * @return bool
2445 */
2446 function dbErrorLogged() {
2447 static $filesize;
2448
2449 // first time running this function
2450 if ( !isset( $filesize ) ) {
2451 // create log if it does not exist
2452 if ( DB_ERROR_LOG_FILE && !file_exists( DB_ERROR_LOG_FILE ) ) {
2453 saveFile( '', DB_ERROR_LOG_FILE );
2454 }
2455 $filesize = filesize( DB_ERROR_LOG_FILE );
2456 return false;
2457 }
2458
2459 $newsize = filesize( DB_ERROR_LOG_FILE );
2460 // if the log has grown, then assume the current test caused it.
2461 if ( $newsize != $filesize ) {
2462 $filesize = $newsize;
2463 return true;
2464 }
2465
2466 return false;
2467 }
2468
2469 // //////////////// TOP-LEVEL PROBLEM-FINDING FUNCTION ////////////////////////
2470
2471 /**
2472 * takes a page test, and runs it and tests it for problems in the output.
2473 * Returns: False on finding a problem, or True on no problems being found.
2474 * @param $test pageTest
2475 * @param $testname
2476 * @param $can_overwrite bool
2477 * @return bool
2478 */
2479 function runWikiTest( pageTest $test, &$testname, $can_overwrite = false ) {
2480
2481 // by default don't overwrite a previous test of the same name.
2482 while ( ! $can_overwrite && file_exists( DIRECTORY . "/" . $testname . DATA_FILE ) ) {
2483 $testname .= "-" . mt_rand( 0, 9 );
2484 }
2485
2486 $filename = DIRECTORY . "/" . $testname . DATA_FILE;
2487
2488 // Store the time before and after, to find slow pages.
2489 $before = microtime( true );
2490
2491 // Get MediaWiki to give us the output of this test.
2492 $wiki_preview = wikiTestOutput( $test );
2493
2494 $after = microtime( true );
2495
2496 // if we received no response, then that's interesting.
2497 if ( $wiki_preview == "" ) {
2498 print "\nNo response received for: $filename";
2499 return false;
2500 }
2501
2502 // save output HTML to file.
2503 $html_file = DIRECTORY . "/" . $testname . HTML_FILE;
2504 saveFile( $wiki_preview, $html_file );
2505
2506 // if there were PHP errors in the output, then that's interesting too.
2507 if ( strpos( $wiki_preview, "<b>Warning</b>: " ) !== false
2508 || strpos( $wiki_preview, "<b>Fatal error</b>: " ) !== false
2509 || strpos( $wiki_preview, "<b>Notice</b>: " ) !== false
2510 || strpos( $wiki_preview, "<b>Error</b>: " ) !== false
2511 || strpos( $wiki_preview, "<b>Strict Standards:</b>" ) !== false
2512 ) {
2513 $error = substr( $wiki_preview, strpos( $wiki_preview, "</b>:" ) + 7, 50 );
2514 // Avoid probable PHP bug with bad session ids; http://bugs.php.net/bug.php?id=38224
2515 if ( $error != "Unknown: The session id contains illegal character" ) {
2516 print "\nPHP error/warning/notice in HTML output: $html_file ; $error";
2517 return false;
2518 }
2519 }
2520
2521 // if there was a MediaWiki Backtrace message in the output, then that's also interesting.
2522 if ( strpos( $wiki_preview, "Backtrace:" ) !== false ) {
2523 print "\nInternal MediaWiki error in HTML output: $html_file";
2524 return false;
2525 }
2526
2527 // if there was a Parser error comment in the output, then that's potentially interesting.
2528 if ( strpos( $wiki_preview, "!-- ERR" ) !== false ) {
2529 print "\nParser Error comment in HTML output: $html_file";
2530 return false;
2531 }
2532
2533 // if a database error was logged, then that's definitely interesting.
2534 if ( dbErrorLogged() ) {
2535 print "\nDatabase Error logged for: $filename";
2536 return false;
2537 }
2538
2539 // validate result
2540 $valid = true;
2541 if ( VALIDATE_ON_WEB ) {
2542 list ( $valid, $validator_output ) = validateHTML( $wiki_preview );
2543 if ( !$valid ) print "\nW3C web validation failed - view details with: html2text " . DIRECTORY . "/" . $testname . ".validator_output.html";
2544 }
2545
2546 // Get tidy to check the page, unless we already know it produces non-XHTML output.
2547 if ( $test->tidyValidate() ) {
2548 $valid = tidyCheckFile( $testname . HTML_FILE ) && $valid;
2549 }
2550
2551 // if it took more than 2 seconds to render, then it may be interesting too. (Possible DoS attack?)
2552 if ( ( $after - $before ) >= 2 ) {
2553 print "\nParticularly slow to render (" . round( $after - $before, 2 ) . " seconds): $filename";
2554 return false;
2555 }
2556
2557 if ( $valid ) {
2558 // Remove temp HTML file if test was valid:
2559 unlink( $html_file );
2560 } elseif ( VALIDATE_ON_WEB ) {
2561 saveFile( $validator_output, DIRECTORY . "/" . $testname . ".validator_output.html" );
2562 }
2563
2564 return $valid;
2565 }
2566
2567
2568 // ///////////////// RERUNNING OLD TESTS ///////////////////
2569
2570 /**
2571 ** We keep our failed tests so that they can be rerun.
2572 ** This function does that retesting.
2573 */
2574 function rerunPreviousTests() {
2575 print "Retesting previously found problems.\n";
2576
2577 $dir_contents = scandir ( DIRECTORY );
2578
2579 // sort file into the order a normal person would use.
2580 natsort ( $dir_contents );
2581
2582 foreach ( $dir_contents as $file ) {
2583
2584 // if file is not a test, then skip it.
2585 // Note we need to escape any periods or will be treated as "any character".
2586 $matches = array();
2587 if ( !preg_match( "/(.*)" . str_replace( ".", "\.", DATA_FILE ) . "$/", $file, $matches ) ) continue;
2588
2589 // reload the test.
2590 $full_path = DIRECTORY . "/" . $file;
2591 $test = unserialize( file_get_contents( $full_path ) );
2592
2593 // if this is not a valid test, then skip it.
2594 if ( ! $test instanceof pageTest ) {
2595 print "\nSkipping invalid test - $full_path";
2596 continue;
2597 }
2598
2599 // The date format is in Apache log format, which makes it easier to locate
2600 // which retest caused which error in the Apache logs (only happens usually if
2601 // apache segfaults).
2602 if ( !QUIET ) print "[" . date ( "D M d H:i:s Y" ) . "] Retesting $file (" . get_class( $test ) . ")";
2603
2604 // run test
2605 $testname = $matches[1];
2606 $valid = runWikiTest( $test, $testname, true );
2607
2608 if ( !$valid ) {
2609 saveTest( $test, $testname );
2610 if ( QUIET ) {
2611 print "\nTest: " . get_class( $test ) . " ; Testname: $testname\n------";
2612 } else {
2613 print "\n";
2614 }
2615 }
2616 else {
2617 if ( !QUIET ) print "\r";
2618 if ( DELETE_PASSED_RETESTS ) {
2619 $prefix = DIRECTORY . "/" . $testname;
2620 if ( is_file( $prefix . DATA_FILE ) ) unlink( $prefix . DATA_FILE );
2621 if ( is_file( $prefix . PHP_TEST ) ) unlink( $prefix . PHP_TEST );
2622 if ( is_file( $prefix . CURL_TEST ) ) unlink( $prefix . CURL_TEST );
2623 if ( is_file( $prefix . INFO_FILE ) ) unlink( $prefix . INFO_FILE );
2624 }
2625 }
2626 }
2627
2628 print "\nDone retesting.\n";
2629 }
2630
2631
2632 // //////////////////// MAIN LOOP ////////////////////////
2633
2634
2635 // first check whether CURL is installed, because sometimes it's not.
2636 if ( ! function_exists( 'curl_init' ) ) {
2637 die( "Could not find 'curl_init' function. Is the curl extension compiled into PHP?\n" );
2638 }
2639
2640 // Initialization of types. wikiFuzz doesn't have a constructor because we want to
2641 // access it staticly and not have any globals.
2642 wikiFuzz::$types = array_keys( wikiFuzz::$data );
2643
2644 // Make directory if doesn't exist
2645 if ( !is_dir( DIRECTORY ) ) {
2646 mkdir ( DIRECTORY, 0700 );
2647 }
2648 // otherwise, we first retest the things that we have found in previous runs
2649 elseif ( RERUN_OLD_TESTS ) {
2650 rerunPreviousTests();
2651 }
2652
2653 // main loop.
2654 $start_time = date( "U" );
2655 $num_errors = 0;
2656 if ( !QUIET ) {
2657 print "Beginning main loop. Results are stored in the " . DIRECTORY . " directory.\n";
2658 print "Press CTRL+C to stop testing.\n";
2659 }
2660
2661 for ( $count = 0; true; $count++ ) {
2662 if ( !QUIET ) {
2663 // spinning progress indicator.
2664 switch( $count % 4 ) {
2665 case '0': print "\r/"; break;
2666 case '1': print "\r-"; break;
2667 case '2': print "\r\\"; break;
2668 case '3': print "\r|"; break;
2669 }
2670 print " $count";
2671 }
2672
2673 // generate a page test to run.
2674 $test = selectPageTest( $count );
2675
2676 $mins = ( date( "U" ) - $start_time ) / 60;
2677 if ( !QUIET && $mins > 0 ) {
2678 print ". $num_errors poss errors. "
2679 . floor( $mins ) . " mins. "
2680 . round ( $count / $mins, 0 ) . " tests/min. "
2681 . get_class( $test ); // includes the current test name.
2682 }
2683
2684 // run this test against MediaWiki, and see if the output was valid.
2685 $testname = $count;
2686 $valid = runWikiTest( $test, $testname, false );
2687
2688 // save the failed test
2689 if ( ! $valid ) {
2690 if ( QUIET ) {
2691 print "\nTest: " . get_class( $test ) . " ; Testname: $testname\n------";
2692 } else {
2693 print "\n";
2694 }
2695 saveTest( $test, $testname );
2696 $num_errors += 1;
2697 } elseif ( KEEP_PASSED_TESTS ) {
2698 // print current time, with microseconds (matches "strace" format), and the test name.
2699 print " " . date( "H:i:s." ) . substr( current( explode( " ", microtime() ) ), 2 ) . " " . $testname;
2700 saveTest( $test, $testname );
2701 }
2702
2703 // stop if we have reached max number of errors.
2704 if ( defined( "MAX_ERRORS" ) && $num_errors >= MAX_ERRORS ) {
2705 break;
2706 }
2707
2708 // stop if we have reached max number of mins runtime.
2709 if ( defined( "MAX_RUNTIME" ) && $mins >= MAX_RUNTIME ) {
2710 break;
2711 }
2712 }