r88425 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r88424‎ | r88425 | r88426 >
Date:19:56, 19 May 2011
Author:demon
Status:ok (Comments)
Tags:
Comment:
Revert r88399 (delete fuzz-tester). Apparently people use it ;-)
Modified paths:
  • /trunk/phase3/maintenance/fuzz-tester.php (added) (history)

Diff [purge]

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

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r88399Unmaintained and unused...reedy00:27, 19 May 2011

Comments

#Comment by Hashar (talk | contribs)   21:09, 20 May 2011

Reedy owe me a beer :b

#Comment by Hashar (talk | contribs)   21:36, 8 July 2011

removing tag 'hasharissilly' to have less tags showing in the cloud tag.

Status & tagging log