Part of bug 26280: added license headers to PHP files in maintenance
[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 /**
821 ** Strips out the stuff that Mediawiki balks at in a page's title.
822 ** Implementation copied/pasted from cleanupTable.inc & cleanupImages.php
823 */
824 static public function makeTitleSafe( $str ) {
825 $legalTitleChars = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF";
826 return preg_replace_callback(
827 "/([^$legalTitleChars])/",
828 create_function(
829 // single quotes are essential here,
830 // or alternative escape all $ as \$
831 '$matches',
832 'return sprintf( "\\x%02x", ord( $matches[1] ) );'
833 ),
834 $str );
835 }
836
837 /**
838 ** Returns a string of fuzz text.
839 */
840 static private function loop() {
841 switch ( wikiFuzz::randnum( 3 ) ) {
842 case 1: // an opening tag, with parameters.
843 $string = "";
844 $i = wikiFuzz::randnum( count( wikiFuzz::$types ) - 1 );
845 $t = wikiFuzz::$types[$i];
846 $arr = wikiFuzz::$data[$t];
847 $string .= "<" . $t . " ";
848 $num_params = min( wikiFuzz::$maxparams, count( $arr ) );
849 for ( $z = 0; $z < $num_params; $z++ ) {
850 $badparam = $arr[wikiFuzz::randnum( count( $arr ) - 1 )];
851 $badstring = wikiFuzz::makestring();
852 $string .= $badparam . "=" . wikiFuzz::getRandQuote() . $badstring . wikiFuzz::getRandQuote() . " ";
853 }
854 $string .= ">\n";
855 return $string;
856 case 2: // a closing tag.
857 $i = wikiFuzz::randnum( count( wikiFuzz::$types ) - 1 );
858 return "</" . wikiFuzz::$types[$i] . ">";
859 case 3: // a random string, between tags.
860 return wikiFuzz::makeString();
861 }
862 return ""; // catch-all, should never be called.
863 }
864
865 /**
866 ** Returns one of the three styles of random quote: ', ", and nothing.
867 */
868 static private function getRandQuote() {
869 switch ( wikiFuzz::randnum( 3 ) ) {
870 case 1 : return "'";
871 case 2 : return "\"";
872 default: return "";
873 }
874 }
875
876 /**
877 ** Returns fuzz text, with the parameter indicating approximately how many lines of text you want.
878 */
879 static public function makeFuzz( $maxtypes = 2 ) {
880 $page = "";
881 for ( $k = 0; $k < $maxtypes; $k++ ) {
882 $page .= wikiFuzz::loop();
883 }
884 return $page;
885 }
886 }
887
888
889 // ////// MEDIAWIKI PAGES TO TEST, AND HOW TO TEST THEM ///////
890
891 /**
892 ** A page test has just these things:
893 ** 1) Form parameters.
894 ** 2) the URL we are going to test those parameters on.
895 ** 3) Any cookies required for the test.
896 ** 4) Whether Tidy should validate the page. Defaults to true, but can be turned off.
897 ** Declared abstract because it should be extended by a class
898 ** that supplies these parameters.
899 */
900 abstract class pageTest {
901 protected $params;
902 protected $pagePath;
903 protected $cookie = "";
904 protected $tidyValidate = true;
905
906 public function getParams() {
907 return $this->params;
908 }
909
910 public function getPagePath() {
911 return $this->pagePath;
912 }
913
914 public function getCookie() {
915 return $this->cookie;
916 }
917
918 public function tidyValidate() {
919 return $this->tidyValidate;
920 }
921 }
922
923
924 /**
925 ** a page test for the "Edit" page. Tests Parser.php and Sanitizer.php.
926 */
927 class editPageTest extends pageTest {
928 function __construct() {
929 $this->pagePath = "index.php?title=WIKIFUZZ";
930
931 $this->params = array (
932 "action" => "submit",
933 "wpMinoredit" => wikiFuzz::makeFuzz( 2 ),
934 "wpPreview" => wikiFuzz::makeFuzz( 2 ),
935 "wpSection" => wikiFuzz::makeFuzz( 2 ),
936 "wpEdittime" => wikiFuzz::makeFuzz( 2 ),
937 "wpSummary" => wikiFuzz::makeFuzz( 2 ),
938 "wpScrolltop" => wikiFuzz::makeFuzz( 2 ),
939 "wpStarttime" => wikiFuzz::makeFuzz( 2 ),
940 "wpAutoSummary" => wikiFuzz::makeFuzz( 2 ),
941 "wpTextbox1" => wikiFuzz::makeFuzz( 40 ) // the main wiki text, need lots of this.
942 );
943
944 // sometimes we don't want to specify certain parameters.
945 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpSection"] );
946 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpEdittime"] );
947 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpSummary"] );
948 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpScrolltop"] );
949 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpStarttime"] );
950 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpAutoSummary"] );
951 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpTextbox1"] );
952 }
953 }
954
955
956 /**
957 ** a page test for "Special:Listusers".
958 */
959 class listusersTest extends pageTest {
960 function __construct() {
961 $this->pagePath = "index.php?title=Special:Listusers";
962
963 $this->params = array (
964 "title" => wikiFuzz::makeFuzz( 2 ),
965 "group" => wikiFuzz::makeFuzz( 2 ),
966 "username" => wikiFuzz::makeFuzz( 2 ),
967 "Go" => wikiFuzz::makeFuzz( 2 ),
968 "limit" => wikiFuzz::chooseInput( array( "0", "-1", "---'----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
969 "offset" => wikiFuzz::chooseInput( array( "0", "-1", "--------'-----0", "+1", "81343242346234234", wikiFuzz::makeFuzz( 2 ) ) )
970 );
971 }
972 }
973
974
975 /**
976 ** a page test for "Special:Search".
977 */
978 class searchTest extends pageTest {
979 function __construct() {
980 $this->pagePath = "index.php?title=Special:Search";
981
982 $this->params = array (
983 "action" => "index.php?title=Special:Search",
984 "ns0" => wikiFuzz::makeFuzz( 2 ),
985 "ns1" => wikiFuzz::makeFuzz( 2 ),
986 "ns2" => wikiFuzz::makeFuzz( 2 ),
987 "ns3" => wikiFuzz::makeFuzz( 2 ),
988 "ns4" => wikiFuzz::makeFuzz( 2 ),
989 "ns5" => wikiFuzz::makeFuzz( 2 ),
990 "ns6" => wikiFuzz::makeFuzz( 2 ),
991 "ns7" => wikiFuzz::makeFuzz( 2 ),
992 "ns8" => wikiFuzz::makeFuzz( 2 ),
993 "ns9" => wikiFuzz::makeFuzz( 2 ),
994 "ns10" => wikiFuzz::makeFuzz( 2 ),
995 "ns11" => wikiFuzz::makeFuzz( 2 ),
996 "ns12" => wikiFuzz::makeFuzz( 2 ),
997 "ns13" => wikiFuzz::makeFuzz( 2 ),
998 "ns14" => wikiFuzz::makeFuzz( 2 ),
999 "ns15" => wikiFuzz::makeFuzz( 2 ),
1000 "redirs" => wikiFuzz::makeFuzz( 2 ),
1001 "search" => wikiFuzz::makeFuzz( 2 ),
1002 "offset" => wikiFuzz::chooseInput( array( "", "0", "-1", "--------'-----0", "+1", "81343242346234234", wikiFuzz::makeFuzz( 2 ) ) ),
1003 "fulltext" => wikiFuzz::chooseInput( array( "", "0", "1", "--------'-----0", "+1", wikiFuzz::makeFuzz( 2 ) ) ),
1004 "searchx" => wikiFuzz::chooseInput( array( "", "0", "1", "--------'-----0", "+1", wikiFuzz::makeFuzz( 2 ) ) )
1005 );
1006 }
1007 }
1008
1009
1010 /**
1011 ** a page test for "Special:Recentchanges".
1012 */
1013 class recentchangesTest extends pageTest {
1014 function __construct() {
1015 $this->pagePath = "index.php?title=Special:Recentchanges";
1016
1017 $this->params = array (
1018 "action" => wikiFuzz::makeFuzz( 2 ),
1019 "title" => wikiFuzz::makeFuzz( 2 ),
1020 "namespace" => wikiFuzz::chooseInput( range( -1, 15 ) ),
1021 "Go" => wikiFuzz::makeFuzz( 2 ),
1022 "invert" => wikiFuzz::chooseInput( array( "-1", "---'----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1023 "hideanons" => wikiFuzz::chooseInput( array( "-1", "------'-------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1024 'limit' => wikiFuzz::chooseInput( array( "0", "-1", "---------'----0", "+1", "81340909772349234", wikiFuzz::makeFuzz( 2 ) ) ),
1025 "days" => wikiFuzz::chooseInput( array( "-1", "----------'---0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1026 "hideminor" => wikiFuzz::chooseInput( array( "-1", "-----------'--0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1027 "hidebots" => wikiFuzz::chooseInput( array( "-1", "---------'----0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1028 "hideliu" => wikiFuzz::chooseInput( array( "-1", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1029 "hidepatrolled" => wikiFuzz::chooseInput( array( "-1", "-----'--------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1030 "hidemyself" => wikiFuzz::chooseInput( array( "-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1031 'categories_any' => wikiFuzz::chooseInput( array( "-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1032 'categories' => wikiFuzz::chooseInput( array( "-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1033 'feed' => wikiFuzz::chooseInput( array( "-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) )
1034 );
1035 }
1036 }
1037
1038
1039 /**
1040 ** a page test for "Special:Prefixindex".
1041 */
1042 class prefixindexTest extends pageTest {
1043 function __construct() {
1044 $this->pagePath = "index.php?title=Special:Prefixindex";
1045
1046 $this->params = array (
1047 "title" => "Special:Prefixindex",
1048 "namespace" => wikiFuzz::randnum( -10, 101 ),
1049 "Go" => wikiFuzz::makeFuzz( 2 )
1050 );
1051
1052 // sometimes we want 'prefix', sometimes we want 'from', and sometimes we want nothing.
1053 if ( wikiFuzz::randnum( 3 ) == 0 ) {
1054 $this->params["prefix"] = wikiFuzz::chooseInput( array( "-1", "-----'--------0", "+++--+1",
1055 wikiFuzz::randnum( -10, 8134 ), wikiFuzz::makeFuzz( 2 ) ) );
1056 }
1057 if ( wikiFuzz::randnum( 3 ) == 0 ) {
1058 $this->params["from"] = wikiFuzz::chooseInput( array( "-1", "-----'--------0", "+++--+1",
1059 wikiFuzz::randnum( -10, 8134 ), wikiFuzz::makeFuzz( 2 ) ) );
1060 }
1061 }
1062 }
1063
1064
1065 /**
1066 ** a page test for "Special:MIMEsearch".
1067 */
1068 class mimeSearchTest extends pageTest {
1069 function __construct() {
1070 $this->pagePath = "index.php?title=Special:MIMEsearch";
1071
1072 $this->params = array (
1073 "action" => "index.php?title=Special:MIMEsearch",
1074 "mime" => wikiFuzz::makeFuzz( 3 ),
1075 'limit' => wikiFuzz::chooseInput( array( "0", "-1", "-------'------0", "+1", "81342321351235325", wikiFuzz::makeFuzz( 2 ) ) ),
1076 'offset' => wikiFuzz::chooseInput( array( "0", "-1", "-----'--------0", "+1", "81341231235365252234324", wikiFuzz::makeFuzz( 2 ) ) )
1077 );
1078 }
1079 }
1080
1081
1082 /**
1083 ** a page test for "Special:Log".
1084 */
1085 class specialLogTest extends pageTest {
1086 function __construct() {
1087 $this->pagePath = "index.php?title=Special:Log";
1088
1089 $this->params = array (
1090 "type" => wikiFuzz::chooseInput( array( "", wikiFuzz::makeFuzz( 2 ) ) ),
1091 "par" => wikiFuzz::makeFuzz( 2 ),
1092 "user" => wikiFuzz::makeFuzz( 2 ),
1093 "page" => wikiFuzz::makeFuzz( 2 ),
1094 "from" => wikiFuzz::makeFuzz( 2 ),
1095 "until" => wikiFuzz::makeFuzz( 2 ),
1096 "title" => wikiFuzz::makeFuzz( 2 )
1097 );
1098 }
1099 }
1100
1101
1102 /**
1103 ** a page test for "Special:Userlogin", with a successful login.
1104 */
1105 class successfulUserLoginTest extends pageTest {
1106 function __construct() {
1107 $this->pagePath = "index.php?title=Special:Userlogin&action=submitlogin&type=login&returnto=" . wikiFuzz::makeFuzz( 2 );
1108
1109 $this->params = array (
1110 "wpName" => USER_ON_WIKI,
1111 // sometimes real password, sometimes not:
1112 'wpPassword' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), USER_PASSWORD ) ),
1113 'wpRemember' => wikiFuzz::makeFuzz( 2 )
1114 );
1115
1116 $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array( "1" , wikiFuzz::makeFuzz( 2 ) ) );
1117 }
1118 }
1119
1120
1121 /**
1122 ** a page test for "Special:Userlogin".
1123 */
1124 class userLoginTest extends pageTest {
1125 function __construct() {
1126
1127 $this->pagePath = "index.php?title=Special:Userlogin";
1128
1129 $this->params = array (
1130 'wpRetype' => wikiFuzz::makeFuzz( 2 ),
1131 'wpRemember' => wikiFuzz::makeFuzz( 2 ),
1132 'wpRealName' => wikiFuzz::makeFuzz( 2 ),
1133 'wpPassword' => wikiFuzz::makeFuzz( 2 ),
1134 'wpName' => wikiFuzz::makeFuzz( 2 ),
1135 'wpMailmypassword' => wikiFuzz::makeFuzz( 2 ),
1136 'wpLoginattempt' => wikiFuzz::makeFuzz( 2 ),
1137 'wpEmail' => wikiFuzz::makeFuzz( 2 ),
1138 'wpDomain' => wikiFuzz::chooseInput( array( "", "local", wikiFuzz::makeFuzz( 2 ) ) ),
1139 'wpCreateaccountMail' => wikiFuzz::chooseInput( array( "", wikiFuzz::makeFuzz( 2 ) ) ),
1140 'wpCreateaccount' => wikiFuzz::chooseInput( array( "", wikiFuzz::makeFuzz( 2 ) ) ),
1141 'wpCookieCheck' => wikiFuzz::chooseInput( array( "", wikiFuzz::makeFuzz( 2 ) ) ),
1142 'type' => wikiFuzz::chooseInput( array( "signup", "login", "", wikiFuzz::makeFuzz( 2 ) ) ),
1143 'returnto' => wikiFuzz::makeFuzz( 2 ),
1144 'action' => wikiFuzz::chooseInput( array( "", "submitlogin", wikiFuzz::makeFuzz( 2 ) ) )
1145 );
1146
1147 $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array( "1" , wikiFuzz::makeFuzz( 2 ) ) );
1148 }
1149 }
1150
1151
1152 /**
1153 ** a page test for "Special:Ipblocklist" (also includes unblocking)
1154 */
1155 class ipblocklistTest extends pageTest {
1156 function __construct() {
1157 $this->pagePath = "index.php?title=Special:Ipblocklist";
1158
1159 $this->params = array (
1160 'wpUnblockAddress' => wikiFuzz::makeFuzz( 2 ),
1161 'ip' => wikiFuzz::chooseInput( array( "20398702394", "", "Nickj2", wikiFuzz::makeFuzz( 2 ),
1162 // something like an IP address, sometimes invalid:
1163 ( wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) . "."
1164 . wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) ) ) ),
1165 'id' => wikiFuzz::makeFuzz( 2 ),
1166 'wpUnblockReason' => wikiFuzz::makeFuzz( 2 ),
1167 'action' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), "success", "submit", "unblock" ) ),
1168 'wpEditToken' => wikiFuzz::makeFuzz( 2 ),
1169 'wpBlock' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), "" ) ),
1170 'limit' => wikiFuzz::chooseInput( array( "0", "-1", "--------'-----0", "+1",
1171 "09700982312351132098234", wikiFuzz::makeFuzz( 2 ) ) ),
1172 'offset' => wikiFuzz::chooseInput( array( "0", "-1", "------'-------0", "+1",
1173 "09700980982341535324234234", wikiFuzz::makeFuzz( 2 ) ) )
1174 );
1175
1176 // sometimes we don't want to specify certain parameters.
1177 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["action"] );
1178 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["ip"] );
1179 if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["id"] );
1180 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["wpUnblockAddress"] );
1181 }
1182 }
1183
1184
1185 /**
1186 ** a page test for "Special:Newimages".
1187 */
1188 class newImagesTest extends pageTest {
1189 function __construct() {
1190 $this->pagePath = "index.php?title=Special:Newimages";
1191
1192 $this->params = array (
1193 'hidebots' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), "1", "", "-1" ) ),
1194 'wpIlMatch' => wikiFuzz::makeFuzz( 2 ),
1195 'until' => wikiFuzz::makeFuzz( 2 ),
1196 'from' => wikiFuzz::makeFuzz( 2 )
1197 );
1198
1199 // sometimes we don't want to specify certain parameters.
1200 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["until"] );
1201 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["from"] );
1202 }
1203 }
1204
1205
1206 /**
1207 ** a page test for the "Special:Imagelist" page.
1208 */
1209 class imagelistTest extends pageTest {
1210 function __construct() {
1211 $this->pagePath = "index.php?title=Special:Imagelist";
1212
1213 $this->params = array (
1214 'sort' => wikiFuzz::chooseInput( array( "bysize", "byname" , "bydate", wikiFuzz::makeFuzz( 2 ) ) ),
1215 'limit' => wikiFuzz::chooseInput( array( "0", "-1", "--------'-----0", "+1", "09700982312351132098234", wikiFuzz::makeFuzz( 2 ) ) ),
1216 'offset' => wikiFuzz::chooseInput( array( "0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz( 2 ) ) ),
1217 'wpIlMatch' => wikiFuzz::makeFuzz( 2 )
1218 );
1219 }
1220 }
1221
1222
1223 /**
1224 ** a page test for "Special:Export".
1225 */
1226 class specialExportTest extends pageTest {
1227 function __construct() {
1228 $this->pagePath = "index.php?title=Special:Export";
1229
1230 $this->params = array (
1231 'action' => wikiFuzz::chooseInput( array( "submit", "", wikiFuzz::makeFuzz( 2 ) ) ),
1232 'pages' => wikiFuzz::makeFuzz( 2 ),
1233 'curonly' => wikiFuzz::chooseInput( array( "", "0", "-1", wikiFuzz::makeFuzz( 2 ) ) ),
1234 'listauthors' => wikiFuzz::chooseInput( array( "", "0", "-1", wikiFuzz::makeFuzz( 2 ) ) ),
1235 'history' => wikiFuzz::chooseInput( array( "0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz( 2 ) ) ),
1236
1237 );
1238
1239 // For the time being, need to disable "submit" action as Tidy barfs on MediaWiki's XML export.
1240 if ( $this->params['action'] == 'submit' ) $this->params['action'] = '';
1241
1242 // Sometimes remove the history field.
1243 if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["history"] );
1244
1245 // page does not produce HTML.
1246 $this->tidyValidate = false;
1247 }
1248 }
1249
1250
1251 /**
1252 ** a page test for "Special:Booksources".
1253 */
1254 class specialBooksourcesTest extends pageTest {
1255 function __construct() {
1256 $this->pagePath = "index.php?title=Special:Booksources";
1257
1258 $this->params = array (
1259 'go' => wikiFuzz::makeFuzz( 2 ),
1260 // ISBN codes have to contain some semi-numeric stuff or will be ignored:
1261 'isbn' => "0X0" . wikiFuzz::makeFuzz( 2 )
1262 );
1263 }
1264 }
1265
1266
1267 /**
1268 ** a page test for "Special:Allpages".
1269 */
1270 class specialAllpagesTest extends pageTest {
1271 function __construct() {
1272 $this->pagePath = "index.php?title=Special%3AAllpages";
1273
1274 $this->params = array (
1275 'from' => wikiFuzz::makeFuzz( 2 ),
1276 'namespace' => wikiFuzz::chooseInput( range( -1, 15 ) ),
1277 'go' => wikiFuzz::makeFuzz( 2 )
1278 );
1279 }
1280 }
1281
1282
1283 /**
1284 ** a page test for the page History.
1285 */
1286 class pageHistoryTest extends pageTest {
1287 function __construct() {
1288 $this->pagePath = "index.php?title=Main_Page&action=history";
1289
1290 $this->params = array (
1291 'limit' => wikiFuzz::chooseInput( array( "-1", "0", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1292 'offset' => wikiFuzz::chooseInput( array( "-1", "0", "------'-------0", "+1", "9823412312312412435", wikiFuzz::makeFuzz( 2 ) ) ),
1293 "go" => wikiFuzz::chooseInput( array( "first", "last", wikiFuzz::makeFuzz( 2 ) ) ),
1294 "dir" => wikiFuzz::chooseInput( array( "prev", "next", wikiFuzz::makeFuzz( 2 ) ) ),
1295 "diff" => wikiFuzz::chooseInput( array( "-1", "--------'-----0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1296 "oldid" => wikiFuzz::chooseInput( array( "prev", "-1", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1297 "feed" => wikiFuzz::makeFuzz( 2 )
1298 );
1299 }
1300 }
1301
1302
1303 /**
1304 ** a page test for the Special:Contributions".
1305 */
1306 class contributionsTest extends pageTest {
1307 function __construct() {
1308 $this->pagePath = "index.php?title=Special:Contributions/" . USER_ON_WIKI;
1309
1310 $this->params = array (
1311 'target' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), "newbies", USER_ON_WIKI ) ),
1312 'namespace' => wikiFuzz::chooseInput( array( -1, 15, 1, wikiFuzz::makeFuzz( 2 ) ) ),
1313 'offset' => wikiFuzz::chooseInput( array( "0", "-1", "------'-------0", "+1", "982342131232131231241", wikiFuzz::makeFuzz( 2 ) ) ),
1314 'bot' => wikiFuzz::chooseInput( array( "", "-1", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
1315 'go' => wikiFuzz::chooseInput( array( "-1", 'prev', 'next', wikiFuzz::makeFuzz( 2 ) ) )
1316 );
1317 }
1318 }
1319
1320
1321 /**
1322 ** a page test for viewing a normal page, whilst posting various params.
1323 */
1324 class viewPageTest extends pageTest {
1325 function __construct() {
1326 $this->pagePath = "index.php?title=Main_Page";
1327
1328 $this->params = array (
1329 "useskin" => wikiFuzz::chooseInput( array( "chick", "cologneblue", "myskin",
1330 "nostalgia", "simple", "standard", wikiFuzz::makeFuzz( 2 ) ) ),
1331 "uselang" => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ),
1332 "ab", "af", "an", "ar", "arc", "as", "ast", "av", "ay", "az", "ba",
1333 "bat-smg", "be", "bg", "bm", "bn", "bo", "bpy", "br", "bs", "ca",
1334 "ce", "cs", "csb", "cv", "cy", "da", "de", "dv", "dz", "el", "en",
1335 "eo", "es", "et", "eu", "fa", "fi", "fo", "fr", "fur", "fy", "ga",
1336 "gn", "gsw", "gu", "he", "hi", "hr", "hu", "ia", "id", "ii", "is",
1337 "it", "ja", "jv", "ka", "km", "kn", "ko", "ks", "ku", "kv", "la",
1338 "li", "lo", "lt", "lv", "mk", "ml", "ms", "nah", "nap", "nds",
1339 "nds-nl", "nl", "nn", "no", "non", "nv", "oc", "or", "os", "pa",
1340 "pl", "pms", "ps", "pt", "pt-br", "qu", "rmy", "ro", "ru", "sc",
1341 "sd", "sk", "sl", "sq", "sr", "sr-ec", "sr-el",
1342 "su", "sv", "ta", "te", "th", "tr", "tt", "ty", "tyv", "udm",
1343 "ug", "uk", "ur", "utf8", "vec", "vi", "wa", "xal", "yi", "za",
1344 "zh", "zh-cn", "zh-hk", "zh-sg", "zh-tw", "zh-tw" ) ),
1345 "returnto" => wikiFuzz::makeFuzz( 2 ),
1346 "feed" => wikiFuzz::chooseInput( array( "atom", "rss", wikiFuzz::makeFuzz( 2 ) ) ),
1347 "rcid" => wikiFuzz::makeFuzz( 2 ),
1348 "action" => wikiFuzz::chooseInput( array( "view", "raw", "render", wikiFuzz::makeFuzz( 2 ), "markpatrolled" ) ),
1349 "printable" => wikiFuzz::makeFuzz( 2 ),
1350 "oldid" => wikiFuzz::makeFuzz( 2 ),
1351 "redirect" => wikiFuzz::makeFuzz( 2 ),
1352 "diff" => wikiFuzz::makeFuzz( 2 ),
1353 "search" => wikiFuzz::makeFuzz( 2 ),
1354 "rdfrom" => wikiFuzz::makeFuzz( 2 ), // things from Article.php from here on:
1355 "token" => wikiFuzz::makeFuzz( 2 ),
1356 "tbid" => wikiFuzz::makeFuzz( 2 ),
1357 "action" => wikiFuzz::chooseInput( array( "purge", wikiFuzz::makeFuzz( 2 ) ) ),
1358 "wpReason" => wikiFuzz::makeFuzz( 2 ),
1359 "wpEditToken" => wikiFuzz::makeFuzz( 2 ),
1360 "from" => wikiFuzz::makeFuzz( 2 ),
1361 "bot" => wikiFuzz::makeFuzz( 2 ),
1362 "summary" => wikiFuzz::makeFuzz( 2 ),
1363 "direction" => wikiFuzz::chooseInput( array( "next", "prev", wikiFuzz::makeFuzz( 2 ) ) ),
1364 "section" => wikiFuzz::makeFuzz( 2 ),
1365 "preload" => wikiFuzz::makeFuzz( 2 ),
1366
1367 );
1368
1369 // Tidy does not know how to valid atom or rss, so exclude from testing for the time being.
1370 if ( $this->params["feed"] == "atom" ) { unset( $this->params["feed"] ); }
1371 else if ( $this->params["feed"] == "rss" ) { unset( $this->params["feed"] ); }
1372
1373 // Raw pages cannot really be validated
1374 if ( $this->params["action"] == "raw" ) unset( $this->params["action"] );
1375
1376 // sometimes we don't want to specify certain parameters.
1377 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["rcid"] );
1378 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["diff"] );
1379 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["rdfrom"] );
1380 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["oldid"] );
1381
1382 // usually don't want action == purge.
1383 if ( wikiFuzz::randnum( 6 ) > 1 ) unset( $this->params["action"] );
1384 }
1385 }
1386
1387
1388 /**
1389 ** a page test for "Special:Allmessages".
1390 */
1391 class specialAllmessagesTest extends pageTest {
1392 function __construct() {
1393 $this->pagePath = "index.php?title=Special:Allmessages";
1394
1395 // only really has one parameter
1396 $this->params = array (
1397 "ot" => wikiFuzz::chooseInput( array( "php", "html", wikiFuzz::makeFuzz( 2 ) ) )
1398 );
1399 }
1400 }
1401
1402 /**
1403 ** a page test for "Special:Newpages".
1404 */
1405 class specialNewpages extends pageTest {
1406 function __construct() {
1407 $this->pagePath = "index.php?title=Special:Newpages";
1408
1409 $this->params = array (
1410 "namespace" => wikiFuzz::chooseInput( range( -1, 15 ) ),
1411 "feed" => wikiFuzz::chooseInput( array( "atom", "rss", wikiFuzz::makeFuzz( 2 ) ) ),
1412 'limit' => wikiFuzz::chooseInput( array( "-1", "0", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
1413 'offset' => wikiFuzz::chooseInput( array( "-1", "0", "------'-------0", "+1", "9823412312312412435", wikiFuzz::makeFuzz( 2 ) ) )
1414 );
1415
1416 // Tidy does not know how to valid atom or rss, so exclude from testing for the time being.
1417 if ( $this->params["feed"] == "atom" ) { unset( $this->params["feed"] ); }
1418 else if ( $this->params["feed"] == "rss" ) { unset( $this->params["feed"] ); }
1419 }
1420 }
1421
1422 /**
1423 ** a page test for "redirect.php"
1424 */
1425 class redirectTest extends pageTest {
1426 function __construct() {
1427 $this->pagePath = "redirect.php";
1428
1429 $this->params = array (
1430 "wpDropdown" => wikiFuzz::makeFuzz( 2 )
1431 );
1432
1433 // sometimes we don't want to specify certain parameters.
1434 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpDropdown"] );
1435 }
1436 }
1437
1438
1439 /**
1440 ** a page test for "Special:Confirmemail"
1441 */
1442 class confirmEmail extends pageTest {
1443 function __construct() {
1444 // sometimes we send a bogus confirmation code, and sometimes we don't.
1445 $this->pagePath = "index.php?title=Special:Confirmemail" . wikiFuzz::chooseInput( array( "", "/" . wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 1 ) ) ) );
1446
1447 $this->params = array (
1448 "token" => wikiFuzz::makeFuzz( 2 )
1449 );
1450 }
1451 }
1452
1453
1454 /**
1455 ** a page test for "Special:Watchlist"
1456 ** Note: this test would be better if we were logged in.
1457 */
1458 class watchlistTest extends pageTest {
1459 function __construct() {
1460 $this->pagePath = "index.php?title=Special:Watchlist";
1461
1462 $this->params = array (
1463 "remove" => wikiFuzz::chooseInput( array( "Remove checked items from watchlist", wikiFuzz::makeFuzz( 2 ) ) ),
1464 'days' => wikiFuzz::chooseInput( array( 0, -1, -230, "--", 3, 9, wikiFuzz::makeFuzz( 2 ) ) ),
1465 'hideOwn' => wikiFuzz::chooseInput( array( "", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
1466 'hideBots' => wikiFuzz::chooseInput( array( "", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
1467 'namespace' => wikiFuzz::chooseInput( array( "", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
1468 'action' => wikiFuzz::chooseInput( array( "submit", "clear", wikiFuzz::makeFuzz( 2 ) ) ),
1469 'id[]' => wikiFuzz::makeFuzz( 2 ),
1470 'edit' => wikiFuzz::makeFuzz( 2 ),
1471 'token' => wikiFuzz::chooseInput( array( "", "1243213", wikiFuzz::makeFuzz( 2 ) ) )
1472 );
1473
1474 // sometimes we specifiy "reset", and sometimes we don't.
1475 if ( wikiFuzz::randnum( 3 ) == 0 ) $this->params["reset"] = wikiFuzz::chooseInput( array( "", "0", "1", wikiFuzz::makeFuzz( 2 ) ) );
1476 }
1477 }
1478
1479
1480 /**
1481 ** a page test for "Special:Blockme"
1482 */
1483 class specialBlockmeTest extends pageTest {
1484 function __construct() {
1485 $this->pagePath = "index.php?title=Special:Blockme";
1486
1487 $this->params = array ( );
1488
1489 // sometimes we specify "ip", and sometimes we don't.
1490 if ( wikiFuzz::randnum( 1 ) == 0 ) {
1491 $this->params["ip"] = wikiFuzz::chooseInput( array( "10.12.41.213", wikiFuzz::randnum( -10, 8134 ), wikiFuzz::makeFuzz( 2 ) ) );
1492 }
1493 }
1494 }
1495
1496
1497 /**
1498 ** a page test for "Special:Movepage"
1499 */
1500 class specialMovePage extends pageTest {
1501 function __construct() {
1502 $this->pagePath = "index.php?title=Special:Movepage";
1503
1504 $this->params = array (
1505 "action" => wikiFuzz::chooseInput( array( "success", "submit", "", wikiFuzz::makeFuzz( 2 ) ) ),
1506 'wpEditToken' => wikiFuzz::chooseInput( array( '', 0, 34987987, wikiFuzz::makeFuzz( 2 ) ) ),
1507 'target' => wikiFuzz::chooseInput( array( "x", wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 2 ) ) ) ),
1508 'wpOldTitle' => wikiFuzz::chooseInput( array( "z", wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 2 ) ), wikiFuzz::makeFuzz( 2 ) ) ),
1509 'wpNewTitle' => wikiFuzz::chooseInput( array( "y", wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 2 ) ), wikiFuzz::makeFuzz( 2 ) ) ),
1510 'wpReason' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ) ) ),
1511 'wpMovetalk' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1512 'wpDeleteAndMove' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1513 'wpConfirm' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1514 'talkmoved' => wikiFuzz::chooseInput( array( "1", wikiFuzz::makeFuzz( 2 ), "articleexists", 'notalkpage' ) ),
1515 'oldtitle' => wikiFuzz::makeFuzz( 2 ),
1516 'newtitle' => wikiFuzz::makeFuzz( 2 ),
1517 'wpMovetalk' => wikiFuzz::chooseInput( array( "1", "0", wikiFuzz::makeFuzz( 2 ) ) )
1518 );
1519
1520 // sometimes we don't want to specify certain parameters.
1521 if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["wpEditToken"] );
1522 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["target"] );
1523 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["wpNewTitle"] );
1524 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpReason"] );
1525 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpOldTitle"] );
1526 }
1527 }
1528
1529
1530 /**
1531 ** a page test for "Special:Undelete"
1532 */
1533 class specialUndelete extends pageTest {
1534 function __construct() {
1535 $this->pagePath = "index.php?title=Special:Undelete";
1536
1537 $this->params = array (
1538 "action" => wikiFuzz::chooseInput( array( "submit", "", wikiFuzz::makeFuzz( 2 ) ) ),
1539 'wpEditToken' => wikiFuzz::chooseInput( array( '', 0, 34987987, wikiFuzz::makeFuzz( 2 ) ) ),
1540 'target' => wikiFuzz::chooseInput( array( "x", wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 2 ) ) ) ),
1541 'timestamp' => wikiFuzz::chooseInput( array( "125223", wikiFuzz::makeFuzz( 2 ) ) ),
1542 'file' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1543 'restore' => wikiFuzz::chooseInput( array( "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
1544 'preview' => wikiFuzz::chooseInput( array( "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
1545 'wpComment' => wikiFuzz::makeFuzz( 2 )
1546 );
1547
1548 // sometimes we don't want to specify certain parameters.
1549 if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["wpEditToken"] );
1550 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["target"] );
1551 if ( wikiFuzz::randnum( 1 ) == 0 ) unset( $this->params["restore"] );
1552 if ( wikiFuzz::randnum( 1 ) == 0 ) unset( $this->params["preview"] );
1553 }
1554 }
1555
1556
1557 /**
1558 ** a page test for "Special:Unlockdb"
1559 */
1560 class specialUnlockdb extends pageTest {
1561 function __construct() {
1562 $this->pagePath = "index.php?title=Special:Unlockdb";
1563
1564 $this->params = array (
1565 "action" => wikiFuzz::chooseInput( array( "submit", "success", "", wikiFuzz::makeFuzz( 2 ) ) ),
1566 'wpEditToken' => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1567 'wpLockConfirm' => wikiFuzz::chooseInput( array( "0", "1", wikiFuzz::makeFuzz( 2 ) ) )
1568 );
1569
1570 // sometimes we don't want to specify certain parameters.
1571 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpEditToken"] );
1572 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["action"] );
1573 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpLockConfirm"] );
1574 }
1575 }
1576
1577
1578 /**
1579 ** a page test for "Special:Lockdb"
1580 */
1581 class specialLockdb extends pageTest {
1582 function __construct() {
1583 $this->pagePath = "index.php?title=Special:Lockdb";
1584
1585 $this->params = array (
1586 "action" => wikiFuzz::chooseInput( array( "submit", "success", "", wikiFuzz::makeFuzz( 2 ) ) ),
1587 'wpEditToken' => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1588 'wpLockReason' => wikiFuzz::makeFuzz( 2 ),
1589 'wpLockConfirm' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) )
1590 );
1591
1592 // sometimes we don't want to specify certain parameters.
1593 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpEditToken"] );
1594 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["action"] );
1595 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpLockConfirm"] );
1596 }
1597 }
1598
1599
1600 /**
1601 ** a page test for "Special:Userrights"
1602 */
1603 class specialUserrights extends pageTest {
1604 function __construct() {
1605 $this->pagePath = "index.php?title=Special:Userrights";
1606
1607 $this->params = array (
1608 'wpEditToken' => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1609 'user-editname' => wikiFuzz::chooseInput( array( "Nickj2", "Nickj2\n<xyz>", wikiFuzz::makeFuzz( 2 ) ) ),
1610 'ssearchuser' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1611 'saveusergroups' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ), "Save User Groups" ),
1612 'member[]' => wikiFuzz::chooseInput( array( "0", "bot", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1613 "available[]" => wikiFuzz::chooseInput( array( "0", "sysop", "bureaucrat", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) )
1614 );
1615
1616 // sometimes we don't want to specify certain parameters.
1617 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params['ssearchuser'] );
1618 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params['saveusergroups'] );
1619 }
1620 }
1621
1622
1623 /**
1624 ** a test for page protection and unprotection.
1625 */
1626 class pageProtectionForm extends pageTest {
1627 function __construct() {
1628 $this->pagePath = "index.php?title=Main_Page";
1629
1630 $this->params = array (
1631 "action" => "protect",
1632 'wpEditToken' => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1633 "mwProtect-level-edit" => wikiFuzz::chooseInput( array( '', 'autoconfirmed', 'sysop', wikiFuzz::makeFuzz( 2 ) ) ),
1634 "mwProtect-level-move" => wikiFuzz::chooseInput( array( '', 'autoconfirmed', 'sysop', wikiFuzz::makeFuzz( 2 ) ) ),
1635 "mwProtectUnchained" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1636 'mwProtect-reason' => wikiFuzz::chooseInput( array( "because it was there", wikiFuzz::makeFuzz( 2 ) ) )
1637 );
1638
1639
1640 // sometimes we don't want to specify certain parameters.
1641 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["mwProtectUnchained"] );
1642 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params['mwProtect-reason'] );
1643 }
1644 }
1645
1646
1647 /**
1648 ** a page test for "Special:Blockip".
1649 */
1650 class specialBlockip extends pageTest {
1651 function __construct() {
1652 $this->pagePath = "index.php?title=Special:Blockip";
1653
1654 $this->params = array (
1655 "action" => wikiFuzz::chooseInput( array( "submit", "", wikiFuzz::makeFuzz( 2 ) ) ),
1656 'wpEditToken' => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1657 "wpBlockAddress" => wikiFuzz::chooseInput( array( "20398702394", "", "Nickj2", wikiFuzz::makeFuzz( 2 ),
1658 // something like an IP address, sometimes invalid:
1659 ( wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) . "."
1660 . wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) ) ) ),
1661 "ip" => wikiFuzz::chooseInput( array( "20398702394", "", "Nickj2", wikiFuzz::makeFuzz( 2 ),
1662 // something like an IP address, sometimes invalid:
1663 ( wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) . "."
1664 . wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) ) ) ),
1665 "wpBlockOther" => wikiFuzz::chooseInput( array( '', 'Nickj2', wikiFuzz::makeFuzz( 2 ) ) ),
1666 "wpBlockExpiry" => wikiFuzz::chooseInput( array( "other", "2 hours", "1 day", "3 days", "1 week", "2 weeks",
1667 "1 month", "3 months", "6 months", "1 year", "infinite", wikiFuzz::makeFuzz( 2 ) ) ),
1668 "wpBlockReason" => wikiFuzz::chooseInput( array( "because it was there", wikiFuzz::makeFuzz( 2 ) ) ),
1669 "wpAnonOnly" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1670 "wpCreateAccount" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1671 "wpBlock" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) )
1672 );
1673
1674 // sometimes we don't want to specify certain parameters.
1675 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpBlockOther"] );
1676 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpBlockExpiry"] );
1677 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpBlockReason"] );
1678 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpAnonOnly"] );
1679 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpCreateAccount"] );
1680 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpBlockAddress"] );
1681 if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["ip"] );
1682 }
1683 }
1684
1685
1686 /**
1687 ** a test for the imagepage.
1688 */
1689 class imagepageTest extends pageTest {
1690 function __construct() {
1691 $this->pagePath = "index.php?title=Image:Small-email.png";
1692
1693 $this->params = array (
1694 "image" => wikiFuzz::chooseInput( array( "Small-email.png", wikiFuzz::makeFuzz( 2 ) ) ),
1695 "wpReason" => wikiFuzz::makeFuzz( 2 ),
1696 "oldimage" => wikiFuzz::chooseInput( array( "Small-email.png", wikiFuzz::makeFuzz( 2 ) ) ),
1697 "wpEditToken" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1698 );
1699
1700 // sometimes we don't want to specify certain parameters.
1701 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["image"] );
1702 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpReason"] );
1703 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["oldimage"] );
1704 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpEditToken"] );
1705 }
1706 }
1707
1708
1709 /**
1710 ** a test for page deletion form.
1711 */
1712 class pageDeletion extends pageTest {
1713 function __construct() {
1714 $this->pagePath = "index.php?title=Main_Page&action=delete";
1715
1716 $this->params = array (
1717 "wpEditToken" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1718 "wpReason" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1719 "wpConfirm" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1720 );
1721
1722 // sometimes we don't want to specify certain parameters.
1723 if ( wikiFuzz::randnum( 5 ) == 0 ) unset( $this->params["wpReason"] );
1724 if ( wikiFuzz::randnum( 5 ) == 0 ) unset( $this->params["wpEditToken"] );
1725 if ( wikiFuzz::randnum( 5 ) == 0 ) unset( $this->params["wpConfirm"] );
1726 }
1727 }
1728
1729
1730
1731 /**
1732 ** a test for Revision Deletion.
1733 */
1734 class specialRevisionDelete extends pageTest {
1735 function __construct() {
1736 $this->pagePath = "index.php?title=Special:Revisiondelete";
1737
1738 $this->params = array (
1739 "target" => wikiFuzz::chooseInput( array( "Main Page", wikiFuzz::makeFuzz( 2 ) ) ),
1740 "oldid" => wikiFuzz::makeFuzz( 2 ),
1741 "oldid[]" => wikiFuzz::makeFuzz( 2 ),
1742 "wpReason" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1743 "revdelete-hide-text" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1744 "revdelete-hide-comment" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1745 "revdelete-hide-user" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1746 "revdelete-hide-restricted" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1747 );
1748
1749 // sometimes we don't want to specify certain parameters.
1750 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["target"] );
1751 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["oldid"] );
1752 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["oldid[]"] );
1753 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpReason"] );
1754 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["revdelete-hide-text"] );
1755 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["revdelete-hide-comment"] );
1756 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["revdelete-hide-user"] );
1757 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["revdelete-hide-restricted"] );
1758 }
1759 }
1760
1761
1762 /**
1763 ** a test for Special:Import.
1764 */
1765 class specialImport extends pageTest {
1766 function __construct() {
1767 $this->pagePath = "index.php?title=Special:Import";
1768
1769 $this->params = array (
1770 "action" => "submit",
1771 "source" => wikiFuzz::chooseInput( array( "upload", "interwiki", wikiFuzz::makeFuzz( 2 ) ) ),
1772 "MAX_FILE_SIZE" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1773 "xmlimport" => wikiFuzz::chooseInput( array( "/var/www/hosts/mediawiki/wiki/AdminSettings.php", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1774 "namespace" => wikiFuzz::chooseInput( array( wikiFuzz::randnum( 30, -6 ), wikiFuzz::makeFuzz( 2 ) ) ),
1775 "interwiki" => wikiFuzz::makeFuzz( 2 ),
1776 "interwikiHistory" => wikiFuzz::makeFuzz( 2 ),
1777 "frompage" => wikiFuzz::makeFuzz( 2 ),
1778 );
1779
1780 // sometimes we don't want to specify certain parameters.
1781 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["action"] );
1782 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["source"] );
1783 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["MAX_FILE_SIZE"] );
1784 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["xmlimport"] );
1785 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["interwiki"] );
1786 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["interwikiHistory"] );
1787 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["frompage"] );
1788
1789 // Note: Need to do a file upload to fully test this Special page.
1790 }
1791 }
1792
1793
1794 /**
1795 ** a test for thumb.php
1796 */
1797 class thumbTest extends pageTest {
1798 function __construct() {
1799 $this->pagePath = "thumb.php";
1800
1801 $this->params = array (
1802 "f" => wikiFuzz::chooseInput( array( "..", "\\", "small-email.png", wikiFuzz::makeFuzz( 2 ) ) ),
1803 "w" => wikiFuzz::chooseInput( array( "80", wikiFuzz::randnum( 6000, -200 ), wikiFuzz::makeFuzz( 2 ) ) ),
1804 "r" => wikiFuzz::chooseInput( array( "0", wikiFuzz::makeFuzz( 2 ) ) ),
1805 );
1806
1807 // sometimes we don't want to specify certain parameters.
1808 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["f"] );
1809 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["w"] );
1810 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["r"] );
1811 }
1812 }
1813
1814
1815 /**
1816 ** a test for trackback.php
1817 */
1818 class trackbackTest extends pageTest {
1819 function __construct() {
1820 $this->pagePath = "trackback.php";
1821
1822 $this->params = array (
1823 "url" => wikiFuzz::makeFuzz( 2 ),
1824 "blog_name" => wikiFuzz::chooseInput( array( "80", wikiFuzz::randnum( 6000, -200 ), wikiFuzz::makeFuzz( 2 ) ) ),
1825 "article" => wikiFuzz::chooseInput( array( "Main Page", wikiFuzz::makeFuzz( 2 ) ) ),
1826 "title" => wikiFuzz::chooseInput( array( "Main Page", wikiFuzz::makeFuzz( 2 ) ) ),
1827 "excerpt" => wikiFuzz::makeFuzz( 2 ),
1828 );
1829
1830 // sometimes we don't want to specify certain parameters.
1831 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["title"] );
1832 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["excerpt"] );
1833
1834 // page does not produce HTML.
1835 $this->tidyValidate = false;
1836 }
1837 }
1838
1839
1840 /**
1841 ** a test for profileinfo.php
1842 */
1843 class profileInfo extends pageTest {
1844 function __construct() {
1845 $this->pagePath = "profileinfo.php";
1846
1847 $this->params = array (
1848 "expand" => wikiFuzz::makeFuzz( 2 ),
1849 "sort" => wikiFuzz::chooseInput( array( "time", "count", "name", wikiFuzz::makeFuzz( 2 ) ) ),
1850 "filter" => wikiFuzz::chooseInput( array( "Main Page", wikiFuzz::makeFuzz( 2 ) ) ),
1851 );
1852
1853 // sometimes we don't want to specify certain parameters.
1854 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["sort"] );
1855 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["filter"] );
1856 }
1857 }
1858
1859
1860 /**
1861 ** a test for Special:Cite (extension Special page).
1862 */
1863 class specialCite extends pageTest {
1864 function __construct() {
1865 $this->pagePath = "index.php?title=Special:Cite";
1866
1867 $this->params = array (
1868 "page" => wikiFuzz::chooseInput( array( "\" onmouseover=\"alert(1);\"", "Main Page", wikiFuzz::makeFuzz( 2 ) ) ),
1869 "id" => wikiFuzz::chooseInput( array( "-1", "0", "------'-------0", "+1", "-9823412312312412435", wikiFuzz::makeFuzz( 2 ) ) ),
1870 );
1871
1872 // sometimes we don't want to specify certain parameters.
1873 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["page"] );
1874 if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["id"] );
1875 }
1876 }
1877
1878
1879 /**
1880 ** a test for Special:Filepath (extension Special page).
1881 */
1882 class specialFilepath extends pageTest {
1883 function __construct() {
1884 $this->pagePath = "index.php?title=Special:Filepath";
1885
1886 $this->params = array (
1887 "file" => wikiFuzz::chooseInput( array( "Small-email.png", "Small-email.png" . wikiFuzz::makeFuzz( 1 ), wikiFuzz::makeFuzz( 2 ) ) ),
1888 );
1889 }
1890 }
1891
1892
1893 /**
1894 ** a test for Special:Makebot (extension Special page).
1895 */
1896 class specialMakebot extends pageTest {
1897 function __construct() {
1898 $this->pagePath = "index.php?title=Special:Makebot";
1899
1900 $this->params = array (
1901 "username" => wikiFuzz::chooseInput( array( "Nickj2", "192.168.0.2", wikiFuzz::makeFuzz( 1 ) ) ),
1902 "dosearch" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1903 "grant" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1904 "comment" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1905 "token" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1906 );
1907
1908 // sometimes we don't want to specify certain parameters.
1909 if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["dosearch"] );
1910 if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["grant"] );
1911 if ( wikiFuzz::randnum( 5 ) == 0 ) unset( $this->params["token"] );
1912 }
1913 }
1914
1915
1916 /**
1917 ** a test for Special:Makesysop (extension Special page).
1918 */
1919 class specialMakesysop extends pageTest {
1920 function __construct() {
1921 $this->pagePath = "index.php?title=Special:Makesysop";
1922
1923 $this->params = array (
1924 "wpMakesysopUser" => wikiFuzz::chooseInput( array( "Nickj2", "192.168.0.2", wikiFuzz::makeFuzz( 1 ) ) ),
1925 "action" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1926 "wpMakesysopSubmit" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
1927 "wpEditToken" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1928 "wpSetBureaucrat" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1929 );
1930
1931 // sometimes we don't want to specify certain parameters.
1932 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["wpMakesysopSubmit"] );
1933 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["wpEditToken"] );
1934 if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["wpSetBureaucrat"] );
1935 }
1936 }
1937
1938
1939 /**
1940 ** a test for Special:Renameuser (extension Special page).
1941 */
1942 class specialRenameuser extends pageTest {
1943 function __construct() {
1944 $this->pagePath = "index.php?title=Special:Renameuser";
1945
1946 $this->params = array (
1947 "oldusername" => wikiFuzz::chooseInput( array( "Nickj2", "192.168.0.2", wikiFuzz::makeFuzz( 1 ) ) ),
1948 "newusername" => wikiFuzz::chooseInput( array( "Nickj2", "192.168.0.2", wikiFuzz::makeFuzz( 1 ) ) ),
1949 "token" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
1950 );
1951 }
1952 }
1953
1954
1955 /**
1956 ** a test for Special:Linksearch (extension Special page).
1957 */
1958 class specialLinksearch extends pageTest {
1959 function __construct() {
1960 $this->pagePath = "index.php?title=Special%3ALinksearch";
1961
1962 $this->params = array (
1963 "target" => wikiFuzz::makeFuzz( 2 ),
1964 );
1965
1966 // sometimes we don't want to specify certain parameters.
1967 if ( wikiFuzz::randnum( 10 ) == 0 ) unset( $this->params["target"] );
1968 }
1969 }
1970
1971
1972 /**
1973 ** a test for Special:CategoryTree (extension Special page).
1974 */
1975 class specialCategoryTree extends pageTest {
1976 function __construct() {
1977 $this->pagePath = "index.php?title=Special:CategoryTree";
1978
1979 $this->params = array (
1980 "target" => wikiFuzz::makeFuzz( 2 ),
1981 "from" => wikiFuzz::makeFuzz( 2 ),
1982 "until" => wikiFuzz::makeFuzz( 2 ),
1983 "showas" => wikiFuzz::makeFuzz( 2 ),
1984 "mode" => wikiFuzz::chooseInput( array( "pages", "categories", "all", wikiFuzz::makeFuzz( 2 ) ) ),
1985 );
1986
1987 // sometimes we do want to specify certain parameters.
1988 if ( wikiFuzz::randnum( 5 ) == 0 ) $this->params["notree"] = wikiFuzz::chooseInput( array( "1", 0, "", wikiFuzz::makeFuzz( 2 ) ) );
1989 }
1990 }
1991
1992
1993 /**
1994 ** a test for "Special:Chemicalsources" (extension Special page).
1995 */
1996 class specialChemicalsourcesTest extends pageTest {
1997 function __construct() {
1998 $this->pagePath = "index.php?title=Special:Chemicalsources";
1999
2000 // choose an input format to use.
2001 $format = wikiFuzz::chooseInput(
2002 array( 'go',
2003 'CAS',
2004 'EINECS',
2005 'CHEBI',
2006 'PubChem',
2007 'SMILES',
2008 'InChI',
2009 'ATCCode',
2010 'KEGG',
2011 'RTECS',
2012 'ECNumber',
2013 'DrugBank',
2014 'Formula',
2015 'Name'
2016 )
2017 );
2018
2019 // values for different formats usually start with either letters or numbers.
2020 switch ( $format ) {
2021 case 'Name' : $value = "A"; break;
2022 case 'InChI' :
2023 case 'SMILES' :
2024 case 'Formula': $value = "C"; break;
2025 default : $value = "0"; break;
2026 }
2027
2028 // and then we append the fuzz input.
2029 $this->params = array ( $format => $value . wikiFuzz::makeFuzz( 2 ) );
2030 }
2031 }
2032
2033
2034 /**
2035 ** A test for api.php (programmatic interface to MediaWiki in XML/JSON/RSS/etc formats).
2036 ** Quite involved to test because there are lots of options/parameters, and because
2037 ** for a lot of the functionality if all the parameters don't make sense then it just
2038 ** returns the help screen - so currently a lot of the tests aren't actually doing much
2039 ** because something wasn't right in the query.
2040 **
2041 ** @todo: Incomplete / unfinished; Runs too fast (suggests not much testing going on).
2042 */
2043 class api extends pageTest {
2044
2045 // API login mode.
2046 private static function loginMode() {
2047 $arr = array ( "lgname" => wikiFuzz::makeFuzz( 2 ),
2048 "lgpassword" => wikiFuzz::makeFuzz( 2 ),
2049 );
2050 // sometimes we want to specify the extra "lgdomain" parameter.
2051 if ( wikiFuzz::randnum( 3 ) == 0 ) {
2052 $arr["lgdomain"] = wikiFuzz::chooseInput( array( "1", 0, "", wikiFuzz::makeFuzz( 2 ) ) );
2053 }
2054
2055 return $arr;
2056 }
2057
2058 // API OpenSearch mode.
2059 private static function opensearchMode() {
2060 return array ( "search" => wikiFuzz::makeFuzz( 2 ) );
2061 }
2062
2063 // API watchlist feed mode.
2064 private static function feedwatchlistMode() {
2065 // FIXME: add "wikiFuzz::makeFuzz(2)" as possible value below?
2066 return array ( "feedformat" => wikiFuzz::chooseInput( array( "rss", "atom" ) ) );
2067 }
2068
2069 // API query mode.
2070 private static function queryMode() {
2071 // FIXME: add "wikiFuzz::makeFuzz(2)" as possible params for the elements below?
2072 // Suspect this will stuff up the tests more, but need to check.
2073 $params = array (
2074 // FIXME: More titles.
2075 "titles" => wikiFuzz::chooseInput( array( "Main Page" ) ),
2076 // FIXME: More pageids.
2077 "pageids" => 1,
2078 "prop" => wikiFuzz::chooseInput( array( "info", "revisions", "watchlist" ) ),
2079 "list" => wikiFuzz::chooseInput( array( "allpages", "logevents", "watchlist", "usercontribs", "recentchanges", "backlinks", "embeddedin", "imagelinks" ) ),
2080 "meta" => wikiFuzz::chooseInput( array( "siteinfo" ) ),
2081 "generator" => wikiFuzz::chooseInput( array( "allpages", "logevents", "watchlist", "info", "revisions" ) ),
2082 "siprop" => wikiFuzz::chooseInput( array( "general", "namespaces", "general|namespaces" ) ),
2083 );
2084
2085 // Add extra parameters based on what list choice we got.
2086 switch ( $params["list"] ) {
2087 case "usercontribs" : self::addListParams ( $params, "uc", array( "limit", "start", "end", "user", "dir" ) ); break;
2088 case "allpages" : self::addListParams ( $params, "ap", array( "from", "prefix", "namespace", "filterredir", "limit" ) ); break;
2089 case "watchlist" : self::addListParams ( $params, "wl", array( "allrev", "start", "end", "namespace", "dir", "limit", "prop" ) ); break;
2090 case "logevents" : self::addListParams ( $params, "le", array( "limit", "type", "start", "end", "user", "dir" ) ); break;
2091 case "recentchanges": self::addListParams ( $params, "rc", array( "limit", "prop", "show", "namespace", "start", "end", "dir" ) ); break;
2092 case "backlinks" : self::addListParams ( $params, "bl", array( "continue", "namespace", "redirect", "limit" ) ); break;
2093 case "embeddedin" : self::addListParams ( $params, "ei", array( "continue", "namespace", "redirect", "limit" ) ); break;
2094 case "imagelinks" : self::addListParams ( $params, "il", array( "continue", "namespace", "redirect", "limit" ) ); break;
2095 }
2096
2097 if ( $params["prop"] == "revisions" ) {
2098 self::addListParams ( $params, "rv", array( "prop", "limit", "startid", "endid", "end", "dir" ) );
2099 }
2100
2101 // Sometimes we want redirects, sometimes we don't.
2102 if ( wikiFuzz::randnum( 3 ) == 0 ) {
2103 $params["redirects"] = wikiFuzz::chooseInput( array( "1", 0, "", wikiFuzz::makeFuzz( 2 ) ) );
2104 }
2105
2106 return $params;
2107 }
2108
2109 // Adds all the elements to the array, using the specified prefix.
2110 private static function addListParams( &$array, $prefix, $elements ) {
2111 foreach ( $elements as $element ) {
2112 $array[$prefix . $element] = self::getParamDetails( $element );
2113 }
2114 }
2115
2116 // For a given element name, returns the data for that element.
2117 private static function getParamDetails( $element ) {
2118 switch ( $element ) {
2119 case 'startid' :
2120 case 'endid' :
2121 case 'start' :
2122 case 'end' :
2123 case 'limit' : return wikiFuzz::chooseInput( array( "0", "-1", "---'----------0", "+1", "8134", "320742734234235", "20060230121212", wikiFuzz::randnum( 9000, -100 ), wikiFuzz::makeFuzz( 2 ) ) );
2124 case 'dir' : return wikiFuzz::chooseInput( array( "newer", "older", wikiFuzz::makeFuzz( 2 ) ) );
2125 case 'user' : return wikiFuzz::chooseInput( array( USER_ON_WIKI, wikiFuzz::makeFuzz( 2 ) ) );
2126 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 ) ) );
2127 case 'filterredir': return wikiFuzz::chooseInput( array( "all", "redirects", "nonredirectsallpages", wikiFuzz::makeFuzz( 2 ) ) );
2128 case 'allrev' : return wikiFuzz::chooseInput( array( "1", 0, "", wikiFuzz::makeFuzz( 2 ) ) );
2129 case 'prop' : return wikiFuzz::chooseInput( array( "user", "comment", "timestamp", "patrol", "flags", "user|user|comment|flags", wikiFuzz::makeFuzz( 2 ) ) );
2130 case 'type' : return wikiFuzz::chooseInput( array( "block", "protect", "rights", "delete", "upload", "move", "import", "renameuser", "newusers", "makebot", wikiFuzz::makeFuzz( 2 ) ) );
2131 case 'hide' : return wikiFuzz::chooseInput( array( "minor", "bots", "anons", "liu", "liu|bots|", wikiFuzz::makeFuzz( 2 ) ) );
2132 case 'show' : return wikiFuzz::chooseInput( array( 'minor', '!minor', 'bot', '!bot', 'anon', '!anon', wikiFuzz::makeFuzz( 2 ) ) );
2133 default : return wikiFuzz::makeFuzz( 2 );
2134 }
2135 }
2136
2137 // Entry point.
2138 function __construct() {
2139 $this->pagePath = "api.php";
2140
2141 $modes = array ( "help",
2142 "login",
2143 "opensearch",
2144 "feedwatchlist",
2145 "query" );
2146 $action = wikiFuzz::chooseInput( array_merge ( $modes, array( wikiFuzz::makeFuzz( 2 ) ) ) );
2147
2148 switch ( $action ) {
2149 case "login" : $this->params = self::loginMode();
2150 break;
2151 case "opensearch" : $this->params = self::opensearchMode();
2152 break;
2153 case "feedwatchlist" : $this->params = self::feedwatchlistMode();
2154 break;
2155 case "query" : $this->params = self::queryMode();
2156 break;
2157 case "help" :
2158 default : // Do something random - "Crazy Ivan" mode.
2159 $random_mode = wikiFuzz::chooseInput( $modes ) . "Mode";
2160 // There is no "helpMode".
2161 if ( $random_mode == "helpMode" ) $random_mode = "queryMode";
2162 $this->params = self::$random_mode();
2163 break;
2164 }
2165
2166 // Save the selected action.
2167 $this->params["action"] = $action;
2168
2169 // Set the cookie:
2170 // FIXME: need to get this cookie dynamically set, rather than hard-coded.
2171 $this->cookie = "wikidbUserID=10001; wikidbUserName=Test; wikidb_session=178df0fe68c75834643af65dec9ec98a; wikidbToken=1adc6753d62c44aec950c024d7ae0540";
2172
2173 // Output format
2174 $this->params["format"] = wikiFuzz::chooseInput( array( "json", "jsonfm", "php", "phpfm",
2175 "wddx", "wddxfm", "xml", "xmlfm",
2176 "yaml", "yamlfm", "raw", "rawfm",
2177 wikiFuzz::makeFuzz( 2 ) ) );
2178
2179 // Page does not produce HTML (sometimes).
2180 $this->tidyValidate = false;
2181 }
2182 }
2183
2184
2185 /**
2186 ** a page test for the GeSHi extension.
2187 */
2188 class GeSHi_Test extends pageTest {
2189
2190 private function getGeSHiContent() {
2191 return "<source lang=\"" . $this->getLang() . "\" "
2192 . ( wikiFuzz::randnum( 2 ) == 0 ? "line " : "" )
2193 . ( wikiFuzz::randnum( 2 ) == 0 ? "strict " : "" )
2194 . "start=" . wikiFuzz::chooseInput( array( wikiFuzz::randnum( -6000, 6000 ), wikiFuzz::makeFuzz( 2 ) ) )
2195 . ">"
2196 . wikiFuzz::makeFuzz( 2 )
2197 . "</source>";
2198 }
2199
2200 private function getLang() {
2201 return wikiFuzz::chooseInput( array( "actionscript", "ada", "apache", "applescript", "asm", "asp", "autoit", "bash", "blitzbasic", "bnf", "c", "c_mac", "caddcl", "cadlisp",
2202 "cfdg", "cfm", "cpp", "cpp-qt", "csharp", "css", "d", "delphi", "diff", "div", "dos", "eiffel", "fortran", "freebasic", "gml", "groovy", "html4strict", "idl",
2203 "ini", "inno", "io", "java", "java5", "javascript", "latex", "lisp", "lua", "matlab", "mirc", "mpasm", "mysql", "nsis", "objc", "ocaml", "ocaml-brief", "oobas",
2204 "oracle8", "pascal", "perl", "php", "php-brief", "plsql", "python", "qbasic", "rails", "reg", "robots", "ruby", "sas", "scheme", "sdlbasic", "smalltalk", "smarty",
2205 "sql", "tcl", "text", "thinbasic", "tsql", "vb", "vbnet", "vhdl", "visualfoxpro", "winbatch", "xml", "xpp", "z80", wikiFuzz::makeFuzz( 1 ) ) );
2206 }
2207
2208 function __construct() {
2209 $this->pagePath = "index.php?title=WIKIFUZZ";
2210
2211 $this->params = array (
2212 "action" => "submit",
2213 "wpMinoredit" => "test",
2214 "wpPreview" => "test",
2215 "wpSection" => "test",
2216 "wpEdittime" => "test",
2217 "wpSummary" => "test",
2218 "wpScrolltop" => "test",
2219 "wpStarttime" => "test",
2220 "wpAutoSummary" => "test",
2221 "wpTextbox1" => $this->getGeSHiContent() // the main wiki text, contains fake GeSHi content.
2222 );
2223 }
2224 }
2225
2226
2227 /**
2228 ** selects a page test to run.
2229 */
2230 function selectPageTest( $count ) {
2231
2232 // if the user only wants a specific test, then only ever give them that.
2233 if ( defined( "SPECIFIC_TEST" ) ) {
2234 $testType = SPECIFIC_TEST;
2235 return new $testType ();
2236 }
2237
2238 // Some of the time we test Special pages, the remaining
2239 // time we test using the standard edit page.
2240 switch ( $count % 100 ) {
2241 case 0 : return new successfulUserLoginTest();
2242 case 1 : return new listusersTest();
2243 case 2 : return new searchTest();
2244 case 3 : return new recentchangesTest();
2245 case 4 : return new prefixindexTest();
2246 case 5 : return new mimeSearchTest();
2247 case 6 : return new specialLogTest();
2248 case 7 : return new userLoginTest();
2249 case 8 : return new ipblocklistTest();
2250 case 9 : return new newImagesTest();
2251 case 10: return new imagelistTest();
2252 case 11: return new specialExportTest();
2253 case 12: return new specialBooksourcesTest();
2254 case 13: return new specialAllpagesTest();
2255 case 14: return new pageHistoryTest();
2256 case 15: return new contributionsTest();
2257 case 16: return new viewPageTest();
2258 case 17: return new specialAllmessagesTest();
2259 case 18: return new specialNewpages();
2260 case 19: return new searchTest();
2261 case 20: return new redirectTest();
2262 case 21: return new confirmEmail();
2263 case 22: return new watchlistTest();
2264 case 23: return new specialBlockmeTest();
2265 case 24: return new specialUndelete();
2266 case 25: return new specialMovePage();
2267 case 26: return new specialUnlockdb();
2268 case 27: return new specialLockdb();
2269 case 28: return new specialUserrights();
2270 case 29: return new pageProtectionForm();
2271 case 30: return new specialBlockip();
2272 case 31: return new imagepageTest();
2273 case 32: return new pageDeletion();
2274 case 33: return new specialRevisionDelete();
2275 case 34: return new specialImport();
2276 case 35: return new thumbTest();
2277 case 36: return new trackbackTest();
2278 case 37: return new profileInfo();
2279 case 38: return new specialCite();
2280 case 39: return new specialFilepath();
2281 case 40: return new specialMakebot();
2282 case 41: return new specialMakesysop();
2283 case 42: return new specialRenameuser();
2284 case 43: return new specialLinksearch();
2285 case 44: return new specialCategoryTree();
2286 case 45: return new api();
2287 case 45: return new specialChemicalsourcesTest();
2288 default: return new editPageTest();
2289 }
2290 }
2291
2292
2293 // ///////////////////// SAVING OUTPUT /////////////////////////
2294
2295 /**
2296 ** Utility function for saving a file. Currently has no error checking.
2297 */
2298 function saveFile( $data, $name ) {
2299 file_put_contents( $name, $data );
2300 }
2301
2302
2303 /**
2304 ** Returns a test as an experimental GET-to-POST URL.
2305 ** This doesn't seem to always work though, and sometimes the output is too long
2306 ** to be a valid GET URL, so we also save in other formats.
2307 */
2308 function getAsURL( pageTest $test ) {
2309 $used_question_mark = ( strpos( $test->getPagePath(), "?" ) !== false );
2310 $retval = "http://get-to-post.nickj.org/?" . WIKI_BASE_URL . $test->getPagePath();
2311 foreach ( $test->getParams() as $param => $value ) {
2312 if ( !$used_question_mark ) {
2313 $retval .= "?";
2314 $used_question_mark = true;
2315 }
2316 else {
2317 $retval .= "&";
2318 }
2319 $retval .= $param . "=" . urlencode( $value );
2320 }
2321 return $retval;
2322 }
2323
2324
2325 /**
2326 ** Saves a plain-text human-readable version of a test.
2327 */
2328 function saveTestAsText( pageTest $test, $filename ) {
2329 $str = "Test: " . $test->getPagePath();
2330 foreach ( $test->getParams() as $param => $value ) {
2331 $str .= "\n$param: $value";
2332 }
2333 $str .= "\nGet-to-post URL: " . getAsURL( $test ) . "\n";
2334 saveFile( $str, $filename );
2335 }
2336
2337
2338 /**
2339 ** Saves a test as a standalone basic PHP script that shows this one problem.
2340 ** Resulting script requires PHP-Curl be installed in order to work.
2341 */
2342 function saveTestAsPHP( pageTest $test, $filename ) {
2343 $str = "<?php\n"
2344 . "\$params = " . var_export( escapeForCurl( $test->getParams() ), true ) . ";\n"
2345 . "\$ch = curl_init();\n"
2346 . "curl_setopt(\$ch, CURLOPT_POST, 1);\n"
2347 . "curl_setopt(\$ch, CURLOPT_POSTFIELDS, \$params );\n"
2348 . "curl_setopt(\$ch, CURLOPT_URL, " . var_export( WIKI_BASE_URL . $test->getPagePath(), true ) . ");\n"
2349 . "curl_setopt(\$ch, CURLOPT_RETURNTRANSFER,1);\n"
2350 . ( $test->getCookie() ? "curl_setopt(\$ch, CURLOPT_COOKIE, " . var_export( $test->getCookie(), true ) . ");\n" : "" )
2351 . "\$result=curl_exec(\$ch);\n"
2352 . "curl_close (\$ch);\n"
2353 . "print \$result;\n"
2354 . "?>\n";
2355 saveFile( $str, $filename );
2356 }
2357
2358
2359 /**
2360 ** Escapes a value so that it can be used on the command line by Curl.
2361 ** Specifically, "<" and "@" need to be escaped if they are the first character,
2362 ** otherwise curl interprets these as meaning that we want to insert a file.
2363 */
2364 function escapeForCurl( array $input_params ) {
2365 $output_params = array();
2366 foreach ( $input_params as $param => $value ) {
2367 if ( strlen( $value ) > 0 && ( $value[0] == "@" || $value[0] == "<" ) ) {
2368 $value = "\\" . $value;
2369 }
2370 $output_params[$param] = $value;
2371 }
2372 return $output_params;
2373 }
2374
2375
2376 /**
2377 ** Saves a test as a standalone CURL shell script that shows this one problem.
2378 ** Resulting script requires standalone Curl be installed in order to work.
2379 */
2380 function saveTestAsCurl( pageTest $test, $filename ) {
2381 $str = "#!/bin/bash\n"
2382 . "curl --silent --include --globoff \\\n"
2383 . ( $test->getCookie() ? " --cookie " . escapeshellarg( $test->getCookie() ) . " \\\n" : "" );
2384 foreach ( escapeForCurl( $test->getParams() ) as $param => $value ) {
2385 $str .= " -F " . escapeshellarg( $param ) . "=" . escapeshellarg( $value ) . " \\\n";
2386 }
2387 $str .= " " . escapeshellarg( WIKI_BASE_URL . $test->getPagePath() ); // beginning space matters.
2388 $str .= "\n";
2389 saveFile( $str, $filename );
2390 chmod( $filename, 0755 ); // make executable
2391 }
2392
2393
2394 /**
2395 ** Saves the internal data structure to file.
2396 */
2397 function saveTestData ( pageTest $test, $filename ) {
2398 saveFile( serialize( $test ), $filename );
2399 }
2400
2401
2402 /**
2403 ** saves a test in the various formats.
2404 */
2405 function saveTest( pageTest $test, $testname ) {
2406 $base_name = DIRECTORY . "/" . $testname;
2407 saveTestAsText( $test, $base_name . INFO_FILE );
2408 saveTestAsPHP ( $test, $base_name . PHP_TEST );
2409 saveTestAsCurl( $test, $base_name . CURL_TEST );
2410 saveTestData ( $test, $base_name . DATA_FILE );
2411 }
2412
2413
2414 // ////////////////// MEDIAWIKI OUTPUT /////////////////////////
2415
2416 /**
2417 ** Asks MediaWiki for the HTML output of a test.
2418 */
2419 function wikiTestOutput( pageTest $test ) {
2420
2421 $ch = curl_init();
2422
2423 // specify the cookie, if required.
2424 if ( $test->getCookie() ) curl_setopt( $ch, CURLOPT_COOKIE, $test->getCookie() );
2425 curl_setopt( $ch, CURLOPT_POST, 1 ); // save form using a POST
2426
2427 $params = escapeForCurl( $test->getParams() );
2428 curl_setopt( $ch, CURLOPT_POSTFIELDS, $params ); // load the POST variables
2429
2430 curl_setopt( $ch, CURLOPT_URL, WIKI_BASE_URL . $test->getPagePath() ); // set url to post to
2431 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); // return into a variable
2432
2433 $result = curl_exec ( $ch );
2434
2435 // if we encountered an error, then say so, and return an empty string.
2436 if ( curl_error( $ch ) ) {
2437 print "\nCurl error #: " . curl_errno( $ch ) . " - " . curl_error ( $ch );
2438 $result = "";
2439 }
2440
2441 curl_close ( $ch );
2442
2443 return $result;
2444 }
2445
2446
2447 // ////////////////// HTML VALIDATION /////////////////////////
2448
2449 /*
2450 ** Asks the validator whether this is valid HTML, or not.
2451 */
2452 function validateHTML( $text ) {
2453
2454 $params = array ( "fragment" => $text );
2455
2456 $ch = curl_init();
2457
2458 curl_setopt( $ch, CURLOPT_POST, 1 ); // save form using a POST
2459 curl_setopt( $ch, CURLOPT_POSTFIELDS, $params ); // load the POST variables
2460 curl_setopt( $ch, CURLOPT_URL, VALIDATOR_URL ); // set url to post to
2461 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); // return into a variable
2462
2463 $result = curl_exec ( $ch );
2464
2465 // if we encountered an error, then log it, and exit.
2466 if ( curl_error( $ch ) ) {
2467 trigger_error( "Curl error #: " . curl_errno( $ch ) . " - " . curl_error ( $ch ) );
2468 print "Curl error #: " . curl_errno( $ch ) . " - " . curl_error ( $ch ) . " - exiting.\n";
2469 exit( 1 );
2470 }
2471
2472 curl_close ( $ch );
2473
2474 $valid = ( strpos( $result, "Failed validation" ) === false ? true : false );
2475
2476 return array( $valid, $result );
2477 }
2478
2479
2480 /**
2481 ** Get tidy to check for no HTML errors in the output file (e.g. unescaped strings).
2482 */
2483 function tidyCheckFile( $name ) {
2484 $file = DIRECTORY . "/" . $name;
2485 $command = PATH_TO_TIDY . " -output /tmp/out.html -quiet $file 2>&1";
2486 $x = `$command`;
2487
2488 // Look for the most interesting Tidy errors and warnings.
2489 if ( strpos( $x, "end of file while parsing attributes" ) !== false
2490 || strpos( $x, "attribute with missing trailing quote mark" ) !== false
2491 || strpos( $x, "missing '>' for end of tag" ) !== false
2492 || strpos( $x, "Error:" ) !== false ) {
2493 print "\nTidy found something - view details with: $command";
2494 return false;
2495 } else {
2496 return true;
2497 }
2498 }
2499
2500
2501 /**
2502 ** Returns whether or not an database error log file has changed in size since
2503 ** the last time this was run. This is used to tell if a test caused a DB error.
2504 */
2505 function dbErrorLogged() {
2506 static $filesize;
2507
2508 // first time running this function
2509 if ( !isset( $filesize ) ) {
2510 // create log if it does not exist
2511 if ( !file_exists( DB_ERROR_LOG_FILE ) ) {
2512 saveFile( "", DB_ERROR_LOG_FILE );
2513 }
2514 $filesize = filesize( DB_ERROR_LOG_FILE );
2515 return false;
2516 }
2517
2518 $newsize = filesize( DB_ERROR_LOG_FILE );
2519 // if the log has grown, then assume the current test caused it.
2520 if ( $newsize != $filesize ) {
2521 $filesize = $newsize;
2522 return true;
2523 }
2524
2525 return false;
2526 }
2527
2528 // //////////////// TOP-LEVEL PROBLEM-FINDING FUNCTION ////////////////////////
2529
2530 /**
2531 ** takes a page test, and runs it and tests it for problems in the output.
2532 ** Returns: False on finding a problem, or True on no problems being found.
2533 */
2534 function runWikiTest( pageTest $test, &$testname, $can_overwrite = false ) {
2535
2536 // by default don't overwrite a previous test of the same name.
2537 while ( ! $can_overwrite && file_exists( DIRECTORY . "/" . $testname . DATA_FILE ) ) {
2538 $testname .= "-" . mt_rand( 0, 9 );
2539 }
2540
2541 $filename = DIRECTORY . "/" . $testname . DATA_FILE;
2542
2543 // Store the time before and after, to find slow pages.
2544 $before = microtime( true );
2545
2546 // Get MediaWiki to give us the output of this test.
2547 $wiki_preview = wikiTestOutput( $test );
2548
2549 $after = microtime( true );
2550
2551 // if we received no response, then that's interesting.
2552 if ( $wiki_preview == "" ) {
2553 print "\nNo response received for: $filename";
2554 return false;
2555 }
2556
2557 // save output HTML to file.
2558 $html_file = DIRECTORY . "/" . $testname . HTML_FILE;
2559 saveFile( $wiki_preview, $html_file );
2560
2561 // if there were PHP errors in the output, then that's interesting too.
2562 if ( strpos( $wiki_preview, "<b>Warning</b>: " ) !== false
2563 || strpos( $wiki_preview, "<b>Fatal error</b>: " ) !== false
2564 || strpos( $wiki_preview, "<b>Notice</b>: " ) !== false
2565 || strpos( $wiki_preview, "<b>Error</b>: " ) !== false
2566 || strpos( $wiki_preview, "<b>Strict Standards:</b>" ) !== false
2567 ) {
2568 $error = substr( $wiki_preview, strpos( $wiki_preview, "</b>:" ) + 7, 50 );
2569 // Avoid probable PHP bug with bad session ids; http://bugs.php.net/bug.php?id=38224
2570 if ( $error != "Unknown: The session id contains illegal character" ) {
2571 print "\nPHP error/warning/notice in HTML output: $html_file ; $error";
2572 return false;
2573 }
2574 }
2575
2576 // if there was a MediaWiki Backtrace message in the output, then that's also interesting.
2577 if ( strpos( $wiki_preview, "Backtrace:" ) !== false ) {
2578 print "\nInternal MediaWiki error in HTML output: $html_file";
2579 return false;
2580 }
2581
2582 // if there was a Parser error comment in the output, then that's potentially interesting.
2583 if ( strpos( $wiki_preview, "!-- ERR" ) !== false ) {
2584 print "\nParser Error comment in HTML output: $html_file";
2585 return false;
2586 }
2587
2588 // if a database error was logged, then that's definitely interesting.
2589 if ( dbErrorLogged() ) {
2590 print "\nDatabase Error logged for: $filename";
2591 return false;
2592 }
2593
2594 // validate result
2595 $valid = true;
2596 if ( VALIDATE_ON_WEB ) {
2597 list ( $valid, $validator_output ) = validateHTML( $wiki_preview );
2598 if ( !$valid ) print "\nW3C web validation failed - view details with: html2text " . DIRECTORY . "/" . $testname . ".validator_output.html";
2599 }
2600
2601 // Get tidy to check the page, unless we already know it produces non-XHTML output.
2602 if ( $test->tidyValidate() ) {
2603 $valid = tidyCheckFile( $testname . HTML_FILE ) && $valid;
2604 }
2605
2606 // if it took more than 2 seconds to render, then it may be interesting too. (Possible DoS attack?)
2607 if ( ( $after - $before ) >= 2 ) {
2608 print "\nParticularly slow to render (" . round( $after - $before, 2 ) . " seconds): $filename";
2609 return false;
2610 }
2611
2612 if ( $valid ) {
2613 // Remove temp HTML file if test was valid:
2614 unlink( $html_file );
2615 } elseif ( VALIDATE_ON_WEB ) {
2616 saveFile( $validator_output, DIRECTORY . "/" . $testname . ".validator_output.html" );
2617 }
2618
2619 return $valid;
2620 }
2621
2622
2623 // ///////////////// RERUNNING OLD TESTS ///////////////////
2624
2625 /**
2626 ** We keep our failed tests so that they can be rerun.
2627 ** This function does that retesting.
2628 */
2629 function rerunPreviousTests() {
2630 print "Retesting previously found problems.\n";
2631
2632 $dir_contents = scandir ( DIRECTORY );
2633
2634 // sort file into the order a normal person would use.
2635 natsort ( $dir_contents );
2636
2637 foreach ( $dir_contents as $file ) {
2638
2639 // if file is not a test, then skip it.
2640 // Note we need to escape any periods or will be treated as "any character".
2641 $matches = array();
2642 if ( !preg_match( "/(.*)" . str_replace( ".", "\.", DATA_FILE ) . "$/", $file, $matches ) ) continue;
2643
2644 // reload the test.
2645 $full_path = DIRECTORY . "/" . $file;
2646 $test = unserialize( file_get_contents( $full_path ) );
2647
2648 // if this is not a valid test, then skip it.
2649 if ( ! $test instanceof pageTest ) {
2650 print "\nSkipping invalid test - $full_path";
2651 continue;
2652 }
2653
2654 // The date format is in Apache log format, which makes it easier to locate
2655 // which retest caused which error in the Apache logs (only happens usually if
2656 // apache segfaults).
2657 if ( !QUIET ) print "[" . date ( "D M d H:i:s Y" ) . "] Retesting $file (" . get_class( $test ) . ")";
2658
2659 // run test
2660 $testname = $matches[1];
2661 $valid = runWikiTest( $test, $testname, true );
2662
2663 if ( !$valid ) {
2664 saveTest( $test, $testname );
2665 if ( QUIET ) {
2666 print "\nTest: " . get_class( $test ) . " ; Testname: $testname\n------";
2667 } else {
2668 print "\n";
2669 }
2670 }
2671 else {
2672 if ( !QUIET ) print "\r";
2673 if ( DELETE_PASSED_RETESTS ) {
2674 $prefix = DIRECTORY . "/" . $testname;
2675 if ( is_file( $prefix . DATA_FILE ) ) unlink( $prefix . DATA_FILE );
2676 if ( is_file( $prefix . PHP_TEST ) ) unlink( $prefix . PHP_TEST );
2677 if ( is_file( $prefix . CURL_TEST ) ) unlink( $prefix . CURL_TEST );
2678 if ( is_file( $prefix . INFO_FILE ) ) unlink( $prefix . INFO_FILE );
2679 }
2680 }
2681 }
2682
2683 print "\nDone retesting.\n";
2684 }
2685
2686
2687 // //////////////////// MAIN LOOP ////////////////////////
2688
2689
2690 // first check whether CURL is installed, because sometimes it's not.
2691 if ( ! function_exists( 'curl_init' ) ) {
2692 die( "Could not find 'curl_init' function. Is the curl extension compiled into PHP?\n" );
2693 }
2694
2695 // Initialization of types. wikiFuzz doesn't have a constructor because we want to
2696 // access it staticly and not have any globals.
2697 wikiFuzz::$types = array_keys( wikiFuzz::$data );
2698
2699 // Make directory if doesn't exist
2700 if ( !is_dir( DIRECTORY ) ) {
2701 mkdir ( DIRECTORY, 0700 );
2702 }
2703 // otherwise, we first retest the things that we have found in previous runs
2704 else if ( RERUN_OLD_TESTS ) {
2705 rerunPreviousTests();
2706 }
2707
2708 // main loop.
2709 $start_time = date( "U" );
2710 $num_errors = 0;
2711 if ( !QUIET ) {
2712 print "Beginning main loop. Results are stored in the " . DIRECTORY . " directory.\n";
2713 print "Press CTRL+C to stop testing.\n";
2714 }
2715
2716 for ( $count = 0; true; $count++ ) {
2717 if ( !QUIET ) {
2718 // spinning progress indicator.
2719 switch( $count % 4 ) {
2720 case '0': print "\r/"; break;
2721 case '1': print "\r-"; break;
2722 case '2': print "\r\\"; break;
2723 case '3': print "\r|"; break;
2724 }
2725 print " $count";
2726 }
2727
2728 // generate a page test to run.
2729 $test = selectPageTest( $count );
2730
2731 $mins = ( date( "U" ) - $start_time ) / 60;
2732 if ( !QUIET && $mins > 0 ) {
2733 print ". $num_errors poss errors. "
2734 . floor( $mins ) . " mins. "
2735 . round ( $count / $mins, 0 ) . " tests/min. "
2736 . get_class( $test ); // includes the current test name.
2737 }
2738
2739 // run this test against MediaWiki, and see if the output was valid.
2740 $testname = $count;
2741 $valid = runWikiTest( $test, $testname, false );
2742
2743 // save the failed test
2744 if ( ! $valid ) {
2745 if ( QUIET ) {
2746 print "\nTest: " . get_class( $test ) . " ; Testname: $testname\n------";
2747 } else {
2748 print "\n";
2749 }
2750 saveTest( $test, $testname );
2751 $num_errors += 1;
2752 } else if ( KEEP_PASSED_TESTS ) {
2753 // print current time, with microseconds (matches "strace" format), and the test name.
2754 print " " . date( "H:i:s." ) . substr( current( explode( " ", microtime() ) ), 2 ) . " " . $testname;
2755 saveTest( $test, $testname );
2756 }
2757
2758 // stop if we have reached max number of errors.
2759 if ( defined( "MAX_ERRORS" ) && $num_errors >= MAX_ERRORS ) {
2760 break;
2761 }
2762
2763 // stop if we have reached max number of mins runtime.
2764 if ( defined( "MAX_RUNTIME" ) && $mins >= MAX_RUNTIME ) {
2765 break;
2766 }
2767 }
2768
2769