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