Fixing a variable name, and a PHP warning.
[mediawiki.git] / maintenance / fuzz-tester.php
blobaf12d6a1f84c7f03c3d18c07e871dd1123c4fd3a
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',
548 'upright=',
549 'border',
551 // misc stuff to throw at the Parser.
552 '%08X',
553 '/',
554 ":x{|",
555 "\n|+",
556 "<noinclude>",
557 "</noinclude>",
558 " \302\273",
559 " :",
560 " !",
561 " ;",
562 "\302\253",
563 "[[category:",
564 "?=",
565 "(",
566 ")",
567 "]]]",
568 "../",
569 "{{{{",
570 "}}}}",
571 "[[Special:",
572 "<includeonly>",
573 "</includeonly>",
574 "<!--MWTEMPLATESECTION=",
575 '<!--MWTOC-->',
577 // implicit link creation on booknum, RFC, and PubMed ID usage (both with and without IDs)
578 "ISBN 2",
579 "RFC 000",
580 "PMID 000",
581 "ISBN ",
582 "RFC ",
583 "PMID ",
585 // magic words:
586 '__NOTOC__',
587 '__FORCETOC__',
588 '__NOEDITSECTION__',
589 '__START__',
590 '__NOTITLECONVERT__',
591 '__NOCONTENTCONVERT__',
592 '__END__',
593 '__TOC__',
594 '__NOTC__',
595 '__NOCC__',
596 "__FORCETOC__",
597 "__NEWSECTIONLINK__",
598 "__NOGALLERY__",
600 // more magic words / internal templates.
601 '{{PAGENAME}}',
602 '{{PAGENAMEE}}',
603 '{{NAMESPACE}}',
604 "{{MSG:",
605 "}}",
606 "{{MSGNW:",
607 "}}",
608 "{{INT:",
609 "}}",
610 '{{SITENAME}}',
611 "{{NS:",
612 "}}",
613 "{{LOCALURL:",
614 "}}",
615 "{{LOCALURLE:",
616 "}}",
617 "{{SCRIPTPATH}}",
618 "{{GRAMMAR:gentiv|",
619 "}}",
620 "{{REVISIONID}}",
621 "{{SUBPAGENAME}}",
622 "{{SUBPAGENAMEE}}",
623 "{{ns:0}}",
624 "{{fullurle:",
625 "}}",
626 "{{subst::",
627 "}}",
628 "{{UCFIRST:",
629 "}}",
630 "{{UC:",
631 '{{SERVERNAME}}',
632 '{{SERVER}}',
633 "{{RAW:",
634 "}}",
635 "{{PLURAL:",
636 "}}",
637 "{{LCFIRST:",
638 "}}",
639 "{{LC:",
640 "}}",
641 '{{CURRENTWEEK}}',
642 '{{CURRENTDOW}}',
643 "{{INT:{{LC:contribs-showhideminor}}|",
644 "}}",
645 "{{INT:googlesearch|",
646 "}}",
647 "{{BASEPAGENAME}}",
648 "{{CONTENTLANGUAGE}}",
649 "{{PAGESINNAMESPACE:}}",
650 "{{#language:",
651 "}}",
652 "{{#special:",
653 "}}",
654 "{{#special:emailuser",
655 "}}",
657 // Some raw link for magic words.
658 "{{NUMBEROFPAGES:R",
659 "}}",
660 "{{NUMBEROFUSERS:R",
661 "}}",
662 "{{NUMBEROFARTICLES:R",
663 "}}",
664 "{{NUMBEROFFILES:R",
665 "}}",
666 "{{NUMBEROFADMINS:R",
667 "}}",
668 "{{padleft:",
669 "}}",
670 "{{padright:",
671 "}}",
672 "{{DEFAULTSORT:",
673 "}}",
675 // internal Math "extension":
676 "<math>",
677 "</math>",
679 // Parser extension functions:
680 "{{#expr:",
681 "{{#if:",
682 "{{#ifeq:",
683 "{{#ifexist:",
684 "{{#ifexpr:",
685 "{{#switch:",
686 "{{#time:",
687 "}}",
689 // references table for the Cite extension.
690 "<references/>",
692 // Internal Parser tokens - try inserting some of these.
693 "UNIQ25f46b0524f13e67NOPARSE",
694 "UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002",
695 "\x07UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002-QINU",
697 // Inputbox extension:
698 "<inputbox>\ntype=search\nsearchbuttonlabel=\n",
699 "</inputbox>",
701 // charInsert extension:
702 "<charInsert>",
703 "</charInsert>",
705 // wikiHiero extension:
706 "<hiero>",
707 "</hiero>",
709 // Image gallery:
710 "<gallery>",
711 "</gallery>",
713 // FixedImage extension.
714 "<fundraising/>",
716 // Timeline extension: currently untested.
718 // Nowiki:
719 "<nOwIkI>",
720 "</nowiki>",
722 // an external image to test the external image displaying code
723 "http://debian.org/Pics/debian.png",
725 // LabeledSectionTransclusion extension.
726 "{{#lstx:",
727 "}}",
728 "{{#lst:",
729 "}}",
730 "{{#lst:Main Page|",
731 "}}"
735 ** Randomly returns one element of the input array.
737 static public function chooseInput(array $input) {
738 $randindex = wikiFuzz::randnum(count($input) - 1);
739 return $input[$randindex];
742 // Max number of parameters for HTML attributes.
743 static private $maxparams = 10;
745 /**
746 ** Returns random number between finish and start.
748 static public function randnum($finish,$start=0) {
749 return mt_rand($start,$finish);
753 ** Returns a mix of random text and random wiki syntax.
755 static private function randstring() {
756 $thestring = "";
758 for ($i=0; $i<40; $i++) {
759 $what = wikiFuzz::randnum(1);
761 if ($what == 0) { // include some random wiki syntax
762 $which = wikiFuzz::randnum(count(wikiFuzz::$ext) - 1);
763 $thestring .= wikiFuzz::$ext[$which];
765 else { // include some random text
766 $char = INCLUDE_BINARY
767 // Decimal version:
768 // "&#" . wikiFuzz::randnum(255) . ";"
769 // Hex version:
770 ? "&#x" . str_pad(dechex(wikiFuzz::randnum(255)), wikiFuzz::randnum(2, 7), "0", STR_PAD_LEFT) . ";"
771 // A truly binary version:
772 // ? chr(wikiFuzz::randnum(0,255))
773 : chr(wikiFuzz::randnum(126,32));
775 $length = wikiFuzz::randnum(8);
776 $thestring .= str_repeat ($char, $length);
779 return $thestring;
783 ** Returns either random text, or random wiki syntax, or random data from "ints",
784 ** or random data from "other".
786 static private function makestring() {
787 $what = wikiFuzz::randnum(2);
788 if ($what == 0) {
789 return wikiFuzz::randstring();
791 elseif ($what == 1) {
792 return wikiFuzz::$ints[wikiFuzz::randnum(count(wikiFuzz::$ints) - 1)];
794 else {
795 return wikiFuzz::$other[wikiFuzz::randnum(count(wikiFuzz::$other) - 1)];
801 ** Strips out the stuff that Mediawiki balks at in a page's title.
802 ** Implementation copied/pasted from cleanupTable.inc & cleanupImages.php
804 static public function makeTitleSafe($str) {
805 $legalTitleChars = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF";
806 return preg_replace_callback(
807 "/([^$legalTitleChars])/",
808 create_function(
809 // single quotes are essential here,
810 // or alternative escape all $ as \$
811 '$matches',
812 'return sprintf( "\\x%02x", ord( $matches[1] ) );'
814 $str );
818 ** Returns a string of fuzz text.
820 static private function loop() {
821 switch ( wikiFuzz::randnum(3) ) {
822 case 1: // an opening tag, with parameters.
823 $string = "";
824 $i = wikiFuzz::randnum(count(wikiFuzz::$types) - 1);
825 $t = wikiFuzz::$types[$i];
826 $arr = wikiFuzz::$data[$t];
827 $string .= "<" . $t . " ";
828 $num_params = min(wikiFuzz::$maxparams, count($arr));
829 for ($z=0; $z<$num_params; $z++) {
830 $badparam = $arr[wikiFuzz::randnum(count($arr) - 1)];
831 $badstring = wikiFuzz::makestring();
832 $string .= $badparam . "=" . wikiFuzz::getRandQuote() . $badstring . wikiFuzz::getRandQuote() . " ";
834 $string .= ">\n";
835 return $string;
836 case 2: // a closing tag.
837 $i = wikiFuzz::randnum(count(wikiFuzz::$types) - 1);
838 return "</". wikiFuzz::$types[$i] . ">";
839 case 3: // a random string, between tags.
840 return wikiFuzz::makeString();
842 return ""; // catch-all, should never be called.
846 ** Returns one of the three styles of random quote: ', ", and nothing.
848 static private function getRandQuote() {
849 switch ( wikiFuzz::randnum(3) ) {
850 case 1 : return "'";
851 case 2 : return "\"";
852 default: return "";
857 ** Returns fuzz text, with the parameter indicating approximately how many lines of text you want.
859 static public function makeFuzz($maxtypes = 2) {
860 $page = "";
861 for ($k=0; $k<$maxtypes; $k++) {
862 $page .= wikiFuzz::loop();
864 return $page;
869 //////// MEDIAWIKI PAGES TO TEST, AND HOW TO TEST THEM ///////
872 ** A page test has just these things:
873 ** 1) Form parameters.
874 ** 2) the URL we are going to test those parameters on.
875 ** 3) Any cookies required for the test.
876 ** 4) Whether Tidy should validate the page. Defaults to true, but can be turned off.
877 ** Declared abstract because it should be extended by a class
878 ** that supplies these parameters.
880 abstract class pageTest {
881 protected $params;
882 protected $pagePath;
883 protected $cookie = "";
884 protected $tidyValidate = true;
886 public function getParams() {
887 return $this->params;
890 public function getPagePath() {
891 return $this->pagePath;
894 public function getCookie() {
895 return $this->cookie;
898 public function tidyValidate() {
899 return $this->tidyValidate;
905 ** a page test for the "Edit" page. Tests Parser.php and Sanitizer.php.
907 class editPageTest extends pageTest {
908 function __construct() {
909 $this->pagePath = "index.php?title=WIKIFUZZ";
911 $this->params = array (
912 "action" => "submit",
913 "wpMinoredit" => wikiFuzz::makeFuzz(2),
914 "wpPreview" => wikiFuzz::makeFuzz(2),
915 "wpSection" => wikiFuzz::makeFuzz(2),
916 "wpEdittime" => wikiFuzz::makeFuzz(2),
917 "wpSummary" => wikiFuzz::makeFuzz(2),
918 "wpScrolltop" => wikiFuzz::makeFuzz(2),
919 "wpStarttime" => wikiFuzz::makeFuzz(2),
920 "wpAutoSummary" => wikiFuzz::makeFuzz(2),
921 "wpTextbox1" => wikiFuzz::makeFuzz(40) // the main wiki text, need lots of this.
924 // sometimes we don't want to specify certain parameters.
925 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpSection"]);
926 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpEdittime"]);
927 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpSummary"]);
928 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpScrolltop"]);
929 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpStarttime"]);
930 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpAutoSummary"]);
931 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpTextbox1"]);
937 ** a page test for "Special:Listusers".
939 class listusersTest extends pageTest {
940 function __construct() {
941 $this->pagePath = "index.php?title=Special:Listusers";
943 $this->params = array (
944 "title" => wikiFuzz::makeFuzz(2),
945 "group" => wikiFuzz::makeFuzz(2),
946 "username" => wikiFuzz::makeFuzz(2),
947 "Go" => wikiFuzz::makeFuzz(2),
948 "limit" => wikiFuzz::chooseInput( array("0", "-1", "---'----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
949 "offset" => wikiFuzz::chooseInput( array("0", "-1", "--------'-----0", "+1", "81343242346234234", wikiFuzz::makeFuzz(2)) )
956 ** a page test for "Special:Search".
958 class searchTest extends pageTest {
959 function __construct() {
960 $this->pagePath = "index.php?title=Special:Search";
962 $this->params = array (
963 "action" => "index.php?title=Special:Search",
964 "ns0" => wikiFuzz::makeFuzz(2),
965 "ns1" => wikiFuzz::makeFuzz(2),
966 "ns2" => wikiFuzz::makeFuzz(2),
967 "ns3" => wikiFuzz::makeFuzz(2),
968 "ns4" => wikiFuzz::makeFuzz(2),
969 "ns5" => wikiFuzz::makeFuzz(2),
970 "ns6" => wikiFuzz::makeFuzz(2),
971 "ns7" => wikiFuzz::makeFuzz(2),
972 "ns8" => wikiFuzz::makeFuzz(2),
973 "ns9" => wikiFuzz::makeFuzz(2),
974 "ns10" => wikiFuzz::makeFuzz(2),
975 "ns11" => wikiFuzz::makeFuzz(2),
976 "ns12" => wikiFuzz::makeFuzz(2),
977 "ns13" => wikiFuzz::makeFuzz(2),
978 "ns14" => wikiFuzz::makeFuzz(2),
979 "ns15" => wikiFuzz::makeFuzz(2),
980 "redirs" => wikiFuzz::makeFuzz(2),
981 "search" => wikiFuzz::makeFuzz(2),
982 "offset" => wikiFuzz::chooseInput( array("", "0", "-1", "--------'-----0", "+1", "81343242346234234", wikiFuzz::makeFuzz(2)) ),
983 "fulltext" => wikiFuzz::chooseInput( array("", "0", "1", "--------'-----0", "+1", wikiFuzz::makeFuzz(2)) ),
984 "searchx" => wikiFuzz::chooseInput( array("", "0", "1", "--------'-----0", "+1", wikiFuzz::makeFuzz(2)) )
991 ** a page test for "Special:Recentchanges".
993 class recentchangesTest extends pageTest {
994 function __construct() {
995 $this->pagePath = "index.php?title=Special:Recentchanges";
997 $this->params = array (
998 "action" => wikiFuzz::makeFuzz(2),
999 "title" => wikiFuzz::makeFuzz(2),
1000 "namespace" => wikiFuzz::chooseInput( range(-1, 15) ),
1001 "Go" => wikiFuzz::makeFuzz(2),
1002 "invert" => wikiFuzz::chooseInput( array("-1", "---'----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1003 "hideanons" => wikiFuzz::chooseInput( array("-1", "------'-------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1004 'limit' => wikiFuzz::chooseInput( array("0", "-1", "---------'----0", "+1", "81340909772349234", wikiFuzz::makeFuzz(2)) ),
1005 "days" => wikiFuzz::chooseInput( array("-1", "----------'---0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1006 "hideminor" => wikiFuzz::chooseInput( array("-1", "-----------'--0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1007 "hidebots" => wikiFuzz::chooseInput( array("-1", "---------'----0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1008 "hideliu" => wikiFuzz::chooseInput( array("-1", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1009 "hidepatrolled" => wikiFuzz::chooseInput( array("-1", "-----'--------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1010 "hidemyself" => wikiFuzz::chooseInput( array("-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1011 'categories_any'=> wikiFuzz::chooseInput( array("-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1012 'categories' => wikiFuzz::chooseInput( array("-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1013 'feed' => wikiFuzz::chooseInput( array("-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) )
1020 ** a page test for "Special:Prefixindex".
1022 class prefixindexTest extends pageTest {
1023 function __construct() {
1024 $this->pagePath = "index.php?title=Special:Prefixindex";
1026 $this->params = array (
1027 "title" => "Special:Prefixindex",
1028 "namespace" => wikiFuzz::randnum(-10,101),
1029 "Go" => wikiFuzz::makeFuzz(2)
1032 // sometimes we want 'prefix', sometimes we want 'from', and sometimes we want nothing.
1033 if (wikiFuzz::randnum(3) == 0) {
1034 $this->params["prefix"] = wikiFuzz::chooseInput( array("-1", "-----'--------0", "+++--+1",
1035 wikiFuzz::randnum(-10,8134), wikiFuzz::makeFuzz(2)) );
1037 if (wikiFuzz::randnum(3) == 0) {
1038 $this->params["from"] = wikiFuzz::chooseInput( array("-1", "-----'--------0", "+++--+1",
1039 wikiFuzz::randnum(-10,8134), wikiFuzz::makeFuzz(2)) );
1046 ** a page test for "Special:MIMEsearch".
1048 class mimeSearchTest extends pageTest {
1049 function __construct() {
1050 $this->pagePath = "index.php?title=Special:MIMEsearch";
1052 $this->params = array (
1053 "action" => "index.php?title=Special:MIMEsearch",
1054 "mime" => wikiFuzz::makeFuzz(3),
1055 'limit' => wikiFuzz::chooseInput( array("0", "-1", "-------'------0", "+1", "81342321351235325", wikiFuzz::makeFuzz(2)) ),
1056 'offset' => wikiFuzz::chooseInput( array("0", "-1", "-----'--------0", "+1", "81341231235365252234324", wikiFuzz::makeFuzz(2)) )
1063 ** a page test for "Special:Log".
1065 class specialLogTest extends pageTest {
1066 function __construct() {
1067 $this->pagePath = "index.php?title=Special:Log";
1069 $this->params = array (
1070 "type" => wikiFuzz::chooseInput( array("", wikiFuzz::makeFuzz(2)) ),
1071 "par" => wikiFuzz::makeFuzz(2),
1072 "user" => wikiFuzz::makeFuzz(2),
1073 "page" => wikiFuzz::makeFuzz(2),
1074 "from" => wikiFuzz::makeFuzz(2),
1075 "until" => wikiFuzz::makeFuzz(2),
1076 "title" => wikiFuzz::makeFuzz(2)
1083 ** a page test for "Special:Userlogin", with a successful login.
1085 class successfulUserLoginTest extends pageTest {
1086 function __construct() {
1087 $this->pagePath = "index.php?title=Special:Userlogin&action=submitlogin&type=login&returnto=" . wikiFuzz::makeFuzz(2);
1089 $this->params = array (
1090 "wpName" => USER_ON_WIKI,
1091 // sometimes real password, sometimes not:
1092 'wpPassword' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz(2), USER_PASSWORD ) ),
1093 'wpRemember' => wikiFuzz::makeFuzz(2)
1096 $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array("1" , wikiFuzz::makeFuzz(2) ) );
1102 ** a page test for "Special:Userlogin".
1104 class userLoginTest extends pageTest {
1105 function __construct() {
1107 $this->pagePath = "index.php?title=Special:Userlogin";
1109 $this->params = array (
1110 'wpRetype' => wikiFuzz::makeFuzz(2),
1111 'wpRemember' => wikiFuzz::makeFuzz(2),
1112 'wpRealName' => wikiFuzz::makeFuzz(2),
1113 'wpPassword' => wikiFuzz::makeFuzz(2),
1114 'wpName' => wikiFuzz::makeFuzz(2),
1115 'wpMailmypassword'=> wikiFuzz::makeFuzz(2),
1116 'wpLoginattempt' => wikiFuzz::makeFuzz(2),
1117 'wpEmail' => wikiFuzz::makeFuzz(2),
1118 'wpDomain' => wikiFuzz::chooseInput( array("", "local", wikiFuzz::makeFuzz(2)) ),
1119 'wpCreateaccountMail' => wikiFuzz::chooseInput( array("", wikiFuzz::makeFuzz(2)) ),
1120 'wpCreateaccount' => wikiFuzz::chooseInput( array("", wikiFuzz::makeFuzz(2)) ),
1121 'wpCookieCheck' => wikiFuzz::chooseInput( array("", wikiFuzz::makeFuzz(2)) ),
1122 'type' => wikiFuzz::chooseInput( array("signup", "login", "", wikiFuzz::makeFuzz(2)) ),
1123 'returnto' => wikiFuzz::makeFuzz(2),
1124 'action' => wikiFuzz::chooseInput( array("", "submitlogin", wikiFuzz::makeFuzz(2)) )
1127 $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array("1" , wikiFuzz::makeFuzz(2) ) );
1133 ** a page test for "Special:Ipblocklist" (also includes unblocking)
1135 class ipblocklistTest extends pageTest {
1136 function __construct() {
1137 $this->pagePath = "index.php?title=Special:Ipblocklist";
1139 $this->params = array (
1140 'wpUnblockAddress'=> wikiFuzz::makeFuzz(2),
1141 'ip' => wikiFuzz::chooseInput( array("20398702394", "", "Nickj2", wikiFuzz::makeFuzz(2),
1142 // something like an IP address, sometimes invalid:
1143 ( wikiFuzz::randnum(300,-20) . "." . wikiFuzz::randnum(300,-20) . "."
1144 . wikiFuzz::randnum(300,-20) . "." .wikiFuzz::randnum(300,-20) ) ) ),
1145 'id' => wikiFuzz::makeFuzz(2),
1146 'wpUnblockReason' => wikiFuzz::makeFuzz(2),
1147 'action' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2), "success", "submit", "unblock") ),
1148 'wpEditToken' => wikiFuzz::makeFuzz(2),
1149 'wpBlock' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2), "") ),
1150 'limit' => wikiFuzz::chooseInput( array("0", "-1", "--------'-----0", "+1",
1151 "09700982312351132098234", wikiFuzz::makeFuzz(2)) ),
1152 'offset' => wikiFuzz::chooseInput( array("0", "-1", "------'-------0", "+1",
1153 "09700980982341535324234234", wikiFuzz::makeFuzz(2)) )
1156 // sometimes we don't want to specify certain parameters.
1157 if (wikiFuzz::randnum(4) == 0) unset($this->params["action"]);
1158 if (wikiFuzz::randnum(3) == 0) unset($this->params["ip"]);
1159 if (wikiFuzz::randnum(2) == 0) unset($this->params["id"]);
1160 if (wikiFuzz::randnum(3) == 0) unset($this->params["wpUnblockAddress"]);
1166 ** a page test for "Special:Newimages".
1168 class newImagesTest extends pageTest {
1169 function __construct() {
1170 $this->pagePath = "index.php?title=Special:Newimages";
1172 $this->params = array (
1173 'hidebots' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2), "1", "", "-1") ),
1174 'wpIlMatch' => wikiFuzz::makeFuzz(2),
1175 'until' => wikiFuzz::makeFuzz(2),
1176 'from' => wikiFuzz::makeFuzz(2)
1179 // sometimes we don't want to specify certain parameters.
1180 if (wikiFuzz::randnum(6) == 0) unset($this->params["until"]);
1181 if (wikiFuzz::randnum(6) == 0) unset($this->params["from"]);
1187 ** a page test for the "Special:Imagelist" page.
1189 class imagelistTest extends pageTest {
1190 function __construct() {
1191 $this->pagePath = "index.php?title=Special:Imagelist";
1193 $this->params = array (
1194 'sort' => wikiFuzz::chooseInput( array("bysize", "byname" , "bydate", wikiFuzz::makeFuzz(2)) ),
1195 'limit' => wikiFuzz::chooseInput( array("0", "-1", "--------'-----0", "+1", "09700982312351132098234", wikiFuzz::makeFuzz(2)) ),
1196 'offset' => wikiFuzz::chooseInput( array("0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz(2)) ),
1197 'wpIlMatch' => wikiFuzz::makeFuzz(2)
1204 ** a page test for "Special:Export".
1206 class specialExportTest extends pageTest {
1207 function __construct() {
1208 $this->pagePath = "index.php?title=Special:Export";
1210 $this->params = array (
1211 'action' => wikiFuzz::chooseInput( array("submit", "", wikiFuzz::makeFuzz(2)) ),
1212 'pages' => wikiFuzz::makeFuzz(2),
1213 'curonly' => wikiFuzz::chooseInput( array("", "0", "-1", wikiFuzz::makeFuzz(2)) ),
1214 'listauthors' => wikiFuzz::chooseInput( array("", "0", "-1", wikiFuzz::makeFuzz(2)) ),
1215 'history' => wikiFuzz::chooseInput( array("0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz(2)) ),
1219 // For the time being, need to disable "submit" action as Tidy barfs on MediaWiki's XML export.
1220 if ($this->params['action'] == 'submit') $this->params['action'] = '';
1222 // Sometimes remove the history field.
1223 if (wikiFuzz::randnum(2) == 0) unset($this->params["history"]);
1225 // page does not produce HTML.
1226 $this->tidyValidate = false;
1232 ** a page test for "Special:Booksources".
1234 class specialBooksourcesTest extends pageTest {
1235 function __construct() {
1236 $this->pagePath = "index.php?title=Special:Booksources";
1238 $this->params = array (
1239 'go' => wikiFuzz::makeFuzz(2),
1240 // ISBN codes have to contain some semi-numeric stuff or will be ignored:
1241 'isbn' => "0X0" . wikiFuzz::makeFuzz(2)
1248 ** a page test for "Special:Allpages".
1250 class specialAllpagesTest extends pageTest {
1251 function __construct() {
1252 $this->pagePath = "index.php?title=Special%3AAllpages";
1254 $this->params = array (
1255 'from' => wikiFuzz::makeFuzz(2),
1256 'namespace' => wikiFuzz::chooseInput( range(-1, 15) ),
1257 'go' => wikiFuzz::makeFuzz(2)
1264 ** a page test for the page History.
1266 class pageHistoryTest extends pageTest {
1267 function __construct() {
1268 $this->pagePath = "index.php?title=Main_Page&action=history";
1270 $this->params = array (
1271 'limit' => wikiFuzz::chooseInput( array("-1", "0", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1272 'offset' => wikiFuzz::chooseInput( array("-1", "0", "------'-------0", "+1", "9823412312312412435", wikiFuzz::makeFuzz(2)) ),
1273 "go" => wikiFuzz::chooseInput( array("first", "last", wikiFuzz::makeFuzz(2)) ),
1274 "dir" => wikiFuzz::chooseInput( array("prev", "next", wikiFuzz::makeFuzz(2)) ),
1275 "diff" => wikiFuzz::chooseInput( array("-1", "--------'-----0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1276 "oldid" => wikiFuzz::chooseInput( array("prev", "-1", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1277 "feed" => wikiFuzz::makeFuzz(2)
1284 ** a page test for the Special:Contributions".
1286 class contributionsTest extends pageTest {
1287 function __construct() {
1288 $this->pagePath = "index.php?title=Special:Contributions/" . USER_ON_WIKI;
1290 $this->params = array (
1291 'target' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2), "newbies", USER_ON_WIKI) ),
1292 'namespace' => wikiFuzz::chooseInput( array(-1, 15, 1, wikiFuzz::makeFuzz(2)) ),
1293 'offset' => wikiFuzz::chooseInput( array("0", "-1", "------'-------0", "+1", "982342131232131231241", wikiFuzz::makeFuzz(2)) ),
1294 'bot' => wikiFuzz::chooseInput( array("", "-1", "0", "1", wikiFuzz::makeFuzz(2)) ),
1295 'go' => wikiFuzz::chooseInput( array("-1", 'prev', 'next', wikiFuzz::makeFuzz(2)) )
1302 ** a page test for viewing a normal page, whilst posting various params.
1304 class viewPageTest extends pageTest {
1305 function __construct() {
1306 $this->pagePath = "index.php?title=Main_Page";
1308 $this->params = array (
1309 "useskin" => wikiFuzz::chooseInput( array("chick", "cologneblue", "myskin",
1310 "nostalgia", "simple", "standard", wikiFuzz::makeFuzz(2)) ),
1311 "uselang" => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz(2),
1312 "ab", "af", "an", "ar", "arc", "as", "ast", "av", "ay", "az", "ba",
1313 "bat-smg", "be", "bg", "bm", "bn", "bo", "bpy", "br", "bs", "ca",
1314 "ce", "cs", "csb", "cv", "cy", "da", "de", "dv", "dz", "el", "en",
1315 "eo", "es", "et", "eu", "fa", "fi", "fo", "fr", "fur", "fy", "ga",
1316 "gn", "gsw", "gu", "he", "hi", "hr", "hu", "ia", "id", "ii", "is",
1317 "it", "ja", "jv", "ka", "km", "kn", "ko", "ks", "ku", "kv", "la",
1318 "li", "lo", "lt", "lv", "mk", "ml", "ms", "nah", "nap", "nds",
1319 "nds-nl", "nl", "nn", "no", "non", "nv", "oc", "or", "os", "pa",
1320 "pl", "pms", "ps", "pt", "pt-br", "qu", "rmy", "ro", "ru", "sc",
1321 "sd", "sk", "sl", "sq", "sr", "sr-ec", "sr-el", "sr-jc", "sr-jl",
1322 "su", "sv", "ta", "te", "th", "tlh", "tr", "tt", "ty", "tyv", "udm",
1323 "ug", "uk", "ur", "utf8", "vec", "vi", "wa", "xal", "yi", "za",
1324 "zh", "zh-cn", "zh-hk", "zh-sg", "zh-tw", "zh-tw") ),
1325 "returnto" => wikiFuzz::makeFuzz(2),
1326 "feed" => wikiFuzz::chooseInput( array("atom", "rss", wikiFuzz::makeFuzz(2)) ),
1327 "rcid" => wikiFuzz::makeFuzz(2),
1328 "action" => wikiFuzz::chooseInput( array("view", "raw", "render", wikiFuzz::makeFuzz(2), "markpatrolled") ),
1329 "printable" => wikiFuzz::makeFuzz(2),
1330 "oldid" => wikiFuzz::makeFuzz(2),
1331 "redirect" => wikiFuzz::makeFuzz(2),
1332 "diff" => wikiFuzz::makeFuzz(2),
1333 "search" => wikiFuzz::makeFuzz(2),
1334 "rdfrom" => wikiFuzz::makeFuzz(2), // things from Article.php from here on:
1335 "token" => wikiFuzz::makeFuzz(2),
1336 "tbid" => wikiFuzz::makeFuzz(2),
1337 "action" => wikiFuzz::chooseInput( array("purge", wikiFuzz::makeFuzz(2)) ),
1338 "wpReason" => wikiFuzz::makeFuzz(2),
1339 "wpEditToken" => wikiFuzz::makeFuzz(2),
1340 "from" => wikiFuzz::makeFuzz(2),
1341 "bot" => wikiFuzz::makeFuzz(2),
1342 "summary" => wikiFuzz::makeFuzz(2),
1343 "direction" => wikiFuzz::chooseInput( array("next", "prev", wikiFuzz::makeFuzz(2)) ),
1344 "section" => wikiFuzz::makeFuzz(2),
1345 "preload" => wikiFuzz::makeFuzz(2),
1349 // Tidy does not know how to valid atom or rss, so exclude from testing for the time being.
1350 if ($this->params["feed"] == "atom") { unset($this->params["feed"]); }
1351 else if ($this->params["feed"] == "rss") { unset($this->params["feed"]); }
1353 // Raw pages cannot really be validated
1354 if ($this->params["action"] == "raw") unset($this->params["action"]);
1356 // sometimes we don't want to specify certain parameters.
1357 if (wikiFuzz::randnum(6) == 0) unset($this->params["rcid"]);
1358 if (wikiFuzz::randnum(6) == 0) unset($this->params["diff"]);
1359 if (wikiFuzz::randnum(6) == 0) unset($this->params["rdfrom"]);
1360 if (wikiFuzz::randnum(3) == 0) unset($this->params["oldid"]);
1362 // usually don't want action == purge.
1363 if (wikiFuzz::randnum(6) > 1) unset($this->params["action"]);
1369 ** a page test for "Special:Allmessages".
1371 class specialAllmessagesTest extends pageTest {
1372 function __construct() {
1373 $this->pagePath = "index.php?title=Special:Allmessages";
1375 // only really has one parameter
1376 $this->params = array (
1377 "ot" => wikiFuzz::chooseInput( array("php", "html", wikiFuzz::makeFuzz(2)) )
1383 ** a page test for "Special:Newpages".
1385 class specialNewpages extends pageTest {
1386 function __construct() {
1387 $this->pagePath = "index.php?title=Special:Newpages";
1389 $this->params = array (
1390 "namespace" => wikiFuzz::chooseInput( range(-1, 15) ),
1391 "feed" => wikiFuzz::chooseInput( array("atom", "rss", wikiFuzz::makeFuzz(2)) ),
1392 'limit' => wikiFuzz::chooseInput( array("-1", "0", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1393 'offset' => wikiFuzz::chooseInput( array("-1", "0", "------'-------0", "+1", "9823412312312412435", wikiFuzz::makeFuzz(2)) )
1396 // Tidy does not know how to valid atom or rss, so exclude from testing for the time being.
1397 if ($this->params["feed"] == "atom") { unset($this->params["feed"]); }
1398 else if ($this->params["feed"] == "rss") { unset($this->params["feed"]); }
1403 ** a page test for "redirect.php"
1405 class redirectTest extends pageTest {
1406 function __construct() {
1407 $this->pagePath = "redirect.php";
1409 $this->params = array (
1410 "wpDropdown" => wikiFuzz::makeFuzz(2)
1413 // sometimes we don't want to specify certain parameters.
1414 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpDropdown"]);
1420 ** a page test for "Special:Confirmemail"
1422 class confirmEmail extends pageTest {
1423 function __construct() {
1424 // sometimes we send a bogus confirmation code, and sometimes we don't.
1425 $this->pagePath = "index.php?title=Special:Confirmemail" . wikiFuzz::chooseInput( array("", "/" . wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(1)) ) );
1427 $this->params = array (
1428 "token" => wikiFuzz::makeFuzz(2)
1435 ** a page test for "Special:Watchlist"
1436 ** Note: this test would be better if we were logged in.
1438 class watchlistTest extends pageTest {
1439 function __construct() {
1440 $this->pagePath = "index.php?title=Special:Watchlist";
1442 $this->params = array (
1443 "remove" => wikiFuzz::chooseInput( array("Remove checked items from watchlist", wikiFuzz::makeFuzz(2))),
1444 'days' => wikiFuzz::chooseInput( array(0, -1, -230, "--", 3, 9, wikiFuzz::makeFuzz(2)) ),
1445 'hideOwn' => wikiFuzz::chooseInput( array("", "0", "1", wikiFuzz::makeFuzz(2)) ),
1446 'hideBots' => wikiFuzz::chooseInput( array("", "0", "1", wikiFuzz::makeFuzz(2)) ),
1447 'namespace'=> wikiFuzz::chooseInput( array("", "0", "1", wikiFuzz::makeFuzz(2)) ),
1448 'action' => wikiFuzz::chooseInput( array("submit", "clear", wikiFuzz::makeFuzz(2)) ),
1449 'id[]' => wikiFuzz::makeFuzz(2),
1450 'edit' => wikiFuzz::makeFuzz(2),
1451 'token' => wikiFuzz::chooseInput( array("", "1243213", wikiFuzz::makeFuzz(2)) )
1454 // sometimes we specifiy "reset", and sometimes we don't.
1455 if (wikiFuzz::randnum(3) == 0) $this->params["reset"] = wikiFuzz::chooseInput( array("", "0", "1", wikiFuzz::makeFuzz(2)) );
1461 ** a page test for "Special:Blockme"
1463 class specialBlockmeTest extends pageTest {
1464 function __construct() {
1465 $this->pagePath = "index.php?title=Special:Blockme";
1467 $this->params = array ( );
1469 // sometimes we specify "ip", and sometimes we don't.
1470 if (wikiFuzz::randnum(1) == 0) {
1471 $this->params["ip"] = wikiFuzz::chooseInput( array("10.12.41.213", wikiFuzz::randnum(-10,8134), wikiFuzz::makeFuzz(2)) );
1478 ** a page test for "Special:Movepage"
1480 class specialMovePage extends pageTest {
1481 function __construct() {
1482 $this->pagePath = "index.php?title=Special:Movepage";
1484 $this->params = array (
1485 "action" => wikiFuzz::chooseInput( array("success", "submit", "", wikiFuzz::makeFuzz(2)) ),
1486 'wpEditToken' => wikiFuzz::chooseInput( array('', 0, 34987987, wikiFuzz::makeFuzz(2)) ),
1487 'target' => wikiFuzz::chooseInput( array("x", wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(2)) ) ),
1488 'wpOldTitle' => wikiFuzz::chooseInput( array("z", wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(2)), wikiFuzz::makeFuzz(2) ) ),
1489 'wpNewTitle' => wikiFuzz::chooseInput( array("y", wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(2)), wikiFuzz::makeFuzz(2) ) ),
1490 'wpReason' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2)) ),
1491 'wpMovetalk' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1492 'wpDeleteAndMove' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1493 'wpConfirm' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1494 'talkmoved' => wikiFuzz::chooseInput( array("1", wikiFuzz::makeFuzz(2), "articleexists", 'notalkpage') ),
1495 'oldtitle' => wikiFuzz::makeFuzz(2),
1496 'newtitle' => wikiFuzz::makeFuzz(2),
1497 'wpMovetalk' => wikiFuzz::chooseInput( array("1", "0", wikiFuzz::makeFuzz(2)) )
1500 // sometimes we don't want to specify certain parameters.
1501 if (wikiFuzz::randnum(2) == 0) unset($this->params["wpEditToken"]);
1502 if (wikiFuzz::randnum(3) == 0) unset($this->params["target"]);
1503 if (wikiFuzz::randnum(3) == 0) unset($this->params["wpNewTitle"]);
1504 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpReason"]);
1505 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpOldTitle"]);
1511 ** a page test for "Special:Undelete"
1513 class specialUndelete extends pageTest {
1514 function __construct() {
1515 $this->pagePath = "index.php?title=Special:Undelete";
1517 $this->params = array (
1518 "action" => wikiFuzz::chooseInput( array("submit", "", wikiFuzz::makeFuzz(2)) ),
1519 'wpEditToken' => wikiFuzz::chooseInput( array('', 0, 34987987, wikiFuzz::makeFuzz(2)) ),
1520 'target' => wikiFuzz::chooseInput( array("x", wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(2)) ) ),
1521 'timestamp' => wikiFuzz::chooseInput( array("125223", wikiFuzz::makeFuzz(2) ) ),
1522 'file' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1523 'restore' => wikiFuzz::chooseInput( array("0", "1", wikiFuzz::makeFuzz(2)) ),
1524 'preview' => wikiFuzz::chooseInput( array("0", "1", wikiFuzz::makeFuzz(2)) ),
1525 'wpComment' => wikiFuzz::makeFuzz(2)
1528 // sometimes we don't want to specify certain parameters.
1529 if (wikiFuzz::randnum(2) == 0) unset($this->params["wpEditToken"]);
1530 if (wikiFuzz::randnum(4) == 0) unset($this->params["target"]);
1531 if (wikiFuzz::randnum(1) == 0) unset($this->params["restore"]);
1532 if (wikiFuzz::randnum(1) == 0) unset($this->params["preview"]);
1538 ** a page test for "Special:Unlockdb"
1540 class specialUnlockdb extends pageTest {
1541 function __construct() {
1542 $this->pagePath = "index.php?title=Special:Unlockdb";
1544 $this->params = array (
1545 "action" => wikiFuzz::chooseInput( array("submit", "success", "", wikiFuzz::makeFuzz(2)) ),
1546 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1547 'wpLockConfirm' => wikiFuzz::chooseInput( array("0", "1", wikiFuzz::makeFuzz(2)) )
1550 // sometimes we don't want to specify certain parameters.
1551 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpEditToken"]);
1552 if (wikiFuzz::randnum(4) == 0) unset($this->params["action"]);
1553 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpLockConfirm"]);
1559 ** a page test for "Special:Lockdb"
1561 class specialLockdb extends pageTest {
1562 function __construct() {
1563 $this->pagePath = "index.php?title=Special:Lockdb";
1565 $this->params = array (
1566 "action" => wikiFuzz::chooseInput( array("submit", "success", "", wikiFuzz::makeFuzz(2)) ),
1567 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1568 'wpLockReason' => wikiFuzz::makeFuzz(2),
1569 'wpLockConfirm'=> wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) )
1572 // sometimes we don't want to specify certain parameters.
1573 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpEditToken"]);
1574 if (wikiFuzz::randnum(4) == 0) unset($this->params["action"]);
1575 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpLockConfirm"]);
1581 ** a page test for "Special:Userrights"
1583 class specialUserrights extends pageTest {
1584 function __construct() {
1585 $this->pagePath = "index.php?title=Special:Userrights";
1587 $this->params = array (
1588 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1589 'user-editname' => wikiFuzz::chooseInput( array("Nickj2", "Nickj2\n<xyz>", wikiFuzz::makeFuzz(2)) ),
1590 'ssearchuser' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1591 'saveusergroups'=> wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)), "Save User Groups"),
1592 'member[]' => wikiFuzz::chooseInput( array("0", "bot", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1593 "available[]" => wikiFuzz::chooseInput( array("0", "sysop", "bureaucrat", "1", "++--34234", wikiFuzz::makeFuzz(2)) )
1596 // sometimes we don't want to specify certain parameters.
1597 if (wikiFuzz::randnum(3) == 0) unset($this->params['ssearchuser']);
1598 if (wikiFuzz::randnum(3) == 0) unset($this->params['saveusergroups']);
1604 ** a test for page protection and unprotection.
1606 class pageProtectionForm extends pageTest {
1607 function __construct() {
1608 $this->pagePath = "index.php?title=Main_Page";
1610 $this->params = array (
1611 "action" => "protect",
1612 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1613 "mwProtect-level-edit" => wikiFuzz::chooseInput( array('', 'autoconfirmed', 'sysop', wikifuzz::makeFuzz(2)) ),
1614 "mwProtect-level-move" => wikiFuzz::chooseInput( array('', 'autoconfirmed', 'sysop', wikifuzz::makeFuzz(2)) ),
1615 "mwProtectUnchained" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1616 'mwProtect-reason' => wikiFuzz::chooseInput( array("because it was there", wikifuzz::makeFuzz(2)) )
1620 // sometimes we don't want to specify certain parameters.
1621 if (wikiFuzz::randnum(3) == 0) unset($this->params["mwProtectUnchained"]);
1622 if (wikiFuzz::randnum(3) == 0) unset($this->params['mwProtect-reason']);
1628 ** a page test for "Special:Blockip".
1630 class specialBlockip extends pageTest {
1631 function __construct() {
1632 $this->pagePath = "index.php?title=Special:Blockip";
1634 $this->params = array (
1635 "action" => wikiFuzz::chooseInput( array("submit", "", wikiFuzz::makeFuzz(2)) ),
1636 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1637 "wpBlockAddress" => wikiFuzz::chooseInput( array("20398702394", "", "Nickj2", wikiFuzz::makeFuzz(2),
1638 // something like an IP address, sometimes invalid:
1639 ( wikiFuzz::randnum(300,-20) . "." . wikiFuzz::randnum(300,-20) . "."
1640 . wikiFuzz::randnum(300,-20) . "." .wikiFuzz::randnum(300,-20) ) ) ),
1641 "ip" => wikiFuzz::chooseInput( array("20398702394", "", "Nickj2", wikiFuzz::makeFuzz(2),
1642 // something like an IP address, sometimes invalid:
1643 ( wikiFuzz::randnum(300,-20) . "." . wikiFuzz::randnum(300,-20) . "."
1644 . wikiFuzz::randnum(300,-20) . "." .wikiFuzz::randnum(300,-20) ) ) ),
1645 "wpBlockOther" => wikiFuzz::chooseInput( array('', 'Nickj2', wikifuzz::makeFuzz(2)) ),
1646 "wpBlockExpiry" => wikiFuzz::chooseInput( array("other", "2 hours", "1 day", "3 days", "1 week", "2 weeks",
1647 "1 month", "3 months", "6 months", "1 year", "infinite", wikiFuzz::makeFuzz(2)) ),
1648 "wpBlockReason" => wikiFuzz::chooseInput( array("because it was there", wikifuzz::makeFuzz(2)) ),
1649 "wpAnonOnly" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1650 "wpCreateAccount" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1651 "wpBlock" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) )
1654 // sometimes we don't want to specify certain parameters.
1655 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpBlockOther"]);
1656 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpBlockExpiry"]);
1657 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpBlockReason"]);
1658 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpAnonOnly"]);
1659 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpCreateAccount"]);
1660 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpBlockAddress"]);
1661 if (wikiFuzz::randnum(4) == 0) unset($this->params["ip"]);
1667 ** a test for the imagepage.
1669 class imagepageTest extends pageTest {
1670 function __construct() {
1671 $this->pagePath = "index.php?title=Image:Small-email.png";
1673 $this->params = array (
1674 "image" => wikiFuzz::chooseInput( array("Small-email.png", wikifuzz::makeFuzz(2)) ),
1675 "wpReason" => wikifuzz::makeFuzz(2),
1676 "oldimage" => wikiFuzz::chooseInput( array("Small-email.png", wikifuzz::makeFuzz(2)) ),
1677 "wpEditToken" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1680 // sometimes we don't want to specify certain parameters.
1681 if (wikiFuzz::randnum(6) == 0) unset($this->params["image"]);
1682 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpReason"]);
1683 if (wikiFuzz::randnum(6) == 0) unset($this->params["oldimage"]);
1684 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpEditToken"]);
1690 ** a test for page deletion form.
1692 class pageDeletion extends pageTest {
1693 function __construct() {
1694 $this->pagePath = "index.php?title=Main_Page&action=delete";
1696 $this->params = array (
1697 "wpEditToken" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1698 "wpReason" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1699 "wpConfirm" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1702 // sometimes we don't want to specify certain parameters.
1703 if (wikiFuzz::randnum(5) == 0) unset($this->params["wpReason"]);
1704 if (wikiFuzz::randnum(5) == 0) unset($this->params["wpEditToken"]);
1705 if (wikiFuzz::randnum(5) == 0) unset($this->params["wpConfirm"]);
1712 ** a test for Revision Deletion.
1714 class specialRevisionDelete extends pageTest {
1715 function __construct() {
1716 $this->pagePath = "index.php?title=Special:Revisiondelete";
1718 $this->params = array (
1719 "target" => wikiFuzz::chooseInput( array("Main Page", wikifuzz::makeFuzz(2)) ),
1720 "oldid" => wikifuzz::makeFuzz(2),
1721 "oldid[]" => wikifuzz::makeFuzz(2),
1722 "wpReason" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1723 "revdelete-hide-text" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1724 "revdelete-hide-comment" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1725 "revdelete-hide-user" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1726 "revdelete-hide-restricted" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1729 // sometimes we don't want to specify certain parameters.
1730 if (wikiFuzz::randnum(3) == 0) unset($this->params["target"]);
1731 if (wikiFuzz::randnum(6) == 0) unset($this->params["oldid"]);
1732 if (wikiFuzz::randnum(6) == 0) unset($this->params["oldid[]"]);
1733 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpReason"]);
1734 if (wikiFuzz::randnum(6) == 0) unset($this->params["revdelete-hide-text"]);
1735 if (wikiFuzz::randnum(6) == 0) unset($this->params["revdelete-hide-comment"]);
1736 if (wikiFuzz::randnum(6) == 0) unset($this->params["revdelete-hide-user"]);
1737 if (wikiFuzz::randnum(6) == 0) unset($this->params["revdelete-hide-restricted"]);
1743 ** a test for Special:Import.
1745 class specialImport extends pageTest {
1746 function __construct() {
1747 $this->pagePath = "index.php?title=Special:Import";
1749 $this->params = array (
1750 "action" => "submit",
1751 "source" => wikiFuzz::chooseInput( array("upload", "interwiki", wikifuzz::makeFuzz(2)) ),
1752 "MAX_FILE_SIZE" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ),
1753 "xmlimport" => wikiFuzz::chooseInput( array("/var/www/hosts/mediawiki/wiki/AdminSettings.php", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1754 "namespace" => wikiFuzz::chooseInput( array(wikiFuzz::randnum(30,-6), wikiFuzz::makeFuzz(2)) ),
1755 "interwiki" => wikiFuzz::makeFuzz(2),
1756 "interwikiHistory" => wikiFuzz::makeFuzz(2),
1757 "frompage" => wikiFuzz::makeFuzz(2),
1760 // sometimes we don't want to specify certain parameters.
1761 if (wikiFuzz::randnum(6) == 0) unset($this->params["action"]);
1762 if (wikiFuzz::randnum(6) == 0) unset($this->params["source"]);
1763 if (wikiFuzz::randnum(6) == 0) unset($this->params["MAX_FILE_SIZE"]);
1764 if (wikiFuzz::randnum(6) == 0) unset($this->params["xmlimport"]);
1765 if (wikiFuzz::randnum(6) == 0) unset($this->params["interwiki"]);
1766 if (wikiFuzz::randnum(6) == 0) unset($this->params["interwikiHistory"]);
1767 if (wikiFuzz::randnum(6) == 0) unset($this->params["frompage"]);
1769 // Note: Need to do a file upload to fully test this Special page.
1775 ** a test for thumb.php
1777 class thumbTest extends pageTest {
1778 function __construct() {
1779 $this->pagePath = "thumb.php";
1781 $this->params = array (
1782 "f" => wikiFuzz::chooseInput( array("..", "\\", "small-email.png", wikifuzz::makeFuzz(2)) ),
1783 "w" => wikiFuzz::chooseInput( array("80", wikiFuzz::randnum(6000,-200), wikifuzz::makeFuzz(2)) ),
1784 "r" => wikiFuzz::chooseInput( array("0", wikifuzz::makeFuzz(2)) ),
1787 // sometimes we don't want to specify certain parameters.
1788 if (wikiFuzz::randnum(6) == 0) unset($this->params["f"]);
1789 if (wikiFuzz::randnum(6) == 0) unset($this->params["w"]);
1790 if (wikiFuzz::randnum(6) == 0) unset($this->params["r"]);
1796 ** a test for trackback.php
1798 class trackbackTest extends pageTest {
1799 function __construct() {
1800 $this->pagePath = "trackback.php";
1802 $this->params = array (
1803 "url" => wikifuzz::makeFuzz(2),
1804 "blog_name" => wikiFuzz::chooseInput( array("80", wikiFuzz::randnum(6000,-200), wikifuzz::makeFuzz(2)) ),
1805 "article" => wikiFuzz::chooseInput( array("Main Page", wikifuzz::makeFuzz(2)) ),
1806 "title" => wikiFuzz::chooseInput( array("Main Page", wikifuzz::makeFuzz(2)) ),
1807 "excerpt" => wikifuzz::makeFuzz(2),
1810 // sometimes we don't want to specify certain parameters.
1811 if (wikiFuzz::randnum(3) == 0) unset($this->params["title"]);
1812 if (wikiFuzz::randnum(3) == 0) unset($this->params["excerpt"]);
1814 // page does not produce HTML.
1815 $this->tidyValidate = false;
1821 ** a test for profileinfo.php
1823 class profileInfo extends pageTest {
1824 function __construct() {
1825 $this->pagePath = "profileinfo.php";
1827 $this->params = array (
1828 "expand" => wikifuzz::makeFuzz(2),
1829 "sort" => wikiFuzz::chooseInput( array("time", "count", "name", wikifuzz::makeFuzz(2)) ),
1830 "filter" => wikiFuzz::chooseInput( array("Main Page", wikifuzz::makeFuzz(2)) ),
1833 // sometimes we don't want to specify certain parameters.
1834 if (wikiFuzz::randnum(3) == 0) unset($this->params["sort"]);
1835 if (wikiFuzz::randnum(3) == 0) unset($this->params["filter"]);
1841 ** a test for Special:Cite (extension Special page).
1843 class specialCite extends pageTest {
1844 function __construct() {
1845 $this->pagePath = "index.php?title=Special:Cite";
1847 $this->params = array (
1848 "page" => wikiFuzz::chooseInput( array("\" onmouseover=\"alert(1);\"", "Main Page", wikifuzz::makeFuzz(2)) ),
1849 "id" => wikiFuzz::chooseInput( array("-1", "0", "------'-------0", "+1", "-9823412312312412435", wikiFuzz::makeFuzz(2)) ),
1852 // sometimes we don't want to specify certain parameters.
1853 if (wikiFuzz::randnum(6) == 0) unset($this->params["page"]);
1854 if (wikiFuzz::randnum(6) == 0) unset($this->params["id"]);
1860 ** a test for Special:Filepath (extension Special page).
1862 class specialFilepath extends pageTest {
1863 function __construct() {
1864 $this->pagePath = "index.php?title=Special:Filepath";
1866 $this->params = array (
1867 "file" => wikiFuzz::chooseInput( array("Small-email.png", "Small-email.png" . wikifuzz::makeFuzz(1), wikiFuzz::makeFuzz(2)) ),
1874 ** a test for Special:Makebot (extension Special page).
1876 class specialMakebot extends pageTest {
1877 function __construct() {
1878 $this->pagePath = "index.php?title=Special:Makebot";
1880 $this->params = array (
1881 "username" => wikiFuzz::chooseInput( array("Nickj2", "192.168.0.2", wikifuzz::makeFuzz(1) ) ),
1882 "dosearch" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ),
1883 "grant" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ),
1884 "comment" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1885 "token" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1888 // sometimes we don't want to specify certain parameters.
1889 if (wikiFuzz::randnum(2) == 0) unset($this->params["dosearch"]);
1890 if (wikiFuzz::randnum(2) == 0) unset($this->params["grant"]);
1891 if (wikiFuzz::randnum(5) == 0) unset($this->params["token"]);
1897 ** a test for Special:Makesysop (extension Special page).
1899 class specialMakesysop extends pageTest {
1900 function __construct() {
1901 $this->pagePath = "index.php?title=Special:Makesysop";
1903 $this->params = array (
1904 "wpMakesysopUser" => wikiFuzz::chooseInput( array("Nickj2", "192.168.0.2", wikifuzz::makeFuzz(1) ) ),
1905 "action" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ),
1906 "wpMakesysopSubmit" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ),
1907 "wpEditToken" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1908 "wpSetBureaucrat" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1911 // sometimes we don't want to specify certain parameters.
1912 if (wikiFuzz::randnum(3) == 0) unset($this->params["wpMakesysopSubmit"]);
1913 if (wikiFuzz::randnum(3) == 0) unset($this->params["wpEditToken"]);
1914 if (wikiFuzz::randnum(3) == 0) unset($this->params["wpSetBureaucrat"]);
1920 ** a test for Special:Renameuser (extension Special page).
1922 class specialRenameuser extends pageTest {
1923 function __construct() {
1924 $this->pagePath = "index.php?title=Special:Renameuser";
1926 $this->params = array (
1927 "oldusername" => wikiFuzz::chooseInput( array("Nickj2", "192.168.0.2", wikifuzz::makeFuzz(1) ) ),
1928 "newusername" => wikiFuzz::chooseInput( array("Nickj2", "192.168.0.2", wikifuzz::makeFuzz(1) ) ),
1929 "token" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1936 ** a test for Special:Linksearch (extension Special page).
1938 class specialLinksearch extends pageTest {
1939 function __construct() {
1940 $this->pagePath = "index.php?title=Special%3ALinksearch";
1942 $this->params = array (
1943 "target" => wikifuzz::makeFuzz(2),
1946 // sometimes we don't want to specify certain parameters.
1947 if (wikiFuzz::randnum(10) == 0) unset($this->params["target"]);
1953 ** a test for Special:CategoryTree (extension Special page).
1955 class specialCategoryTree extends pageTest {
1956 function __construct() {
1957 $this->pagePath = "index.php?title=Special:CategoryTree";
1959 $this->params = array (
1960 "target" => wikifuzz::makeFuzz(2),
1961 "from" => wikifuzz::makeFuzz(2),
1962 "until" => wikifuzz::makeFuzz(2),
1963 "showas" => wikifuzz::makeFuzz(2),
1964 "mode" => wikiFuzz::chooseInput( array("pages", "categories", "all", wikifuzz::makeFuzz(2)) ),
1967 // sometimes we do want to specify certain parameters.
1968 if (wikiFuzz::randnum(5) == 0) $this->params["notree"] = wikiFuzz::chooseInput( array("1", 0, "", wikiFuzz::makeFuzz(2)) );
1974 ** a test for "Special:Chemicalsources" (extension Special page).
1976 class specialChemicalsourcesTest extends pageTest {
1977 function __construct() {
1978 $this->pagePath = "index.php?title=Special:Chemicalsources";
1980 // choose an input format to use.
1981 $format = wikiFuzz::chooseInput(
1982 array( 'go',
1983 'CAS',
1984 'EINECS',
1985 'CHEBI',
1986 'PubChem',
1987 'SMILES',
1988 'InChI',
1989 'ATCCode',
1990 'KEGG',
1991 'RTECS',
1992 'ECNumber',
1993 'DrugBank',
1994 'Formula',
1995 'Name'
1999 // values for different formats usually start with either letters or numbers.
2000 switch ($format) {
2001 case 'Name' : $value = "A"; break;
2002 case 'InChI' :
2003 case 'SMILES' :
2004 case 'Formula': $value = "C"; break;
2005 default : $value = "0"; break;
2008 // and then we append the fuzz input.
2009 $this->params = array ($format => $value . wikifuzz::makeFuzz(2) );
2015 ** A test for api.php (programmatic interface to MediaWiki in XML/JSON/RSS/etc formats).
2016 ** Quite involved to test because there are lots of options/parameters, and because
2017 ** for a lot of the functionality if all the parameters don't make sense then it just
2018 ** returns the help screen - so currently a lot of the tests aren't actually doing much
2019 ** because something wasn't right in the query.
2021 ** @todo: Incomplete / unfinished; Runs too fast (suggests not much testing going on).
2023 class api extends pageTest {
2025 // API login mode.
2026 private static function loginMode() {
2027 $arr = array ( "lgname" => wikifuzz::makeFuzz(2),
2028 "lgpassword" => wikifuzz::makeFuzz(2),
2030 // sometimes we want to specify the extra "lgdomain" parameter.
2031 if (wikiFuzz::randnum(3) == 0) {
2032 $arr["lgdomain"] = wikiFuzz::chooseInput( array("1", 0, "", wikiFuzz::makeFuzz(2)) );
2035 return $arr;
2038 // API OpenSearch mode.
2039 private static function opensearchMode() {
2040 return array ("search" => wikifuzz::makeFuzz(2));
2043 // API watchlist feed mode.
2044 private static function feedwatchlistMode() {
2045 // FIXME: add "wikifuzz::makeFuzz(2)" as possible value below?
2046 return array ("feedformat" => wikiFuzz::chooseInput( array("rss", "atom") ) );
2049 // API query mode.
2050 private static function queryMode() {
2051 // FIXME: add "wikifuzz::makeFuzz(2)" as possible params for the elements below?
2052 // Suspect this will stuff up the tests more, but need to check.
2053 $params = array (
2054 // FIXME: More titles.
2055 "titles" => wikiFuzz::chooseInput( array("Main Page")),
2056 // FIXME: More pageids.
2057 "pageids" => 1,
2058 "prop" => wikiFuzz::chooseInput( array("info", "revisions", "watchlist")),
2059 "list" => wikiFuzz::chooseInput( array("allpages", "logevents", "watchlist", "usercontribs", "recentchanges", "backlinks", "embeddedin", "imagelinks") ),
2060 "meta" => wikiFuzz::chooseInput( array("siteinfo")),
2061 "generator" => wikiFuzz::chooseInput( array("allpages", "logevents", "watchlist", "info", "revisions") ),
2062 "siprop" => wikiFuzz::chooseInput( array("general", "namespaces", "general|namespaces") ),
2065 // Add extra parameters based on what list choice we got.
2066 switch ($params["list"]) {
2067 case "usercontribs" : self::addListParams ($params, "uc", array("limit", "start", "end", "user", "dir") ); break;
2068 case "allpages" : self::addListParams ($params, "ap", array("from", "prefix", "namespace", "filterredir", "limit") ); break;
2069 case "watchlist" : self::addListParams ($params, "wl", array("allrev", "start", "end", "namespace", "dir", "limit", "prop") ); break;
2070 case "logevents" : self::addListParams ($params, "le", array("limit", "type", "start", "end", "user", "dir") ); break;
2071 case "recentchanges": self::addListParams ($params, "rc", array("limit", "prop", "show", "namespace", "start", "end", "dir") ); break;
2072 case "backlinks" : self::addListParams ($params, "bl", array("continue", "namespace", "redirect", "limit") ); break;
2073 case "embeddedin" : self::addListParams ($params, "ei", array("continue", "namespace", "redirect", "limit") ); break;
2074 case "imagelinks" : self::addListParams ($params, "il", array("continue", "namespace", "redirect", "limit") ); break;
2077 if ($params["prop"] == "revisions") {
2078 self::addListParams ($params, "rv", array("prop", "limit", "startid", "endid", "end", "dir") );
2081 // Sometimes we want redirects, sometimes we don't.
2082 if (wikiFuzz::randnum(3) == 0) {
2083 $params["redirects"] = wikiFuzz::chooseInput( array("1", 0, "", wikiFuzz::makeFuzz(2)) );
2086 return $params;
2089 // Adds all the elements to the array, using the specified prefix.
2090 private static function addListParams(&$array, $prefix, $elements) {
2091 foreach ($elements as $element) {
2092 $array[$prefix . $element] = self::getParamDetails($element);
2096 // For a given element name, returns the data for that element.
2097 private static function getParamDetails($element) {
2098 switch ($element) {
2099 case 'startid' :
2100 case 'endid' :
2101 case 'start' :
2102 case 'end' :
2103 case 'limit' : return wikiFuzz::chooseInput( array("0", "-1", "---'----------0", "+1", "8134", "320742734234235", "20060230121212", wikiFuzz::randnum(9000, -100), wikiFuzz::makeFuzz(2)) );
2104 case 'dir' : return wikiFuzz::chooseInput( array("newer", "older", wikifuzz::makeFuzz(2) ) );
2105 case 'user' : return wikiFuzz::chooseInput( array(USER_ON_WIKI, wikifuzz::makeFuzz(2) ) );
2106 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)) );
2107 case 'filterredir': return wikiFuzz::chooseInput( array("all", "redirects", "nonredirectsallpages", wikifuzz::makeFuzz(2)) );
2108 case 'allrev' : return wikiFuzz::chooseInput( array("1", 0, "", wikiFuzz::makeFuzz(2)) );
2109 case 'prop' : return wikiFuzz::chooseInput( array("user", "comment", "timestamp", "patrol", "flags", "user|user|comment|flags", wikifuzz::makeFuzz(2) ) );
2110 case 'type' : return wikiFuzz::chooseInput( array("block", "protect", "rights", "delete", "upload", "move", "import", "renameuser", "newusers", "makebot", wikifuzz::makeFuzz(2) ) );
2111 case 'hide' : return wikiFuzz::chooseInput( array("minor", "bots", "anons", "liu", "liu|bots|", wikifuzz::makeFuzz(2) ) );
2112 case 'show' : return wikiFuzz::chooseInput( array('minor', '!minor', 'bot', '!bot', 'anon', '!anon', wikifuzz::makeFuzz(2) ) );
2113 default : return wikifuzz::makeFuzz(2);
2117 // Entry point.
2118 function __construct() {
2119 $this->pagePath = "api.php";
2121 $modes = array ("help",
2122 "login",
2123 "opensearch",
2124 "feedwatchlist",
2125 "query");
2126 $action = wikiFuzz::chooseInput( array_merge ($modes, array(wikifuzz::makeFuzz(2))) );
2128 switch ($action) {
2129 case "login" : $this->params = self::loginMode();
2130 break;
2131 case "opensearch" : $this->params = self::opensearchMode();
2132 break;
2133 case "feedwatchlist" : $this->params = self::feedwatchlistMode();
2134 break;
2135 case "query" : $this->params = self::queryMode();
2136 break;
2137 case "help" :
2138 default : // Do something random - "Crazy Ivan" mode.
2139 $random_mode = wikiFuzz::chooseInput( $modes ) . "Mode";
2140 // There is no "helpMode".
2141 if ($random_mode == "helpMode") $random_mode = "queryMode";
2142 $this->params = self::$random_mode();
2143 break;
2146 // Save the selected action.
2147 $this->params["action"] = $action;
2149 // Set the cookie:
2150 // FIXME: need to get this cookie dynamically set, rather than hard-coded.
2151 $this->cookie = "wikidbUserID=10001; wikidbUserName=Test; wikidb_session=178df0fe68c75834643af65dec9ec98a; wikidbToken=1adc6753d62c44aec950c024d7ae0540";
2153 // Output format
2154 $this->params["format"] = wikiFuzz::chooseInput( array("json", "jsonfm", "php", "phpfm",
2155 "wddx", "wddxfm", "xml", "xmlfm",
2156 "yaml", "yamlfm", "raw", "rawfm",
2157 wikifuzz::makeFuzz(2) ) );
2159 // Page does not produce HTML (sometimes).
2160 $this->tidyValidate = false;
2166 ** a page test for the GeSHi extension.
2168 class GeSHi_Test extends pageTest {
2170 private function getGeSHiContent() {
2171 return "<source lang=\"" . $this->getLang() . "\" "
2172 . (wikiFuzz::randnum(2) == 0 ? "line " : "")
2173 . (wikiFuzz::randnum(2) == 0 ? "strict " : "")
2174 . "start=" . wikiFuzz::chooseInput( array(wikiFuzz::randnum(-6000,6000), wikifuzz::makeFuzz(2)) )
2175 . ">"
2176 . wikiFuzz::makeFuzz(2)
2177 . "</source>";
2180 private function getLang() {
2181 return wikiFuzz::chooseInput( array( "actionscript", "ada", "apache", "applescript", "asm", "asp", "autoit", "bash", "blitzbasic", "bnf", "c", "c_mac", "caddcl", "cadlisp",
2182 "cfdg", "cfm", "cpp", "cpp-qt", "csharp", "css", "d", "delphi", "diff", "div", "dos", "eiffel", "fortran", "freebasic", "gml", "groovy", "html4strict", "idl",
2183 "ini", "inno", "io", "java", "java5", "javascript", "latex", "lisp", "lua", "matlab", "mirc", "mpasm", "mysql", "nsis", "objc", "ocaml", "ocaml-brief", "oobas",
2184 "oracle8", "pascal", "perl", "php", "php-brief", "plsql", "python", "qbasic", "rails", "reg", "robots", "ruby", "sas", "scheme", "sdlbasic", "smalltalk", "smarty",
2185 "sql", "tcl", "text", "thinbasic", "tsql", "vb", "vbnet", "vhdl", "visualfoxpro", "winbatch", "xml", "xpp", "z80", wikifuzz::makeFuzz(1) ) );
2188 function __construct() {
2189 $this->pagePath = "index.php?title=WIKIFUZZ";
2191 $this->params = array (
2192 "action" => "submit",
2193 "wpMinoredit" => "test",
2194 "wpPreview" => "test",
2195 "wpSection" => "test",
2196 "wpEdittime" => "test",
2197 "wpSummary" => "test",
2198 "wpScrolltop" => "test",
2199 "wpStarttime" => "test",
2200 "wpAutoSummary" => "test",
2201 "wpTextbox1" => $this->getGeSHiContent() // the main wiki text, contains fake GeSHi content.
2208 ** selects a page test to run.
2210 function selectPageTest($count) {
2212 // if the user only wants a specific test, then only ever give them that.
2213 if (defined("SPECIFIC_TEST")) {
2214 $testType = SPECIFIC_TEST;
2215 return new $testType ();
2218 // Some of the time we test Special pages, the remaining
2219 // time we test using the standard edit page.
2220 switch ($count % 100) {
2221 case 0 : return new successfulUserLoginTest();
2222 case 1 : return new listusersTest();
2223 case 2 : return new searchTest();
2224 case 3 : return new recentchangesTest();
2225 case 4 : return new prefixindexTest();
2226 case 5 : return new mimeSearchTest();
2227 case 6 : return new specialLogTest();
2228 case 7 : return new userLoginTest();
2229 case 8 : return new ipblocklistTest();
2230 case 9 : return new newImagesTest();
2231 case 10: return new imagelistTest();
2232 case 11: return new specialExportTest();
2233 case 12: return new specialBooksourcesTest();
2234 case 13: return new specialAllpagesTest();
2235 case 14: return new pageHistoryTest();
2236 case 15: return new contributionsTest();
2237 case 16: return new viewPageTest();
2238 case 17: return new specialAllmessagesTest();
2239 case 18: return new specialNewpages();
2240 case 19: return new searchTest();
2241 case 20: return new redirectTest();
2242 case 21: return new confirmEmail();
2243 case 22: return new watchlistTest();
2244 case 23: return new specialBlockmeTest();
2245 case 24: return new specialUndelete();
2246 case 25: return new specialMovePage();
2247 case 26: return new specialUnlockdb();
2248 case 27: return new specialLockdb();
2249 case 28: return new specialUserrights();
2250 case 29: return new pageProtectionForm();
2251 case 30: return new specialBlockip();
2252 case 31: return new imagepageTest();
2253 case 32: return new pageDeletion();
2254 case 33: return new specialRevisionDelete();
2255 case 34: return new specialImport();
2256 case 35: return new thumbTest();
2257 case 36: return new trackbackTest();
2258 case 37: return new profileInfo();
2259 case 38: return new specialCite();
2260 case 39: return new specialFilepath();
2261 case 40: return new specialMakebot();
2262 case 41: return new specialMakesysop();
2263 case 42: return new specialRenameuser();
2264 case 43: return new specialLinksearch();
2265 case 44: return new specialCategoryTree();
2266 case 45: return new api();
2267 case 45: return new specialChemicalsourcesTest();
2268 default: return new editPageTest();
2273 /////////////////////// SAVING OUTPUT /////////////////////////
2276 ** Utility function for saving a file. Currently has no error checking.
2278 function saveFile($data, $name) {
2279 file_put_contents($name, $data);
2284 ** Returns a test as an experimental GET-to-POST URL.
2285 ** This doesn't seem to always work though, and sometimes the output is too long
2286 ** to be a valid GET URL, so we also save in other formats.
2288 function getAsURL(pageTest $test) {
2289 $used_question_mark = (strpos($test->getPagePath(), "?") !== false);
2290 $retval = "http://get-to-post.nickj.org/?" . WIKI_BASE_URL . $test->getPagePath();
2291 foreach ($test->getParams() as $param => $value) {
2292 if (!$used_question_mark) {
2293 $retval .= "?";
2294 $used_question_mark = true;
2296 else {
2297 $retval .= "&";
2299 $retval .= $param . "=" . urlencode($value);
2301 return $retval;
2306 ** Saves a plain-text human-readable version of a test.
2308 function saveTestAsText(pageTest $test, $filename) {
2309 $str = "Test: " . $test->getPagePath();
2310 foreach ($test->getParams() as $param => $value) {
2311 $str .= "\n$param: $value";
2313 $str .= "\nGet-to-post URL: " . getAsURL($test) . "\n";
2314 saveFile($str, $filename);
2319 ** Saves a test as a standalone basic PHP script that shows this one problem.
2320 ** Resulting script requires PHP-Curl be installed in order to work.
2322 function saveTestAsPHP(pageTest $test, $filename) {
2323 $str = "<?php\n"
2324 . "\$params = " . var_export(escapeForCurl($test->getParams()), true) . ";\n"
2325 . "\$ch = curl_init();\n"
2326 . "curl_setopt(\$ch, CURLOPT_POST, 1);\n"
2327 . "curl_setopt(\$ch, CURLOPT_POSTFIELDS, \$params );\n"
2328 . "curl_setopt(\$ch, CURLOPT_URL, " . var_export(WIKI_BASE_URL . $test->getPagePath(), true) . ");\n"
2329 . "curl_setopt(\$ch, CURLOPT_RETURNTRANSFER,1);\n"
2330 . ($test->getCookie() ? "curl_setopt(\$ch, CURLOPT_COOKIE, " . var_export($test->getCookie(), true) . ");\n" : "")
2331 . "\$result=curl_exec(\$ch);\n"
2332 . "curl_close (\$ch);\n"
2333 . "print \$result;\n"
2334 . "?>\n";
2335 saveFile($str, $filename);
2340 ** Escapes a value so that it can be used on the command line by Curl.
2341 ** Specifically, "<" and "@" need to be escaped if they are the first character,
2342 ** otherwise curl interprets these as meaning that we want to insert a file.
2344 function escapeForCurl(array $input_params) {
2345 $output_params = array();
2346 foreach ($input_params as $param => $value) {
2347 if (strlen($value) > 0 && ( $value[0] == "@" || $value[0] == "<")) {
2348 $value = "\\" . $value;
2350 $output_params[$param] = $value;
2352 return $output_params;
2357 ** Saves a test as a standalone CURL shell script that shows this one problem.
2358 ** Resulting script requires standalone Curl be installed in order to work.
2360 function saveTestAsCurl(pageTest $test, $filename) {
2361 $str = "#!/bin/bash\n"
2362 . "curl --silent --include --globoff \\\n"
2363 . ($test->getCookie() ? " --cookie " . escapeshellarg($test->getCookie()) . " \\\n" : "");
2364 foreach (escapeForCurl($test->getParams()) as $param => $value) {
2365 $str .= " -F " . escapeshellarg($param) . "=" . escapeshellarg($value) . " \\\n";
2367 $str .= " " . escapeshellarg(WIKI_BASE_URL . $test->getPagePath()); // beginning space matters.
2368 $str .= "\n";
2369 saveFile($str, $filename);
2370 chmod($filename, 0755); // make executable
2375 ** Saves the internal data structure to file.
2377 function saveTestData (pageTest $test, $filename) {
2378 saveFile(serialize($test), $filename);
2383 ** saves a test in the various formats.
2385 function saveTest(pageTest $test, $testname) {
2386 $base_name = DIRECTORY . "/" . $testname;
2387 saveTestAsText($test, $base_name . INFO_FILE);
2388 saveTestAsPHP ($test, $base_name . PHP_TEST );
2389 saveTestAsCurl($test, $base_name . CURL_TEST);
2390 saveTestData ($test, $base_name . DATA_FILE);
2394 //////////////////// MEDIAWIKI OUTPUT /////////////////////////
2397 ** Asks MediaWiki for the HTML output of a test.
2399 function wikiTestOutput(pageTest $test) {
2401 $ch = curl_init();
2403 // specify the cookie, if required.
2404 if ($test->getCookie()) curl_setopt($ch, CURLOPT_COOKIE, $test->getCookie());
2405 curl_setopt($ch, CURLOPT_POST, 1); // save form using a POST
2407 $params = escapeForCurl($test->getParams());
2408 curl_setopt($ch, CURLOPT_POSTFIELDS, $params ); // load the POST variables
2410 curl_setopt($ch, CURLOPT_URL, WIKI_BASE_URL . $test->getPagePath() ); // set url to post to
2411 curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); // return into a variable
2413 $result=curl_exec ($ch);
2415 // if we encountered an error, then say so, and return an empty string.
2416 if (curl_error($ch)) {
2417 print "\nCurl error #: " . curl_errno($ch) . " - " . curl_error ($ch);
2418 $result = "";
2421 curl_close ($ch);
2423 return $result;
2427 //////////////////// HTML VALIDATION /////////////////////////
2430 ** Asks the validator whether this is valid HTML, or not.
2432 function validateHTML($text) {
2434 $params = array ("fragment" => $text);
2436 $ch = curl_init();
2438 curl_setopt($ch, CURLOPT_POST, 1); // save form using a POST
2439 curl_setopt($ch, CURLOPT_POSTFIELDS, $params); // load the POST variables
2440 curl_setopt($ch, CURLOPT_URL, VALIDATOR_URL); // set url to post to
2441 curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); // return into a variable
2443 $result=curl_exec ($ch);
2445 // if we encountered an error, then log it, and exit.
2446 if (curl_error($ch)) {
2447 trigger_error("Curl error #: " . curl_errno($ch) . " - " . curl_error ($ch) );
2448 print "Curl error #: " . curl_errno($ch) . " - " . curl_error ($ch) . " - exiting.\n";
2449 exit();
2452 curl_close ($ch);
2454 $valid = (strpos($result, "Failed validation") === false ? true : false);
2456 return array($valid, $result);
2461 ** Get tidy to check for no HTML errors in the output file (e.g. unescaped strings).
2463 function tidyCheckFile($name) {
2464 $file = DIRECTORY . "/" . $name;
2465 $command = PATH_TO_TIDY . " -output /tmp/out.html -quiet $file 2>&1";
2466 $x = `$command`;
2468 // Look for the most interesting Tidy errors and warnings.
2469 if ( strpos($x,"end of file while parsing attributes") !== false
2470 || strpos($x,"attribute with missing trailing quote mark") !== false
2471 || strpos($x,"missing '>' for end of tag") !== false
2472 || strpos($x,"Error:") !== false) {
2473 print "\nTidy found something - view details with: $command";
2474 return false;
2475 } else {
2476 return true;
2482 ** Returns whether or not an database error log file has changed in size since
2483 ** the last time this was run. This is used to tell if a test caused a DB error.
2485 function dbErrorLogged() {
2486 static $filesize;
2488 // first time running this function
2489 if (!isset($filesize)) {
2490 // create log if it does not exist
2491 if (!file_exists(DB_ERROR_LOG_FILE)) {
2492 saveFile("", DB_ERROR_LOG_FILE);
2494 $filesize = filesize(DB_ERROR_LOG_FILE);
2495 return false;
2498 $newsize = filesize(DB_ERROR_LOG_FILE);
2499 // if the log has grown, then assume the current test caused it.
2500 if ($newsize != $filesize) {
2501 $filesize = $newsize;
2502 return true;
2505 return false;
2508 ////////////////// TOP-LEVEL PROBLEM-FINDING FUNCTION ////////////////////////
2511 ** takes a page test, and runs it and tests it for problems in the output.
2512 ** Returns: False on finding a problem, or True on no problems being found.
2514 function runWikiTest(pageTest $test, &$testname, $can_overwrite = false) {
2516 // by default don't overwrite a previous test of the same name.
2517 while ( ! $can_overwrite && file_exists(DIRECTORY . "/" . $testname . DATA_FILE)) {
2518 $testname .= "-" . mt_rand(0,9);
2521 $filename = DIRECTORY . "/" . $testname . DATA_FILE;
2523 // Store the time before and after, to find slow pages.
2524 $before = microtime(true);
2526 // Get MediaWiki to give us the output of this test.
2527 $wiki_preview = wikiTestOutput($test);
2529 $after = microtime(true);
2531 // if we received no response, then that's interesting.
2532 if ($wiki_preview == "") {
2533 print "\nNo response received for: $filename";
2534 return false;
2537 // save output HTML to file.
2538 $html_file = DIRECTORY . "/" . $testname . HTML_FILE;
2539 saveFile($wiki_preview, $html_file);
2541 // if there were PHP errors in the output, then that's interesting too.
2542 if ( strpos($wiki_preview, "<b>Warning</b>: " ) !== false
2543 || strpos($wiki_preview, "<b>Fatal error</b>: " ) !== false
2544 || strpos($wiki_preview, "<b>Notice</b>: " ) !== false
2545 || strpos($wiki_preview, "<b>Error</b>: " ) !== false
2546 || strpos($wiki_preview, "<b>Strict Standards:</b>") !== false
2548 $error = substr($wiki_preview, strpos($wiki_preview, "</b>:") + 7, 50);
2549 // Avoid probable PHP bug with bad session ids; http://bugs.php.net/bug.php?id=38224
2550 if ($error != "Unknown: The session id contains illegal character") {
2551 print "\nPHP error/warning/notice in HTML output: $html_file ; $error";
2552 return false;
2556 // if there was a MediaWiki Backtrace message in the output, then that's also interesting.
2557 if( strpos($wiki_preview, "Backtrace:") !== false ) {
2558 print "\nInternal MediaWiki error in HTML output: $html_file";
2559 return false;
2562 // if there was a Parser error comment in the output, then that's potentially interesting.
2563 if( strpos($wiki_preview, "!-- ERR") !== false ) {
2564 print "\nParser Error comment in HTML output: $html_file";
2565 return false;
2568 // if a database error was logged, then that's definitely interesting.
2569 if( dbErrorLogged() ) {
2570 print "\nDatabase Error logged for: $filename";
2571 return false;
2574 // validate result
2575 $valid = true;
2576 if( VALIDATE_ON_WEB ) {
2577 list ($valid, $validator_output) = validateHTML($wiki_preview);
2578 if (!$valid) print "\nW3C web validation failed - view details with: html2text " . DIRECTORY . "/" . $testname . ".validator_output.html";
2581 // Get tidy to check the page, unless we already know it produces non-XHTML output.
2582 if( $test->tidyValidate() ) {
2583 $valid = tidyCheckFile( $testname . HTML_FILE ) && $valid;
2586 // if it took more than 2 seconds to render, then it may be interesting too. (Possible DoS attack?)
2587 if (($after - $before) >= 2) {
2588 print "\nParticularly slow to render (" . round($after - $before, 2) . " seconds): $filename";
2589 return false;
2592 if( $valid ) {
2593 // Remove temp HTML file if test was valid:
2594 unlink( $html_file );
2595 } elseif( VALIDATE_ON_WEB ) {
2596 saveFile($validator_output, DIRECTORY . "/" . $testname . ".validator_output.html");
2599 return $valid;
2603 /////////////////// RERUNNING OLD TESTS ///////////////////
2606 ** We keep our failed tests so that they can be rerun.
2607 ** This function does that retesting.
2609 function rerunPreviousTests() {
2610 print "Retesting previously found problems.\n";
2612 $dir_contents = scandir (DIRECTORY);
2614 // sort file into the order a normal person would use.
2615 natsort ($dir_contents);
2617 foreach ($dir_contents as $file) {
2619 // if file is not a test, then skip it.
2620 // Note we need to escape any periods or will be treated as "any character".
2621 $matches = array();
2622 if (!ereg("(.*)" . str_replace(".", "\.", DATA_FILE) . "$", $file, $matches)) continue;
2624 // reload the test.
2625 $full_path = DIRECTORY . "/" . $file;
2626 $test = unserialize(file_get_contents($full_path));
2628 // if this is not a valid test, then skip it.
2629 if (! $test instanceof pageTest) {
2630 print "\nSkipping invalid test - $full_path";
2631 continue;
2634 // The date format is in Apache log format, which makes it easier to locate
2635 // which retest caused which error in the Apache logs (only happens usually if
2636 // apache segfaults).
2637 if (!QUIET) print "[" . date ("D M d H:i:s Y") . "] Retesting $file (" . get_class($test) . ")";
2639 // run test
2640 $testname = $matches[1];
2641 $valid = runWikiTest($test, $testname, true);
2643 if (!$valid) {
2644 saveTest($test, $testname);
2645 if (QUIET) {
2646 print "\nTest: " . get_class($test) . " ; Testname: $testname\n------";
2647 } else {
2648 print "\n";
2651 else {
2652 if (!QUIET) print "\r";
2653 if (DELETE_PASSED_RETESTS) {
2654 $prefix = DIRECTORY . "/" . $testname;
2655 if (is_file($prefix . DATA_FILE)) unlink($prefix . DATA_FILE);
2656 if (is_file($prefix . PHP_TEST )) unlink($prefix . PHP_TEST );
2657 if (is_file($prefix . CURL_TEST)) unlink($prefix . CURL_TEST);
2658 if (is_file($prefix . INFO_FILE)) unlink($prefix . INFO_FILE);
2663 print "\nDone retesting.\n";
2667 ////////////////////// MAIN LOOP ////////////////////////
2670 // first check whether CURL is installed, because sometimes it's not.
2671 if( ! function_exists('curl_init') ) {
2672 die("Could not find 'curl_init' function. Is the curl extension compiled into PHP?\n");
2675 // Initialization of types. wikiFuzz doesn't have a constructor because we want to
2676 // access it staticly and not have any globals.
2677 wikiFuzz::$types = array_keys(wikiFuzz::$data);
2679 // Make directory if doesn't exist
2680 if (!is_dir(DIRECTORY)) {
2681 mkdir (DIRECTORY, 0700 );
2683 // otherwise, we first retest the things that we have found in previous runs
2684 else if (RERUN_OLD_TESTS) {
2685 rerunPreviousTests();
2688 // main loop.
2689 $start_time = date("U");
2690 $num_errors = 0;
2691 if (!QUIET) {
2692 print "Beginning main loop. Results are stored in the " . DIRECTORY . " directory.\n";
2693 print "Press CTRL+C to stop testing.\n";
2696 for ($count=0; true; $count++) {
2697 if (!QUIET) {
2698 // spinning progress indicator.
2699 switch( $count % 4 ) {
2700 case '0': print "\r/"; break;
2701 case '1': print "\r-"; break;
2702 case '2': print "\r\\"; break;
2703 case '3': print "\r|"; break;
2705 print " $count";
2708 // generate a page test to run.
2709 $test = selectPageTest($count);
2711 $mins = ( date("U") - $start_time ) / 60;
2712 if (!QUIET && $mins > 0) {
2713 print ". $num_errors poss errors. "
2714 . floor($mins) . " mins. "
2715 . round ($count / $mins, 0) . " tests/min. "
2716 . get_class($test); // includes the current test name.
2719 // run this test against MediaWiki, and see if the output was valid.
2720 $testname = $count;
2721 $valid = runWikiTest($test, $testname, false);
2723 // save the failed test
2724 if ( ! $valid ) {
2725 if (QUIET) {
2726 print "\nTest: " . get_class($test) . " ; Testname: $testname\n------";
2727 } else {
2728 print "\n";
2730 saveTest($test, $testname);
2731 $num_errors += 1;
2732 } else if ( KEEP_PASSED_TESTS ) {
2733 // print current time, with microseconds (matches "strace" format), and the test name.
2734 print " " . date("H:i:s.") . substr(current(explode(" ", microtime())), 2) . " " . $testname;
2735 saveTest($test, $testname);
2738 // stop if we have reached max number of errors.
2739 if (defined("MAX_ERRORS") && $num_errors>=MAX_ERRORS) {
2740 break;
2743 // stop if we have reached max number of mins runtime.
2744 if (defined("MAX_RUNTIME") && $mins>=MAX_RUNTIME) {
2745 break;