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