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