Added description for two new tables
[mediawiki.git] / maintenance / fuzz-tester.php
blob23c3cd7c7aec0aa328b93a9e8f4882ef5dbcad61
1 <?php
2 /**
3 * @package MediaWiki
4 * @subpackage Maintainance
5 * @author Nick Jenkins ( http://nickj.org/ ).
6 * @copyright 2006 Nick Jenkins
7 * @licence GNU General Public Licence 2.0
9 Started: 18 May 2006.
11 Description:
12 Performs fuzz-style testing of MediaWiki's parser and forms.
14 How:
15 - Generate lots of nasty wiki text.
16 - Ask the Parser to render that wiki text to HTML, or ask MediaWiki's forms
17 to deal with that wiki text.
18 - Check MediaWiki's output for problems.
19 - Repeat.
21 Why:
22 - To help find bugs.
23 - To help find security issues, or potential security issues.
25 What type of problems are being checked for:
26 - Unclosed tags.
27 - Errors or interesting warnings from Tidy.
28 - PHP errors / warnings / notices.
29 - MediaWiki internal errors.
30 - Very slow responses.
31 - No response from apache.
32 - Optionally checking for malformed HTML using the W3C validator.
34 Background:
35 Many of the wikiFuzz class methods are a modified PHP port,
36 of a "shameless" Python port, of LCAMTUF'S MANGELME:
37 - http://www.securiteam.com/tools/6Z00N1PBFK.html
38 - http://www.securityfocus.com/archive/1/378632/2004-10-15/2004-10-21/0
40 Video:
41 There's an XviD video discussing this fuzz tester. You can get it from:
42 http://files.nickj.org/MediaWiki/Fuzz-Testing-MediaWiki-xvid.avi
44 Requirements:
45 To run this, you will need:
46 - Command-line PHP5, with PHP-curl enabled (not all installations have this
47 enabled - try "apt-get install php5-curl" if you're on Debian to install).
48 - the Tidy standalone executable. ("apt-get install tidy").
50 Optional:
51 - If you want to run the curl scripts, you'll need standalone curl installed
52 ("apt-get install curl")
53 - For viewing the W3C validator output on a command line, the "html2text"
54 program may be useful ("apt-get install html2text")
56 Saving tests and test results:
57 Any of the fuzz tests which find problems are saved for later review.
58 In order to help track down problems, tests are saved in a number of
59 different formats. The default filename extensions and their meanings are:
60 - ".test.php" : PHP script that reproduces just that one problem using PHP-Curl.
61 - ".curl.sh" : Shell script that reproduces that problem using standalone curl.
62 - ".data.bin" : The serialized PHP data so that this script can re-run the test.
63 - ".info.txt" : A human-readable text file with details of the field contents.
65 Wiki configuration for testing:
66 You should make some additions to LocalSettings.php in order to catch the most
67 errors. Note this configuration is for **TESTING PURPOSES ONLY**, and is IN NO
68 WAY, SHAPE, OR FORM suitable for deployment on a hostile network. That said,
69 personally I find these additions to be the most helpful for testing purposes:
71 // --------- Start ---------
72 // Everyone can do everything. Very useful for testing, yet useless for deployment.
73 $wgGroupPermissions['*']['autoconfirmed'] = true;
74 $wgGroupPermissions['*']['block'] = true;
75 $wgGroupPermissions['*']['bot'] = true;
76 $wgGroupPermissions['*']['delete'] = true;
77 $wgGroupPermissions['*']['deletedhistory'] = true;
78 $wgGroupPermissions['*']['deleterevision'] = true;
79 $wgGroupPermissions['*']['editinterface'] = true;
80 $wgGroupPermissions['*']['hiderevision'] = true;
81 $wgGroupPermissions['*']['import'] = true;
82 $wgGroupPermissions['*']['importupload'] = true;
83 $wgGroupPermissions['*']['minoredit'] = true;
84 $wgGroupPermissions['*']['move'] = true;
85 $wgGroupPermissions['*']['patrol'] = true;
86 $wgGroupPermissions['*']['protect'] = true;
87 $wgGroupPermissions['*']['proxyunbannable'] = true;
88 $wgGroupPermissions['*']['renameuser'] = true;
89 $wgGroupPermissions['*']['reupload'] = true;
90 $wgGroupPermissions['*']['reupload-shared'] = true;
91 $wgGroupPermissions['*']['rollback'] = true;
92 $wgGroupPermissions['*']['siteadmin'] = true;
93 $wgGroupPermissions['*']['trackback'] = true;
94 $wgGroupPermissions['*']['unwatchedpages'] = true;
95 $wgGroupPermissions['*']['upload'] = true;
96 $wgGroupPermissions['*']['userrights'] = true;
97 $wgGroupPermissions['*']['renameuser'] = true;
98 $wgGroupPermissions['*']['makebot'] = true;
99 $wgGroupPermissions['*']['makesysop'] = true;
101 // Enable weird and wonderful options:
102 // Increase default error reporting level.
103 error_reporting (E_ALL); // At a later date could be increased to E_ALL | E_STRICT
104 $wgBlockOpenProxies = true; // Some block pages require this to be true in order to test.
105 $wgEnableUploads = true; // enable uploads.
106 //$wgUseTrackbacks = true; // enable trackbacks; However this breaks the viewPageTest, so currently disabled.
107 $wgDBerrorLog = "/root/mediawiki-db-error-log.txt"; // log DB errors, replace with suitable path.
108 $wgShowSQLErrors = true; // Show SQL errors (instead of saying the query was hidden).
110 // Install & enable Parser Hook extensions to increase code coverage. E.g.:
111 require_once("extensions/ParserFunctions/ParserFunctions.php");
112 require_once("extensions/Cite/Cite.php");
113 require_once("extensions/inputbox/inputbox.php");
114 require_once("extensions/Sort/Sort.php");
115 require_once("extensions/wikihiero/wikihiero.php");
116 require_once("extensions/CharInsert/CharInsert.php");
117 require_once("extensions/FixedImage/FixedImage.php");
119 // Install & enable Special Page extensions to increase code coverage. E.g.:
120 require_once("extensions/Cite/SpecialCite.php");
121 require_once("extensions/Filepath/SpecialFilepath.php");
122 require_once("extensions/Makebot/Makebot.php");
123 require_once("extensions/Makesysop/SpecialMakesysop.php");
124 require_once("extensions/Renameuser/SpecialRenameuser.php");
125 require_once("extensions/LinkSearch/LinkSearch.php");
126 // --------- End ---------
128 Also add/change this in AdminSettings.php:
129 // --------- Start ---------
130 $wgEnableProfileInfo = true;
131 $wgDBserver = "localhost"; // replace with DB server hostname
132 // --------- End ---------
134 Usage:
135 Run with "php fuzz-tester.php".
136 To see the various command-line options, run "php fuzz-tester.php --help".
137 To stop the script, press Ctrl-C.
139 Console output:
140 - If requested, first any previously failed tests will be rerun.
141 - Then new tests will be generated and run. Any tests that fail will be saved,
142 and a brief message about why they failed will be printed on the console.
143 - The console will show the number of tests run, time run, number of tests
144 failed, number of tests being done per minute, and the name of the current test.
146 TODO:
147 Some known things that could improve this script:
148 - Logging in with cookie jar storage needed for some tests (as there are some
149 pages that cannot be tested without being logged in, and which are currently
150 untested - e.g. Special:Emailuser, Special:Preferences, adding to Watchist).
151 - Testing of Timeline extension (I cannot test as ploticus has/had issues on
152 my architecture).
156 /////////////////////////// COMMAND LINE HELP ////////////////////////////////////
158 // This is a command line script, load MediaWiki env (gives command line options);
159 include('commandLine.inc');
161 // if the user asked for an explanation of command line options.
162 if ( isset( $options["help"] ) ) {
163 print <<<ENDS
164 MediaWiki $wgVersion fuzz tester
165 Usage: php {$_SERVER["SCRIPT_NAME"]} [--quiet] [--base-url=<url-to-test-wiki>]
166 [--directory=<failed-test-path>] [--include-binary]
167 [--w3c-validate] [--delete-passed-retests] [--help]
168 [--user=<username>] [--password=<password>]
169 [--rerun-failed-tests] [--max-errors=<int>]
170 [--max-runtime=<num-minutes>]
171 [--specific-test=<test-name>]
173 Options:
174 --quiet : Hides passed tests, shows only failed tests.
175 --base-url : URL to a wiki on which to run the tests.
176 The "http://" is optional and can be omitted.
177 --directory : Full path to directory for storing failed tests.
178 Will be created if it does not exist.
179 --include-binary : Includes non-alphanumeric characters in the tests.
180 --w3c-validate : Validates pages using the W3C's web validator.
181 Slow. Currently many pages fail validation.
182 --user : Login name of a valid user on your test wiki.
183 --password : Password for the valid user on your test wiki.
184 --delete-passed-retests : Will delete retests that now pass.
185 Requires --rerun-failed-tests to be meaningful.
186 --rerun-failed-tests : Whether to rerun any previously failed tests.
187 --max-errors : Maximum number of errors to report before exiting.
188 Does not include errors from --rerun-failed-tests
189 --max-runtime : Maximum runtime, in minutes, to run before exiting.
190 Only applies to new tests, not --rerun-failed-tests
191 --specific-test : Runs only the specified fuzz test.
192 Only applies to new tests, not --rerun-failed-tests
193 --help : Show this help message.
195 Example:
196 If you wanted to fuzz test a nightly MediaWiki checkout using cron for 1 hour,
197 and only wanted to be informed of errors, and did not want to redo previously
198 failed tests, and wanted a maximum of 100 errors, then you could do:
199 php {$_SERVER["SCRIPT_NAME"]} --quiet --max-errors=100 --max-runtime=60
202 ENDS;
204 exit( 0 );
208 // if we got command line options, check they look valid.
209 $validOptions = array ("quiet", "base-url", "directory", "include-binary",
210 "w3c-validate", "user", "password", "delete-passed-retests",
211 "rerun-failed-tests", "max-errors",
212 "max-runtime", "specific-test", "help" );
213 if (!empty($options)) {
214 $unknownArgs = array_diff (array_keys($options), $validOptions);
215 foreach ($unknownArgs as $invalidArg) {
216 print "Ignoring invalid command-line option: --$invalidArg\n";
221 ///////////////////////////// CONFIGURATION ////////////////////////////////////
223 // URL to some wiki on which we can run our tests.
224 if (!empty($options["base-url"])) {
225 define("WIKI_BASE_URL", $options["base-url"]);
226 } else {
227 define("WIKI_BASE_URL", $wgServer . $wgScriptPath . '/');
230 // The directory name where we store the output.
231 // Example for Windows: "c:\\temp\\wiki-fuzz"
232 if (!empty($options["directory"])) {
233 define("DIRECTORY", $options["directory"] );
234 } else {
235 define("DIRECTORY", "{$wgUploadDirectory}/fuzz-tests");
238 // Should our test fuzz data include binary strings?
239 define("INCLUDE_BINARY", isset($options["include-binary"]) );
241 // Whether we want to validate HTML output on the web.
242 // At the moment very few generated pages will validate, so not recommended.
243 define("VALIDATE_ON_WEB", isset($options["w3c-validate"]) );
244 // URL to use to validate our output:
245 define("VALIDATOR_URL", "http://validator.w3.org/check");
247 // Location of Tidy standalone executable.
248 define("PATH_TO_TIDY", "/usr/bin/tidy");
250 // The name of a user who has edited on your wiki. Used
251 // when testing the Special:Contributions and Special:Userlogin page.
252 if (!empty($options["user"])) {
253 define("USER_ON_WIKI", $options["user"] );
254 } else {
255 define("USER_ON_WIKI", "nickj");
258 // The password of the above user. Used when testing the login page,
259 // and to do this we sometimes need to login successfully.
260 if (!empty($options["password"])) {
261 define("USER_PASSWORD", $options["password"] );
262 } else {
263 // And no, this is not a valid password on any public wiki.
264 define("USER_PASSWORD", "nickj");
267 // If we have a test that failed, and then we run it again, and it passes,
268 // do you want to delete it or keep it?
269 define("DELETE_PASSED_RETESTS", isset($options["delete-passed-retests"]) );
271 // Do we want to rerun old saved tests at script startup?
272 // Set to true to help catch regressions, or false if you only want new stuff.
273 define("RERUN_OLD_TESTS", isset($options["rerun-failed-tests"]) );
275 // File where the database errors are logged. Should be defined in LocalSettings.php.
276 define("DB_ERROR_LOG_FILE", $wgDBerrorLog );
278 // Run in chatty mode (all output, default), or run in quiet mode (only prints out details of failed tests)?
279 define("QUIET", isset($options["quiet"]) );
281 // The maximum runtime, if specified.
282 if (!empty($options["max-runtime"]) && intval($options["max-runtime"])>0) {
283 define("MAX_RUNTIME", intval($options["max-runtime"]) );
286 // The maximum number of problems to find, if specified. Excludes retest errors.
287 if (!empty($options["max-errors"]) && intval($options["max-errors"])>0) {
288 define("MAX_ERRORS", intval($options["max-errors"]) );
291 // if the user has requested a specific test (instead of all tests), and the test they asked for looks valid.
292 if (!empty($options["specific-test"])) {
293 if (class_exists($options["specific-test"]) && get_parent_class($options["specific-test"])=="pageTest") {
294 define("SPECIFIC_TEST", $options["specific-test"] );
296 else {
297 print "Ignoring invalid --specific-test\n";
301 // Define the file extensions we'll use:
302 define("PHP_TEST" , ".test.php");
303 define("CURL_TEST", ".curl.sh" );
304 define("DATA_FILE", ".data.bin");
305 define("INFO_FILE", ".info.txt");
306 define("HTML_FILE", ".wiki_preview.html");
308 // If it goes wrong, we want to know about it.
309 error_reporting(E_ALL | E_STRICT);
311 //////////////// A CLASS THAT GENERATES RANDOM NASTY WIKI & HTML STRINGS //////////////////////
313 class wikiFuzz {
315 // Only some HTML tags are understood with params by MediaWiki, the rest are ignored.
316 // List the tags that accept params below, as well as what those params are.
317 public static $data = array(
318 "B" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
319 "CAPTION" => array("CLASS", "ID", "STYLE", "align", "lang", "dir", "title"),
320 "CENTER" => array("CLASS", "STYLE", "ID", "lang", "dir", "title"),
321 "DIV" => array("CLASS", "STYLE", "ID", "align", "lang", "dir", "title"),
322 "FONT" => array("CLASS", "STYLE", "ID", "lang", "dir", "title", "face", "size", "color"),
323 "H1" => array("STYLE", "CLASS", "ID", "align", "lang", "dir", "title"),
324 "H2" => array("STYLE", "CLASS", "ID", "align", "lang", "dir", "title"),
325 "HR" => array("STYLE", "CLASS", "ID", "WIDTH", "lang", "dir", "title", "size", "noshade"),
326 "LI" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "type", "value"),
327 "TABLE" => array("STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "BORDER", "CELLPADDING",
328 "CELLSPACING", "lang", "dir", "title", "summary", "frame", "rules"),
329 "TD" => array("STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "COLSPAN", "ROWSPAN",
330 "VALIGN", "abbr", "axis", "headers", "scope", "nowrap", "height", "lang",
331 "dir", "title", "char", "charoff"),
332 "TH" => array("STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "COLSPAN", "ROWSPAN",
333 "VALIGN", "abbr", "axis", "headers", "scope", "nowrap", "height", "lang",
334 "dir", "title", "char", "charoff"),
335 "TR" => array("CLASS", "STYLE", "ID", "BGCOLOR", "ALIGN", "VALIGN", "lang", "dir", "title", "char", "charoff"),
336 "UL" => array("CLASS", "STYLE", "ID", "lang", "dir", "title", "type"),
337 "P" => array("style", "class", "id", "align", "lang", "dir", "title"),
338 "blockquote" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "cite"),
339 "span" => array("CLASS", "ID", "STYLE", "align", "lang", "dir", "title"),
340 "code" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
341 "tt" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
342 "small" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
343 "big" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
344 "s" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
345 "u" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
346 "del" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "datetime", "cite"),
347 "ins" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "datetime", "cite"),
348 "sub" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
349 "sup" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
350 "ol" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "type", "start"),
351 "br" => array("CLASS", "ID", "STYLE", "title", "clear"),
352 "cite" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
353 "var" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
354 "dl" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
355 "ruby" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
356 "rt" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
357 "rp" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
358 "dt" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
359 "dl" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
360 "em" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
361 "strong" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
362 "i" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"),
363 "thead" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign'),
364 "tfoot" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign'),
365 "tbody" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign'),
366 "colgroup" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign', 'span', 'width'),
367 "col" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign', 'span', 'width'),
368 "pre" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "width"),
370 // extension tags that accept parameters:
371 "sort" => array("order", "class"),
372 "ref" => array("name"),
373 "categorytree" => array("hideroot", "mode", "style"),
376 // The types of the HTML that we will be testing were defined above
377 // Note: this needs to be initialized later to be equal to: array_keys(wikiFuzz::$data);
378 // as such, it also needs to also be publicly modifiable.
379 public static $types;
382 // Some attribute values.
383 static private $other = array("&","=",":","?","\"","\n","%n%n%n%n%n%n%n%n%n%n%n%n","\\");
384 static private $ints = array(
385 // various numbers
386 "0","-1","127","-7897","89000","808080","90928345",
387 "0xfffffff","ffff",
389 // Different ways of saying: '
390 "&#0000039;", // Long UTF-8 Unicode encoding
391 "&#39;", // dec version.
392 "&#x27;", // hex version.
393 "&#xA7;", // malformed hex variant, MSB not zero.
395 // Different ways of saying: "
396 "&#0000034;", // Long UTF-8 Unicode encoding
397 "&#34;",
398 "&#x22;", // hex version.
399 "&#xA2;", // malformed hex variant, MSB not zero.
401 // Different ways of saying: <
402 "<",
403 "&#0000060", // Long UTF-8 Unicode encoding without semicolon (Mediawiki wants the colon)
404 "&#0000060;", // Long UTF-8 Unicode encoding with semicolon
405 "&#60;",
406 "&#x3C;", // hex version.
407 "&#xBC;", // malformed hex variant, MSB not zero.
408 "&#x0003C;", // mid-length hex version
409 "&#X00003C;", // slightly longer hex version, with capital "X"
411 // Different ways of saying: >
412 ">",
413 "&#0000062;", // Long UTF-8 Unicode encoding
414 "&#62;",
415 "&#x3E;", // hex version.
416 "&#xBE;", // malformed variant, MSB not zero.
418 // Different ways of saying: [
419 "&#0000091;", // Long UTF-8 Unicode encoding
420 "&#91;",
421 "&#x5B;", // hex version.
423 // Different ways of saying: {{
424 "&#0000123;&#0000123;", // Long UTF-8 Unicode encoding
425 "&#123;&#123;",
426 "&#x7B;&#x7B;", // hex version.
428 // Different ways of saying: |
429 "&#0000124;", // Long UTF-8 Unicode encoding
430 "&#124;",
431 "&#x7C;", // hex version.
432 "&#xFC;", // malformed hex variant, MSB not zero.
434 // a "lignature" - http://www.robinlionheart.com/stds/html4/spchars#ligature
435 "&zwnj;"
438 // Defines various wiki-related bits of syntax, that can potentially cause
439 // MediaWiki to do something other than just print that literal text.
440 static private $ext = array(
441 // links, templates, parameters.
442 "[[", "]]", "{{", "}}", "|", "[", "]", "{{{", "}}}", "|]]",
444 // wiki tables.
445 "\n{|", "\n|}",
446 "!",
447 "\n!",
448 "!!",
449 "||",
450 "\n|-", "| ", "\n|",
452 // section headings.
453 "=", "==", "===", "====", "=====", "======",
455 // lists (ordered and unordered) and indentation.
456 "\n*", "*", "\n:", ":",
457 "\n#", "#",
459 // definition lists (dl, dt, dd), newline, and newline with pre, and a tab.
460 "\n;", ";", "\n ",
462 // Whitespace: newline, tab, space.
463 "\n", "\t", " ",
465 // Some XSS attack vectors from http://ha.ckers.org/xss.html
466 "&#x09;", // tab
467 "&#x0A;", // newline
468 "&#x0D;", // carriage return
469 "\0", // null character
470 " &#14; ", // spaces and meta characters
471 "'';!--\"<XSS>=&{()}", // compact injection of XSS & SQL tester
473 // various NULL fields
474 "%00",
475 "&#00;",
476 "\0",
478 // horizontal rule.
479 "-----", "\n-----",
481 // signature, redirect, bold, italics.
482 "~~~~", "#REDIRECT [[", "'''", "''",
484 // comments.
485 "<!--", "-->",
487 // quotes.
488 "\"", "'",
490 // tag start and tag end.
491 "<", ">",
493 // implicit link creation on URIs.
494 "http://",
495 "https://",
496 "ftp://",
497 "irc://",
498 "news:",
499 'gopher://',
500 'telnet://',
501 'nntp://',
502 'worldwind://',
503 'mailto:',
505 // images.
506 "[[image:",
507 ".gif",
508 ".png",
509 ".jpg",
510 ".jpeg",
511 'thumbnail=',
512 'thumbnail',
513 'thumb=',
514 'thumb',
515 'right',
516 'none',
517 'left',
518 'framed',
519 'frame',
520 'enframed',
521 'centre',
522 'center',
523 "Image:",
524 "[[:Image",
525 'px',
527 // misc stuff to throw at the Parser.
528 '%08X',
529 '/',
530 ":x{|",
531 "\n|+",
532 "<noinclude>",
533 "</noinclude>",
534 " \302\273",
535 " :",
536 " !",
537 " ;",
538 "\302\253",
539 "[[category:",
540 "?=",
541 "(",
542 ")",
543 "]]]",
544 "../",
545 "{{{{",
546 "}}}}",
547 "[[Special:",
548 "<includeonly>",
549 "</includeonly>",
550 "<!--MWTEMPLATESECTION=",
551 '<!--MWTOC-->',
553 // implicit link creation on booknum, RFC, and PubMed ID usage (both with and without IDs)
554 "ISBN 2",
555 "RFC 000",
556 "PMID 000",
557 "ISBN ",
558 "RFC ",
559 "PMID ",
561 // magic words:
562 '__NOTOC__',
563 '__FORCETOC__',
564 '__NOEDITSECTION__',
565 '__START__',
566 '__NOTITLECONVERT__',
567 '__NOCONTENTCONVERT__',
568 '__END__',
569 '__TOC__',
570 '__NOTC__',
571 '__NOCC__',
572 "__FORCETOC__",
573 "__NEWSECTIONLINK__",
574 "__NOGALLERY__",
576 // more magic words / internal templates.
577 '{{PAGENAME}}',
578 '{{PAGENAMEE}}',
579 '{{NAMESPACE}}',
580 "{{MSG:",
581 "}}",
582 "{{MSGNW:",
583 "}}",
584 "{{INT:",
585 "}}",
586 '{{SITENAME}}',
587 "{{NS:",
588 "}}",
589 "{{LOCALURL:",
590 "}}",
591 "{{LOCALURLE:",
592 "}}",
593 "{{SCRIPTPATH}}",
594 "{{GRAMMAR:gentiv|",
595 "}}",
596 "{{REVISIONID}}",
597 "{{SUBPAGENAME}}",
598 "{{SUBPAGENAMEE}}",
599 "{{ns:0}}",
600 "{{fullurle:",
601 "}}",
602 "{{subst:",
603 "}}",
604 "{{UCFIRST:",
605 "}}",
606 "{{UC:",
607 '{{SERVERNAME}}',
608 '{{SERVER}}',
609 "{{RAW:",
610 "}}",
611 "{{PLURAL:",
612 "}}",
613 "{{LCFIRST:",
614 "}}",
615 "{{LC:",
616 "}}",
617 '{{CURRENTWEEK}}',
618 '{{CURRENTDOW}}',
619 "{{INT:{{LC:contribs-showhideminor}}|",
620 "}}",
621 "{{INT:googlesearch|",
622 "}}",
623 "{{BASEPAGENAME}}",
624 "{{CONTENTLANGUAGE}}",
625 "{{PAGESINNAMESPACE:}}",
626 "{{#language:",
627 "}}",
629 // Some raw link for magic words.
630 "{{NUMBEROFPAGES:R",
631 "}}",
632 "{{NUMBEROFUSERS:R",
633 "}}",
634 "{{NUMBEROFARTICLES:R",
635 "}}",
636 "{{NUMBEROFFILES:R",
637 "}}",
638 "{{NUMBEROFADMINS:R",
639 "}}",
640 "{{padleft:",
641 "}}",
642 "{{padright:",
643 "}}",
645 // internal Math "extension":
646 "<math>",
647 "</math>",
649 // Parser extension functions:
650 "{{#expr:",
651 "{{#if:",
652 "{{#ifeq:",
653 "{{#ifexist:",
654 "{{#ifexpr:",
655 "{{#switch:",
656 "{{#time:",
657 "}}",
659 // references table for the Cite extension.
660 "<references/>",
662 // Internal Parser tokens - try inserting some of these.
663 "UNIQ25f46b0524f13e67NOPARSE",
664 "UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002",
665 "\x07UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002-QINU",
667 // Inputbox extension:
668 "<inputbox>\ntype=search\nsearchbuttonlabel=\n",
669 "</inputbox>",
671 // charInsert extension:
672 "<charInsert>",
673 "</charInsert>",
675 // wikiHiero extension:
676 "<hiero>",
677 "</hiero>",
679 // Image gallery:
680 "<gallery>",
681 "</gallery>",
683 // FixedImage:
684 "<fundraising/>",
686 // Timeline extension: currently untested.
688 // Nowiki:
689 "<nOwIkI>",
690 "</nowiki>",
692 // an external image to test the external image displaying code
693 "http://debian.org/Pics/debian.png",
697 ** @desc: Randomly returns one element of the input array.
699 static public function chooseInput(array $input) {
700 $randindex = wikiFuzz::randnum(count($input) - 1);
701 return $input[$randindex];
704 // Max number of parameters for HTML attributes.
705 static private $maxparams = 10;
707 /**
708 ** @desc: Returns random number between finish and start.
710 static public function randnum($finish,$start=0) {
711 return mt_rand($start,$finish);
715 ** @desc: Returns a mix of random text and random wiki syntax.
717 static private function randstring() {
718 $thestring = "";
720 for ($i=0; $i<40; $i++) {
721 $what = wikiFuzz::randnum(1);
723 if ($what == 0) { // include some random wiki syntax
724 $which = wikiFuzz::randnum(count(wikiFuzz::$ext) - 1);
725 $thestring .= wikiFuzz::$ext[$which];
727 else { // include some random text
728 $char = INCLUDE_BINARY
729 // Decimal version:
730 // "&#" . wikiFuzz::randnum(255) . ";"
731 // Hex version:
732 ? "&#x" . str_pad(dechex(wikiFuzz::randnum(255)), wikiFuzz::randnum(2, 7), "0", STR_PAD_LEFT) . ";"
733 : chr(wikiFuzz::randnum(126,32));
735 $length = wikiFuzz::randnum(8);
736 $thestring .= str_repeat ($char, $length);
739 return $thestring;
743 ** @desc: Returns either random text, or random wiki syntax, or random data from "ints",
744 ** or random data from "other".
746 static private function makestring() {
747 $what = wikiFuzz::randnum(2);
748 if ($what == 0) {
749 return wikiFuzz::randstring();
751 elseif ($what == 1) {
752 return wikiFuzz::$ints[wikiFuzz::randnum(count(wikiFuzz::$ints) - 1)];
754 else {
755 return wikiFuzz::$other[wikiFuzz::randnum(count(wikiFuzz::$other) - 1)];
761 ** @desc: Strips out the stuff that Mediawiki balks at in a page's title.
762 ** Implementation copied/pasted from cleanupTable.inc & cleanupImages.php
764 static public function makeTitleSafe($str) {
765 $legalTitleChars = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF";
766 return preg_replace_callback(
767 "/([^$legalTitleChars])/",
768 create_function(
769 // single quotes are essential here,
770 // or alternative escape all $ as \$
771 '$matches',
772 'return sprintf( "\\x%02x", ord( $matches[1] ) );'
774 $str );
778 ** @desc: Returns a string of fuzz text.
780 static private function loop() {
781 switch ( wikiFuzz::randnum(3) ) {
782 case 1: // an opening tag, with parameters.
783 $string = "";
784 $i = wikiFuzz::randnum(count(wikiFuzz::$types) - 1);
785 $t = wikiFuzz::$types[$i];
786 $arr = wikiFuzz::$data[$t];
787 $string .= "<" . $t . " ";
788 $num_params = min(wikiFuzz::$maxparams, count($arr));
789 for ($z=0; $z<$num_params; $z++) {
790 $badparam = $arr[wikiFuzz::randnum(count($arr) - 1)];
791 $badstring = wikiFuzz::makestring();
792 $string .= $badparam . "=" . wikiFuzz::getRandQuote() . $badstring . wikiFuzz::getRandQuote() . " ";
794 $string .= ">\n";
795 return $string;
796 case 2: // a closing tag.
797 $i = wikiFuzz::randnum(count(wikiFuzz::$types) - 1);
798 return "</". wikiFuzz::$types[$i] . ">";
799 case 3: // a random string, between tags.
800 return wikiFuzz::makeString();
802 return ""; // catch-all, should never be called.
806 ** @desc: Returns one of the three styles of random quote: ', ", and nothing.
808 static private function getRandQuote() {
809 switch ( wikiFuzz::randnum(3) ) {
810 case 1 : return "'";
811 case 2 : return "\"";
812 default: return "";
817 ** @desc: Returns fuzz text, with the parameter indicating approximately how many lines of text you want.
819 static public function makeFuzz($maxtypes = 2) {
820 $page = "";
821 for ($k=0; $k<$maxtypes; $k++) {
822 $page .= wikiFuzz::loop();
824 return $page;
829 //////// MEDIAWIKI PAGES TO TEST, AND HOW TO TEST THEM ///////
832 ** @desc: A page test has just these things:
833 ** 1) Form parameters.
834 ** 2) the URL we are going to test those parameters on.
835 ** 3) Any cookies required for the test.
836 ** Declared abstract because it should be extended by a class
837 ** that supplies these parameters.
839 abstract class pageTest {
840 protected $params;
841 protected $pagePath;
842 protected $cookie = "";
844 public function getParams() {
845 return $this->params;
848 public function getPagePath() {
849 return $this->pagePath;
852 public function getCookie() {
853 return $this->cookie;
859 ** @desc: a page test for the "Edit" page. Tests Parser.php and Sanitizer.php.
861 class editPageTest extends pageTest {
862 function __construct() {
863 $this->pagePath = "index.php?title=WIKIFUZZ";
865 $this->params = array (
866 "action" => "submit",
867 "wpMinoredit" => wikiFuzz::makeFuzz(2),
868 "wpPreview" => wikiFuzz::makeFuzz(2),
869 "wpSection" => wikiFuzz::makeFuzz(2),
870 "wpEdittime" => wikiFuzz::makeFuzz(2),
871 "wpSummary" => wikiFuzz::makeFuzz(2),
872 "wpScrolltop" => wikiFuzz::makeFuzz(2),
873 "wpStarttime" => wikiFuzz::makeFuzz(2),
874 "wpAutoSummary" => wikiFuzz::makeFuzz(2),
875 "wpTextbox1" => wikiFuzz::makeFuzz(40) // the main wiki text, need lots of this.
878 // sometimes we don't want to specify certain parameters.
879 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpSection"]);
880 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpEdittime"]);
881 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpSummary"]);
882 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpScrolltop"]);
883 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpStarttime"]);
884 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpAutoSummary"]);
885 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpTextbox1"]);
891 ** @desc: a page test for "Special:Listusers".
893 class listusersTest extends pageTest {
894 function __construct() {
895 $this->pagePath = "index.php/Special:Listusers";
897 $this->params = array (
898 "title" => wikiFuzz::makeFuzz(2),
899 "group" => wikiFuzz::makeFuzz(2),
900 "username" => wikiFuzz::makeFuzz(2),
901 "Go" => wikiFuzz::makeFuzz(2),
902 "limit" => wikiFuzz::chooseInput( array("0", "-1", "---'----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
903 "offset" => wikiFuzz::chooseInput( array("0", "-1", "--------'-----0", "+1", "81343242346234234", wikiFuzz::makeFuzz(2)) )
910 ** @desc: a page test for "Special:Search".
912 class searchTest extends pageTest {
913 function __construct() {
914 $this->pagePath = "index.php/Special:Search";
916 $this->params = array (
917 "action" => "index.php/Special:Search",
918 "ns0" => wikiFuzz::makeFuzz(2),
919 "ns1" => wikiFuzz::makeFuzz(2),
920 "ns2" => wikiFuzz::makeFuzz(2),
921 "ns3" => wikiFuzz::makeFuzz(2),
922 "ns4" => wikiFuzz::makeFuzz(2),
923 "ns5" => wikiFuzz::makeFuzz(2),
924 "ns6" => wikiFuzz::makeFuzz(2),
925 "ns7" => wikiFuzz::makeFuzz(2),
926 "ns8" => wikiFuzz::makeFuzz(2),
927 "ns9" => wikiFuzz::makeFuzz(2),
928 "ns10" => wikiFuzz::makeFuzz(2),
929 "ns11" => wikiFuzz::makeFuzz(2),
930 "ns12" => wikiFuzz::makeFuzz(2),
931 "ns13" => wikiFuzz::makeFuzz(2),
932 "ns14" => wikiFuzz::makeFuzz(2),
933 "ns15" => wikiFuzz::makeFuzz(2),
934 "redirs" => wikiFuzz::makeFuzz(2),
935 "search" => wikiFuzz::makeFuzz(2),
936 "offset" => wikiFuzz::chooseInput( array("", "0", "-1", "--------'-----0", "+1", "81343242346234234", wikiFuzz::makeFuzz(2)) ),
937 "fulltext" => wikiFuzz::chooseInput( array("", "0", "1", "--------'-----0", "+1", wikiFuzz::makeFuzz(2)) ),
938 "searchx" => wikiFuzz::chooseInput( array("", "0", "1", "--------'-----0", "+1", wikiFuzz::makeFuzz(2)) )
945 ** @desc: a page test for "Special:Recentchanges".
947 class recentchangesTest extends pageTest {
948 function __construct() {
949 $this->pagePath = "index.php/Special:Recentchanges";
951 $this->params = array (
952 "action" => wikiFuzz::makeFuzz(2),
953 "title" => wikiFuzz::makeFuzz(2),
954 "namespace" => wikiFuzz::chooseInput( range(-1, 15) ),
955 "Go" => wikiFuzz::makeFuzz(2),
956 "invert" => wikiFuzz::chooseInput( array("-1", "---'----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
957 "hideanons" => wikiFuzz::chooseInput( array("-1", "------'-------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
958 'limit' => wikiFuzz::chooseInput( array("0", "-1", "---------'----0", "+1", "81340909772349234", wikiFuzz::makeFuzz(2)) ),
959 "days" => wikiFuzz::chooseInput( array("-1", "----------'---0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
960 "hideminor" => wikiFuzz::chooseInput( array("-1", "-----------'--0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
961 "hidebots" => wikiFuzz::chooseInput( array("-1", "---------'----0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
962 "hideliu" => wikiFuzz::chooseInput( array("-1", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
963 "hidepatrolled" => wikiFuzz::chooseInput( array("-1", "-----'--------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
964 "hidemyself" => wikiFuzz::chooseInput( array("-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
965 'categories_any'=> wikiFuzz::chooseInput( array("-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
966 'categories' => wikiFuzz::chooseInput( array("-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
967 'feed' => wikiFuzz::chooseInput( array("-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) )
974 ** @desc: a page test for "Special:Prefixindex".
976 class prefixindexTest extends pageTest {
977 function __construct() {
978 $this->pagePath = "index.php/Special:Prefixindex";
980 $this->params = array (
981 "title" => "Special:Prefixindex",
982 "namespace" => wikiFuzz::randnum(-10,101),
983 "Go" => wikiFuzz::makeFuzz(2)
986 // sometimes we want 'prefix', sometimes we want 'from', and sometimes we want nothing.
987 if (wikiFuzz::randnum(3) == 0) {
988 $this->params["prefix"] = wikiFuzz::chooseInput( array("-1", "-----'--------0", "+++--+1",
989 wikiFuzz::randnum(-10,8134), wikiFuzz::makeFuzz(2)) );
991 if (wikiFuzz::randnum(3) == 0) {
992 $this->params["from"] = wikiFuzz::chooseInput( array("-1", "-----'--------0", "+++--+1",
993 wikiFuzz::randnum(-10,8134), wikiFuzz::makeFuzz(2)) );
1000 ** @desc: a page test for "Special:MIMEsearch".
1002 class mimeSearchTest extends pageTest {
1003 function __construct() {
1004 $this->pagePath = "index.php/Special:MIMEsearch";
1006 $this->params = array (
1007 "action" => "/wiki/index.php/Special:MIMEsearch",
1008 "mime" => wikiFuzz::makeFuzz(3),
1009 'limit' => wikiFuzz::chooseInput( array("0", "-1", "-------'------0", "+1", "81342321351235325", wikiFuzz::makeFuzz(2)) ),
1010 'offset' => wikiFuzz::chooseInput( array("0", "-1", "-----'--------0", "+1", "81341231235365252234324", wikiFuzz::makeFuzz(2)) )
1017 ** @desc: a page test for "Special:Log".
1019 class specialLogTest extends pageTest {
1020 function __construct() {
1021 $this->pagePath = "index.php/Special:Log";
1023 $this->params = array (
1024 "type" => wikiFuzz::chooseInput( array("", wikiFuzz::makeFuzz(2)) ),
1025 "par" => wikiFuzz::makeFuzz(2),
1026 "user" => wikiFuzz::makeFuzz(2),
1027 "page" => wikiFuzz::makeFuzz(2),
1028 "from" => wikiFuzz::makeFuzz(2),
1029 "until" => wikiFuzz::makeFuzz(2),
1030 "title" => wikiFuzz::makeFuzz(2)
1037 ** @desc: a page test for "Special:Userlogin", with a successful login.
1039 class successfulUserLoginTest extends pageTest {
1040 function __construct() {
1041 $this->pagePath = "index.php?title=Special:Userlogin&action=submitlogin&type=login&returnto=" . wikiFuzz::makeFuzz(2);
1043 $this->params = array (
1044 "wpName" => USER_ON_WIKI,
1045 // sometimes real password, sometimes not:
1046 'wpPassword' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz(2), USER_PASSWORD ) ),
1047 'wpRemember' => wikiFuzz::makeFuzz(2)
1050 $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array("1" , wikiFuzz::makeFuzz(2) ) );
1056 ** @desc: a page test for "Special:Userlogin".
1058 class userLoginTest extends pageTest {
1059 function __construct() {
1061 $this->pagePath = "index.php/Special:Userlogin";
1063 $this->params = array (
1064 'wpRetype' => wikiFuzz::makeFuzz(2),
1065 'wpRemember' => wikiFuzz::makeFuzz(2),
1066 'wpRealName' => wikiFuzz::makeFuzz(2),
1067 'wpPassword' => wikiFuzz::makeFuzz(2),
1068 'wpName' => wikiFuzz::makeFuzz(2),
1069 'wpMailmypassword'=> wikiFuzz::makeFuzz(2),
1070 'wpLoginattempt' => wikiFuzz::makeFuzz(2),
1071 'wpEmail' => wikiFuzz::makeFuzz(2),
1072 'wpDomain' => wikiFuzz::chooseInput( array("", "local", wikiFuzz::makeFuzz(2)) ),
1073 'wpCreateaccountMail' => wikiFuzz::chooseInput( array("", wikiFuzz::makeFuzz(2)) ),
1074 'wpCreateaccount' => wikiFuzz::chooseInput( array("", wikiFuzz::makeFuzz(2)) ),
1075 'wpCookieCheck' => wikiFuzz::chooseInput( array("", wikiFuzz::makeFuzz(2)) ),
1076 'type' => wikiFuzz::chooseInput( array("signup", "login", "", wikiFuzz::makeFuzz(2)) ),
1077 'returnto' => wikiFuzz::makeFuzz(2),
1078 'action' => wikiFuzz::chooseInput( array("", "submitlogin", wikiFuzz::makeFuzz(2)) )
1081 $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array("1" , wikiFuzz::makeFuzz(2) ) );
1087 ** @desc: a page test for "Special:Ipblocklist" (also includes unblocking)
1089 class ipblocklistTest extends pageTest {
1090 function __construct() {
1091 $this->pagePath = "index.php/Special:Ipblocklist";
1093 $this->params = array (
1094 'wpUnblockAddress'=> wikiFuzz::makeFuzz(2),
1095 'ip' => wikiFuzz::chooseInput( array("20398702394", "", "Nickj2", wikiFuzz::makeFuzz(2),
1096 // something like an IP address, sometimes invalid:
1097 ( wikiFuzz::randnum(300,-20) . "." . wikiFuzz::randnum(300,-20) . "."
1098 . wikiFuzz::randnum(300,-20) . "." .wikiFuzz::randnum(300,-20) ) ) ),
1099 'id' => wikiFuzz::makeFuzz(2),
1100 'wpUnblockReason' => wikiFuzz::makeFuzz(2),
1101 'action' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2), "success", "submit", "unblock") ),
1102 'wpEditToken' => wikiFuzz::makeFuzz(2),
1103 'wpBlock' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2), "") ),
1104 'limit' => wikiFuzz::chooseInput( array("0", "-1", "--------'-----0", "+1",
1105 "09700982312351132098234", wikiFuzz::makeFuzz(2)) ),
1106 'offset' => wikiFuzz::chooseInput( array("0", "-1", "------'-------0", "+1",
1107 "09700980982341535324234234", wikiFuzz::makeFuzz(2)) )
1110 // sometimes we don't want to specify certain parameters.
1111 if (wikiFuzz::randnum(4) == 0) unset($this->params["action"]);
1112 if (wikiFuzz::randnum(3) == 0) unset($this->params["ip"]);
1113 if (wikiFuzz::randnum(2) == 0) unset($this->params["id"]);
1114 if (wikiFuzz::randnum(3) == 0) unset($this->params["wpUnblockAddress"]);
1120 ** @desc: a page test for "Special:Newimages".
1122 class newImagesTest extends pageTest {
1123 function __construct() {
1124 $this->pagePath = "index.php/Special:Newimages";
1126 $this->params = array (
1127 'hidebots' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2), "1", "", "-1") ),
1128 'wpIlMatch' => wikiFuzz::makeFuzz(2),
1129 'until' => wikiFuzz::makeFuzz(2),
1130 'from' => wikiFuzz::makeFuzz(2)
1133 // sometimes we don't want to specify certain parameters.
1134 if (wikiFuzz::randnum(6) == 0) unset($this->params["until"]);
1135 if (wikiFuzz::randnum(6) == 0) unset($this->params["from"]);
1141 ** @desc: a page test for the "Special:Imagelist" page.
1143 class imagelistTest extends pageTest {
1144 function __construct() {
1145 $this->pagePath = "index.php/Special:Imagelist";
1147 $this->params = array (
1148 'sort' => wikiFuzz::chooseInput( array("bysize", "byname" , "bydate", wikiFuzz::makeFuzz(2)) ),
1149 'limit' => wikiFuzz::chooseInput( array("0", "-1", "--------'-----0", "+1", "09700982312351132098234", wikiFuzz::makeFuzz(2)) ),
1150 'offset' => wikiFuzz::chooseInput( array("0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz(2)) ),
1151 'wpIlMatch' => wikiFuzz::makeFuzz(2)
1158 ** @desc: a page test for "Special:Export".
1160 class specialExportTest extends pageTest {
1161 function __construct() {
1162 $this->pagePath = "index.php/Special:Export";
1164 $this->params = array (
1165 'action' => wikiFuzz::chooseInput( array("submit", "", wikiFuzz::makeFuzz(2)) ),
1166 'pages' => wikiFuzz::makeFuzz(2),
1167 'curonly' => wikiFuzz::chooseInput( array("", "0", "-1", wikiFuzz::makeFuzz(2)) ),
1168 'listauthors' => wikiFuzz::chooseInput( array("", "0", "-1", wikiFuzz::makeFuzz(2)) ),
1169 'history' => wikiFuzz::chooseInput( array("0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz(2)) ),
1173 // For the time being, need to disable "submit" action as Tidy barfs on MediaWiki's XML export.
1174 if ($this->params['action'] == 'submit') $this->params['action'] = '';
1176 // Sometimes remove the history field.
1177 if (wikiFuzz::randnum(2) == 0) unset($this->params["history"]);
1183 ** @desc: a page test for "Special:Booksources".
1185 class specialBooksourcesTest extends pageTest {
1186 function __construct() {
1187 $this->pagePath = "index.php/Special:Booksources";
1189 $this->params = array (
1190 'go' => wikiFuzz::makeFuzz(2),
1191 // ISBN codes have to contain some semi-numeric stuff or will be ignored:
1192 'isbn' => "0X0" . wikiFuzz::makeFuzz(2)
1199 ** @desc: a page test for "Special:Allpages".
1201 class specialAllpagesTest extends pageTest {
1202 function __construct() {
1203 $this->pagePath = "index.php?title=Special%3AAllpages";
1205 $this->params = array (
1206 'from' => wikiFuzz::makeFuzz(2),
1207 'namespace' => wikiFuzz::chooseInput( range(-1, 15) ),
1208 'go' => wikiFuzz::makeFuzz(2)
1215 ** @desc: a page test for the page History.
1217 class pageHistoryTest extends pageTest {
1218 function __construct() {
1219 $this->pagePath = "index.php?title=Main_Page&action=history";
1221 $this->params = array (
1222 'limit' => wikiFuzz::chooseInput( array("-1", "0", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1223 'offset' => wikiFuzz::chooseInput( array("-1", "0", "------'-------0", "+1", "9823412312312412435", wikiFuzz::makeFuzz(2)) ),
1224 "go" => wikiFuzz::chooseInput( array("first", "last", wikiFuzz::makeFuzz(2)) ),
1225 "dir" => wikiFuzz::chooseInput( array("prev", "next", wikiFuzz::makeFuzz(2)) ),
1226 "diff" => wikiFuzz::chooseInput( array("-1", "--------'-----0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1227 "oldid" => wikiFuzz::chooseInput( array("prev", "-1", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1228 "feed" => wikiFuzz::makeFuzz(2)
1235 ** @desc: a page test for the Special:Contributions".
1237 class contributionsTest extends pageTest {
1238 function __construct() {
1239 $this->pagePath = "index.php/Special:Contributions/" . USER_ON_WIKI;
1241 $this->params = array (
1242 'target' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2), "newbies") ),
1243 'namespace' => wikiFuzz::chooseInput( array(-1, 15, 1, wikiFuzz::makeFuzz(2)) ),
1244 'offset' => wikiFuzz::chooseInput( array("0", "-1", "------'-------0", "+1", "982342131232131231241", wikiFuzz::makeFuzz(2)) ),
1245 'bot' => wikiFuzz::chooseInput( array("", "-1", "0", "1", wikiFuzz::makeFuzz(2)) ),
1246 'go' => wikiFuzz::chooseInput( array("-1", 'prev', 'next', wikiFuzz::makeFuzz(2)) )
1253 ** @desc: a page test for viewing a normal page, whilst posting various params.
1255 class viewPageTest extends pageTest {
1256 function __construct() {
1257 $this->pagePath = "index.php/Main_Page";
1259 $this->params = array (
1260 "useskin" => wikiFuzz::chooseInput( array("chick", "cologneblue", "myskin",
1261 "nostalgia", "simple", "standard", wikiFuzz::makeFuzz(2)) ),
1262 "uselang" => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz(2),
1263 "ab", "af", "an", "ar", "arc", "as", "ast", "av", "ay", "az", "ba",
1264 "bat-smg", "be", "bg", "bm", "bn", "bo", "bpy", "br", "bs", "ca",
1265 "ce", "cs", "csb", "cv", "cy", "da", "de", "dv", "dz", "el", "en",
1266 "eo", "es", "et", "eu", "fa", "fi", "fo", "fr", "fur", "fy", "ga",
1267 "gn", "gsw", "gu", "he", "hi", "hr", "hu", "ia", "id", "ii", "is",
1268 "it", "ja", "jv", "ka", "km", "kn", "ko", "ks", "ku", "kv", "la",
1269 "li", "lo", "lt", "lv", "mk", "ml", "ms", "nah", "nap", "nds",
1270 "nds-nl", "nl", "nn", "no", "non", "nv", "oc", "or", "os", "pa",
1271 "pl", "pms", "ps", "pt", "pt-br", "qu", "rmy", "ro", "ru", "sc",
1272 "sd", "sk", "sl", "sq", "sr", "sr-ec", "sr-el", "sr-jc", "sr-jl",
1273 "su", "sv", "ta", "te", "th", "tlh", "tr", "tt", "ty", "tyv", "udm",
1274 "ug", "uk", "ur", "utf8", "vec", "vi", "wa", "xal", "yi", "za",
1275 "zh", "zh-cn", "zh-hk", "zh-sg", "zh-tw", "zh-tw") ),
1276 "returnto" => wikiFuzz::makeFuzz(2),
1277 "feed" => wikiFuzz::chooseInput( array("atom", "rss", wikiFuzz::makeFuzz(2)) ),
1278 "rcid" => wikiFuzz::makeFuzz(2),
1279 "action" => wikiFuzz::chooseInput( array("view", "raw", "render", wikiFuzz::makeFuzz(2), "markpatrolled") ),
1280 "printable" => wikiFuzz::makeFuzz(2),
1281 "oldid" => wikiFuzz::makeFuzz(2),
1282 "redirect" => wikiFuzz::makeFuzz(2),
1283 "diff" => wikiFuzz::makeFuzz(2),
1284 "search" => wikiFuzz::makeFuzz(2),
1285 "rdfrom" => wikiFuzz::makeFuzz(2), // things from Article.php from here on:
1286 "token" => wikiFuzz::makeFuzz(2),
1287 "tbid" => wikiFuzz::makeFuzz(2),
1288 "action" => wikiFuzz::chooseInput( array("purge", wikiFuzz::makeFuzz(2)) ),
1289 "wpReason" => wikiFuzz::makeFuzz(2),
1290 "wpEditToken" => wikiFuzz::makeFuzz(2),
1291 "from" => wikiFuzz::makeFuzz(2),
1292 "bot" => wikiFuzz::makeFuzz(2),
1293 "summary" => wikiFuzz::makeFuzz(2),
1294 "direction" => wikiFuzz::chooseInput( array("next", "prev", wikiFuzz::makeFuzz(2)) ),
1295 "section" => wikiFuzz::makeFuzz(2),
1296 "preload" => wikiFuzz::makeFuzz(2),
1300 // Tidy does not know how to valid atom or rss, so exclude from testing for the time being.
1301 if ($this->params["feed"] == "atom") unset($this->params["feed"]);
1302 else if ($this->params["feed"] == "rss") unset($this->params["feed"]);
1304 // Raw pages cannot really be validated
1305 if ($this->params["action"] == "raw") unset($this->params["action"]);
1307 // sometimes we don't want to specify certain parameters.
1308 if (wikiFuzz::randnum(6) == 0) unset($this->params["rcid"]);
1309 if (wikiFuzz::randnum(6) == 0) unset($this->params["diff"]);
1310 if (wikiFuzz::randnum(6) == 0) unset($this->params["rdfrom"]);
1311 if (wikiFuzz::randnum(3) == 0) unset($this->params["oldid"]);
1313 // usually don't want action == purge.
1314 if (wikiFuzz::randnum(6) > 1) unset($this->params["action"]);
1320 ** @desc: a page test for "Special:Allmessages".
1322 class specialAllmessagesTest extends pageTest {
1323 function __construct() {
1324 $this->pagePath = "index.php?title=Special:Allmessages";
1326 // only really has one parameter
1327 $this->params = array (
1328 "ot" => wikiFuzz::chooseInput( array("php", "html", wikiFuzz::makeFuzz(2)) )
1334 ** @desc: a page test for "Special:Newpages".
1336 class specialNewpages extends pageTest {
1337 function __construct() {
1338 $this->pagePath = "index.php/Special:Newpages";
1340 $this->params = array (
1341 "namespace" => wikiFuzz::chooseInput( range(-1, 15) ),
1342 "feed" => wikiFuzz::chooseInput( array("atom", "rss", wikiFuzz::makeFuzz(2)) ),
1343 'limit' => wikiFuzz::chooseInput( array("-1", "0", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ),
1344 'offset' => wikiFuzz::chooseInput( array("-1", "0", "------'-------0", "+1", "9823412312312412435", 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"]);
1354 ** @desc: a page test for "redirect.php"
1356 class redirectTest extends pageTest {
1357 function __construct() {
1358 $this->pagePath = "redirect.php";
1360 $this->params = array (
1361 "wpDropdown" => wikiFuzz::makeFuzz(2)
1364 // sometimes we don't want to specify certain parameters.
1365 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpDropdown"]);
1371 ** @desc: a page test for "Special:Confirmemail"
1373 class confirmEmail extends pageTest {
1374 function __construct() {
1375 // sometimes we send a bogus confirmation code, and sometimes we don't.
1376 $this->pagePath = "index.php?title=Special:Confirmemail" . wikiFuzz::chooseInput( array("", "/" . wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(1)) ) );
1378 $this->params = array (
1379 "token" => wikiFuzz::makeFuzz(2)
1386 ** @desc: a page test for "Special:Watchlist"
1387 ** Note: this test would be better if we were logged in.
1389 class watchlistTest extends pageTest {
1390 function __construct() {
1391 $this->pagePath = "index.php?title=Special:Watchlist";
1393 $this->params = array (
1394 "remove" => wikiFuzz::chooseInput( array("Remove checked items from watchlist", wikiFuzz::makeFuzz(2))),
1395 'days' => wikiFuzz::chooseInput( array(0, -1, -230, "--", 3, 9, wikiFuzz::makeFuzz(2)) ),
1396 'hideOwn' => wikiFuzz::chooseInput( array("", "0", "1", wikiFuzz::makeFuzz(2)) ),
1397 'hideBots' => wikiFuzz::chooseInput( array("", "0", "1", wikiFuzz::makeFuzz(2)) ),
1398 'namespace'=> wikiFuzz::chooseInput( array("", "0", "1", wikiFuzz::makeFuzz(2)) ),
1399 'action' => wikiFuzz::chooseInput( array("submit", "clear", wikiFuzz::makeFuzz(2)) ),
1400 'id[]' => wikiFuzz::makeFuzz(2),
1401 'edit' => wikiFuzz::makeFuzz(2),
1402 'token' => wikiFuzz::chooseInput( array("", "1243213", wikiFuzz::makeFuzz(2)) )
1405 // sometimes we specifiy "reset", and sometimes we don't.
1406 if (wikiFuzz::randnum(3) == 0) $this->params["reset"] = wikiFuzz::chooseInput( array("", "0", "1", wikiFuzz::makeFuzz(2)) );
1412 ** @desc: a page test for "Special:Blockme"
1414 class specialBlockmeTest extends pageTest {
1415 function __construct() {
1416 $this->pagePath = "index.php?title=Special:Blockme";
1418 $this->params = array ( );
1420 // sometimes we specify "ip", and sometimes we don't.
1421 if (wikiFuzz::randnum(1) == 0) {
1422 $this->params["ip"] = wikiFuzz::chooseInput( array("10.12.41.213", wikiFuzz::randnum(-10,8134), wikiFuzz::makeFuzz(2)) );
1429 ** @desc: a page test for "Special:Movepage"
1431 class specialMovePage extends pageTest {
1432 function __construct() {
1433 $this->pagePath = "index.php?title=Special:Movepage";
1435 $this->params = array (
1436 "action" => wikiFuzz::chooseInput( array("success", "submit", "", wikiFuzz::makeFuzz(2)) ),
1437 'wpEditToken' => wikiFuzz::chooseInput( array('', 0, 34987987, wikiFuzz::makeFuzz(2)) ),
1438 'target' => wikiFuzz::chooseInput( array("x", wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(2)) ) ),
1439 'wpOldTitle' => wikiFuzz::chooseInput( array("z", wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(2)), wikiFuzz::makeFuzz(2) ) ),
1440 'wpNewTitle' => wikiFuzz::chooseInput( array("y", wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(2)), wikiFuzz::makeFuzz(2) ) ),
1441 'wpReason' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2)) ),
1442 'wpMovetalk' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1443 'wpDeleteAndMove' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1444 'wpConfirm' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1445 'talkmoved' => wikiFuzz::chooseInput( array("1", wikiFuzz::makeFuzz(2), "articleexists", 'notalkpage') ),
1446 'oldtitle' => wikiFuzz::makeFuzz(2),
1447 'newtitle' => wikiFuzz::makeFuzz(2),
1448 'wpMovetalk' => wikiFuzz::chooseInput( array("1", "0", wikiFuzz::makeFuzz(2)) )
1451 // sometimes we don't want to specify certain parameters.
1452 if (wikiFuzz::randnum(2) == 0) unset($this->params["wpEditToken"]);
1453 if (wikiFuzz::randnum(3) == 0) unset($this->params["target"]);
1454 if (wikiFuzz::randnum(3) == 0) unset($this->params["wpNewTitle"]);
1455 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpReason"]);
1456 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpOldTitle"]);
1462 ** @desc: a page test for "Special:Undelete"
1464 class specialUndelete extends pageTest {
1465 function __construct() {
1466 $this->pagePath = "index.php?title=Special:Undelete";
1468 $this->params = array (
1469 "action" => wikiFuzz::chooseInput( array("submit", "", wikiFuzz::makeFuzz(2)) ),
1470 'wpEditToken' => wikiFuzz::chooseInput( array('', 0, 34987987, wikiFuzz::makeFuzz(2)) ),
1471 'target' => wikiFuzz::chooseInput( array("x", wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(2)) ) ),
1472 'timestamp' => wikiFuzz::chooseInput( array("125223", wikiFuzz::makeFuzz(2) ) ),
1473 'file' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1474 'restore' => wikiFuzz::chooseInput( array("0", "1", wikiFuzz::makeFuzz(2)) ),
1475 'preview' => wikiFuzz::chooseInput( array("0", "1", wikiFuzz::makeFuzz(2)) ),
1476 'wpComment' => wikiFuzz::makeFuzz(2)
1479 // sometimes we don't want to specify certain parameters.
1480 if (wikiFuzz::randnum(2) == 0) unset($this->params["wpEditToken"]);
1481 if (wikiFuzz::randnum(4) == 0) unset($this->params["target"]);
1482 if (wikiFuzz::randnum(1) == 0) unset($this->params["restore"]);
1483 if (wikiFuzz::randnum(1) == 0) unset($this->params["preview"]);
1489 ** @desc: a page test for "Special:Unlockdb"
1491 class specialUnlockdb extends pageTest {
1492 function __construct() {
1493 $this->pagePath = "index.php?title=Special:Unlockdb";
1495 $this->params = array (
1496 "action" => wikiFuzz::chooseInput( array("submit", "success", "", wikiFuzz::makeFuzz(2)) ),
1497 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1498 'wpLockConfirm' => wikiFuzz::chooseInput( array("0", "1", wikiFuzz::makeFuzz(2)) )
1501 // sometimes we don't want to specify certain parameters.
1502 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpEditToken"]);
1503 if (wikiFuzz::randnum(4) == 0) unset($this->params["action"]);
1504 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpLockConfirm"]);
1510 ** @desc: a page test for "Special:Lockdb"
1512 class specialLockdb extends pageTest {
1513 function __construct() {
1514 $this->pagePath = "index.php?title=Special:Lockdb";
1516 $this->params = array (
1517 "action" => wikiFuzz::chooseInput( array("submit", "success", "", wikiFuzz::makeFuzz(2)) ),
1518 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1519 'wpLockReason' => wikiFuzz::makeFuzz(2),
1520 'wpLockConfirm'=> wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) )
1523 // sometimes we don't want to specify certain parameters.
1524 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpEditToken"]);
1525 if (wikiFuzz::randnum(4) == 0) unset($this->params["action"]);
1526 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpLockConfirm"]);
1532 ** @desc: a page test for "Special:Userrights"
1534 class specialUserrights extends pageTest {
1535 function __construct() {
1536 $this->pagePath = "index.php/Special:Userrights";
1538 $this->params = array (
1539 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1540 'user-editname' => wikiFuzz::chooseInput( array("Nickj2", "Nickj2\n<xyz>", wikiFuzz::makeFuzz(2)) ),
1541 'ssearchuser' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1542 'saveusergroups'=> wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)), "Save User Groups"),
1543 'member[]' => wikiFuzz::chooseInput( array("0", "bot", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1544 "available[]" => wikiFuzz::chooseInput( array("0", "sysop", "bureaucrat", "1", "++--34234", wikiFuzz::makeFuzz(2)) )
1547 // sometimes we don't want to specify certain parameters.
1548 if (wikiFuzz::randnum(3) == 0) unset($this->params['ssearchuser']);
1549 if (wikiFuzz::randnum(3) == 0) unset($this->params['saveusergroups']);
1555 ** @desc: a test for page protection and unprotection.
1557 class pageProtectionForm extends pageTest {
1558 function __construct() {
1559 $this->pagePath = "index.php?title=Main_Page";
1561 $this->params = array (
1562 "action" => "protect",
1563 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1564 "mwProtect-level-edit" => wikiFuzz::chooseInput( array('', 'autoconfirmed', 'sysop', wikifuzz::makeFuzz(2)) ),
1565 "mwProtect-level-move" => wikiFuzz::chooseInput( array('', 'autoconfirmed', 'sysop', wikifuzz::makeFuzz(2)) ),
1566 "mwProtectUnchained" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1567 'mwProtect-reason' => wikiFuzz::chooseInput( array("because it was there", wikifuzz::makeFuzz(2)) )
1571 // sometimes we don't want to specify certain parameters.
1572 if (wikiFuzz::randnum(3) == 0) unset($this->params["mwProtectUnchained"]);
1573 if (wikiFuzz::randnum(3) == 0) unset($this->params['mwProtect-reason']);
1579 ** @desc: a page test for "Special:Blockip".
1581 class specialBlockip extends pageTest {
1582 function __construct() {
1583 $this->pagePath = "index.php/Special:Blockip";
1585 $this->params = array (
1586 "action" => wikiFuzz::chooseInput( array("submit", "", wikiFuzz::makeFuzz(2)) ),
1587 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1588 "wpBlockAddress" => wikiFuzz::chooseInput( array("20398702394", "", "Nickj2", wikiFuzz::makeFuzz(2),
1589 // something like an IP address, sometimes invalid:
1590 ( wikiFuzz::randnum(300,-20) . "." . wikiFuzz::randnum(300,-20) . "."
1591 . wikiFuzz::randnum(300,-20) . "." .wikiFuzz::randnum(300,-20) ) ) ),
1592 "ip" => wikiFuzz::chooseInput( array("20398702394", "", "Nickj2", wikiFuzz::makeFuzz(2),
1593 // something like an IP address, sometimes invalid:
1594 ( wikiFuzz::randnum(300,-20) . "." . wikiFuzz::randnum(300,-20) . "."
1595 . wikiFuzz::randnum(300,-20) . "." .wikiFuzz::randnum(300,-20) ) ) ),
1596 "wpBlockOther" => wikiFuzz::chooseInput( array('', 'Nickj2', wikifuzz::makeFuzz(2)) ),
1597 "wpBlockExpiry" => wikiFuzz::chooseInput( array("other", "2 hours", "1 day", "3 days", "1 week", "2 weeks",
1598 "1 month", "3 months", "6 months", "1 year", "infinite", wikiFuzz::makeFuzz(2)) ),
1599 "wpBlockReason" => wikiFuzz::chooseInput( array("because it was there", wikifuzz::makeFuzz(2)) ),
1600 "wpAnonOnly" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1601 "wpCreateAccount" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1602 "wpBlock" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) )
1605 // sometimes we don't want to specify certain parameters.
1606 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpBlockOther"]);
1607 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpBlockExpiry"]);
1608 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpBlockReason"]);
1609 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpAnonOnly"]);
1610 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpCreateAccount"]);
1611 if (wikiFuzz::randnum(4) == 0) unset($this->params["wpBlockAddress"]);
1612 if (wikiFuzz::randnum(4) == 0) unset($this->params["ip"]);
1618 ** @desc: a test for the imagepage.
1620 class imagepageTest extends pageTest {
1621 function __construct() {
1622 $this->pagePath = "index.php/Image:Small-email.png";
1624 $this->params = array (
1625 "image" => wikiFuzz::chooseInput( array("Small-email.png", wikifuzz::makeFuzz(2)) ),
1626 "wpReason" => wikifuzz::makeFuzz(2),
1627 "oldimage" => wikiFuzz::chooseInput( array("Small-email.png", wikifuzz::makeFuzz(2)) ),
1628 "wpEditToken" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1631 // sometimes we don't want to specify certain parameters.
1632 if (wikiFuzz::randnum(6) == 0) unset($this->params["image"]);
1633 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpReason"]);
1634 if (wikiFuzz::randnum(6) == 0) unset($this->params["oldimage"]);
1635 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpEditToken"]);
1641 ** @desc: a test for page deletion form.
1643 class pageDeletion extends pageTest {
1644 function __construct() {
1645 $this->pagePath = "index.php?title=Main_Page&action=delete";
1647 $this->params = array (
1648 "wpEditToken" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1649 "wpReason" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1650 "wpConfirm" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1653 // sometimes we don't want to specify certain parameters.
1654 if (wikiFuzz::randnum(5) == 0) unset($this->params["wpReason"]);
1655 if (wikiFuzz::randnum(5) == 0) unset($this->params["wpEditToken"]);
1656 if (wikiFuzz::randnum(5) == 0) unset($this->params["wpConfirm"]);
1663 ** @desc: a test for Revision Deletion.
1665 class specialRevisionDelete extends pageTest {
1666 function __construct() {
1667 $this->pagePath = "index.php?title=Special:Revisiondelete";
1669 $this->params = array (
1670 "target" => wikiFuzz::chooseInput( array("Main Page", wikifuzz::makeFuzz(2)) ),
1671 "oldid" => wikifuzz::makeFuzz(2),
1672 "oldid[]" => wikifuzz::makeFuzz(2),
1673 "wpReason" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1674 "revdelete-hide-text" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1675 "revdelete-hide-comment" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1676 "revdelete-hide-user" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1677 "revdelete-hide-restricted" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1680 // sometimes we don't want to specify certain parameters.
1681 if (wikiFuzz::randnum(3) == 0) unset($this->params["target"]);
1682 if (wikiFuzz::randnum(6) == 0) unset($this->params["oldid"]);
1683 if (wikiFuzz::randnum(6) == 0) unset($this->params["oldid[]"]);
1684 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpReason"]);
1685 if (wikiFuzz::randnum(6) == 0) unset($this->params["revdelete-hide-text"]);
1686 if (wikiFuzz::randnum(6) == 0) unset($this->params["revdelete-hide-comment"]);
1687 if (wikiFuzz::randnum(6) == 0) unset($this->params["revdelete-hide-user"]);
1688 if (wikiFuzz::randnum(6) == 0) unset($this->params["revdelete-hide-restricted"]);
1694 ** @desc: a test for Special:Import.
1696 class specialImport extends pageTest {
1697 function __construct() {
1698 $this->pagePath = "index.php/Special:Import";
1700 $this->params = array (
1701 "action" => "submit",
1702 "source" => wikiFuzz::chooseInput( array("upload", "interwiki", wikifuzz::makeFuzz(2)) ),
1703 "MAX_FILE_SIZE" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ),
1704 "xmlimport" => wikiFuzz::chooseInput( array("/var/www/hosts/mediawiki/wiki/AdminSettings.php", "1", "++--34234", wikiFuzz::makeFuzz(2)) ),
1705 "namespace" => wikiFuzz::chooseInput( array(wikiFuzz::randnum(30,-6), wikiFuzz::makeFuzz(2)) ),
1706 "interwiki" => wikiFuzz::makeFuzz(2),
1707 "interwikiHistory" => wikiFuzz::makeFuzz(2),
1708 "frompage" => wikiFuzz::makeFuzz(2),
1711 // sometimes we don't want to specify certain parameters.
1712 if (wikiFuzz::randnum(6) == 0) unset($this->params["action"]);
1713 if (wikiFuzz::randnum(6) == 0) unset($this->params["source"]);
1714 if (wikiFuzz::randnum(6) == 0) unset($this->params["MAX_FILE_SIZE"]);
1715 if (wikiFuzz::randnum(6) == 0) unset($this->params["xmlimport"]);
1716 if (wikiFuzz::randnum(6) == 0) unset($this->params["interwiki"]);
1717 if (wikiFuzz::randnum(6) == 0) unset($this->params["interwikiHistory"]);
1718 if (wikiFuzz::randnum(6) == 0) unset($this->params["frompage"]);
1720 // Note: Need to do a file upload to fully test this Special page.
1727 ** @desc: a test for thumb.php
1729 class thumbTest extends pageTest {
1730 function __construct() {
1731 $this->pagePath = "thumb.php";
1733 $this->params = array (
1734 "f" => wikiFuzz::chooseInput( array("..", "\\", "small-email.png", wikifuzz::makeFuzz(2)) ),
1735 "w" => wikiFuzz::chooseInput( array("80", wikiFuzz::randnum(6000,-200), wikifuzz::makeFuzz(2)) ),
1736 "r" => wikiFuzz::chooseInput( array("0", wikifuzz::makeFuzz(2)) ),
1739 // sometimes we don't want to specify certain parameters.
1740 if (wikiFuzz::randnum(6) == 0) unset($this->params["f"]);
1741 if (wikiFuzz::randnum(6) == 0) unset($this->params["w"]);
1742 if (wikiFuzz::randnum(6) == 0) unset($this->params["r"]);
1748 ** @desc: a test for trackback.php
1750 class trackbackTest extends pageTest {
1751 function __construct() {
1752 $this->pagePath = "trackback.php";
1754 $this->params = array (
1755 "url" => wikifuzz::makeFuzz(2),
1756 "blog_name" => wikiFuzz::chooseInput( array("80", wikiFuzz::randnum(6000,-200), wikifuzz::makeFuzz(2)) ),
1757 "article" => wikiFuzz::chooseInput( array("Main Page", wikifuzz::makeFuzz(2)) ),
1758 "title" => wikiFuzz::chooseInput( array("Main Page", wikifuzz::makeFuzz(2)) ),
1759 "excerpt" => wikifuzz::makeFuzz(2),
1762 // sometimes we don't want to specify certain parameters.
1763 if (wikiFuzz::randnum(3) == 0) unset($this->params["title"]);
1764 if (wikiFuzz::randnum(3) == 0) unset($this->params["excerpt"]);
1770 ** @desc: a test for profileinfo.php
1772 class profileInfo extends pageTest {
1773 function __construct() {
1774 $this->pagePath = "profileinfo.php";
1776 $this->params = array (
1777 "expand" => wikifuzz::makeFuzz(2),
1778 "sort" => wikiFuzz::chooseInput( array("time", "count", "name", wikifuzz::makeFuzz(2)) ),
1779 "filter" => wikiFuzz::chooseInput( array("Main Page", wikifuzz::makeFuzz(2)) ),
1782 // sometimes we don't want to specify certain parameters.
1783 if (wikiFuzz::randnum(3) == 0) unset($this->params["sort"]);
1784 if (wikiFuzz::randnum(3) == 0) unset($this->params["filter"]);
1790 ** @desc: a test for Special:Cite (extension Special page).
1792 class specialCite extends pageTest {
1793 function __construct() {
1794 $this->pagePath = "index.php?title=Special:Cite";
1796 $this->params = array (
1797 "page" => wikiFuzz::chooseInput( array("\" onmouseover=\"alert(1);\"", "Main Page", wikifuzz::makeFuzz(2)) ),
1798 "id" => wikiFuzz::chooseInput( array("-1", "0", "------'-------0", "+1", "-9823412312312412435", wikiFuzz::makeFuzz(2)) ),
1801 // sometimes we don't want to specify certain parameters.
1802 if (wikiFuzz::randnum(6) == 0) unset($this->params["page"]);
1803 if (wikiFuzz::randnum(6) == 0) unset($this->params["id"]);
1809 ** @desc: a test for Special:Filepath (extension Special page).
1811 class specialFilepath extends pageTest {
1812 function __construct() {
1813 $this->pagePath = "index.php/Special:Filepath";
1815 $this->params = array (
1816 "file" => wikiFuzz::chooseInput( array("Small-email.png", "Small-email.png" . wikifuzz::makeFuzz(1), wikiFuzz::makeFuzz(2)) ),
1823 ** @desc: a test for Special:Makebot (extension Special page).
1825 class specialMakebot extends pageTest {
1826 function __construct() {
1827 $this->pagePath = "index.php/Special:Makebot";
1829 $this->params = array (
1830 "username" => wikiFuzz::chooseInput( array("Nickj2", "192.168.0.2", wikifuzz::makeFuzz(1) ) ),
1831 "dosearch" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ),
1832 "grant" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ),
1833 "comment" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1834 "token" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1837 // sometimes we don't want to specify certain parameters.
1838 if (wikiFuzz::randnum(2) == 0) unset($this->params["dosearch"]);
1839 if (wikiFuzz::randnum(2) == 0) unset($this->params["grant"]);
1840 if (wikiFuzz::randnum(5) == 0) unset($this->params["token"]);
1846 ** @desc: a test for Special:Makesysop (extension Special page).
1848 class specialMakesysop extends pageTest {
1849 function __construct() {
1850 $this->pagePath = "index.php/Special:Makesysop";
1852 $this->params = array (
1853 "wpMakesysopUser" => wikiFuzz::chooseInput( array("Nickj2", "192.168.0.2", wikifuzz::makeFuzz(1) ) ),
1854 "action" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ),
1855 "wpMakesysopSubmit" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ),
1856 "wpEditToken" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1857 "wpSetBureaucrat" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1860 // sometimes we don't want to specify certain parameters.
1861 if (wikiFuzz::randnum(3) == 0) unset($this->params["wpMakesysopSubmit"]);
1862 if (wikiFuzz::randnum(3) == 0) unset($this->params["wpEditToken"]);
1863 if (wikiFuzz::randnum(3) == 0) unset($this->params["wpSetBureaucrat"]);
1869 ** @desc: a test for Special:Renameuser (extension Special page).
1871 class specialRenameuser extends pageTest {
1872 function __construct() {
1873 $this->pagePath = "index.php/Special:Renameuser";
1875 $this->params = array (
1876 "oldusername" => wikiFuzz::chooseInput( array("Nickj2", "192.168.0.2", wikifuzz::makeFuzz(1) ) ),
1877 "newusername" => wikiFuzz::chooseInput( array("Nickj2", "192.168.0.2", wikifuzz::makeFuzz(1) ) ),
1878 "token" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ),
1885 ** @desc: a test for Special:Linksearch (extension Special page).
1887 class specialLinksearch extends pageTest {
1888 function __construct() {
1889 $this->pagePath = "index.php?title=Special%3ALinksearch";
1891 $this->params = array (
1892 "target" => wikifuzz::makeFuzz(2),
1895 // sometimes we don't want to specify certain parameters.
1896 if (wikiFuzz::randnum(10) == 0) unset($this->params["target"]);
1902 ** @desc: a test for Special:CategoryTree (extension Special page).
1904 class specialCategoryTree extends pageTest {
1905 function __construct() {
1906 $this->pagePath = "index.php?title=Special:CategoryTree";
1908 $this->params = array (
1909 "target" => wikifuzz::makeFuzz(2),
1910 "from" => wikifuzz::makeFuzz(2),
1911 "until" => wikifuzz::makeFuzz(2),
1912 "showas" => wikifuzz::makeFuzz(2),
1913 "mode" => wikiFuzz::chooseInput( array("pages", "categories", "all", wikifuzz::makeFuzz(2)) ),
1916 // sometimes we do want to specify certain parameters.
1917 if (wikiFuzz::randnum(5) == 0) $this->params["notree"] = wikiFuzz::chooseInput( array("1", 0, "", wikiFuzz::makeFuzz(2)) );
1924 ** @desc: selects a page test to run.
1926 function selectPageTest($count) {
1928 // if the user only wants a specific test, then only ever give them that.
1929 if (defined("SPECIFIC_TEST")) {
1930 $testType = SPECIFIC_TEST;
1931 return new $testType ();
1934 // Some of the time we test Special pages, the remaining
1935 // time we test using the standard edit page.
1936 switch ($count % 100) {
1937 case 0 : return new successfulUserLoginTest();
1938 case 1 : return new listusersTest();
1939 case 2 : return new searchTest();
1940 case 3 : return new recentchangesTest();
1941 case 4 : return new prefixindexTest();
1942 case 5 : return new mimeSearchTest();
1943 case 6 : return new specialLogTest();
1944 case 7 : return new userLoginTest();
1945 case 8 : return new ipblocklistTest();
1946 case 9 : return new newImagesTest();
1947 case 10: return new imagelistTest();
1948 case 11: return new specialExportTest();
1949 case 12: return new specialBooksourcesTest();
1950 case 13: return new specialAllpagesTest();
1951 case 14: return new pageHistoryTest();
1952 case 15: return new contributionsTest();
1953 case 16: return new viewPageTest();
1954 case 17: return new specialAllmessagesTest();
1955 case 18: return new specialNewpages();
1956 case 19: return new searchTest();
1957 case 20: return new redirectTest();
1958 case 21: return new confirmEmail();
1959 case 22: return new watchlistTest();
1960 case 23: return new specialBlockmeTest();
1961 case 24: return new specialUndelete();
1962 case 25: return new specialMovePage();
1963 case 26: return new specialUnlockdb();
1964 case 27: return new specialLockdb();
1965 case 28: return new specialUserrights();
1966 case 29: return new pageProtectionForm();
1967 case 30: return new specialBlockip();
1968 case 31: return new imagepageTest();
1969 case 32: return new pageDeletion();
1970 case 33: return new specialRevisionDelete();
1971 case 34: return new specialImport();
1972 case 35: return new thumbTest();
1973 case 36: return new trackbackTest();
1974 case 37: return new profileInfo();
1975 case 38: return new specialCite();
1976 case 39: return new specialFilepath();
1977 case 40: return new specialMakebot();
1978 case 41: return new specialMakesysop();
1979 case 42: return new specialRenameuser();
1980 case 43: return new specialLinksearch();
1981 case 44: return new specialCategoryTree();
1982 default: return new editPageTest();
1987 /////////////////////// SAVING OUTPUT /////////////////////////
1990 ** @desc: Utility function for saving a file. Currently has no error checking.
1992 function saveFile($data, $name) {
1993 file_put_contents($name, $data);
1998 ** @desc: Returns a test as an experimental GET-to-POST URL.
1999 ** This doesn't seem to always work though, and sometimes the output is too long
2000 ** to be a valid GET URL, so we also save in other formats.
2002 function getAsURL(pageTest $test) {
2003 $used_question_mark = (strpos($test->getPagePath(), "?") !== false);
2004 $retval = "http://get-to-post.nickj.org/?http://" . WIKI_BASE_URL . $test->getPagePath();
2005 foreach ($test->getParams() as $param => $value) {
2006 if (!$used_question_mark) {
2007 $retval .= "?";
2008 $used_question_mark = true;
2010 else {
2011 $retval .= "&";
2013 $retval .= $param . "=" . urlencode($value);
2015 return $retval;
2020 ** @desc: Saves a plain-text human-readable version of a test.
2022 function saveTestAsText(pageTest $test, $filename) {
2023 $str = "Test: " . $test->getPagePath();
2024 foreach ($test->getParams() as $param => $value) {
2025 $str .= "\n$param: $value";
2027 $str .= "\nGet-to-post URL: " . getAsURL($test) . "\n";
2028 saveFile($str, $filename);
2033 ** @desc: Saves a test as a standalone basic PHP script that shows this one problem.
2034 ** Resulting script requires PHP-Curl be installed in order to work.
2036 function saveTestAsPHP(pageTest $test, $filename) {
2037 $str = "<?php\n"
2038 . "\$params = " . var_export(escapeForCurl($test->getParams()), true) . ";\n"
2039 . "\$ch = curl_init();\n"
2040 . "curl_setopt(\$ch, CURLOPT_POST, 1);\n"
2041 . "curl_setopt(\$ch, CURLOPT_POSTFIELDS, \$params );\n"
2042 . "curl_setopt(\$ch, CURLOPT_URL, " . var_export(WIKI_BASE_URL . $test->getPagePath(), true) . ");\n"
2043 . "curl_setopt(\$ch, CURLOPT_RETURNTRANSFER,1);\n"
2044 . ($test->getCookie() ? "curl_setopt(\$ch, CURLOPT_COOKIE, " . var_export($test->getCookie(), true) . ");\n" : "")
2045 . "\$result=curl_exec(\$ch);\n"
2046 . "curl_close (\$ch);\n"
2047 . "print \$result;\n"
2048 . "?>\n";
2049 saveFile($str, $filename);
2054 ** @desc: Escapes a value so that it can be used on the command line by Curl.
2055 ** Specifically, "<" and "@" need to be escaped if they are the first character,
2056 ** otherwise curl interprets these as meaning that we want to insert a file.
2058 function escapeForCurl(array $input_params) {
2059 $output_params = array();
2060 foreach ($input_params as $param => $value) {
2061 if (strlen($value) > 0 && ( $value[0] == "@" || $value[0] == "<")) {
2062 $value = "\\" . $value;
2064 $output_params[$param] = $value;
2066 return $output_params;
2071 ** @desc: Saves a test as a standalone CURL shell script that shows this one problem.
2072 ** Resulting script requires standalone Curl be installed in order to work.
2074 function saveTestAsCurl(pageTest $test, $filename) {
2075 $str = "#!/bin/bash\n"
2076 . "curl --silent --include --globoff \\\n"
2077 . ($test->getCookie() ? " --cookie " . escapeshellarg($test->getCookie()) . " \\\n" : "");
2078 foreach (escapeForCurl($test->getParams()) as $param => $value) {
2079 $str .= " -F " . escapeshellarg($param) . "=" . escapeshellarg($value) . " \\\n";
2081 $str .= " " . escapeshellarg(WIKI_BASE_URL . $test->getPagePath()); // beginning space matters.
2082 $str .= "\n";
2083 saveFile($str, $filename);
2084 chmod($filename, 0755); // make executable
2089 ** @desc: Saves the internal data structure to file.
2091 function saveTestData (pageTest $test, $filename) {
2092 saveFile(serialize($test), $filename);
2097 ** @desc: saves a test in the various formats.
2099 function saveTest(pageTest $test, $testname) {
2100 $base_name = DIRECTORY . "/" . $testname;
2101 saveTestAsText($test, $base_name . INFO_FILE);
2102 saveTestAsPHP ($test, $base_name . PHP_TEST );
2103 saveTestAsCurl($test, $base_name . CURL_TEST);
2104 saveTestData ($test, $base_name . DATA_FILE);
2108 //////////////////// MEDIAWIKI OUTPUT /////////////////////////
2111 ** @desc: Asks MediaWiki for the HTML output of a test.
2113 function wikiTestOutput(pageTest $test) {
2115 $ch = curl_init();
2117 // specify the cookie, if required.
2118 if ($test->getCookie()) curl_setopt($ch, CURLOPT_COOKIE, $test->getCookie());
2119 curl_setopt($ch, CURLOPT_POST, 1); // save form using a POST
2121 $params = escapeForCurl($test->getParams());
2122 curl_setopt($ch, CURLOPT_POSTFIELDS, $params ); // load the POST variables
2124 curl_setopt($ch, CURLOPT_URL, WIKI_BASE_URL . $test->getPagePath() ); // set url to post to
2125 curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); // return into a variable
2127 $result=curl_exec ($ch);
2129 // if we encountered an error, then say so, and return an empty string.
2130 if (curl_error($ch)) {
2131 print "\nCurl error #: " . curl_errno($ch) . " - " . curl_error ($ch);
2132 $result = "";
2135 curl_close ($ch);
2137 return $result;
2141 //////////////////// HTML VALIDATION /////////////////////////
2144 ** @desc: Asks the validator whether this is valid HTML, or not.
2146 function validateHTML($text) {
2148 $params = array ("fragment" => $text);
2150 $ch = curl_init();
2152 curl_setopt($ch, CURLOPT_POST, 1); // save form using a POST
2153 curl_setopt($ch, CURLOPT_POSTFIELDS, $params); // load the POST variables
2154 curl_setopt($ch, CURLOPT_URL, VALIDATOR_URL); // set url to post to
2155 curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); // return into a variable
2157 $result=curl_exec ($ch);
2159 // if we encountered an error, then log it, and exit.
2160 if (curl_error($ch)) {
2161 trigger_error("Curl error #: " . curl_errno($ch) . " - " . curl_error ($ch) );
2162 print "Curl error #: " . curl_errno($ch) . " - " . curl_error ($ch) . " - exiting.\n";
2163 exit();
2166 curl_close ($ch);
2168 $valid = (strpos($result, "Failed validation") === false ? true : false);
2170 return array($valid, $result);
2175 ** @desc: Get tidy to check for no HTML errors in the output file (e.g. unescaped strings).
2177 function tidyCheckFile($name) {
2178 $file = DIRECTORY . "/" . $name;
2179 $command = PATH_TO_TIDY . " -output /tmp/out.html -quiet $file 2>&1";
2180 $x = `$command`;
2182 // Look for the most interesting Tidy errors and warnings.
2183 if ( strpos($x,"end of file while parsing attributes") !== false
2184 || strpos($x,"attribute with missing trailing quote mark") !== false
2185 || strpos($x,"missing '>' for end of tag") !== false
2186 || strpos($x,"Error:") !== false) {
2187 print "\nTidy found something - view details with: $command";
2188 return false;
2189 } else {
2190 return true;
2196 ** @desc: Returns whether or not an database error log file has changed in size since
2197 ** the last time this was run. This is used to tell if a test caused a DB error.
2199 function dbErrorLogged() {
2200 static $filesize;
2202 // first time running this function
2203 if (!isset($filesize)) {
2204 // create log if it does not exist
2205 if (!file_exists(DB_ERROR_LOG_FILE)) {
2206 saveFile("", DB_ERROR_LOG_FILE);
2208 $filesize = filesize(DB_ERROR_LOG_FILE);
2209 return false;
2212 $newsize = filesize(DB_ERROR_LOG_FILE);
2213 // if the log has grown, then assume the current test caused it.
2214 if ($newsize != $filesize) {
2215 $filesize = $newsize;
2216 return true;
2219 return false;
2222 ////////////////// TOP-LEVEL PROBLEM-FINDING FUNCTION ////////////////////////
2225 ** @desc: takes a page test, and runs it and tests it for problems in the output.
2226 ** Returns: False on finding a problem, or True on no problems being found.
2228 function runWikiTest(pageTest $test, &$testname, $can_overwrite = false) {
2230 // by default don't overwrite a previous test of the same name.
2231 while ( ! $can_overwrite && file_exists(DIRECTORY . "/" . $testname . DATA_FILE)) {
2232 $testname .= "-" . mt_rand(0,9);
2235 $filename = DIRECTORY . "/" . $testname . DATA_FILE;
2237 // Store the time before and after, to find slow pages.
2238 $before = microtime(true);
2240 // Get MediaWiki to give us the output of this test.
2241 $wiki_preview = wikiTestOutput($test);
2243 $after = microtime(true);
2245 // if we received no response, then that's interesting.
2246 if ($wiki_preview == "") {
2247 print "\nNo response received for: $filename";
2248 return false;
2251 // save output HTML to file.
2252 $html_file = DIRECTORY . "/" . $testname . HTML_FILE;
2253 saveFile($wiki_preview, $html_file);
2255 // if there were PHP errors in the output, then that's interesting too.
2256 if ( strpos($wiki_preview, "<b>Warning</b>: " ) !== false
2257 || strpos($wiki_preview, "<b>Fatal error</b>: ") !== false
2258 || strpos($wiki_preview, "<b>Notice</b>: " ) !== false
2259 || strpos($wiki_preview, "<b>Error</b>: " ) !== false ) {
2260 $error = substr($wiki_preview, strpos($wiki_preview, "</b>:") + 7, 50);
2261 // Avoid probable PHP bug with bad session ids; http://bugs.php.net/bug.php?id=38224
2262 if ($error != "Unknown: The session id contains illegal character") {
2263 print "\nPHP error/warning/notice in HTML output: $html_file ; $error";
2264 return false;
2268 // if there was a MediaWiki Backtrace message in the output, then that's also interesting.
2269 if (strpos($wiki_preview, "Backtrace:") !== false) {
2270 print "\nInternal MediaWiki error in HTML output: $html_file";
2271 return false;
2274 // if there was a Parser error comment in the output, then that's potentially interesting.
2275 if (strpos($wiki_preview, "!-- ERR") !== false) {
2276 print "\nParser Error comment in HTML output: $html_file";
2277 return false;
2280 // if a database error was logged, then that's definitely interesting.
2281 if (dbErrorLogged()) {
2282 print "\nDatabase Error logged for: $filename";
2283 return false;
2286 // validate result
2287 $valid = true;
2288 if (VALIDATE_ON_WEB) {
2289 list ($valid, $validator_output) = validateHTML($wiki_preview);
2290 if (!$valid) print "\nW3C web validation failed - view details with: html2text " . DIRECTORY . "/" . $testname . ".validator_output.html";
2293 // Get tidy to check the page, unless it is a test which produces XML.
2294 if (!$test instanceof trackbackTest && !$test instanceof specialExportTest) {
2295 $valid = tidyCheckFile( $testname . HTML_FILE ) && $valid;
2298 // if it took more than 2 seconds to render, then it may be interesting too. (Possible DoS attack?)
2299 if (($after - $before) >= 2) {
2300 print "\nParticularly slow to render (" . round($after - $before, 2) . " seconds): $filename";
2301 return false;
2304 if( $valid ) {
2305 // Remove temp HTML file if test was valid:
2306 unlink( $html_file );
2307 } elseif( VALIDATE_ON_WEB ) {
2308 saveFile($validator_output, DIRECTORY . "/" . $testname . ".validator_output.html");
2311 return $valid;
2315 /////////////////// RERUNNING OLD TESTS ///////////////////
2318 ** @desc: We keep our failed tests so that they can be rerun.
2319 ** This function does that retesting.
2321 function rerunPreviousTests() {
2322 print "Retesting previously found problems.\n";
2324 $dir_contents = scandir (DIRECTORY);
2326 // sort file into the order a normal person would use.
2327 natsort ($dir_contents);
2329 foreach ($dir_contents as $file) {
2331 // if file is not a test, then skip it.
2332 // Note we need to escape any periods or will be treated as "any character".
2333 $matches = array();
2334 if (!ereg("(.*)" . str_replace(".", "\.", DATA_FILE) . "$", $file, $matches)) continue;
2336 // reload the test.
2337 $full_path = DIRECTORY . "/" . $file;
2338 $test = unserialize(file_get_contents($full_path));
2340 // if this is not a valid test, then skip it.
2341 if (! $test instanceof pageTest) {
2342 print "\nSkipping invalid test - $full_path";
2343 continue;
2346 // The date format is in Apache log format, which makes it easier to locate
2347 // which retest caused which error in the Apache logs (only happens usually if
2348 // apache segfaults).
2349 if (!QUIET) print "[" . date ("D M d H:i:s Y") . "] Retesting $file (" . get_class($test) . ")";
2351 // run test
2352 $testname = $matches[1];
2353 $valid = runWikiTest($test, $testname, true);
2355 if (!$valid) {
2356 saveTest($test, $testname);
2357 if (QUIET) {
2358 print "\nTest: " . get_class($test) . " ; Testname: $testname\n------";
2359 } else {
2360 print "\n";
2363 else {
2364 if (!QUIET) print "\r";
2365 if (DELETE_PASSED_RETESTS) {
2366 $prefix = DIRECTORY . "/" . $testname;
2367 if (is_file($prefix . DATA_FILE)) unlink($prefix . DATA_FILE);
2368 if (is_file($prefix . PHP_TEST )) unlink($prefix . PHP_TEST );
2369 if (is_file($prefix . CURL_TEST)) unlink($prefix . CURL_TEST);
2370 if (is_file($prefix . INFO_FILE)) unlink($prefix . INFO_FILE);
2375 print "\nDone retesting.\n";
2379 ////////////////////// MAIN LOOP ////////////////////////
2382 // first check whether CURL is installed, because sometimes it's not.
2383 if( ! function_exists('curl_init') ) {
2384 die("Could not find 'curl_init' function. Is the curl extension compiled into PHP?\n");
2387 // Initialization of types. wikiFuzz doesn't have a constructor because we want to
2388 // access it staticly and not have any globals.
2389 wikiFuzz::$types = array_keys(wikiFuzz::$data);
2391 // Make directory if doesn't exist
2392 if (!is_dir(DIRECTORY)) {
2393 mkdir (DIRECTORY, 0700 );
2395 // otherwise, we first retest the things that we have found in previous runs
2396 else if (RERUN_OLD_TESTS) {
2397 rerunPreviousTests();
2400 // seed the random number generator
2401 mt_srand(crc32(microtime()));
2403 // main loop.
2404 $start_time = date("U");
2405 $num_errors = 0;
2406 if (!QUIET) print "Beginning main loop. Results are stored in the " . DIRECTORY . " directory.\n";
2407 if (!QUIET) print "Press CTRL+C to stop testing.\n";
2409 for ($count=0; true; $count++) {
2410 if (!QUIET) {
2411 // spinning progress indicator.
2412 switch( $count % 4 ) {
2413 case '0': print "\r/"; break;
2414 case '1': print "\r-"; break;
2415 case '2': print "\r\\"; break;
2416 case '3': print "\r|"; break;
2418 print " $count";
2421 // generate a page test to run.
2422 $test = selectPageTest($count);
2424 $mins = ( date("U") - $start_time ) / 60;
2425 if (!QUIET && $mins > 0) {
2426 print ". $num_errors poss errors. "
2427 . floor($mins) . " mins. "
2428 . round ($count / $mins, 0) . " tests/min. "
2429 . get_class($test); // includes the current test name.
2432 // run this test against MediaWiki, and see if the output was valid.
2433 $testname = $count;
2434 $valid = runWikiTest($test, $testname, false);
2436 // save the failed test
2437 if (!$valid) {
2438 if (QUIET) {
2439 print "\nTest: " . get_class($test) . " ; Testname: $testname\n------";
2440 } else {
2441 print "\n";
2443 saveTest($test, $testname);
2444 $num_errors += 1;
2447 // stop if we have reached max number of errors.
2448 if (defined("MAX_ERRORS") && $num_errors>=MAX_ERRORS) {
2449 break;
2452 // stop if we have reached max number of mins runtime.
2453 if (defined("MAX_RUNTIME") && $mins>=MAX_RUNTIME) {
2454 break;