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