Update.
[lhc/web/wiklou.git] / maintenance / fuzz-tester.php
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
8
9 Started: 18 May 2006.
10
11 Description:
12 Performs fuzz-style testing of MediaWiki's parser and forms.
13
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.
20
21 Why:
22 - To help find bugs.
23 - To help find security issues, or potential security issues.
24
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.
33
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
39
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
43
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").
49
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")
55
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.
64
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:
70
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;
100
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).
109
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");
118
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 ---------
127
128 Also add/change this in AdminSettings.php:
129 // --------- Start ---------
130 $wgEnableProfileInfo = true;
131 $wgDBserver = "localhost"; // replace with DB server hostname
132 // --------- End ---------
133
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.
138
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.
145
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).
153
154 */
155
156 /////////////////////////// COMMAND LINE HELP ////////////////////////////////////
157
158 // This is a command line script, load MediaWiki env (gives command line options);
159 include('commandLine.inc');
160
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>]
172
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.
194
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
200
201
202 ENDS;
203
204 exit( 0 );
205 }
206
207
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";
217 }
218 }
219
220
221 ///////////////////////////// CONFIGURATION ////////////////////////////////////
222
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 . '/');
228 }
229
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");
236 }
237
238 // Should our test fuzz data include binary strings?
239 define("INCLUDE_BINARY", isset($options["include-binary"]) );
240
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");
246
247 // Location of Tidy standalone executable.
248 define("PATH_TO_TIDY", "/usr/bin/tidy");
249
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");
256 }
257
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");
265 }
266
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"]) );
270
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"]) );
274
275 // File where the database errors are logged. Should be defined in LocalSettings.php.
276 define("DB_ERROR_LOG_FILE", $wgDBerrorLog );
277
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"]) );
280
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"]) );
284 }
285
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"]) );
289 }
290
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"] );
295 }
296 else {
297 print "Ignoring invalid --specific-test\n";
298 }
299 }
300
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");
307
308 // If it goes wrong, we want to know about it.
309 error_reporting(E_ALL | E_STRICT);
310
311 //////////////// A CLASS THAT GENERATES RANDOM NASTY WIKI & HTML STRINGS //////////////////////
312
313 class wikiFuzz {
314
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"),
369
370 // extension tags that accept parameters:
371 "sort" => array("order", "class"),
372 "ref" => array("name"),
373 "categorytree" => array("hideroot", "mode", "style"),
374 );
375
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;
380
381
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",
388
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.
394
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.
400
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"
410
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.
417
418 // Different ways of saying: [
419 "&#0000091;", // Long UTF-8 Unicode encoding
420 "&#91;",
421 "&#x5B;", // hex version.
422
423 // Different ways of saying: {{
424 "&#0000123;&#0000123;", // Long UTF-8 Unicode encoding
425 "&#123;&#123;",
426 "&#x7B;&#x7B;", // hex version.
427
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.
433
434 // a "lignature" - http://www.robinlionheart.com/stds/html4/spchars#ligature
435 "&zwnj;"
436 );
437
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 "[[", "]]", "{{", "}}", "|", "[", "]", "{{{", "}}}", "|]]",
443
444 // wiki tables.
445 "\n{|", "\n|}",
446 "!",
447 "\n!",
448 "!!",
449 "||",
450 "\n|-", "| ", "\n|",
451
452 // section headings.
453 "=", "==", "===", "====", "=====", "======",
454
455 // lists (ordered and unordered) and indentation.
456 "\n*", "*", "\n:", ":",
457 "\n#", "#",
458
459 // definition lists (dl, dt, dd), newline, and newline with pre, and a tab.
460 "\n;", ";", "\n ",
461
462 // Whitespace: newline, tab, space.
463 "\n", "\t", " ",
464
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
472
473 // various NULL fields
474 "%00",
475 "&#00;",
476 "\0",
477
478 // horizontal rule.
479 "-----", "\n-----",
480
481 // signature, redirect, bold, italics.
482 "~~~~", "#REDIRECT [[", "'''", "''",
483
484 // comments.
485 "<!--", "-->",
486
487 // quotes.
488 "\"", "'",
489
490 // tag start and tag end.
491 "<", ">",
492
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:',
504
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',
526
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-->',
552
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 ",
560
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__",
575
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 "}}",
628
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 "}}",
644
645 // internal Math "extension":
646 "<math>",
647 "</math>",
648
649 // Parser extension functions:
650 "{{#expr:",
651 "{{#if:",
652 "{{#ifeq:",
653 "{{#ifexist:",
654 "{{#ifexpr:",
655 "{{#switch:",
656 "{{#time:",
657 "}}",
658
659 // references table for the Cite extension.
660 "<references/>",
661
662 // Internal Parser tokens - try inserting some of these.
663 "UNIQ25f46b0524f13e67NOPARSE",
664 "UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002",
665 "\x07UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002-QINU",
666
667 // Inputbox extension:
668 "<inputbox>\ntype=search\nsearchbuttonlabel=\n",
669 "</inputbox>",
670
671 // charInsert extension:
672 "<charInsert>",
673 "</charInsert>",
674
675 // wikiHiero extension:
676 "<hiero>",
677 "</hiero>",
678
679 // Image gallery:
680 "<gallery>",
681 "</gallery>",
682
683 // FixedImage:
684 "<fundraising/>",
685
686 // Timeline extension: currently untested.
687
688 // Nowiki:
689 "<nOwIkI>",
690 "</nowiki>",
691
692 // an external image to test the external image displaying code
693 "http://debian.org/Pics/debian.png",
694 );
695
696 /**
697 ** Randomly returns one element of the input array.
698 */
699 static public function chooseInput(array $input) {
700 $randindex = wikiFuzz::randnum(count($input) - 1);
701 return $input[$randindex];
702 }
703
704 // Max number of parameters for HTML attributes.
705 static private $maxparams = 10;
706
707 /**
708 ** Returns random number between finish and start.
709 */
710 static public function randnum($finish,$start=0) {
711 return mt_rand($start,$finish);
712 }
713
714 /**
715 ** Returns a mix of random text and random wiki syntax.
716 */
717 static private function randstring() {
718 $thestring = "";
719
720 for ($i=0; $i<40; $i++) {
721 $what = wikiFuzz::randnum(1);
722
723 if ($what == 0) { // include some random wiki syntax
724 $which = wikiFuzz::randnum(count(wikiFuzz::$ext) - 1);
725 $thestring .= wikiFuzz::$ext[$which];
726 }
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));
734
735 $length = wikiFuzz::randnum(8);
736 $thestring .= str_repeat ($char, $length);
737 }
738 }
739 return $thestring;
740 }
741
742 /**
743 ** Returns either random text, or random wiki syntax, or random data from "ints",
744 ** or random data from "other".
745 */
746 static private function makestring() {
747 $what = wikiFuzz::randnum(2);
748 if ($what == 0) {
749 return wikiFuzz::randstring();
750 }
751 elseif ($what == 1) {
752 return wikiFuzz::$ints[wikiFuzz::randnum(count(wikiFuzz::$ints) - 1)];
753 }
754 else {
755 return wikiFuzz::$other[wikiFuzz::randnum(count(wikiFuzz::$other) - 1)];
756 }
757 }
758
759
760 /**
761 ** Strips out the stuff that Mediawiki balks at in a page's title.
762 ** Implementation copied/pasted from cleanupTable.inc & cleanupImages.php
763 */
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] ) );'
773 ),
774 $str );
775 }
776
777 /**
778 ** Returns a string of fuzz text.
779 */
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() . " ";
793 }
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();
801 }
802 return ""; // catch-all, should never be called.
803 }
804
805 /**
806 ** Returns one of the three styles of random quote: ', ", and nothing.
807 */
808 static private function getRandQuote() {
809 switch ( wikiFuzz::randnum(3) ) {
810 case 1 : return "'";
811 case 2 : return "\"";
812 default: return "";
813 }
814 }
815
816 /**
817 ** Returns fuzz text, with the parameter indicating approximately how many lines of text you want.
818 */
819 static public function makeFuzz($maxtypes = 2) {
820 $page = "";
821 for ($k=0; $k<$maxtypes; $k++) {
822 $page .= wikiFuzz::loop();
823 }
824 return $page;
825 }
826 }
827
828
829 //////// MEDIAWIKI PAGES TO TEST, AND HOW TO TEST THEM ///////
830
831 /**
832 ** 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.
838 */
839 abstract class pageTest {
840 protected $params;
841 protected $pagePath;
842 protected $cookie = "";
843
844 public function getParams() {
845 return $this->params;
846 }
847
848 public function getPagePath() {
849 return $this->pagePath;
850 }
851
852 public function getCookie() {
853 return $this->cookie;
854 }
855 }
856
857
858 /**
859 ** a page test for the "Edit" page. Tests Parser.php and Sanitizer.php.
860 */
861 class editPageTest extends pageTest {
862 function __construct() {
863 $this->pagePath = "index.php?title=WIKIFUZZ";
864
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.
876 );
877
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"]);
886 }
887 }
888
889
890 /**
891 ** a page test for "Special:Listusers".
892 */
893 class listusersTest extends pageTest {
894 function __construct() {
895 $this->pagePath = "index.php/Special:Listusers";
896
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)) )
904 );
905 }
906 }
907
908
909 /**
910 ** a page test for "Special:Search".
911 */
912 class searchTest extends pageTest {
913 function __construct() {
914 $this->pagePath = "index.php/Special:Search";
915
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)) )
939 );
940 }
941 }
942
943
944 /**
945 ** a page test for "Special:Recentchanges".
946 */
947 class recentchangesTest extends pageTest {
948 function __construct() {
949 $this->pagePath = "index.php/Special:Recentchanges";
950
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)) )
968 );
969 }
970 }
971
972
973 /**
974 ** a page test for "Special:Prefixindex".
975 */
976 class prefixindexTest extends pageTest {
977 function __construct() {
978 $this->pagePath = "index.php/Special:Prefixindex";
979
980 $this->params = array (
981 "title" => "Special:Prefixindex",
982 "namespace" => wikiFuzz::randnum(-10,101),
983 "Go" => wikiFuzz::makeFuzz(2)
984 );
985
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)) );
990 }
991 if (wikiFuzz::randnum(3) == 0) {
992 $this->params["from"] = wikiFuzz::chooseInput( array("-1", "-----'--------0", "+++--+1",
993 wikiFuzz::randnum(-10,8134), wikiFuzz::makeFuzz(2)) );
994 }
995 }
996 }
997
998
999 /**
1000 ** a page test for "Special:MIMEsearch".
1001 */
1002 class mimeSearchTest extends pageTest {
1003 function __construct() {
1004 $this->pagePath = "index.php/Special:MIMEsearch";
1005
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)) )
1011 );
1012 }
1013 }
1014
1015
1016 /**
1017 ** a page test for "Special:Log".
1018 */
1019 class specialLogTest extends pageTest {
1020 function __construct() {
1021 $this->pagePath = "index.php/Special:Log";
1022
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)
1031 );
1032 }
1033 }
1034
1035
1036 /**
1037 ** a page test for "Special:Userlogin", with a successful login.
1038 */
1039 class successfulUserLoginTest extends pageTest {
1040 function __construct() {
1041 $this->pagePath = "index.php?title=Special:Userlogin&action=submitlogin&type=login&returnto=" . wikiFuzz::makeFuzz(2);
1042
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)
1048 );
1049
1050 $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array("1" , wikiFuzz::makeFuzz(2) ) );
1051 }
1052 }
1053
1054
1055 /**
1056 ** a page test for "Special:Userlogin".
1057 */
1058 class userLoginTest extends pageTest {
1059 function __construct() {
1060
1061 $this->pagePath = "index.php/Special:Userlogin";
1062
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)) )
1079 );
1080
1081 $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array("1" , wikiFuzz::makeFuzz(2) ) );
1082 }
1083 }
1084
1085
1086 /**
1087 ** a page test for "Special:Ipblocklist" (also includes unblocking)
1088 */
1089 class ipblocklistTest extends pageTest {
1090 function __construct() {
1091 $this->pagePath = "index.php/Special:Ipblocklist";
1092
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)) )
1108 );
1109
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"]);
1115 }
1116 }
1117
1118
1119 /**
1120 ** a page test for "Special:Newimages".
1121 */
1122 class newImagesTest extends pageTest {
1123 function __construct() {
1124 $this->pagePath = "index.php/Special:Newimages";
1125
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)
1131 );
1132
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"]);
1136 }
1137 }
1138
1139
1140 /**
1141 ** a page test for the "Special:Imagelist" page.
1142 */
1143 class imagelistTest extends pageTest {
1144 function __construct() {
1145 $this->pagePath = "index.php/Special:Imagelist";
1146
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)
1152 );
1153 }
1154 }
1155
1156
1157 /**
1158 ** a page test for "Special:Export".
1159 */
1160 class specialExportTest extends pageTest {
1161 function __construct() {
1162 $this->pagePath = "index.php/Special:Export";
1163
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)) ),
1170
1171 );
1172
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'] = '';
1175
1176 // Sometimes remove the history field.
1177 if (wikiFuzz::randnum(2) == 0) unset($this->params["history"]);
1178 }
1179 }
1180
1181
1182 /**
1183 ** a page test for "Special:Booksources".
1184 */
1185 class specialBooksourcesTest extends pageTest {
1186 function __construct() {
1187 $this->pagePath = "index.php/Special:Booksources";
1188
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)
1193 );
1194 }
1195 }
1196
1197
1198 /**
1199 ** a page test for "Special:Allpages".
1200 */
1201 class specialAllpagesTest extends pageTest {
1202 function __construct() {
1203 $this->pagePath = "index.php?title=Special%3AAllpages";
1204
1205 $this->params = array (
1206 'from' => wikiFuzz::makeFuzz(2),
1207 'namespace' => wikiFuzz::chooseInput( range(-1, 15) ),
1208 'go' => wikiFuzz::makeFuzz(2)
1209 );
1210 }
1211 }
1212
1213
1214 /**
1215 ** a page test for the page History.
1216 */
1217 class pageHistoryTest extends pageTest {
1218 function __construct() {
1219 $this->pagePath = "index.php?title=Main_Page&action=history";
1220
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)
1229 );
1230 }
1231 }
1232
1233
1234 /**
1235 ** a page test for the Special:Contributions".
1236 */
1237 class contributionsTest extends pageTest {
1238 function __construct() {
1239 $this->pagePath = "index.php/Special:Contributions/" . USER_ON_WIKI;
1240
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)) )
1247 );
1248 }
1249 }
1250
1251
1252 /**
1253 ** a page test for viewing a normal page, whilst posting various params.
1254 */
1255 class viewPageTest extends pageTest {
1256 function __construct() {
1257 $this->pagePath = "index.php/Main_Page";
1258
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),
1297
1298 );
1299
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"]);
1303
1304 // Raw pages cannot really be validated
1305 if ($this->params["action"] == "raw") unset($this->params["action"]);
1306
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"]);
1312
1313 // usually don't want action == purge.
1314 if (wikiFuzz::randnum(6) > 1) unset($this->params["action"]);
1315 }
1316 }
1317
1318
1319 /**
1320 ** a page test for "Special:Allmessages".
1321 */
1322 class specialAllmessagesTest extends pageTest {
1323 function __construct() {
1324 $this->pagePath = "index.php?title=Special:Allmessages";
1325
1326 // only really has one parameter
1327 $this->params = array (
1328 "ot" => wikiFuzz::chooseInput( array("php", "html", wikiFuzz::makeFuzz(2)) )
1329 );
1330 }
1331 }
1332
1333 /**
1334 ** a page test for "Special:Newpages".
1335 */
1336 class specialNewpages extends pageTest {
1337 function __construct() {
1338 $this->pagePath = "index.php/Special:Newpages";
1339
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)) )
1345 );
1346
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"]);
1350 }
1351 }
1352
1353 /**
1354 ** a page test for "redirect.php"
1355 */
1356 class redirectTest extends pageTest {
1357 function __construct() {
1358 $this->pagePath = "redirect.php";
1359
1360 $this->params = array (
1361 "wpDropdown" => wikiFuzz::makeFuzz(2)
1362 );
1363
1364 // sometimes we don't want to specify certain parameters.
1365 if (wikiFuzz::randnum(6) == 0) unset($this->params["wpDropdown"]);
1366 }
1367 }
1368
1369
1370 /**
1371 ** a page test for "Special:Confirmemail"
1372 */
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)) ) );
1377
1378 $this->params = array (
1379 "token" => wikiFuzz::makeFuzz(2)
1380 );
1381 }
1382 }
1383
1384
1385 /**
1386 ** a page test for "Special:Watchlist"
1387 ** Note: this test would be better if we were logged in.
1388 */
1389 class watchlistTest extends pageTest {
1390 function __construct() {
1391 $this->pagePath = "index.php?title=Special:Watchlist";
1392
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)) )
1403 );
1404
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)) );
1407 }
1408 }
1409
1410
1411 /**
1412 ** a page test for "Special:Blockme"
1413 */
1414 class specialBlockmeTest extends pageTest {
1415 function __construct() {
1416 $this->pagePath = "index.php?title=Special:Blockme";
1417
1418 $this->params = array ( );
1419
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)) );
1423 }
1424 }
1425 }
1426
1427
1428 /**
1429 ** a page test for "Special:Movepage"
1430 */
1431 class specialMovePage extends pageTest {
1432 function __construct() {
1433 $this->pagePath = "index.php?title=Special:Movepage";
1434
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)) )
1449 );
1450
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"]);
1457 }
1458 }
1459
1460
1461 /**
1462 ** a page test for "Special:Undelete"
1463 */
1464 class specialUndelete extends pageTest {
1465 function __construct() {
1466 $this->pagePath = "index.php?title=Special:Undelete";
1467
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)
1477 );
1478
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"]);
1484 }
1485 }
1486
1487
1488 /**
1489 ** a page test for "Special:Unlockdb"
1490 */
1491 class specialUnlockdb extends pageTest {
1492 function __construct() {
1493 $this->pagePath = "index.php?title=Special:Unlockdb";
1494
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)) )
1499 );
1500
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"]);
1505 }
1506 }
1507
1508
1509 /**
1510 ** a page test for "Special:Lockdb"
1511 */
1512 class specialLockdb extends pageTest {
1513 function __construct() {
1514 $this->pagePath = "index.php?title=Special:Lockdb";
1515
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)) )
1521 );
1522
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"]);
1527 }
1528 }
1529
1530
1531 /**
1532 ** a page test for "Special:Userrights"
1533 */
1534 class specialUserrights extends pageTest {
1535 function __construct() {
1536 $this->pagePath = "index.php/Special:Userrights";
1537
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)) )
1545 );
1546
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']);
1550 }
1551 }
1552
1553
1554 /**
1555 ** a test for page protection and unprotection.
1556 */
1557 class pageProtectionForm extends pageTest {
1558 function __construct() {
1559 $this->pagePath = "index.php?title=Main_Page";
1560
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)) )
1568 );
1569
1570
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']);
1574 }
1575 }
1576
1577
1578 /**
1579 ** a page test for "Special:Blockip".
1580 */
1581 class specialBlockip extends pageTest {
1582 function __construct() {
1583 $this->pagePath = "index.php/Special:Blockip";
1584
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)) )
1603 );
1604
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"]);
1613 }
1614 }
1615
1616
1617 /**
1618 ** a test for the imagepage.
1619 */
1620 class imagepageTest extends pageTest {
1621 function __construct() {
1622 $this->pagePath = "index.php/Image:Small-email.png";
1623
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)) ),
1629 );
1630
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"]);
1636 }
1637 }
1638
1639
1640 /**
1641 ** a test for page deletion form.
1642 */
1643 class pageDeletion extends pageTest {
1644 function __construct() {
1645 $this->pagePath = "index.php?title=Main_Page&action=delete";
1646
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)) ),
1651 );
1652
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"]);
1657 }
1658 }
1659
1660
1661
1662 /**
1663 ** a test for Revision Deletion.
1664 */
1665 class specialRevisionDelete extends pageTest {
1666 function __construct() {
1667 $this->pagePath = "index.php?title=Special:Revisiondelete";
1668
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)) ),
1678 );
1679
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"]);
1689 }
1690 }
1691
1692
1693 /**
1694 ** a test for Special:Import.
1695 */
1696 class specialImport extends pageTest {
1697 function __construct() {
1698 $this->pagePath = "index.php/Special:Import";
1699
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),
1709 );
1710
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"]);
1719
1720 // Note: Need to do a file upload to fully test this Special page.
1721 }
1722 }
1723
1724
1725
1726 /**
1727 ** a test for thumb.php
1728 */
1729 class thumbTest extends pageTest {
1730 function __construct() {
1731 $this->pagePath = "thumb.php";
1732
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)) ),
1737 );
1738
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"]);
1743 }
1744 }
1745
1746
1747 /**
1748 ** a test for trackback.php
1749 */
1750 class trackbackTest extends pageTest {
1751 function __construct() {
1752 $this->pagePath = "trackback.php";
1753
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),
1760 );
1761
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"]);
1765 }
1766 }
1767
1768
1769 /**
1770 ** a test for profileinfo.php
1771 */
1772 class profileInfo extends pageTest {
1773 function __construct() {
1774 $this->pagePath = "profileinfo.php";
1775
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)) ),
1780 );
1781
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"]);
1785 }
1786 }
1787
1788
1789 /**
1790 ** a test for Special:Cite (extension Special page).
1791 */
1792 class specialCite extends pageTest {
1793 function __construct() {
1794 $this->pagePath = "index.php?title=Special:Cite";
1795
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)) ),
1799 );
1800
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"]);
1804 }
1805 }
1806
1807
1808 /**
1809 ** a test for Special:Filepath (extension Special page).
1810 */
1811 class specialFilepath extends pageTest {
1812 function __construct() {
1813 $this->pagePath = "index.php/Special:Filepath";
1814
1815 $this->params = array (
1816 "file" => wikiFuzz::chooseInput( array("Small-email.png", "Small-email.png" . wikifuzz::makeFuzz(1), wikiFuzz::makeFuzz(2)) ),
1817 );
1818 }
1819 }
1820
1821
1822 /**
1823 ** a test for Special:Makebot (extension Special page).
1824 */
1825 class specialMakebot extends pageTest {
1826 function __construct() {
1827 $this->pagePath = "index.php/Special:Makebot";
1828
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)) ),
1835 );
1836
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"]);
1841 }
1842 }
1843
1844
1845 /**
1846 ** a test for Special:Makesysop (extension Special page).
1847 */
1848 class specialMakesysop extends pageTest {
1849 function __construct() {
1850 $this->pagePath = "index.php/Special:Makesysop";
1851
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)) ),
1858 );
1859
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"]);
1864 }
1865 }
1866
1867
1868 /**
1869 ** a test for Special:Renameuser (extension Special page).
1870 */
1871 class specialRenameuser extends pageTest {
1872 function __construct() {
1873 $this->pagePath = "index.php/Special:Renameuser";
1874
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)) ),
1879 );
1880 }
1881 }
1882
1883
1884 /**
1885 ** a test for Special:Linksearch (extension Special page).
1886 */
1887 class specialLinksearch extends pageTest {
1888 function __construct() {
1889 $this->pagePath = "index.php?title=Special%3ALinksearch";
1890
1891 $this->params = array (
1892 "target" => wikifuzz::makeFuzz(2),
1893 );
1894
1895 // sometimes we don't want to specify certain parameters.
1896 if (wikiFuzz::randnum(10) == 0) unset($this->params["target"]);
1897 }
1898 }
1899
1900
1901 /**
1902 ** a test for Special:CategoryTree (extension Special page).
1903 */
1904 class specialCategoryTree extends pageTest {
1905 function __construct() {
1906 $this->pagePath = "index.php?title=Special:CategoryTree";
1907
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)) ),
1914 );
1915
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)) );
1918 }
1919 }
1920
1921
1922
1923 /**
1924 ** selects a page test to run.
1925 */
1926 function selectPageTest($count) {
1927
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 ();
1932 }
1933
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();
1983 }
1984 }
1985
1986
1987 /////////////////////// SAVING OUTPUT /////////////////////////
1988
1989 /**
1990 ** Utility function for saving a file. Currently has no error checking.
1991 */
1992 function saveFile($data, $name) {
1993 file_put_contents($name, $data);
1994 }
1995
1996
1997 /**
1998 ** 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.
2001 */
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;
2009 }
2010 else {
2011 $retval .= "&";
2012 }
2013 $retval .= $param . "=" . urlencode($value);
2014 }
2015 return $retval;
2016 }
2017
2018
2019 /**
2020 ** Saves a plain-text human-readable version of a test.
2021 */
2022 function saveTestAsText(pageTest $test, $filename) {
2023 $str = "Test: " . $test->getPagePath();
2024 foreach ($test->getParams() as $param => $value) {
2025 $str .= "\n$param: $value";
2026 }
2027 $str .= "\nGet-to-post URL: " . getAsURL($test) . "\n";
2028 saveFile($str, $filename);
2029 }
2030
2031
2032 /**
2033 ** 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.
2035 */
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);
2050 }
2051
2052
2053 /**
2054 ** 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.
2057 */
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;
2063 }
2064 $output_params[$param] = $value;
2065 }
2066 return $output_params;
2067 }
2068
2069
2070 /**
2071 ** 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.
2073 */
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";
2080 }
2081 $str .= " " . escapeshellarg(WIKI_BASE_URL . $test->getPagePath()); // beginning space matters.
2082 $str .= "\n";
2083 saveFile($str, $filename);
2084 chmod($filename, 0755); // make executable
2085 }
2086
2087
2088 /**
2089 ** Saves the internal data structure to file.
2090 */
2091 function saveTestData (pageTest $test, $filename) {
2092 saveFile(serialize($test), $filename);
2093 }
2094
2095
2096 /**
2097 ** saves a test in the various formats.
2098 */
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);
2105 }
2106
2107
2108 //////////////////// MEDIAWIKI OUTPUT /////////////////////////
2109
2110 /**
2111 ** Asks MediaWiki for the HTML output of a test.
2112 */
2113 function wikiTestOutput(pageTest $test) {
2114
2115 $ch = curl_init();
2116
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
2120
2121 $params = escapeForCurl($test->getParams());
2122 curl_setopt($ch, CURLOPT_POSTFIELDS, $params ); // load the POST variables
2123
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
2126
2127 $result=curl_exec ($ch);
2128
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 = "";
2133 }
2134
2135 curl_close ($ch);
2136
2137 return $result;
2138 }
2139
2140
2141 //////////////////// HTML VALIDATION /////////////////////////
2142
2143 /*
2144 ** Asks the validator whether this is valid HTML, or not.
2145 */
2146 function validateHTML($text) {
2147
2148 $params = array ("fragment" => $text);
2149
2150 $ch = curl_init();
2151
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
2156
2157 $result=curl_exec ($ch);
2158
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();
2164 }
2165
2166 curl_close ($ch);
2167
2168 $valid = (strpos($result, "Failed validation") === false ? true : false);
2169
2170 return array($valid, $result);
2171 }
2172
2173
2174 /**
2175 ** Get tidy to check for no HTML errors in the output file (e.g. unescaped strings).
2176 */
2177 function tidyCheckFile($name) {
2178 $file = DIRECTORY . "/" . $name;
2179 $command = PATH_TO_TIDY . " -output /tmp/out.html -quiet $file 2>&1";
2180 $x = `$command`;
2181
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;
2191 }
2192 }
2193
2194
2195 /**
2196 ** 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.
2198 */
2199 function dbErrorLogged() {
2200 static $filesize;
2201
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);
2207 }
2208 $filesize = filesize(DB_ERROR_LOG_FILE);
2209 return false;
2210 }
2211
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;
2217 }
2218
2219 return false;
2220 }
2221
2222 ////////////////// TOP-LEVEL PROBLEM-FINDING FUNCTION ////////////////////////
2223
2224 /**
2225 ** 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.
2227 */
2228 function runWikiTest(pageTest $test, &$testname, $can_overwrite = false) {
2229
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);
2233 }
2234
2235 $filename = DIRECTORY . "/" . $testname . DATA_FILE;
2236
2237 // Store the time before and after, to find slow pages.
2238 $before = microtime(true);
2239
2240 // Get MediaWiki to give us the output of this test.
2241 $wiki_preview = wikiTestOutput($test);
2242
2243 $after = microtime(true);
2244
2245 // if we received no response, then that's interesting.
2246 if ($wiki_preview == "") {
2247 print "\nNo response received for: $filename";
2248 return false;
2249 }
2250
2251 // save output HTML to file.
2252 $html_file = DIRECTORY . "/" . $testname . HTML_FILE;
2253 saveFile($wiki_preview, $html_file);
2254
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;
2265 }
2266 }
2267
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;
2272 }
2273
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;
2278 }
2279
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;
2284 }
2285
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";
2291 }
2292
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;
2296 }
2297
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;
2302 }
2303
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");
2309 }
2310
2311 return $valid;
2312 }
2313
2314
2315 /////////////////// RERUNNING OLD TESTS ///////////////////
2316
2317 /**
2318 ** We keep our failed tests so that they can be rerun.
2319 ** This function does that retesting.
2320 */
2321 function rerunPreviousTests() {
2322 print "Retesting previously found problems.\n";
2323
2324 $dir_contents = scandir (DIRECTORY);
2325
2326 // sort file into the order a normal person would use.
2327 natsort ($dir_contents);
2328
2329 foreach ($dir_contents as $file) {
2330
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;
2335
2336 // reload the test.
2337 $full_path = DIRECTORY . "/" . $file;
2338 $test = unserialize(file_get_contents($full_path));
2339
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;
2344 }
2345
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) . ")";
2350
2351 // run test
2352 $testname = $matches[1];
2353 $valid = runWikiTest($test, $testname, true);
2354
2355 if (!$valid) {
2356 saveTest($test, $testname);
2357 if (QUIET) {
2358 print "\nTest: " . get_class($test) . " ; Testname: $testname\n------";
2359 } else {
2360 print "\n";
2361 }
2362 }
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);
2371 }
2372 }
2373 }
2374
2375 print "\nDone retesting.\n";
2376 }
2377
2378
2379 ////////////////////// MAIN LOOP ////////////////////////
2380
2381
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");
2385 }
2386
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);
2390
2391 // Make directory if doesn't exist
2392 if (!is_dir(DIRECTORY)) {
2393 mkdir (DIRECTORY, 0700 );
2394 }
2395 // otherwise, we first retest the things that we have found in previous runs
2396 else if (RERUN_OLD_TESTS) {
2397 rerunPreviousTests();
2398 }
2399
2400 // seed the random number generator
2401 mt_srand(crc32(microtime()));
2402
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";
2408
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;
2417 }
2418 print " $count";
2419 }
2420
2421 // generate a page test to run.
2422 $test = selectPageTest($count);
2423
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.
2430 }
2431
2432 // run this test against MediaWiki, and see if the output was valid.
2433 $testname = $count;
2434 $valid = runWikiTest($test, $testname, false);
2435
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";
2442 }
2443 saveTest($test, $testname);
2444 $num_errors += 1;
2445 }
2446
2447 // stop if we have reached max number of errors.
2448 if (defined("MAX_ERRORS") && $num_errors>=MAX_ERRORS) {
2449 break;
2450 }
2451
2452 // stop if we have reached max number of mins runtime.
2453 if (defined("MAX_RUNTIME") && $mins>=MAX_RUNTIME) {
2454 break;
2455 }
2456 }
2457
2458 ?>