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