*Re-add some things lost in merge
[mediawiki.git] / maintenance / fuzz-tester.php
blob00f66c1552265bb9a9dacb38ac4e08d4091e6635
1 <?php
2 /**
3 * @addtogroup Maintenance
4 * @author Nick Jenkins ( http://nickj.org/ ).
5 * @copyright 2006 Nick Jenkins
6 * @licence GNU General Public Licence 2.0
8 Started: 18 May 2006.
10 Description:
11 Performs fuzz-style testing of MediaWiki's parser and forms.
13 How:
14 - Generate lots of nasty wiki text.
15 - Ask the Parser to render that wiki text to HTML, or ask MediaWiki's forms
16 to deal with that wiki text.
17 - Check MediaWiki's output for problems.
18 - Repeat.
20 Why:
21 - To help find bugs.
22 - To help find security issues, or potential security issues.
24 What type of problems are being checked for:
25 - Unclosed tags.
26 - Errors or interesting warnings from Tidy.
27 - PHP errors / warnings / notices.
28 - MediaWiki internal errors.
29 - Very slow responses.
30 - No response from apache.
31 - Optionally checking for malformed HTML using the W3C validator.
33 Background:
34 Many of the wikiFuzz class methods are a modified PHP port,
35 of a "shameless" Python port, of LCAMTUF'S MANGELME:
36 - http://www.securiteam.com/tools/6Z00N1PBFK.html
37 - http://www.securityfocus.com/archive/1/378632/2004-10-15/2004-10-21/0
39 Video:
40 There's an XviD video discussing this fuzz tester. You can get it from:
41 http://files.nickj.org/MediaWiki/Fuzz-Testing-MediaWiki-xvid.avi
43 Requirements:
44 To run this, you will need:
45 - Command-line PHP5, with PHP-curl enabled (not all installations have this
46 enabled - try "apt-get install php5-curl" if you're on Debian to install).
47 - the Tidy standalone executable. ("apt-get install tidy").
49 Optional:
50 - If you want to run the curl scripts, you'll need standalone curl installed
51 ("apt-get install curl")
52 - For viewing the W3C validator output on a command line, the "html2text"
53 program may be useful ("apt-get install html2text")
55 Saving tests and test results:
56 Any of the fuzz tests which find problems are saved for later review.
57 In order to help track down problems, tests are saved in a number of
58 different formats. The default filename extensions and their meanings are:
59 - ".test.php" : PHP script that reproduces just that one problem using PHP-Curl.
60 - ".curl.sh" : Shell script that reproduces that problem using standalone curl.
61 - ".data.bin" : The serialized PHP data so that this script can re-run the test.
62 - ".info.txt" : A human-readable text file with details of the field contents.
64 Wiki configuration for testing:
65 You should make some additions to LocalSettings.php in order to catch the most
66 errors. Note this configuration is for **TESTING PURPOSES ONLY**, and is IN NO
67 WAY, SHAPE, OR FORM suitable for deployment on a hostile network. That said,
68 personally I find these additions to be the most helpful for testing purposes:
70 // --------- Start ---------
71 // Everyone can do everything. Very useful for testing, yet useless for deployment.
72 $wgGroupPermissions['*']['autoconfirmed'] = true;
73 $wgGroupPermissions['*']['block'] = true;
74 $wgGroupPermissions['*']['bot'] = true;
75 $wgGroupPermissions['*']['delete'] = true;
76 $wgGroupPermissions['*']['deletedhistory'] = true;
77 $wgGroupPermissions['*']['deleterevision'] = true;
78 $wgGroupPermissions['*']['editinterface'] = true;
79 $wgGroupPermissions['*']['hiderevision'] = true;
80 $wgGroupPermissions['*']['import'] = true;
81 $wgGroupPermissions['*']['importupload'] = true;
82 $wgGroupPermissions['*']['minoredit'] = true;
83 $wgGroupPermissions['*']['move'] = true;
84 $wgGroupPermissions['*']['patrol'] = true;
85 $wgGroupPermissions['*']['protect'] = true;
86 $wgGroupPermissions['*']['proxyunbannable'] = true;
87 $wgGroupPermissions['*']['renameuser'] = true;
88 $wgGroupPermissions['*']['reupload'] = true;
89 $wgGroupPermissions['*']['reupload-shared'] = true;
90 $wgGroupPermissions['*']['rollback'] = true;
91 $wgGroupPermissions['*']['siteadmin'] = true;
92 $wgGroupPermissions['*']['trackback'] = true;
93 $wgGroupPermissions['*']['unwatchedpages'] = true;
94 $wgGroupPermissions['*']['upload'] = true;
95 $wgGroupPermissions['*']['userrights'] = true;
96 $wgGroupPermissions['*']['renameuser'] = true;
97 $wgGroupPermissions['*']['makebot'] = true;
98 $wgGroupPermissions['*']['makesysop'] = true;
100 // Enable weird and wonderful options:
101 // Increase default error reporting level.
102 error_reporting (E_ALL); // At a later date could be increased to E_ALL | E_STRICT
103 $wgBlockOpenProxies = true; // Some block pages require this to be true in order to test.
104 $wgEnableUploads = true; // enable uploads.
105 //$wgUseTrackbacks = true; // enable trackbacks; However this breaks the viewPageTest, so currently disabled.
106 $wgDBerrorLog = "/root/mediawiki-db-error-log.txt"; // log DB errors, replace with suitable path.
107 $wgShowSQLErrors = true; // Show SQL errors (instead of saying the query was hidden).
108 $wgShowExceptionDetails = true; // want backtraces.
109 $wgEnableAPI = true; // enable API.
110 $wgEnableWriteAPI = true; // enable API.
112 // Install & enable Parser Hook extensions to increase code coverage. E.g.:
113 require_once("extensions/ParserFunctions/ParserFunctions.php");
114 require_once("extensions/Cite/Cite.php");
115 require_once("extensions/inputbox/inputbox.php");
116 require_once("extensions/Sort/Sort.php");
117 require_once("extensions/wikihiero/wikihiero.php");
118 require_once("extensions/CharInsert/CharInsert.php");
119 require_once("extensions/FixedImage/FixedImage.php");
121 // Install & enable Special Page extensions to increase code coverage. E.g.:
122 require_once("extensions/Cite/SpecialCite.php");
123 require_once("extensions/Filepath/SpecialFilepath.php");
124 require_once("extensions/Makebot/Makebot.php");
125 require_once("extensions/Makesysop/SpecialMakesysop.php");
126 require_once("extensions/Renameuser/SpecialRenameuser.php");
127 require_once("extensions/LinkSearch/LinkSearch.php");
128 // --------- End ---------
130 If you want to try E_STRICT error logging, add this to the above:
131 // --------- Start ---------
132 error_reporting (E_ALL | E_STRICT);
133 set_error_handler( 'error_handler' );
134 function error_handler ($type, $message, $file=__FILE__, $line=__LINE__) {
135 if ($message == "var: Deprecated. Please use the public/private/protected modifiers") return;
136 print "<br />\n<b>Strict Standards:</b> Type: <b>$type</b>: $message in <b>$file</b> on line <b>$line</b><br />\n";
138 // --------- End ---------
140 Also add/change this in AdminSettings.php:
141 // --------- Start ---------
142 $wgEnableProfileInfo = true;
143 $wgDBserver = "localhost"; // replace with DB server hostname
144 // --------- End ---------
146 Usage:
147 Run with "php fuzz-tester.php".
148 To see the various command-line options, run "php fuzz-tester.php --help".
149 To stop the script, press Ctrl-C.
151 Console output:
152 - If requested, first any previously failed tests will be rerun.
153 - Then new tests will be generated and run. Any tests that fail will be saved,
154 and a brief message about why they failed will be printed on the console.
155 - The console will show the number of tests run, time run, number of tests
156 failed, number of tests being done per minute, and the name of the current test.
158 TODO:
159 Some known things that could improve this script:
160 - Logging in with cookie jar storage needed for some tests (as there are some
161 pages that cannot be tested without being logged in, and which are currently
162 untested - e.g. Special:Emailuser, Special:Preferences, adding to Watchist).
163 - Testing of Timeline extension (I cannot test as ploticus has/had issues on
164 my architecture).
168 /////////////////////////// COMMAND LINE HELP ////////////////////////////////////
170 // This is a command line script, load MediaWiki env (gives command line options);
171 include('commandLine.inc');
173 // if the user asked for an explanation of command line options.
174 if ( isset( $options["help"] ) ) {
175 print <<<ENDS
176 MediaWiki $wgVersion fuzz tester
177 Usage: php {$_SERVER["SCRIPT_NAME"]} [--quiet] [--base-url=<url-to-test-wiki>]
178 [--directory=<failed-test-path>] [--include-binary]
179 [--w3c-validate] [--delete-passed-retests] [--help]
180 [--user=<username>] [--password=<password>]
181 [--rerun-failed-tests] [--max-errors=<int>]
182 [--max-runtime=<num-minutes>]
183 [--specific-test=<test-name>]
185 Options:
186 --quiet : Hides passed tests, shows only failed tests.
187 --base-url : URL to a wiki on which to run the tests.
188 The "http://" is optional and can be omitted.
189 --directory : Full path to directory for storing failed tests.
190 Will be created if it does not exist.
191 --include-binary : Includes non-alphanumeric characters in the tests.
192 --w3c-validate : Validates pages using the W3C's web validator.
193 Slow. Currently many pages fail validation.
194 --user : Login name of a valid user on your test wiki.
195 --password : Password for the valid user on your test wiki.
196 --delete-passed-retests : Will delete retests that now pass.
197 Requires --rerun-failed-tests to be meaningful.
198 --rerun-failed-tests : Whether to rerun any previously failed tests.
199 --max-errors : Maximum number of errors to report before exiting.
200 Does not include errors from --rerun-failed-tests
201 --max-runtime : Maximum runtime, in minutes, to run before exiting.
202 Only applies to new tests, not --rerun-failed-tests
203 --specific-test : Runs only the specified fuzz test.
204 Only applies to new tests, not --rerun-failed-tests
205 --keep-passed-tests : Saves all test files, even those that pass.
206 --help : Show this help message.
208 Example:
209 If you wanted to fuzz test a nightly MediaWiki checkout using cron for 1 hour,
210 and only wanted to be informed of errors, and did not want to redo previously
211 failed tests, and wanted a maximum of 100 errors, then you could do:
212 php {$_SERVER["SCRIPT_NAME"]} --quiet --max-errors=100 --max-runtime=60
215 ENDS;
217 exit( 0 );
221 // if we got command line options, check they look valid.
222 $validOptions = array ("quiet", "base-url", "directory", "include-binary",
223 "w3c-validate", "user", "password", "delete-passed-retests",
224 "rerun-failed-tests", "max-errors",
225 "max-runtime", "specific-test", "keep-passed-tests", "help" );
226 if (!empty($options)) {
227 $unknownArgs = array_diff (array_keys($options), $validOptions);
228 foreach ($unknownArgs as $invalidArg) {
229 print "Ignoring invalid command-line option: --$invalidArg\n";
234 ///////////////////////////// CONFIGURATION ////////////////////////////////////
236 // URL to some wiki on which we can run our tests.
237 if (!empty($options["base-url"])) {
238 define("WIKI_BASE_URL", $options["base-url"]);
239 } else {
240 define("WIKI_BASE_URL", $wgServer . $wgScriptPath . '/');
243 // The directory name where we store the output.
244 // Example for Windows: "c:\\temp\\wiki-fuzz"
245 if (!empty($options["directory"])) {
246 define("DIRECTORY", $options["directory"] );
247 } else {
248 define("DIRECTORY", "{$wgUploadDirectory}/fuzz-tests");
251 // Should our test fuzz data include binary strings?
252 define("INCLUDE_BINARY", isset($options["include-binary"]) );
254 // Whether we want to validate HTML output on the web.
255 // At the moment very few generated pages will validate, so not recommended.
256 define("VALIDATE_ON_WEB", isset($options["w3c-validate"]) );
257 // URL to use to validate our output:
258 define("VALIDATOR_URL", "http://validator.w3.org/check");
260 // Location of Tidy standalone executable.
261 define("PATH_TO_TIDY", "/usr/bin/tidy");
263 // The name of a user who has edited on your wiki. Used
264 // when testing the Special:Contributions and Special:Userlogin page.
265 if (!empty($options["user"])) {
266 define("USER_ON_WIKI", $options["user"] );
267 } else {
268 define("USER_ON_WIKI", "nickj");
271 // The password of the above user. Used when testing the login page,
272 // and to do this we sometimes need to login successfully.
273 if (!empty($options["password"])) {
274 define("USER_PASSWORD", $options["password"] );
275 } else {
276 // And no, this is not a valid password on any public wiki.
277 define("USER_PASSWORD", "nickj");
280 // If we have a test that failed, and then we run it again, and it passes,
281 // do you want to delete it or keep it?
282 define("DELETE_PASSED_RETESTS", isset($options["delete-passed-retests"]) );
284 // Do we want to rerun old saved tests at script startup?
285 // Set to true to help catch regressions, or false if you only want new stuff.
286 define("RERUN_OLD_TESTS", isset($options["rerun-failed-tests"]) );
288 // File where the database errors are logged. Should be defined in LocalSettings.php.
289 define("DB_ERROR_LOG_FILE", $wgDBerrorLog );
291 // Run in chatty mode (all output, default), or run in quiet mode (only prints out details of failed tests)?
292 define("QUIET", isset($options["quiet"]) );
294 // Keep all test files, even those that pass. Potentially useful to tracking input that causes something
295 // unusual to happen, if you don't know what "unusual" is until later.
296 define("KEEP_PASSED_TESTS", isset($options["keep-passed-tests"]) );
298 // The maximum runtime, if specified.
299 if (!empty($options["max-runtime"]) && intval($options["max-runtime"])>0) {
300 define("MAX_RUNTIME", intval($options["max-runtime"]) );
303 // The maximum number of problems to find, if specified. Excludes retest errors.
304 if (!empty($options["max-errors"]) && intval($options["max-errors"])>0) {
305 define("MAX_ERRORS", intval($options["max-errors"]) );
308 // if the user has requested a specific test (instead of all tests), and the test they asked for looks valid.
309 if (!empty($options["specific-test"])) {
310 if (class_exists($options["specific-test"]) && get_parent_class($options["specific-test"])=="pageTest") {
311 define("SPECIFIC_TEST", $options["specific-test"] );
313 else {
314 print "Ignoring invalid --specific-test\n";
318 // Define the file extensions we'll use:
319 define("PHP_TEST" , ".test.php");
320 define("CURL_TEST", ".curl.sh" );
321 define("DATA_FILE", ".data.bin");
322 define("INFO_FILE", ".info.txt");
323 define("HTML_FILE", ".wiki_preview.html");
325 // If it goes wrong, we want to know about it.
326 error_reporting(E_ALL | E_STRICT);
328 //////////////// A CLASS THAT GENERATES RANDOM NASTY WIKI & HTML STRINGS //////////////////////
330 class wikiFuzz {
332 // Only some HTML tags are understood with params by MediaWiki, the rest are ignored.
333 // List the tags that accept params below, as well as what those params are.
334 public static $data = array(
335 "B" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
336 "CAPTION" => array("CLASS", "ID", "STYLE", "align", "lang", "dir", "title"),
337 "CENTER" => array("CLASS", "STYLE", "ID", "lang", "dir", "title"),
338 "DIV" => array("CLASS", "STYLE", "ID", "align", "lang", "dir", "title"),
339 "FONT" => array("CLASS", "STYLE", "ID", "lang", "dir", "title", "face", "size", "color"),
340 "H1" => array("STYLE", "CLASS", "ID", "align", "lang", "dir", "title"),
341 "H2" => array("STYLE", "CLASS", "ID", "align", "lang", "dir", "title"),
342 "HR" => array("STYLE", "CLASS", "ID", "WIDTH", "lang", "dir", "title", "size", "noshade"),
343 "LI" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "type", "value"),
344 "TABLE" => array("STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "BORDER", "CELLPADDING",
345 "CELLSPACING", "lang", "dir", "title", "summary", "frame", "rules"),
346 "TD" => array("STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "COLSPAN", "ROWSPAN",
347 "VALIGN", "abbr", "axis", "headers", "scope", "nowrap", "height", "lang",
348 "dir", "title", "char", "charoff"),
349 "TH" => array("STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "COLSPAN", "ROWSPAN",
350 "VALIGN", "abbr", "axis", "headers", "scope", "nowrap", "height", "lang",
351 "dir", "title", "char", "charoff"),
352 "TR" => array("CLASS", "STYLE", "ID", "BGCOLOR", "ALIGN", "VALIGN", "lang", "dir", "title", "char", "charoff"),
353 "UL" => array("CLASS", "STYLE", "ID", "lang", "dir", "title", "type"),
354 "P" => array("style", "class", "id", "align", "lang", "dir", "title"),
355 "blockquote" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "cite"),
356 "span" => array("CLASS", "ID", "STYLE", "align", "lang", "dir", "title"),
357 "code" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
358 "tt" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
359 "small" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
360 "big" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
361 "s" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
362 "u" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
363 "del" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "datetime", "cite"),
364 "ins" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "datetime", "cite"),
365 "sub" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
366 "sup" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
367 "ol" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "type", "start"),
368 "br" => array("CLASS", "ID", "STYLE", "title", "clear"),
369 "cite" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
370 "var" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
371 "dl" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
372 "ruby" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
373 "rt" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
374 "rp" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
375 "dt" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
376 "dl" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
377 "em" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
378 "strong" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
379 "i" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
380 "thead" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign'),
381 "tfoot" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign'),
382 "tbody" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign'),
383 "colgroup" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign', 'span', 'width'),
384 "col" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign', 'span', 'width'),
385 "pre" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "width"),
387 // extension tags that accept parameters:
388 "sort" => array("order", "class"),
389 "ref" => array("name"),
390 "categorytree" => array("hideroot", "mode", "style"),
391 "chemform" => array("link", "wikilink", "query"),
392 "section" => array("begin", "new"),
394 // older MW transclusion.
395 "transclude" => array("page"),
398 // The types of the HTML that we will be testing were defined above
399 // Note: this needs to be initialized later to be equal to: array_keys(wikiFuzz::$data);
400 // as such, it also needs to also be publicly modifiable.
401 public static $types;
404 // Some attribute values.
405 static private $other = array("&","=",":","?","\"","\n","%n%n%n%n%n%n%n%n%n%n%n%n","\\");
406 static private $ints = array(
407 // various numbers
408 "0","-1","127","-7897","89000","808080","90928345",
409 "0xfffffff","ffff",
411 // Different ways of saying: '
412 "&#0000039;", // Long UTF-8 Unicode encoding
413 "&#39;", // dec version.
414 "&#x27;", // hex version.
415 "&#xA7;", // malformed hex variant, MSB not zero.
417 // Different ways of saying: "
418 "&#0000034;", // Long UTF-8 Unicode encoding
419 "&#34;",
420 "&#x22;", // hex version.
421 "&#xA2;", // malformed hex variant, MSB not zero.
423 // Different ways of saying: <
424 "<",
425 "&#0000060", // Long UTF-8 Unicode encoding without semicolon (Mediawiki wants the colon)
426 "&#0000060;", // Long UTF-8 Unicode encoding with semicolon
427 "&#60;",
428 "&#x3C;", // hex version.
429 "&#xBC;", // malformed hex variant, MSB not zero.
430 "&#x0003C;", // mid-length hex version
431 "&#X00003C;", // slightly longer hex version, with capital "X"
433 // Different ways of saying: >
434 ">",
435 "&#0000062;", // Long UTF-8 Unicode encoding
436 "&#62;",
437 "&#x3E;", // hex version.
438 "&#xBE;", // malformed variant, MSB not zero.
440 // Different ways of saying: [
441 "&#0000091;", // Long UTF-8 Unicode encoding
442 "&#91;",
443 "&#x5B;", // hex version.
445 // Different ways of saying: {{
446 "&#0000123;&#0000123;", // Long UTF-8 Unicode encoding
447 "&#123;&#123;",
448 "&#x7B;&#x7B;", // hex version.
450 // Different ways of saying: |
451 "&#0000124;", // Long UTF-8 Unicode encoding
452 "&#124;",
453 "&#x7C;", // hex version.
454 "&#xFC;", // malformed hex variant, MSB not zero.
456 // a "lignature" - http://www.robinlionheart.com/stds/html4/spchars#ligature
457 "&zwnj;"
460 // Defines various wiki-related bits of syntax, that can potentially cause
461 // MediaWiki to do something other than just print that literal text.
462 static private $ext = array(
463 // links, templates, parameters.
464 "[[", "]]", "{{", "}}", "|", "[", "]", "{{{", "}}}", "|]]",
466 // wiki tables.
467 "\n{|", "\n|}",
468 "!",
469 "\n!",
470 "!!",
471 "||",
472 "\n|-", "| ", "\n|",
474 // section headings.
475 "=", "==", "===", "====", "=====", "======",
477 // lists (ordered and unordered) and indentation.
478 "\n*", "*", "\n:", ":",
479 "\n#", "#",
481 // definition lists (dl, dt, dd), newline, and newline with pre, and a tab.
482 "\n;", ";", "\n ",
484 // Whitespace: newline, tab, space.
485 "\n", "\t", " ",
487 // Some XSS attack vectors from http://ha.ckers.org/xss.html
488 "&#x09;", // tab
489 "&#x0A;", // newline
490 "&#x0D;", // carriage return
491 "\0", // null character
492 " &#14; ", // spaces and meta characters
493 "'';!--\"<XSS>=&{()}", // compact injection of XSS & SQL tester
495 // various NULL fields
496 "%00",
497 "&#00;",
498 "\0",
500 // horizontal rule.
501 "-----", "\n-----",
503 // signature, redirect, bold, italics.
504 "~~~~", "#REDIRECT [[", "'''", "''",
506 // comments.
507 "<!--", "-->",
509 // quotes.
510 "\"", "'",
512 // tag start and tag end.
513 "<", ">",
515 // implicit link creation on URIs.
516 "http://",
517 "https://",
518 "ftp://",
519 "irc://",
520 "news:",
521 'gopher://',
522 'telnet://',
523 'nntp://',
524 'worldwind://',
525 'mailto:',
527 // images.
528 "[[image:",
529 ".gif",
530 ".png",
531 ".jpg",
532 ".jpeg",
533 'thumbnail=',
534 'thumbnail',
535 'thumb=',
536 'thumb',
537 'right',
538 'none',
539 'left',
540 'framed',
541 'frame',
542 'enframed',
543 'centre',
544 'center',
545 "Image:",
546 "[[:Image",
547 'px',
549 // misc stuff to throw at the Parser.
550 '%08X',
551 '/',
552 ":x{|",
553 "\n|+",
554 "<noinclude>",
555 "</noinclude>",
556 " \302\273",
557 " :",
558 " !",
559 " ;",
560 "\302\253",
561 "[[category:",
562 "?=",
563 "(",
564 ")",
565 "]]]",
566 "../",
567 "{{{{",
568 "}}}}",
569 "[[Special:",
570 "<includeonly>",
571 "</includeonly>",
572 "<!--MWTEMPLATESECTION=",
573 '<!--MWTOC-->',
575 // implicit link creation on booknum, RFC, and PubMed ID usage (both with and without IDs)
576 "ISBN 2",
577 "RFC 000",
578 "PMID 000",
579 "ISBN ",
580 "RFC ",
581 "PMID ",
583 // magic words:
584 '__NOTOC__',
585 '__FORCETOC__',
586 '__NOEDITSECTION__',
587 '__START__',
588 '__NOTITLECONVERT__',
589 '__NOCONTENTCONVERT__',
590 '__END__',
591 '__TOC__',
592 '__NOTC__',
593 '__NOCC__',
594 "__FORCETOC__",
595 "__NEWSECTIONLINK__",
596 "__NOGALLERY__",
598 // more magic words / internal templates.
599 '{{PAGENAME}}',
600 '{{PAGENAMEE}}',
601 '{{NAMESPACE}}',
602 "{{MSG:",
603 "}}",
604 "{{MSGNW:",
605 "}}",
606 "{{INT:",
607 "}}",
608 '{{SITENAME}}',
609 "{{NS:",
610 "}}",
611 "{{LOCALURL:",
612 "}}",
613 "{{LOCALURLE:",
614 "}}",
615 "{{SCRIPTPATH}}",
616 "{{GRAMMAR:gentiv|",
617 "}}",
618 "{{REVISIONID}}",
619 "{{SUBPAGENAME}}",
620 "{{SUBPAGENAMEE}}",
621 "{{ns:0}}",
622 "{{fullurle:",
623 "}}",
624 "{{subst:",
625 "}}",
626 "{{UCFIRST:",
627 "}}",
628 "{{UC:",
629 '{{SERVERNAME}}',
630 '{{SERVER}}',
631 "{{RAW:",
632 "}}",
633 "{{PLURAL:",
634 "}}",
635 "{{LCFIRST:",
636 "}}",
637 "{{LC:",
638 "}}",
639 '{{CURRENTWEEK}}',
640 '{{CURRENTDOW}}',
641 "{{INT:{{LC:contribs-showhideminor}}|",
642 "}}",
643 "{{INT:googlesearch|",
644 "}}",
645 "{{BASEPAGENAME}}",
646 "{{CONTENTLANGUAGE}}",
647 "{{PAGESINNAMESPACE:}}",
648 "{{#language:",
649 "}}",
650 "{{#special:",
651 "}}",
652 "{{#special:emailuser",
653 "}}",
655 // Some raw link for magic words.
656 "{{NUMBEROFPAGES:R",
657 "}}",
658 "{{NUMBEROFUSERS:R",
659 "}}",
660 "{{NUMBEROFARTICLES:R",
661 "}}",
662 "{{NUMBEROFFILES:R",
663 "}}",
664 "{{NUMBEROFADMINS:R",
665 "}}",
666 "{{padleft:",
667 "}}",
668 "{{padright:",
669 "}}",
670 "{{DEFAULTSORT:",
671 "}}",
673 // internal Math "extension":
674 "<math>",
675 "</math>",
677 // Parser extension functions:
678 "{{#expr:",
679 "{{#if:",
680 "{{#ifeq:",
681 "{{#ifexist:",
682 "{{#ifexpr:",
683 "{{#switch:",
684 "{{#time:",
685 "}}",
687 // references table for the Cite extension.
688 "<references/>",
690 // Internal Parser tokens - try inserting some of these.
691 "UNIQ25f46b0524f13e67NOPARSE",
692 "UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002",
693 "\x07UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002-QINU",
695 // Inputbox extension:
696 "<inputbox>\ntype=search\nsearchbuttonlabel=\n",
697 "</inputbox>",
699 // charInsert extension:
700 "<charInsert>",
701 "</charInsert>",
703 // wikiHiero extension:
704 "<hiero>",
705 "</hiero>",
707 // Image gallery:
708 "<gallery>",
709 "</gallery>",
711 // FixedImage extension.
712 "<fundraising/>",
714 // Timeline extension: currently untested.
716 // Nowiki:
717 "<nOwIkI>",
718 "</nowiki>",
720 // an external image to test the external image displaying code
721 "http://debian.org/Pics/debian.png",
723 // LabeledSectionTransclusion extension.
724 "{{#lstx:",
725 "}}",
726 "{{#lst:",
727 "}}",
728 "{{#lst:Main Page|",
729 "}}"
733 ** Randomly returns one element of the input array.
735 static public function chooseInput(array $input) {
736 $randindex = wikiFuzz::randnum(count($input) - 1);
737 return $input[$randindex];
740 // Max number of parameters for HTML attributes.
741 static private $maxparams = 10;
743 /**
744 ** Returns random number between finish and start.
746 static public function randnum($finish,$start=0) {
747 return mt_rand($start,$finish);
751 ** Returns a mix of random text and random wiki syntax.
753 static private function randstring() {
754 $thestring = "";
756 for ($i=0; $i<40; $i++) {
757 $what = wikiFuzz::randnum(1);
759 if ($what == 0) { // include some random wiki syntax
760 $which = wikiFuzz::randnum(count(wikiFuzz::$ext) - 1);
761 $thestring .= wikiFuzz::$ext[$which];
763 else { // include some random text
764 $char = INCLUDE_BINARY
765 // Decimal version:
766 // "&#" . wikiFuzz::randnum(255) . ";"
767 // Hex version:
768 ? "&#x" . str_pad(dechex(wikiFuzz::randnum(255)), wikiFuzz::randnum(2, 7), "0", STR_PAD_LEFT) . ";"
769 // A truly binary version:
770 // ? chr(wikiFuzz::randnum(0,255))
771 : chr(wikiFuzz::randnum(126,32));
773 $length = wikiFuzz::randnum(8);
774 $thestring .= str_repeat ($char, $length);
777 return $thestring;
781 ** Returns either random text, or random wiki syntax, or random data from "ints",
782 ** or random data from "other".
784 static private function makestring() {
785 $what = wikiFuzz::randnum(2);
786 if ($what == 0) {
787 return wikiFuzz::randstring();
789 elseif ($what == 1) {
790 return wikiFuzz::$ints[wikiFuzz::randnum(count(wikiFuzz::$ints) - 1)];
792 else {
793 return wikiFuzz::$other[wikiFuzz::randnum(count(wikiFuzz::$other) - 1)];
799 ** Strips out the stuff that Mediawiki balks at in a page's title.
800 ** Implementation copied/pasted from cleanupTable.inc & cleanupImages.php
802 static public function makeTitleSafe($str) {
803 $legalTitleChars = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF";
804 return preg_replace_callback(
805 "/([^$legalTitleChars])/",
806 create_function(
807 // single quotes are essential here,
808 // or alternative escape all $ as \$
809 '$matches',
810 'return sprintf( "\\x%02x", ord( $matches[1] ) );'
812 $str );
816 ** Returns a string of fuzz text.
818 static private function loop() {
819 switch ( wikiFuzz::randnum(3) ) {
820 case 1: // an opening tag, with parameters.
821 $string = "";
822 $i = wikiFuzz::randnum(count(wikiFuzz::$types) - 1);
823 $t = wikiFuzz::$types[$i];
824 $arr = wikiFuzz::$data[$t];
825 $string .= "<" . $t . " ";
826 $num_params = min(wikiFuzz::$maxparams, count($arr));
827 for ($z=0; $z<$num_params; $z++) {
828 $badparam = $arr[wikiFuzz::randnum(count($arr) - 1)];
829 $badstring = wikiFuzz::makestring();
830 $string .= $badparam . "=" . wikiFuzz::getRandQuote() . $badstring . wikiFuzz::getRandQuote() . " ";
832 $string .= ">\n";
833 return $string;
834 case 2: // a closing tag.
835 $i = wikiFuzz::randnum(count(wikiFuzz::$types) - 1);
836 return "</". wikiFuzz::$types[$i] . ">";
837 case 3: // a random string, between tags.
838 return wikiFuzz::makeString();
840 return ""; // catch-all, should never be called.
844 ** Returns one of the three styles of random quote: ', ", and nothing.
846 static private function getRandQuote() {
847 switch ( wikiFuzz::randnum(3) ) {
848 case 1 : return "'";
849 case 2 : return "\"";
850 default: return "";
855 ** Returns fuzz text, with the parameter indicating approximately how many lines of text you want.
857 static public function makeFuzz($maxtypes = 2) {
858 $page = "";
859 for ($k=0; $k<$maxtypes; $k++) {
860 $page .= wikiFuzz::loop();
862 return $page;
867 //////// MEDIAWIKI PAGES TO TEST, AND HOW TO TEST THEM ///////
870 ** A page test has just these things:
871 ** 1) Form parameters.
872 ** 2) the URL we are going to test those parameters on.
873 ** 3) Any cookies required for the test.
874 ** 4) Whether Tidy should validate the page. Defaults to true, but can be turned off.
875 ** Declared abstract because it should be extended by a class
876 ** that supplies these parameters.
878 abstract class pageTest {
879 protected $params;
880 protected $pagePath;
881 protected $cookie = "";
882 protected $tidyValidate = true;
884 public function getParams() {
885 return $this->params;
888 public function getPagePath() {
889 return $this->pagePath;
892 public function getCookie() {
893 return $this->cookie;
896 public function tidyValidate() {
897 return $this->tidyValidate;
903 ** a page test for the "Edit" page. Tests Parser.php and Sanitizer.php.
905 class editPageTest extends pageTest {
906 function __construct() {
907 $this->pagePath = "index.php?title=WIKIFUZZ";
909 $this->params = array (
910 "action" => "submit",
911 "wpMinoredit" => wikiFuzz::makeFuzz(2),
912 "wpPreview" => wikiFuzz::makeFuzz(2),
913 "wpSection" => wikiFuzz::makeFuzz(2),
914 "wpEdittime" => wikiFuzz::makeFuzz(2),
915 "wpSummary" => wikiFuzz::makeFuzz(2),
916 "wpScrolltop" => wikiFuzz::makeFuzz(2),
917 "wpStarttime" => wikiFuzz::makeFuzz(2),
918 "wpAutoSummary" => wikiFuzz::makeFuzz(2),
919 "wpTextbox1" => wikiFuzz::makeFuzz(40) // the main wiki text, need lots of this.
922 // sometimes we don't want to specify certain parameters.
923 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpSection"]);
924 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpEdittime"]);
925 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpSummary"]);
926 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpScrolltop"]);
927 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpStarttime"]);
928 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpAutoSummary"]);
929 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpTextbox1"]);
935 ** a page test for "Special:Listusers".
937 class listusersTest extends pageTest {
938 function __construct() {
939 $this->pagePath = "index.php?title=Special:Listusers";
941 $this->params = array (
942 "title" => wikiFuzz::makeFuzz(2),
943 "group" => wikiFuzz::makeFuzz(2),
944 "username" => wikiFuzz::makeFuzz(2),
945 "Go" => wikiFuzz::makeFuzz(2),
946 "limit" => wikiFuzz::chooseInput( array("0", "-1", "---'----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
947 "offset" => wikiFuzz::chooseInput( array("0", "-1", "--------'-----0", "+1", "81343242346234234", wikiFuzz::makeFuzz(2)) )
954 ** a page test for "Special:Search".
956 class searchTest extends pageTest {
957 function __construct() {
958 $this->pagePath = "index.php?title=Special:Search";
960 $this->params = array (
961 "action" => "index.php?title=Special:Search",
962 "ns0" => wikiFuzz::makeFuzz(2),
963 "ns1" => wikiFuzz::makeFuzz(2),
964 "ns2" => wikiFuzz::makeFuzz(2),
965 "ns3" => wikiFuzz::makeFuzz(2),
966 "ns4" => wikiFuzz::makeFuzz(2),
967 "ns5" => wikiFuzz::makeFuzz(2),
968 "ns6" => wikiFuzz::makeFuzz(2),
969 "ns7" => wikiFuzz::makeFuzz(2),
970 "ns8" => wikiFuzz::makeFuzz(2),
971 "ns9" => wikiFuzz::makeFuzz(2),
972 "ns10" => wikiFuzz::makeFuzz(2),
973 "ns11" => wikiFuzz::makeFuzz(2),
974 "ns12" => wikiFuzz::makeFuzz(2),
975 "ns13" => wikiFuzz::makeFuzz(2),
976 "ns14" => wikiFuzz::makeFuzz(2),
977 "ns15" => wikiFuzz::makeFuzz(2),
978 "redirs" => wikiFuzz::makeFuzz(2),
979 "search" => wikiFuzz::makeFuzz(2),
980 "offset" => wikiFuzz::chooseInput( array("", "0", "-1", "--------'-----0", "+1", "81343242346234234", wikiFuzz::makeFuzz(2)) ),
981 "fulltext" => wikiFuzz::chooseInput( array("", "0", "1", "--------'-----0", "+1", wikiFuzz::makeFuzz(2)) ),
982 "searchx" => wikiFuzz::chooseInput( array("", "0", "1", "--------'-----0", "+1", wikiFuzz::makeFuzz(2)) )
989 ** a page test for "Special:Recentchanges".
991 class recentchangesTest extends pageTest {
992 function __construct() {
993 $this->pagePath = "index.php?title=Special:Recentchanges";
995 $this->params = array (
996 "action" => wikiFuzz::makeFuzz(2),
997 "title" => wikiFuzz::makeFuzz(2),
998 "namespace" => wikiFuzz::chooseInput( range(-1, 15) ),
999 "Go" => wikiFuzz::makeFuzz(2),
1000 "invert" => wikiFuzz::chooseInput( array("-1", "---'----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1001 "hideanons" => wikiFuzz::chooseInput( array("-1", "------'-------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1002 'limit' => wikiFuzz::chooseInput( array("0", "-1", "---------'----0", "+1", "81340909772349234", wikiFuzz::makeFuzz(2)) ),
1003 "days" => wikiFuzz::chooseInput( array("-1", "----------'---0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1004 "hideminor" => wikiFuzz::chooseInput( array("-1", "-----------'--0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1005 "hidebots" => wikiFuzz::chooseInput( array("-1", "---------'----0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1006 "hideliu" => wikiFuzz::chooseInput( array("-1", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1007 "hidepatrolled" => wikiFuzz::chooseInput( array("-1", "-----'--------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1008 "hidemyself" => wikiFuzz::chooseInput( array("-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1009 'categories_any'=> wikiFuzz::chooseInput( array("-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1010 'categories' => wikiFuzz::chooseInput( array("-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1011 'feed' => wikiFuzz::chooseInput( array("-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) )
1018 ** a page test for "Special:Prefixindex".
1020 class prefixindexTest extends pageTest {
1021 function __construct() {
1022 $this->pagePath = "index.php?title=Special:Prefixindex";
1024 $this->params = array (
1025 "title" => "Special:Prefixindex",
1026 "namespace" => wikiFuzz::randnum(-10,101),
1027 "Go" => wikiFuzz::makeFuzz(2)
1030 // sometimes we want 'prefix', sometimes we want 'from', and sometimes we want nothing.
1031 if (wikiFuzz::randnum(3) == 0) {
1032 $this->params["prefix"] = wikiFuzz::chooseInput( array("-1", "-----'--------0", "+++--+1",
1033 wikiFuzz::randnum(-10,8134), wikiFuzz::makeFuzz(2)) );
1035 if (wikiFuzz::randnum(3) == 0) {
1036 $this->params["from"] = wikiFuzz::chooseInput( array("-1", "-----'--------0", "+++--+1",
1037 wikiFuzz::randnum(-10,8134), wikiFuzz::makeFuzz(2)) );
1044 ** a page test for "Special:MIMEsearch".
1046 class mimeSearchTest extends pageTest {
1047 function __construct() {
1048 $this->pagePath = "index.php?title=Special:MIMEsearch";
1050 $this->params = array (
1051 "action" => "index.php?title=Special:MIMEsearch",
1052 "mime" => wikiFuzz::makeFuzz(3),
1053 'limit' => wikiFuzz::chooseInput( array("0", "-1", "-------'------0", "+1", "81342321351235325", wikiFuzz::makeFuzz(2)) ),
1054 'offset' => wikiFuzz::chooseInput( array("0", "-1", "-----'--------0", "+1", "81341231235365252234324", wikiFuzz::makeFuzz(2)) )
1061 ** a page test for "Special:Log".
1063 class specialLogTest extends pageTest {
1064 function __construct() {
1065 $this->pagePath = "index.php?title=Special:Log";
1067 $this->params = array (
1068 "type" => wikiFuzz::chooseInput( array("", wikiFuzz::makeFuzz(2)) ),
1069 "par" => wikiFuzz::makeFuzz(2),
1070 "user" => wikiFuzz::makeFuzz(2),
1071 "page" => wikiFuzz::makeFuzz(2),
1072 "from" => wikiFuzz::makeFuzz(2),
1073 "until" => wikiFuzz::makeFuzz(2),
1074 "title" => wikiFuzz::makeFuzz(2)
1081 ** a page test for "Special:Userlogin", with a successful login.
1083 class successfulUserLoginTest extends pageTest {
1084 function __construct() {
1085 $this->pagePath = "index.php?title=Special:Userlogin&action=submitlogin&type=login&returnto=" . wikiFuzz::makeFuzz(2);
1087 $this->params = array (
1088 "wpName" => USER_ON_WIKI,
1089 // sometimes real password, sometimes not:
1090 'wpPassword' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz(2), USER_PASSWORD ) ),
1091 'wpRemember' => wikiFuzz::makeFuzz(2)
1094 $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array("1" , wikiFuzz::makeFuzz(2) ) );
1100 ** a page test for "Special:Userlogin".
1102 class userLoginTest extends pageTest {
1103 function __construct() {
1105 $this->pagePath = "index.php?title=Special:Userlogin";
1107 $this->params = array (
1108 'wpRetype' => wikiFuzz::makeFuzz(2),
1109 'wpRemember' => wikiFuzz::makeFuzz(2),
1110 'wpRealName' => wikiFuzz::makeFuzz(2),
1111 'wpPassword' => wikiFuzz::makeFuzz(2),
1112 'wpName' => wikiFuzz::makeFuzz(2),
1113 'wpMailmypassword'=> wikiFuzz::makeFuzz(2),
1114 'wpLoginattempt' => wikiFuzz::makeFuzz(2),
1115 'wpEmail' => wikiFuzz::makeFuzz(2),
1116 'wpDomain' => wikiFuzz::chooseInput( array("", "local", wikiFuzz::makeFuzz(2)) ),
1117 'wpCreateaccountMail' => wikiFuzz::chooseInput( array("", wikiFuzz::makeFuzz(2)) ),
1118 'wpCreateaccount' => wikiFuzz::chooseInput( array("", wikiFuzz::makeFuzz(2)) ),
1119 'wpCookieCheck' => wikiFuzz::chooseInput( array("", wikiFuzz::makeFuzz(2)) ),
1120 'type' => wikiFuzz::chooseInput( array("signup", "login", "", wikiFuzz::makeFuzz(2)) ),
1121 'returnto' => wikiFuzz::makeFuzz(2),
1122 'action' => wikiFuzz::chooseInput( array("", "submitlogin", wikiFuzz::makeFuzz(2)) )
1125 $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array("1" , wikiFuzz::makeFuzz(2) ) );
1131 ** a page test for "Special:Ipblocklist" (also includes unblocking)
1133 class ipblocklistTest extends pageTest {
1134 function __construct() {
1135 $this->pagePath = "index.php?title=Special:Ipblocklist";
1137 $this->params = array (
1138 'wpUnblockAddress'=> wikiFuzz::makeFuzz(2),
1139 'ip' => wikiFuzz::chooseInput( array("20398702394", "", "Nickj2", wikiFuzz::makeFuzz(2),
1140 // something like an IP address, sometimes invalid:
1141 ( wikiFuzz::randnum(300,-20) . "." . wikiFuzz::randnum(300,-20) . "."
1142 . wikiFuzz::randnum(300,-20) . "." .wikiFuzz::randnum(300,-20) ) ) ),
1143 'id' => wikiFuzz::makeFuzz(2),
1144 'wpUnblockReason' => wikiFuzz::makeFuzz(2),
1145 'action' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2), "success", "submit", "unblock") ),
1146 'wpEditToken' => wikiFuzz::makeFuzz(2),
1147 'wpBlock' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2), "") ),
1148 'limit' => wikiFuzz::chooseInput( array("0", "-1", "--------'-----0", "+1",
1149 "09700982312351132098234", wikiFuzz::makeFuzz(2)) ),
1150 'offset' => wikiFuzz::chooseInput( array("0", "-1", "------'-------0", "+1",
1151 "09700980982341535324234234", wikiFuzz::makeFuzz(2)) )
1154 // sometimes we don't want to specify certain parameters.
1155 if (wikiFuzz::randnum(4) == 0) unset($this->params["action"]);
1156 if (wikiFuzz::randnum(3) == 0) unset($this->params["ip"]);
1157 if (wikiFuzz::randnum(2) == 0) unset($this->params["id"]);
1158 if (wikiFuzz::randnum(3) == 0) unset($this->params["wpUnblockAddress"]);
1164 ** a page test for "Special:Newimages".
1166 class newImagesTest extends pageTest {
1167 function __construct() {
1168 $this->pagePath = "index.php?title=Special:Newimages";
1170 $this->params = array (
1171 'hidebots' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2), "1", "", "-1") ),
1172 'wpIlMatch' => wikiFuzz::makeFuzz(2),
1173 'until' => wikiFuzz::makeFuzz(2),
1174 'from' => wikiFuzz::makeFuzz(2)
1177 // sometimes we don't want to specify certain parameters.
1178 if (wikiFuzz::randnum(6) == 0) unset($this->params["until"]);
1179 if (wikiFuzz::randnum(6) == 0) unset($this->params["from"]);
1185 ** a page test for the "Special:Imagelist" page.
1187 class imagelistTest extends pageTest {
1188 function __construct() {
1189 $this->pagePath = "index.php?title=Special:Imagelist";
1191 $this->params = array (
1192 'sort' => wikiFuzz::chooseInput( array("bysize", "byname" , "bydate", wikiFuzz::makeFuzz(2)) ),
1193 'limit' => wikiFuzz::chooseInput( array("0", "-1", "--------'-----0", "+1", "09700982312351132098234", wikiFuzz::makeFuzz(2)) ),
1194 'offset' => wikiFuzz::chooseInput( array("0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz(2)) ),
1195 'wpIlMatch' => wikiFuzz::makeFuzz(2)
1202 ** a page test for "Special:Export".
1204 class specialExportTest extends pageTest {
1205 function __construct() {
1206 $this->pagePath = "index.php?title=Special:Export";
1208 $this->params = array (
1209 'action' => wikiFuzz::chooseInput( array("submit", "", wikiFuzz::makeFuzz(2)) ),
1210 'pages' => wikiFuzz::makeFuzz(2),
1211 'curonly' => wikiFuzz::chooseInput( array("", "0", "-1", wikiFuzz::makeFuzz(2)) ),
1212 'listauthors' => wikiFuzz::chooseInput( array("", "0", "-1", wikiFuzz::makeFuzz(2)) ),
1213 'history' => wikiFuzz::chooseInput( array("0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz(2)) ),
1217 // For the time being, need to disable "submit" action as Tidy barfs on MediaWiki's XML export.
1218 if ($this->params['action'] == 'submit') $this->params['action'] = '';
1220 // Sometimes remove the history field.
1221 if (wikiFuzz::randnum(2) == 0) unset($this->params["history"]);
1223 // page does not produce HTML.
1224 $this->tidyValidate = false;
1230 ** a page test for "Special:Booksources".
1232 class specialBooksourcesTest extends pageTest {
1233 function __construct() {
1234 $this->pagePath = "index.php?title=Special:Booksources";
1236 $this->params = array (
1237 'go' => wikiFuzz::makeFuzz(2),
1238 // ISBN codes have to contain some semi-numeric stuff or will be ignored:
1239 'isbn' => "0X0" . wikiFuzz::makeFuzz(2)
1246 ** a page test for "Special:Allpages".
1248 class specialAllpagesTest extends pageTest {
1249 function __construct() {
1250 $this->pagePath = "index.php?title=Special%3AAllpages";
1252 $this->params = array (
1253 'from' => wikiFuzz::makeFuzz(2),
1254 'namespace' => wikiFuzz::chooseInput( range(-1, 15) ),
1255 'go' => wikiFuzz::makeFuzz(2)
1262 ** a page test for the page History.
1264 class pageHistoryTest extends pageTest {
1265 function __construct() {
1266 $this->pagePath = "index.php?title=Main_Page&action=history";
1268 $this->params = array (
1269 'limit' => wikiFuzz::chooseInput( array("-1", "0", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1270 'offset' => wikiFuzz::chooseInput( array("-1", "0", "------'-------0", "+1", "9823412312312412435", wikiFuzz::makeFuzz(2)) ),
1271 "go" => wikiFuzz::chooseInput( array("first", "last", wikiFuzz::makeFuzz(2)) ),
1272 "dir" => wikiFuzz::chooseInput( array("prev", "next", wikiFuzz::makeFuzz(2)) ),
1273 "diff" => wikiFuzz::chooseInput( array("-1", "--------'-----0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1274 "oldid" => wikiFuzz::chooseInput( array("prev", "-1", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1275 "feed" => wikiFuzz::makeFuzz(2)
1282 ** a page test for the Special:Contributions".
1284 class contributionsTest extends pageTest {
1285 function __construct() {
1286 $this->pagePath = "index.php?title=Special:Contributions/" . USER_ON_WIKI;
1288 $this->params = array (
1289 'target' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2), "newbies", USER_ON_WIKI) ),
1290 'namespace' => wikiFuzz::chooseInput( array(-1, 15, 1, wikiFuzz::makeFuzz(2)) ),
1291 'offset' => wikiFuzz::chooseInput( array("0", "-1", "------'-------0", "+1", "982342131232131231241", wikiFuzz::makeFuzz(2)) ),
1292 'bot' => wikiFuzz::chooseInput( array("", "-1", "0", "1", wikiFuzz::makeFuzz(2)) ),
1293 'go' => wikiFuzz::chooseInput( array("-1", 'prev', 'next', wikiFuzz::makeFuzz(2)) )
1300 ** a page test for viewing a normal page, whilst posting various params.
1302 class viewPageTest extends pageTest {
1303 function __construct() {
1304 $this->pagePath = "index.php?title=Main_Page";
1306 $this->params = array (
1307 "useskin" => wikiFuzz::chooseInput( array("chick", "cologneblue", "myskin",
1308 "nostalgia", "simple", "standard", wikiFuzz::makeFuzz(2)) ),
1309 "uselang" => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz(2),
1310 "ab", "af", "an", "ar", "arc", "as", "ast", "av", "ay", "az", "ba",
1311 "bat-smg", "be", "bg", "bm", "bn", "bo", "bpy", "br", "bs", "ca",
1312 "ce", "cs", "csb", "cv", "cy", "da", "de", "dv", "dz", "el", "en",
1313 "eo", "es", "et", "eu", "fa", "fi", "fo", "fr", "fur", "fy", "ga",
1314 "gn", "gsw", "gu", "he", "hi", "hr", "hu", "ia", "id", "ii", "is",
1315 "it", "ja", "jv", "ka", "km", "kn", "ko", "ks", "ku", "kv", "la",
1316 "li", "lo", "lt", "lv", "mk", "ml", "ms", "nah", "nap", "nds",
1317 "nds-nl", "nl", "nn", "no", "non", "nv", "oc", "or", "os", "pa",
1318 "pl", "pms", "ps", "pt", "pt-br", "qu", "rmy", "ro", "ru", "sc",
1319 "sd", "sk", "sl", "sq", "sr", "sr-ec", "sr-el", "sr-jc", "sr-jl",
1320 "su", "sv", "ta", "te", "th", "tlh", "tr", "tt", "ty", "tyv", "udm",
1321 "ug", "uk", "ur", "utf8", "vec", "vi", "wa", "xal", "yi", "za",
1322 "zh", "zh-cn", "zh-hk", "zh-sg", "zh-tw", "zh-tw") ),
1323 "returnto" => wikiFuzz::makeFuzz(2),
1324 "feed" => wikiFuzz::chooseInput( array("atom", "rss", wikiFuzz::makeFuzz(2)) ),
1325 "rcid" => wikiFuzz::makeFuzz(2),
1326 "action" => wikiFuzz::chooseInput( array("view", "raw", "render", wikiFuzz::makeFuzz(2), "markpatrolled") ),
1327 "printable" => wikiFuzz::makeFuzz(2),
1328 "oldid" => wikiFuzz::makeFuzz(2),
1329 "redirect" => wikiFuzz::makeFuzz(2),
1330 "diff" => wikiFuzz::makeFuzz(2),
1331 "search" => wikiFuzz::makeFuzz(2),
1332 "rdfrom" => wikiFuzz::makeFuzz(2), // things from Article.php from here on:
1333 "token" => wikiFuzz::makeFuzz(2),
1334 "tbid" => wikiFuzz::makeFuzz(2),
1335 "action" => wikiFuzz::chooseInput( array("purge", wikiFuzz::makeFuzz(2)) ),
1336 "wpReason" => wikiFuzz::makeFuzz(2),
1337 "wpEditToken" => wikiFuzz::makeFuzz(2),
1338 "from" => wikiFuzz::makeFuzz(2),
1339 "bot" => wikiFuzz::makeFuzz(2),
1340 "summary" => wikiFuzz::makeFuzz(2),
1341 "direction" => wikiFuzz::chooseInput( array("next", "prev", wikiFuzz::makeFuzz(2)) ),
1342 "section" => wikiFuzz::makeFuzz(2),
1343 "preload" => wikiFuzz::makeFuzz(2),
1347 // Tidy does not know how to valid atom or rss, so exclude from testing for the time being.
1348 if ($this->params["feed"] == "atom") { unset($this->params["feed"]); }
1349 else if ($this->params["feed"] == "rss") { unset($this->params["feed"]); }
1351 // Raw pages cannot really be validated
1352 if ($this->params["action"] == "raw") unset($this->params["action"]);
1354 // sometimes we don't want to specify certain parameters.
1355 if (wikiFuzz::randnum(6) == 0) unset($this->params["rcid"]);
1356 if (wikiFuzz::randnum(6) == 0) unset($this->params["diff"]);
1357 if (wikiFuzz::randnum(6) == 0) unset($this->params["rdfrom"]);
1358 if (wikiFuzz::randnum(3) == 0) unset($this->params["oldid"]);
1360 // usually don't want action == purge.
1361 if (wikiFuzz::randnum(6) > 1) unset($this->params["action"]);
1367 ** a page test for "Special:Allmessages".
1369 class specialAllmessagesTest extends pageTest {
1370 function __construct() {
1371 $this->pagePath = "index.php?title=Special:Allmessages";
1373 // only really has one parameter
1374 $this->params = array (
1375 "ot" => wikiFuzz::chooseInput( array("php", "html", wikiFuzz::makeFuzz(2)) )
1381 ** a page test for "Special:Newpages".
1383 class specialNewpages extends pageTest {
1384 function __construct() {
1385 $this->pagePath = "index.php?title=Special:Newpages";
1387 $this->params = array (
1388 "namespace" => wikiFuzz::chooseInput( range(-1, 15) ),
1389 "feed" => wikiFuzz::chooseInput( array("atom", "rss", wikiFuzz::makeFuzz(2)) ),
1390 'limit' => wikiFuzz::chooseInput( array("-1", "0", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1391 'offset' => wikiFuzz::chooseInput( array("-1", "0", "------'-------0", "+1", "9823412312312412435", wikiFuzz::makeFuzz(2)) )
1394 // Tidy does not know how to valid atom or rss, so exclude from testing for the time being.
1395 if ($this->params["feed"] == "atom") { unset($this->params["feed"]); }
1396 else if ($this->params["feed"] == "rss") { unset($this->params["feed"]); }
1401 ** a page test for "redirect.php"
1403 class redirectTest extends pageTest {
1404 function __construct() {
1405 $this->pagePath = "redirect.php";
1407 $this->params = array (
1408 "wpDropdown" => wikiFuzz::makeFuzz(2)
1411 // sometimes we don't want to specify certain parameters.
1412 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpDropdown"]);
1418 ** a page test for "Special:Confirmemail"
1420 class confirmEmail extends pageTest {
1421 function __construct() {
1422 // sometimes we send a bogus confirmation code, and sometimes we don't.
1423 $this->pagePath = "index.php?title=Special:Confirmemail" . wikiFuzz::chooseInput( array("", "/" . wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(1)) ) );
1425 $this->params = array (
1426 "token" => wikiFuzz::makeFuzz(2)
1433 ** a page test for "Special:Watchlist"
1434 ** Note: this test would be better if we were logged in.
1436 class watchlistTest extends pageTest {
1437 function __construct() {
1438 $this->pagePath = "index.php?title=Special:Watchlist";
1440 $this->params = array (
1441 "remove" => wikiFuzz::chooseInput( array("Remove checked items from watchlist", wikiFuzz::makeFuzz(2))),
1442 'days' => wikiFuzz::chooseInput( array(0, -1, -230, "--", 3, 9, wikiFuzz::makeFuzz(2)) ),
1443 'hideOwn' => wikiFuzz::chooseInput( array("", "0", "1", wikiFuzz::makeFuzz(2)) ),
1444 'hideBots' => wikiFuzz::chooseInput( array("", "0", "1", wikiFuzz::makeFuzz(2)) ),
1445 'namespace'=> wikiFuzz::chooseInput( array("", "0", "1", wikiFuzz::makeFuzz(2)) ),
1446 'action' => wikiFuzz::chooseInput( array("submit", "clear", wikiFuzz::makeFuzz(2)) ),
1447 'id[]' => wikiFuzz::makeFuzz(2),
1448 'edit' => wikiFuzz::makeFuzz(2),
1449 'token' => wikiFuzz::chooseInput( array("", "1243213", wikiFuzz::makeFuzz(2)) )
1452 // sometimes we specifiy "reset", and sometimes we don't.
1453 if (wikiFuzz::randnum(3) == 0) $this->params["reset"] = wikiFuzz::chooseInput( array("", "0", "1", wikiFuzz::makeFuzz(2)) );
1459 ** a page test for "Special:Blockme"
1461 class specialBlockmeTest extends pageTest {
1462 function __construct() {
1463 $this->pagePath = "index.php?title=Special:Blockme";
1465 $this->params = array ( );
1467 // sometimes we specify "ip", and sometimes we don't.
1468 if (wikiFuzz::randnum(1) == 0) {
1469 $this->params["ip"] = wikiFuzz::chooseInput( array("10.12.41.213", wikiFuzz::randnum(-10,8134), wikiFuzz::makeFuzz(2)) );
1476 ** a page test for "Special:Movepage"
1478 class specialMovePage extends pageTest {
1479 function __construct() {
1480 $this->pagePath = "index.php?title=Special:Movepage";
1482 $this->params = array (
1483 "action" => wikiFuzz::chooseInput( array("success", "submit", "", wikiFuzz::makeFuzz(2)) ),
1484 'wpEditToken' => wikiFuzz::chooseInput( array('', 0, 34987987, wikiFuzz::makeFuzz(2)) ),
1485 'target' => wikiFuzz::chooseInput( array("x", wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(2)) ) ),
1486 'wpOldTitle' => wikiFuzz::chooseInput( array("z", wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(2)), wikiFuzz::makeFuzz(2) ) ),
1487 'wpNewTitle' => wikiFuzz::chooseInput( array("y", wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(2)), wikiFuzz::makeFuzz(2) ) ),
1488 'wpReason' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2)) ),
1489 'wpMovetalk' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1490 'wpDeleteAndMove' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1491 'wpConfirm' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1492 'talkmoved' => wikiFuzz::chooseInput( array("1", wikiFuzz::makeFuzz(2), "articleexists", 'notalkpage') ),
1493 'oldtitle' => wikiFuzz::makeFuzz(2),
1494 'newtitle' => wikiFuzz::makeFuzz(2),
1495 'wpMovetalk' => wikiFuzz::chooseInput( array("1", "0", wikiFuzz::makeFuzz(2)) )
1498 // sometimes we don't want to specify certain parameters.
1499 if (wikiFuzz::randnum(2) == 0) unset($this->params["wpEditToken"]);
1500 if (wikiFuzz::randnum(3) == 0) unset($this->params["target"]);
1501 if (wikiFuzz::randnum(3) == 0) unset($this->params["wpNewTitle"]);
1502 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpReason"]);
1503 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpOldTitle"]);
1509 ** a page test for "Special:Undelete"
1511 class specialUndelete extends pageTest {
1512 function __construct() {
1513 $this->pagePath = "index.php?title=Special:Undelete";
1515 $this->params = array (
1516 "action" => wikiFuzz::chooseInput( array("submit", "", wikiFuzz::makeFuzz(2)) ),
1517 'wpEditToken' => wikiFuzz::chooseInput( array('', 0, 34987987, wikiFuzz::makeFuzz(2)) ),
1518 'target' => wikiFuzz::chooseInput( array("x", wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(2)) ) ),
1519 'timestamp' => wikiFuzz::chooseInput( array("125223", wikiFuzz::makeFuzz(2) ) ),
1520 'file' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1521 'restore' => wikiFuzz::chooseInput( array("0", "1", wikiFuzz::makeFuzz(2)) ),
1522 'preview' => wikiFuzz::chooseInput( array("0", "1", wikiFuzz::makeFuzz(2)) ),
1523 'wpComment' => wikiFuzz::makeFuzz(2)
1526 // sometimes we don't want to specify certain parameters.
1527 if (wikiFuzz::randnum(2) == 0) unset($this->params["wpEditToken"]);
1528 if (wikiFuzz::randnum(4) == 0) unset($this->params["target"]);
1529 if (wikiFuzz::randnum(1) == 0) unset($this->params["restore"]);
1530 if (wikiFuzz::randnum(1) == 0) unset($this->params["preview"]);
1536 ** a page test for "Special:Unlockdb"
1538 class specialUnlockdb extends pageTest {
1539 function __construct() {
1540 $this->pagePath = "index.php?title=Special:Unlockdb";
1542 $this->params = array (
1543 "action" => wikiFuzz::chooseInput( array("submit", "success", "", wikiFuzz::makeFuzz(2)) ),
1544 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1545 'wpLockConfirm' => wikiFuzz::chooseInput( array("0", "1", wikiFuzz::makeFuzz(2)) )
1548 // sometimes we don't want to specify certain parameters.
1549 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpEditToken"]);
1550 if (wikiFuzz::randnum(4) == 0) unset($this->params["action"]);
1551 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpLockConfirm"]);
1557 ** a page test for "Special:Lockdb"
1559 class specialLockdb extends pageTest {
1560 function __construct() {
1561 $this->pagePath = "index.php?title=Special:Lockdb";
1563 $this->params = array (
1564 "action" => wikiFuzz::chooseInput( array("submit", "success", "", wikiFuzz::makeFuzz(2)) ),
1565 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1566 'wpLockReason' => wikiFuzz::makeFuzz(2),
1567 'wpLockConfirm'=> wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) )
1570 // sometimes we don't want to specify certain parameters.
1571 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpEditToken"]);
1572 if (wikiFuzz::randnum(4) == 0) unset($this->params["action"]);
1573 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpLockConfirm"]);
1579 ** a page test for "Special:Userrights"
1581 class specialUserrights extends pageTest {
1582 function __construct() {
1583 $this->pagePath = "index.php?title=Special:Userrights";
1585 $this->params = array (
1586 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1587 'user-editname' => wikiFuzz::chooseInput( array("Nickj2", "Nickj2\n<xyz>", wikiFuzz::makeFuzz(2)) ),
1588 'ssearchuser' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1589 'saveusergroups'=> wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)), "Save User Groups"),
1590 'member[]' => wikiFuzz::chooseInput( array("0", "bot", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1591 "available[]" => wikiFuzz::chooseInput( array("0", "sysop", "bureaucrat", "1", "++--34234", wikiFuzz::makeFuzz(2)) )
1594 // sometimes we don't want to specify certain parameters.
1595 if (wikiFuzz::randnum(3) == 0) unset($this->params['ssearchuser']);
1596 if (wikiFuzz::randnum(3) == 0) unset($this->params['saveusergroups']);
1602 ** a test for page protection and unprotection.
1604 class pageProtectionForm extends pageTest {
1605 function __construct() {
1606 $this->pagePath = "index.php?title=Main_Page";
1608 $this->params = array (
1609 "action" => "protect",
1610 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1611 "mwProtect-level-edit" => wikiFuzz::chooseInput( array('', 'autoconfirmed', 'sysop', wikifuzz::makeFuzz(2)) ),
1612 "mwProtect-level-move" => wikiFuzz::chooseInput( array('', 'autoconfirmed', 'sysop', wikifuzz::makeFuzz(2)) ),
1613 "mwProtectUnchained" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1614 'mwProtect-reason' => wikiFuzz::chooseInput( array("because it was there", wikifuzz::makeFuzz(2)) )
1618 // sometimes we don't want to specify certain parameters.
1619 if (wikiFuzz::randnum(3) == 0) unset($this->params["mwProtectUnchained"]);
1620 if (wikiFuzz::randnum(3) == 0) unset($this->params['mwProtect-reason']);
1626 ** a page test for "Special:Blockip".
1628 class specialBlockip extends pageTest {
1629 function __construct() {
1630 $this->pagePath = "index.php?title=Special:Blockip";
1632 $this->params = array (
1633 "action" => wikiFuzz::chooseInput( array("submit", "", wikiFuzz::makeFuzz(2)) ),
1634 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1635 "wpBlockAddress" => wikiFuzz::chooseInput( array("20398702394", "", "Nickj2", wikiFuzz::makeFuzz(2),
1636 // something like an IP address, sometimes invalid:
1637 ( wikiFuzz::randnum(300,-20) . "." . wikiFuzz::randnum(300,-20) . "."
1638 . wikiFuzz::randnum(300,-20) . "." .wikiFuzz::randnum(300,-20) ) ) ),
1639 "ip" => wikiFuzz::chooseInput( array("20398702394", "", "Nickj2", wikiFuzz::makeFuzz(2),
1640 // something like an IP address, sometimes invalid:
1641 ( wikiFuzz::randnum(300,-20) . "." . wikiFuzz::randnum(300,-20) . "."
1642 . wikiFuzz::randnum(300,-20) . "." .wikiFuzz::randnum(300,-20) ) ) ),
1643 "wpBlockOther" => wikiFuzz::chooseInput( array('', 'Nickj2', wikifuzz::makeFuzz(2)) ),
1644 "wpBlockExpiry" => wikiFuzz::chooseInput( array("other", "2 hours", "1 day", "3 days", "1 week", "2 weeks",
1645 "1 month", "3 months", "6 months", "1 year", "infinite", wikiFuzz::makeFuzz(2)) ),
1646 "wpBlockReason" => wikiFuzz::chooseInput( array("because it was there", wikifuzz::makeFuzz(2)) ),
1647 "wpAnonOnly" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1648 "wpCreateAccount" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1649 "wpBlock" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) )
1652 // sometimes we don't want to specify certain parameters.
1653 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpBlockOther"]);
1654 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpBlockExpiry"]);
1655 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpBlockReason"]);
1656 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpAnonOnly"]);
1657 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpCreateAccount"]);
1658 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpBlockAddress"]);
1659 if (wikiFuzz::randnum(4) == 0) unset($this->params["ip"]);
1665 ** a test for the imagepage.
1667 class imagepageTest extends pageTest {
1668 function __construct() {
1669 $this->pagePath = "index.php?title=Image:Small-email.png";
1671 $this->params = array (
1672 "image" => wikiFuzz::chooseInput( array("Small-email.png", wikifuzz::makeFuzz(2)) ),
1673 "wpReason" => wikifuzz::makeFuzz(2),
1674 "oldimage" => wikiFuzz::chooseInput( array("Small-email.png", wikifuzz::makeFuzz(2)) ),
1675 "wpEditToken" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1678 // sometimes we don't want to specify certain parameters.
1679 if (wikiFuzz::randnum(6) == 0) unset($this->params["image"]);
1680 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpReason"]);
1681 if (wikiFuzz::randnum(6) == 0) unset($this->params["oldimage"]);
1682 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpEditToken"]);
1688 ** a test for page deletion form.
1690 class pageDeletion extends pageTest {
1691 function __construct() {
1692 $this->pagePath = "index.php?title=Main_Page&action=delete";
1694 $this->params = array (
1695 "wpEditToken" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1696 "wpReason" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1697 "wpConfirm" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1700 // sometimes we don't want to specify certain parameters.
1701 if (wikiFuzz::randnum(5) == 0) unset($this->params["wpReason"]);
1702 if (wikiFuzz::randnum(5) == 0) unset($this->params["wpEditToken"]);
1703 if (wikiFuzz::randnum(5) == 0) unset($this->params["wpConfirm"]);
1710 ** a test for Revision Deletion.
1712 class specialRevisionDelete extends pageTest {
1713 function __construct() {
1714 $this->pagePath = "index.php?title=Special:Revisiondelete";
1716 $this->params = array (
1717 "target" => wikiFuzz::chooseInput( array("Main Page", wikifuzz::makeFuzz(2)) ),
1718 "oldid" => wikifuzz::makeFuzz(2),
1719 "oldid[]" => wikifuzz::makeFuzz(2),
1720 "wpReason" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1721 "revdelete-hide-text" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1722 "revdelete-hide-comment" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1723 "revdelete-hide-user" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1724 "revdelete-hide-restricted" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1727 // sometimes we don't want to specify certain parameters.
1728 if (wikiFuzz::randnum(3) == 0) unset($this->params["target"]);
1729 if (wikiFuzz::randnum(6) == 0) unset($this->params["oldid"]);
1730 if (wikiFuzz::randnum(6) == 0) unset($this->params["oldid[]"]);
1731 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpReason"]);
1732 if (wikiFuzz::randnum(6) == 0) unset($this->params["revdelete-hide-text"]);
1733 if (wikiFuzz::randnum(6) == 0) unset($this->params["revdelete-hide-comment"]);
1734 if (wikiFuzz::randnum(6) == 0) unset($this->params["revdelete-hide-user"]);
1735 if (wikiFuzz::randnum(6) == 0) unset($this->params["revdelete-hide-restricted"]);
1741 ** a test for Special:Import.
1743 class specialImport extends pageTest {
1744 function __construct() {
1745 $this->pagePath = "index.php?title=Special:Import";
1747 $this->params = array (
1748 "action" => "submit",
1749 "source" => wikiFuzz::chooseInput( array("upload", "interwiki", wikifuzz::makeFuzz(2)) ),
1750 "MAX_FILE_SIZE" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ),
1751 "xmlimport" => wikiFuzz::chooseInput( array("/var/www/hosts/mediawiki/wiki/AdminSettings.php", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1752 "namespace" => wikiFuzz::chooseInput( array(wikiFuzz::randnum(30,-6), wikiFuzz::makeFuzz(2)) ),
1753 "interwiki" => wikiFuzz::makeFuzz(2),
1754 "interwikiHistory" => wikiFuzz::makeFuzz(2),
1755 "frompage" => wikiFuzz::makeFuzz(2),
1758 // sometimes we don't want to specify certain parameters.
1759 if (wikiFuzz::randnum(6) == 0) unset($this->params["action"]);
1760 if (wikiFuzz::randnum(6) == 0) unset($this->params["source"]);
1761 if (wikiFuzz::randnum(6) == 0) unset($this->params["MAX_FILE_SIZE"]);
1762 if (wikiFuzz::randnum(6) == 0) unset($this->params["xmlimport"]);
1763 if (wikiFuzz::randnum(6) == 0) unset($this->params["interwiki"]);
1764 if (wikiFuzz::randnum(6) == 0) unset($this->params["interwikiHistory"]);
1765 if (wikiFuzz::randnum(6) == 0) unset($this->params["frompage"]);
1767 // Note: Need to do a file upload to fully test this Special page.
1773 ** a test for thumb.php
1775 class thumbTest extends pageTest {
1776 function __construct() {
1777 $this->pagePath = "thumb.php";
1779 $this->params = array (
1780 "f" => wikiFuzz::chooseInput( array("..", "\\", "small-email.png", wikifuzz::makeFuzz(2)) ),
1781 "w" => wikiFuzz::chooseInput( array("80", wikiFuzz::randnum(6000,-200), wikifuzz::makeFuzz(2)) ),
1782 "r" => wikiFuzz::chooseInput( array("0", wikifuzz::makeFuzz(2)) ),
1785 // sometimes we don't want to specify certain parameters.
1786 if (wikiFuzz::randnum(6) == 0) unset($this->params["f"]);
1787 if (wikiFuzz::randnum(6) == 0) unset($this->params["w"]);
1788 if (wikiFuzz::randnum(6) == 0) unset($this->params["r"]);
1794 ** a test for trackback.php
1796 class trackbackTest extends pageTest {
1797 function __construct() {
1798 $this->pagePath = "trackback.php";
1800 $this->params = array (
1801 "url" => wikifuzz::makeFuzz(2),
1802 "blog_name" => wikiFuzz::chooseInput( array("80", wikiFuzz::randnum(6000,-200), wikifuzz::makeFuzz(2)) ),
1803 "article" => wikiFuzz::chooseInput( array("Main Page", wikifuzz::makeFuzz(2)) ),
1804 "title" => wikiFuzz::chooseInput( array("Main Page", wikifuzz::makeFuzz(2)) ),
1805 "excerpt" => wikifuzz::makeFuzz(2),
1808 // sometimes we don't want to specify certain parameters.
1809 if (wikiFuzz::randnum(3) == 0) unset($this->params["title"]);
1810 if (wikiFuzz::randnum(3) == 0) unset($this->params["excerpt"]);
1812 // page does not produce HTML.
1813 $this->tidyValidate = false;
1819 ** a test for profileinfo.php
1821 class profileInfo extends pageTest {
1822 function __construct() {
1823 $this->pagePath = "profileinfo.php";
1825 $this->params = array (
1826 "expand" => wikifuzz::makeFuzz(2),
1827 "sort" => wikiFuzz::chooseInput( array("time", "count", "name", wikifuzz::makeFuzz(2)) ),
1828 "filter" => wikiFuzz::chooseInput( array("Main Page", wikifuzz::makeFuzz(2)) ),
1831 // sometimes we don't want to specify certain parameters.
1832 if (wikiFuzz::randnum(3) == 0) unset($this->params["sort"]);
1833 if (wikiFuzz::randnum(3) == 0) unset($this->params["filter"]);
1839 ** a test for Special:Cite (extension Special page).
1841 class specialCite extends pageTest {
1842 function __construct() {
1843 $this->pagePath = "index.php?title=Special:Cite";
1845 $this->params = array (
1846 "page" => wikiFuzz::chooseInput( array("\" onmouseover=\"alert(1);\"", "Main Page", wikifuzz::makeFuzz(2)) ),
1847 "id" => wikiFuzz::chooseInput( array("-1", "0", "------'-------0", "+1", "-9823412312312412435", wikiFuzz::makeFuzz(2)) ),
1850 // sometimes we don't want to specify certain parameters.
1851 if (wikiFuzz::randnum(6) == 0) unset($this->params["page"]);
1852 if (wikiFuzz::randnum(6) == 0) unset($this->params["id"]);
1858 ** a test for Special:Filepath (extension Special page).
1860 class specialFilepath extends pageTest {
1861 function __construct() {
1862 $this->pagePath = "index.php?title=Special:Filepath";
1864 $this->params = array (
1865 "file" => wikiFuzz::chooseInput( array("Small-email.png", "Small-email.png" . wikifuzz::makeFuzz(1), wikiFuzz::makeFuzz(2)) ),
1872 ** a test for Special:Makebot (extension Special page).
1874 class specialMakebot extends pageTest {
1875 function __construct() {
1876 $this->pagePath = "index.php?title=Special:Makebot";
1878 $this->params = array (
1879 "username" => wikiFuzz::chooseInput( array("Nickj2", "192.168.0.2", wikifuzz::makeFuzz(1) ) ),
1880 "dosearch" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ),
1881 "grant" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ),
1882 "comment" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1883 "token" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1886 // sometimes we don't want to specify certain parameters.
1887 if (wikiFuzz::randnum(2) == 0) unset($this->params["dosearch"]);
1888 if (wikiFuzz::randnum(2) == 0) unset($this->params["grant"]);
1889 if (wikiFuzz::randnum(5) == 0) unset($this->params["token"]);
1895 ** a test for Special:Makesysop (extension Special page).
1897 class specialMakesysop extends pageTest {
1898 function __construct() {
1899 $this->pagePath = "index.php?title=Special:Makesysop";
1901 $this->params = array (
1902 "wpMakesysopUser" => wikiFuzz::chooseInput( array("Nickj2", "192.168.0.2", wikifuzz::makeFuzz(1) ) ),
1903 "action" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ),
1904 "wpMakesysopSubmit" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ),
1905 "wpEditToken" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1906 "wpSetBureaucrat" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1909 // sometimes we don't want to specify certain parameters.
1910 if (wikiFuzz::randnum(3) == 0) unset($this->params["wpMakesysopSubmit"]);
1911 if (wikiFuzz::randnum(3) == 0) unset($this->params["wpEditToken"]);
1912 if (wikiFuzz::randnum(3) == 0) unset($this->params["wpSetBureaucrat"]);
1918 ** a test for Special:Renameuser (extension Special page).
1920 class specialRenameuser extends pageTest {
1921 function __construct() {
1922 $this->pagePath = "index.php?title=Special:Renameuser";
1924 $this->params = array (
1925 "oldusername" => wikiFuzz::chooseInput( array("Nickj2", "192.168.0.2", wikifuzz::makeFuzz(1) ) ),
1926 "newusername" => wikiFuzz::chooseInput( array("Nickj2", "192.168.0.2", wikifuzz::makeFuzz(1) ) ),
1927 "token" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1934 ** a test for Special:Linksearch (extension Special page).
1936 class specialLinksearch extends pageTest {
1937 function __construct() {
1938 $this->pagePath = "index.php?title=Special%3ALinksearch";
1940 $this->params = array (
1941 "target" => wikifuzz::makeFuzz(2),
1944 // sometimes we don't want to specify certain parameters.
1945 if (wikiFuzz::randnum(10) == 0) unset($this->params["target"]);
1951 ** a test for Special:CategoryTree (extension Special page).
1953 class specialCategoryTree extends pageTest {
1954 function __construct() {
1955 $this->pagePath = "index.php?title=Special:CategoryTree";
1957 $this->params = array (
1958 "target" => wikifuzz::makeFuzz(2),
1959 "from" => wikifuzz::makeFuzz(2),
1960 "until" => wikifuzz::makeFuzz(2),
1961 "showas" => wikifuzz::makeFuzz(2),
1962 "mode" => wikiFuzz::chooseInput( array("pages", "categories", "all", wikifuzz::makeFuzz(2)) ),
1965 // sometimes we do want to specify certain parameters.
1966 if (wikiFuzz::randnum(5) == 0) $this->params["notree"] = wikiFuzz::chooseInput( array("1", 0, "", wikiFuzz::makeFuzz(2)) );
1972 ** a test for "Special:Chemicalsources" (extension Special page).
1974 class specialChemicalsourcesTest extends pageTest {
1975 function __construct() {
1976 $this->pagePath = "index.php?title=Special:Chemicalsources";
1978 // choose an input format to use.
1979 $format = wikiFuzz::chooseInput(
1980 array( 'go',
1981 'CAS',
1982 'EINECS',
1983 'CHEBI',
1984 'PubChem',
1985 'SMILES',
1986 'InChI',
1987 'ATCCode',
1988 'KEGG',
1989 'RTECS',
1990 'ECNumber',
1991 'DrugBank',
1992 'Formula',
1993 'Name'
1997 // values for different formats usually start with either letters or numbers.
1998 switch ($format) {
1999 case 'Name' : $value = "A"; break;
2000 case 'InChI' :
2001 case 'SMILES' :
2002 case 'Formula': $value = "C"; break;
2003 default : $value = "0"; break;
2006 // and then we append the fuzz input.
2007 $this->params = array ($format => $value . wikifuzz::makeFuzz(2) );
2013 ** A test for api.php (programmatic interface to MediaWiki in XML/JSON/RSS/etc formats).
2014 ** Quite involved to test because there are lots of options/parameters, and because
2015 ** for a lot of the functionality if all the parameters don't make sense then it just
2016 ** returns the help screen - so currently a lot of the tests aren't actually doing much
2017 ** because something wasn't right in the query.
2019 ** @todo: Incomplete / unfinished; Runs too fast (suggests not much testing going on).
2021 class api extends pageTest {
2023 // API login mode.
2024 private static function loginMode() {
2025 $arr = array ( "lgname" => wikifuzz::makeFuzz(2),
2026 "lgpassword" => wikifuzz::makeFuzz(2),
2028 // sometimes we want to specify the extra "lgdomain" parameter.
2029 if (wikiFuzz::randnum(3) == 0) {
2030 $arr["lgdomain"] = wikiFuzz::chooseInput( array("1", 0, "", wikiFuzz::makeFuzz(2)) );
2033 return $arr;
2036 // API OpenSearch mode.
2037 private static function opensearchMode() {
2038 return array ("search" => wikifuzz::makeFuzz(2));
2041 // API watchlist feed mode.
2042 private static function feedwatchlistMode() {
2043 // FIXME: add "wikifuzz::makeFuzz(2)" as possible value below?
2044 return array ("feedformat" => wikiFuzz::chooseInput( array("rss", "atom") ) );
2047 // API query mode.
2048 private static function queryMode() {
2049 // FIXME: add "wikifuzz::makeFuzz(2)" as possible params for the elements below?
2050 // Suspect this will stuff up the tests more, but need to check.
2051 $params = array (
2052 // FIXME: More titles.
2053 "titles" => wikiFuzz::chooseInput( array("Main Page")),
2054 // FIXME: More pageids.
2055 "pageids" => 1,
2056 "prop" => wikiFuzz::chooseInput( array("info", "revisions", "watchlist")),
2057 "list" => wikiFuzz::chooseInput( array("allpages", "logevents", "watchlist", "usercontribs", "recentchanges", "backlinks", "embeddedin", "imagelinks") ),
2058 "meta" => wikiFuzz::chooseInput( array("siteinfo")),
2059 "generator" => wikiFuzz::chooseInput( array("allpages", "logevents", "watchlist", "info", "revisions") ),
2060 "siprop" => wikiFuzz::chooseInput( array("general", "namespaces", "general|namespaces") ),
2063 // Add extra parameters based on what list choice we got.
2064 switch ($params["list"]) {
2065 case "usercontribs" : self::addListParams ($params, "uc", array("limit", "start", "end", "user", "dir") ); break;
2066 case "allpages" : self::addListParams ($params, "ap", array("from", "prefix", "namespace", "filterredir", "limit") ); break;
2067 case "watchlist" : self::addListParams ($params, "wl", array("allrev", "start", "end", "namespace", "dir", "limit", "prop") ); break;
2068 case "logevents" : self::addListParams ($params, "le", array("limit", "type", "start", "end", "user", "dir") ); break;
2069 case "recentchanges": self::addListParams ($params, "rc", array("limit", "prop", "show", "namespace", "start", "end", "dir") ); break;
2070 case "backlinks" : self::addListParams ($params, "bl", array("continue", "namespace", "redirect", "limit") ); break;
2071 case "embeddedin" : self::addListParams ($params, "ei", array("continue", "namespace", "redirect", "limit") ); break;
2072 case "imagelinks" : self::addListParams ($params, "il", array("continue", "namespace", "redirect", "limit") ); break;
2075 if ($params["prop"] == "revisions") {
2076 self::addListParams ($params, "rv", array("prop", "limit", "startid", "endid", "end", "dir") );
2079 // Sometimes we want redirects, sometimes we don't.
2080 if (wikiFuzz::randnum(3) == 0) {
2081 $params["redirects"] = wikiFuzz::chooseInput( array("1", 0, "", wikiFuzz::makeFuzz(2)) );
2084 return $params;
2087 // Adds all the elements to the array, using the specified prefix.
2088 private static function addListParams(&$array, $prefix, $elements) {
2089 foreach ($elements as $element) {
2090 $array[$prefix . $element] = self::getParamDetails($element);
2094 // For a given element name, returns the data for that element.
2095 private static function getParamDetails($element) {
2096 switch ($element) {
2097 case 'startid' :
2098 case 'endid' :
2099 case 'start' :
2100 case 'end' :
2101 case 'limit' : return wikiFuzz::chooseInput( array("0", "-1", "---'----------0", "+1", "8134", "320742734234235", "20060230121212", wikiFuzz::randnum(9000, -100), wikiFuzz::makeFuzz(2)) );
2102 case 'dir' : return wikiFuzz::chooseInput( array("newer", "older", wikifuzz::makeFuzz(2) ) );
2103 case 'user' : return wikiFuzz::chooseInput( array(USER_ON_WIKI, wikifuzz::makeFuzz(2) ) );
2104 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)) );
2105 case 'filterredir': return wikiFuzz::chooseInput( array("all", "redirects", "nonredirectsallpages", wikifuzz::makeFuzz(2)) );
2106 case 'allrev' : return wikiFuzz::chooseInput( array("1", 0, "", wikiFuzz::makeFuzz(2)) );
2107 case 'prop' : return wikiFuzz::chooseInput( array("user", "comment", "timestamp", "patrol", "flags", "user|user|comment|flags", wikifuzz::makeFuzz(2) ) );
2108 case 'type' : return wikiFuzz::chooseInput( array("block", "protect", "rights", "delete", "upload", "move", "import", "renameuser", "newusers", "makebot", wikifuzz::makeFuzz(2) ) );
2109 case 'hide' : return wikiFuzz::chooseInput( array("minor", "bots", "anons", "liu", "liu|bots|", wikifuzz::makeFuzz(2) ) );
2110 case 'show' : return wikiFuzz::chooseInput( array('minor', '!minor', 'bot', '!bot', 'anon', '!anon', wikifuzz::makeFuzz(2) ) );
2111 default : return wikifuzz::makeFuzz(2);
2115 // Entry point.
2116 function __construct() {
2117 $this->pagePath = "api.php";
2119 $modes = array ("help",
2120 "login",
2121 "opensearch",
2122 "feedwatchlist",
2123 "query");
2124 $action = wikiFuzz::chooseInput( array_merge ($modes, array(wikifuzz::makeFuzz(2))) );
2126 switch ($action) {
2127 case "login" : $this->params = self::loginMode();
2128 break;
2129 case "opensearch" : $this->params = self::opensearchMode();
2130 break;
2131 case "feedwatchlist" : $this->params = self::feedwatchlistMode();
2132 break;
2133 case "query" : $this->params = self::queryMode();
2134 break;
2135 case "help" :
2136 default : // Do something random - "Crazy Ivan" mode.
2137 $random_mode = wikiFuzz::chooseInput( $modes ) . "Mode";
2138 // There is no "helpMode".
2139 if ($random_mode == "helpMode") $random_mode = "queryMode";
2140 $this->params = self::$random_mode();
2141 break;
2144 // Save the selected action.
2145 $this->params["action"] = $action;
2147 // Set the cookie:
2148 // FIXME: need to get this cookie dynamically set, rather than hard-coded.
2149 $this->cookie = "wikidbUserID=10001; wikidbUserName=Test; wikidb_session=178df0fe68c75834643af65dec9ec98a; wikidbToken=1adc6753d62c44aec950c024d7ae0540";
2151 // Output format
2152 $this->params["format"] = wikiFuzz::chooseInput( array("json", "jsonfm", "php", "phpfm",
2153 "wddx", "wddxfm", "xml", "xmlfm",
2154 "yaml", "yamlfm", "raw", "rawfm",
2155 wikifuzz::makeFuzz(2) ) );
2157 // Page does not produce HTML (sometimes).
2158 $this->tidyValidate = false;
2164 ** selects a page test to run.
2166 function selectPageTest($count) {
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 ();
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 specialNewpages();
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 specialUndelete();
2202 case 25: return new specialMovePage();
2203 case 26: return new specialUnlockdb();
2204 case 27: return new specialLockdb();
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 specialRevisionDelete();
2211 case 34: return new specialImport();
2212 case 35: return new thumbTest();
2213 case 36: return new trackbackTest();
2214 case 37: return new profileInfo();
2215 case 38: return new specialCite();
2216 case 39: return new specialFilepath();
2217 case 40: return new specialMakebot();
2218 case 41: return new specialMakesysop();
2219 case 42: return new specialRenameuser();
2220 case 43: return new specialLinksearch();
2221 case 44: return new specialCategoryTree();
2222 case 45: return new api();
2223 case 45: return new specialChemicalsourcesTest();
2224 default: return new editPageTest();
2229 /////////////////////// SAVING OUTPUT /////////////////////////
2232 ** Utility function for saving a file. Currently has no error checking.
2234 function saveFile($data, $name) {
2235 file_put_contents($name, $data);
2240 ** Returns a test as an experimental GET-to-POST URL.
2241 ** This doesn't seem to always work though, and sometimes the output is too long
2242 ** to be a valid GET URL, so we also save in other formats.
2244 function getAsURL(pageTest $test) {
2245 $used_question_mark = (strpos($test->getPagePath(), "?") !== false);
2246 $retval = "http://get-to-post.nickj.org/?" . WIKI_BASE_URL . $test->getPagePath();
2247 foreach ($test->getParams() as $param => $value) {
2248 if (!$used_question_mark) {
2249 $retval .= "?";
2250 $used_question_mark = true;
2252 else {
2253 $retval .= "&";
2255 $retval .= $param . "=" . urlencode($value);
2257 return $retval;
2262 ** Saves a plain-text human-readable version of a test.
2264 function saveTestAsText(pageTest $test, $filename) {
2265 $str = "Test: " . $test->getPagePath();
2266 foreach ($test->getParams() as $param => $value) {
2267 $str .= "\n$param: $value";
2269 $str .= "\nGet-to-post URL: " . getAsURL($test) . "\n";
2270 saveFile($str, $filename);
2275 ** Saves a test as a standalone basic PHP script that shows this one problem.
2276 ** Resulting script requires PHP-Curl be installed in order to work.
2278 function saveTestAsPHP(pageTest $test, $filename) {
2279 $str = "<?php\n"
2280 . "\$params = " . var_export(escapeForCurl($test->getParams()), true) . ";\n"
2281 . "\$ch = curl_init();\n"
2282 . "curl_setopt(\$ch, CURLOPT_POST, 1);\n"
2283 . "curl_setopt(\$ch, CURLOPT_POSTFIELDS, \$params );\n"
2284 . "curl_setopt(\$ch, CURLOPT_URL, " . var_export(WIKI_BASE_URL . $test->getPagePath(), true) . ");\n"
2285 . "curl_setopt(\$ch, CURLOPT_RETURNTRANSFER,1);\n"
2286 . ($test->getCookie() ? "curl_setopt(\$ch, CURLOPT_COOKIE, " . var_export($test->getCookie(), true) . ");\n" : "")
2287 . "\$result=curl_exec(\$ch);\n"
2288 . "curl_close (\$ch);\n"
2289 . "print \$result;\n"
2290 . "?>\n";
2291 saveFile($str, $filename);
2296 ** Escapes a value so that it can be used on the command line by Curl.
2297 ** Specifically, "<" and "@" need to be escaped if they are the first character,
2298 ** otherwise curl interprets these as meaning that we want to insert a file.
2300 function escapeForCurl(array $input_params) {
2301 $output_params = array();
2302 foreach ($input_params as $param => $value) {
2303 if (strlen($value) > 0 && ( $value[0] == "@" || $value[0] == "<")) {
2304 $value = "\\" . $value;
2306 $output_params[$param] = $value;
2308 return $output_params;
2313 ** Saves a test as a standalone CURL shell script that shows this one problem.
2314 ** Resulting script requires standalone Curl be installed in order to work.
2316 function saveTestAsCurl(pageTest $test, $filename) {
2317 $str = "#!/bin/bash\n"
2318 . "curl --silent --include --globoff \\\n"
2319 . ($test->getCookie() ? " --cookie " . escapeshellarg($test->getCookie()) . " \\\n" : "");
2320 foreach (escapeForCurl($test->getParams()) as $param => $value) {
2321 $str .= " -F " . escapeshellarg($param) . "=" . escapeshellarg($value) . " \\\n";
2323 $str .= " " . escapeshellarg(WIKI_BASE_URL . $test->getPagePath()); // beginning space matters.
2324 $str .= "\n";
2325 saveFile($str, $filename);
2326 chmod($filename, 0755); // make executable
2331 ** Saves the internal data structure to file.
2333 function saveTestData (pageTest $test, $filename) {
2334 saveFile(serialize($test), $filename);
2339 ** saves a test in the various formats.
2341 function saveTest(pageTest $test, $testname) {
2342 $base_name = DIRECTORY . "/" . $testname;
2343 saveTestAsText($test, $base_name . INFO_FILE);
2344 saveTestAsPHP ($test, $base_name . PHP_TEST );
2345 saveTestAsCurl($test, $base_name . CURL_TEST);
2346 saveTestData ($test, $base_name . DATA_FILE);
2350 //////////////////// MEDIAWIKI OUTPUT /////////////////////////
2353 ** Asks MediaWiki for the HTML output of a test.
2355 function wikiTestOutput(pageTest $test) {
2357 $ch = curl_init();
2359 // specify the cookie, if required.
2360 if ($test->getCookie()) curl_setopt($ch, CURLOPT_COOKIE, $test->getCookie());
2361 curl_setopt($ch, CURLOPT_POST, 1); // save form using a POST
2363 $params = escapeForCurl($test->getParams());
2364 curl_setopt($ch, CURLOPT_POSTFIELDS, $params ); // load the POST variables
2366 curl_setopt($ch, CURLOPT_URL, WIKI_BASE_URL . $test->getPagePath() ); // set url to post to
2367 curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); // return into a variable
2369 $result=curl_exec ($ch);
2371 // if we encountered an error, then say so, and return an empty string.
2372 if (curl_error($ch)) {
2373 print "\nCurl error #: " . curl_errno($ch) . " - " . curl_error ($ch);
2374 $result = "";
2377 curl_close ($ch);
2379 return $result;
2383 //////////////////// HTML VALIDATION /////////////////////////
2386 ** Asks the validator whether this is valid HTML, or not.
2388 function validateHTML($text) {
2390 $params = array ("fragment" => $text);
2392 $ch = curl_init();
2394 curl_setopt($ch, CURLOPT_POST, 1); // save form using a POST
2395 curl_setopt($ch, CURLOPT_POSTFIELDS, $params); // load the POST variables
2396 curl_setopt($ch, CURLOPT_URL, VALIDATOR_URL); // set url to post to
2397 curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); // return into a variable
2399 $result=curl_exec ($ch);
2401 // if we encountered an error, then log it, and exit.
2402 if (curl_error($ch)) {
2403 trigger_error("Curl error #: " . curl_errno($ch) . " - " . curl_error ($ch) );
2404 print "Curl error #: " . curl_errno($ch) . " - " . curl_error ($ch) . " - exiting.\n";
2405 exit();
2408 curl_close ($ch);
2410 $valid = (strpos($result, "Failed validation") === false ? true : false);
2412 return array($valid, $result);
2417 ** Get tidy to check for no HTML errors in the output file (e.g. unescaped strings).
2419 function tidyCheckFile($name) {
2420 $file = DIRECTORY . "/" . $name;
2421 $command = PATH_TO_TIDY . " -output /tmp/out.html -quiet $file 2>&1";
2422 $x = `$command`;
2424 // Look for the most interesting Tidy errors and warnings.
2425 if ( strpos($x,"end of file while parsing attributes") !== false
2426 || strpos($x,"attribute with missing trailing quote mark") !== false
2427 || strpos($x,"missing '>' for end of tag") !== false
2428 || strpos($x,"Error:") !== false) {
2429 print "\nTidy found something - view details with: $command";
2430 return false;
2431 } else {
2432 return true;
2438 ** Returns whether or not an database error log file has changed in size since
2439 ** the last time this was run. This is used to tell if a test caused a DB error.
2441 function dbErrorLogged() {
2442 static $filesize;
2444 // first time running this function
2445 if (!isset($filesize)) {
2446 // create log if it does not exist
2447 if (!file_exists(DB_ERROR_LOG_FILE)) {
2448 saveFile("", DB_ERROR_LOG_FILE);
2450 $filesize = filesize(DB_ERROR_LOG_FILE);
2451 return false;
2454 $newsize = filesize(DB_ERROR_LOG_FILE);
2455 // if the log has grown, then assume the current test caused it.
2456 if ($newsize != $filesize) {
2457 $filesize = $newsize;
2458 return true;
2461 return false;
2464 ////////////////// TOP-LEVEL PROBLEM-FINDING FUNCTION ////////////////////////
2467 ** takes a page test, and runs it and tests it for problems in the output.
2468 ** Returns: False on finding a problem, or True on no problems being found.
2470 function runWikiTest(pageTest $test, &$testname, $can_overwrite = false) {
2472 // by default don't overwrite a previous test of the same name.
2473 while ( ! $can_overwrite && file_exists(DIRECTORY . "/" . $testname . DATA_FILE)) {
2474 $testname .= "-" . mt_rand(0,9);
2477 $filename = DIRECTORY . "/" . $testname . DATA_FILE;
2479 // Store the time before and after, to find slow pages.
2480 $before = microtime(true);
2482 // Get MediaWiki to give us the output of this test.
2483 $wiki_preview = wikiTestOutput($test);
2485 $after = microtime(true);
2487 // if we received no response, then that's interesting.
2488 if ($wiki_preview == "") {
2489 print "\nNo response received for: $filename";
2490 return false;
2493 // save output HTML to file.
2494 $html_file = DIRECTORY . "/" . $testname . HTML_FILE;
2495 saveFile($wiki_preview, $html_file);
2497 // if there were PHP errors in the output, then that's interesting too.
2498 if ( strpos($wiki_preview, "<b>Warning</b>: " ) !== false
2499 || strpos($wiki_preview, "<b>Fatal error</b>: " ) !== false
2500 || strpos($wiki_preview, "<b>Notice</b>: " ) !== false
2501 || strpos($wiki_preview, "<b>Error</b>: " ) !== false
2502 || strpos($wiki_preview, "<b>Strict Standards:</b>") !== false
2504 $error = substr($wiki_preview, strpos($wiki_preview, "</b>:") + 7, 50);
2505 // Avoid probable PHP bug with bad session ids; http://bugs.php.net/bug.php?id=38224
2506 if ($error != "Unknown: The session id contains illegal character") {
2507 print "\nPHP error/warning/notice in HTML output: $html_file ; $error";
2508 return false;
2512 // if there was a MediaWiki Backtrace message in the output, then that's also interesting.
2513 if( strpos($wiki_preview, "Backtrace:") !== false ) {
2514 print "\nInternal MediaWiki error in HTML output: $html_file";
2515 return false;
2518 // if there was a Parser error comment in the output, then that's potentially interesting.
2519 if( strpos($wiki_preview, "!-- ERR") !== false ) {
2520 print "\nParser Error comment in HTML output: $html_file";
2521 return false;
2524 // if a database error was logged, then that's definitely interesting.
2525 if( dbErrorLogged() ) {
2526 print "\nDatabase Error logged for: $filename";
2527 return false;
2530 // validate result
2531 $valid = true;
2532 if( VALIDATE_ON_WEB ) {
2533 list ($valid, $validator_output) = validateHTML($wiki_preview);
2534 if (!$valid) print "\nW3C web validation failed - view details with: html2text " . DIRECTORY . "/" . $testname . ".validator_output.html";
2537 // Get tidy to check the page, unless we already know it produces non-XHTML output.
2538 if( $test->tidyValidate() ) {
2539 $valid = tidyCheckFile( $testname . HTML_FILE ) && $valid;
2542 // if it took more than 2 seconds to render, then it may be interesting too. (Possible DoS attack?)
2543 if (($after - $before) >= 2) {
2544 print "\nParticularly slow to render (" . round($after - $before, 2) . " seconds): $filename";
2545 return false;
2548 if( $valid ) {
2549 // Remove temp HTML file if test was valid:
2550 unlink( $html_file );
2551 } elseif( VALIDATE_ON_WEB ) {
2552 saveFile($validator_output, DIRECTORY . "/" . $testname . ".validator_output.html");
2555 return $valid;
2559 /////////////////// RERUNNING OLD TESTS ///////////////////
2562 ** We keep our failed tests so that they can be rerun.
2563 ** This function does that retesting.
2565 function rerunPreviousTests() {
2566 print "Retesting previously found problems.\n";
2568 $dir_contents = scandir (DIRECTORY);
2570 // sort file into the order a normal person would use.
2571 natsort ($dir_contents);
2573 foreach ($dir_contents as $file) {
2575 // if file is not a test, then skip it.
2576 // Note we need to escape any periods or will be treated as "any character".
2577 $matches = array();
2578 if (!ereg("(.*)" . str_replace(".", "\.", DATA_FILE) . "$", $file, $matches)) continue;
2580 // reload the test.
2581 $full_path = DIRECTORY . "/" . $file;
2582 $test = unserialize(file_get_contents($full_path));
2584 // if this is not a valid test, then skip it.
2585 if (! $test instanceof pageTest) {
2586 print "\nSkipping invalid test - $full_path";
2587 continue;
2590 // The date format is in Apache log format, which makes it easier to locate
2591 // which retest caused which error in the Apache logs (only happens usually if
2592 // apache segfaults).
2593 if (!QUIET) print "[" . date ("D M d H:i:s Y") . "] Retesting $file (" . get_class($test) . ")";
2595 // run test
2596 $testname = $matches[1];
2597 $valid = runWikiTest($test, $testname, true);
2599 if (!$valid) {
2600 saveTest($test, $testname);
2601 if (QUIET) {
2602 print "\nTest: " . get_class($test) . " ; Testname: $testname\n------";
2603 } else {
2604 print "\n";
2607 else {
2608 if (!QUIET) print "\r";
2609 if (DELETE_PASSED_RETESTS) {
2610 $prefix = DIRECTORY . "/" . $testname;
2611 if (is_file($prefix . DATA_FILE)) unlink($prefix . DATA_FILE);
2612 if (is_file($prefix . PHP_TEST )) unlink($prefix . PHP_TEST );
2613 if (is_file($prefix . CURL_TEST)) unlink($prefix . CURL_TEST);
2614 if (is_file($prefix . INFO_FILE)) unlink($prefix . INFO_FILE);
2619 print "\nDone retesting.\n";
2623 ////////////////////// MAIN LOOP ////////////////////////
2626 // first check whether CURL is installed, because sometimes it's not.
2627 if( ! function_exists('curl_init') ) {
2628 die("Could not find 'curl_init' function. Is the curl extension compiled into PHP?\n");
2631 // Initialization of types. wikiFuzz doesn't have a constructor because we want to
2632 // access it staticly and not have any globals.
2633 wikiFuzz::$types = array_keys(wikiFuzz::$data);
2635 // Make directory if doesn't exist
2636 if (!is_dir(DIRECTORY)) {
2637 mkdir (DIRECTORY, 0700 );
2639 // otherwise, we first retest the things that we have found in previous runs
2640 else if (RERUN_OLD_TESTS) {
2641 rerunPreviousTests();
2644 // main loop.
2645 $start_time = date("U");
2646 $num_errors = 0;
2647 if (!QUIET) {
2648 print "Beginning main loop. Results are stored in the " . DIRECTORY . " directory.\n";
2649 print "Press CTRL+C to stop testing.\n";
2652 for ($count=0; true; $count++) {
2653 if (!QUIET) {
2654 // spinning progress indicator.
2655 switch( $count % 4 ) {
2656 case '0': print "\r/"; break;
2657 case '1': print "\r-"; break;
2658 case '2': print "\r\\"; break;
2659 case '3': print "\r|"; break;
2661 print " $count";
2664 // generate a page test to run.
2665 $test = selectPageTest($count);
2667 $mins = ( date("U") - $start_time ) / 60;
2668 if (!QUIET && $mins > 0) {
2669 print ". $num_errors poss errors. "
2670 . floor($mins) . " mins. "
2671 . round ($count / $mins, 0) . " tests/min. "
2672 . get_class($test); // includes the current test name.
2675 // run this test against MediaWiki, and see if the output was valid.
2676 $testname = $count;
2677 $valid = runWikiTest($test, $testname, false);
2679 // save the failed test
2680 if ( ! $valid ) {
2681 if (QUIET) {
2682 print "\nTest: " . get_class($test) . " ; Testname: $testname\n------";
2683 } else {
2684 print "\n";
2686 saveTest($test, $testname);
2687 $num_errors += 1;
2688 } else if ( KEEP_PASSED_TESTS ) {
2689 // print current time, with microseconds (matches "strace" format), and the test name.
2690 print " " . date("H:i:s.") . substr(current(explode(" ", microtime())), 2) . " " . $testname;
2691 saveTest($test, $testname);
2694 // stop if we have reached max number of errors.
2695 if (defined("MAX_ERRORS") && $num_errors>=MAX_ERRORS) {
2696 break;
2699 // stop if we have reached max number of mins runtime.
2700 if (defined("MAX_RUNTIME") && $mins>=MAX_RUNTIME) {
2701 break;