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