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