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