MediaWiki  REL1_22
fuzz-tester.php
Go to the documentation of this file.
00001 <?php
00180 // ///////////////////////// COMMAND LINE HELP ////////////////////////////////////
00181 
00182 // This is a command line script, load MediaWiki env (gives command line options);
00183 require_once __DIR__ . '/commandLine.inc';
00184 
00185 // if the user asked for an explanation of command line options.
00186 if ( isset( $options["help"] ) ) {
00187     print <<<ENDS
00188 MediaWiki $wgVersion fuzz tester
00189 Usage: php {$_SERVER["SCRIPT_NAME"]} [--quiet] [--base-url=<url-to-test-wiki>]
00190                            [--directory=<failed-test-path>] [--include-binary]
00191                            [--w3c-validate] [--delete-passed-retests] [--help]
00192                            [--user=<username>] [--password=<password>]
00193                            [--rerun-failed-tests] [--max-errors=<int>]
00194                            [--max-runtime=<num-minutes>]
00195                            [--specific-test=<test-name>]
00196 
00197 Options:
00198   --quiet                 : Hides passed tests, shows only failed tests.
00199   --base-url              : URL to a wiki on which to run the tests.
00200                             The "http://" is optional and can be omitted.
00201   --directory             : Full path to directory for storing failed tests.
00202                             Will be created if it does not exist.
00203   --include-binary        : Includes non-alphanumeric characters in the tests.
00204   --w3c-validate          : Validates pages using the W3C's web validator.
00205                             Slow. Currently many pages fail validation.
00206   --user                  : Login name of a valid user on your test wiki.
00207   --password              : Password for the valid user on your test wiki.
00208   --delete-passed-retests : Will delete retests that now pass.
00209                             Requires --rerun-failed-tests to be meaningful.
00210   --rerun-failed-tests    : Whether to rerun any previously failed tests.
00211   --max-errors            : Maximum number of errors to report before exiting.
00212                             Does not include errors from --rerun-failed-tests
00213   --max-runtime           : Maximum runtime, in minutes, to run before exiting.
00214                             Only applies to new tests, not --rerun-failed-tests
00215   --specific-test         : Runs only the specified fuzz test.
00216                             Only applies to new tests, not --rerun-failed-tests
00217   --keep-passed-tests     : Saves all test files, even those that pass.
00218   --help                  : Show this help message.
00219 
00220 Example:
00221   If you wanted to fuzz test a nightly MediaWiki checkout using cron for 1 hour,
00222   and only wanted to be informed of errors, and did not want to redo previously
00223   failed tests, and wanted a maximum of 100 errors, then you could do:
00224   php {$_SERVER["SCRIPT_NAME"]} --quiet --max-errors=100 --max-runtime=60
00225 
00226 
00227 ENDS;
00228 
00229     exit( 0 );
00230 }
00231 
00232 
00233 // if we got command line options, check they look valid.
00234 $validOptions = array ( "quiet", "base-url", "directory", "include-binary",
00235         "w3c-validate", "user", "password", "delete-passed-retests",
00236         "rerun-failed-tests", "max-errors",
00237         "max-runtime", "specific-test", "keep-passed-tests", "help" );
00238 if ( !empty( $options ) ) {
00239     $unknownArgs = array_diff ( array_keys( $options ), $validOptions );
00240     foreach ( $unknownArgs as $invalidArg ) {
00241         print "Ignoring invalid command-line option: --$invalidArg\n";
00242     }
00243 }
00244 
00245 
00246 // /////////////////////////// CONFIGURATION ////////////////////////////////////
00247 
00248 // URL to some wiki on which we can run our tests.
00249 if ( !empty( $options["base-url"] ) ) {
00250     define( "WIKI_BASE_URL", $options["base-url"] );
00251 } else {
00252     define( "WIKI_BASE_URL", $wgServer . $wgScriptPath . '/' );
00253 }
00254 
00255 // The directory name where we store the output.
00256 // Example for Windows: "c:\\temp\\wiki-fuzz"
00257 if ( !empty( $options["directory"] ) ) {
00258     define( "DIRECTORY", $options["directory"] );
00259 } else {
00260     define( "DIRECTORY", "{$wgUploadDirectory}/fuzz-tests" );
00261 }
00262 
00263 // Should our test fuzz data include binary strings?
00264 define( "INCLUDE_BINARY",  isset( $options["include-binary"] ) );
00265 
00266 // Whether we want to validate HTML output on the web.
00267 // At the moment very few generated pages will validate, so not recommended.
00268 define( "VALIDATE_ON_WEB", isset( $options["w3c-validate"] ) );
00269 // URL to use to validate our output:
00270 define( "VALIDATOR_URL",  "http://validator.w3.org/check" );
00271 
00272 // Location of Tidy standalone executable.
00273 define( "PATH_TO_TIDY",  "/usr/bin/tidy" );
00274 
00275 // The name of a user who has edited on your wiki. Used
00276 // when testing the Special:Contributions and Special:Userlogin page.
00277 if ( !empty( $options["user"] ) ) {
00278     define( "USER_ON_WIKI", $options["user"] );
00279 } else {
00280     define( "USER_ON_WIKI", "nickj" );
00281 }
00282 
00283 // The password of the above user. Used when testing the login page,
00284 // and to do this we sometimes need to login successfully.
00285 if ( !empty( $options["password"] ) ) {
00286     define( "USER_PASSWORD", $options["password"] );
00287 } else {
00288     // And no, this is not a valid password on any public wiki.
00289     define( "USER_PASSWORD", "nickj" );
00290 }
00291 
00292 // If we have a test that failed, and then we run it again, and it passes,
00293 // do you want to delete it or keep it?
00294 define( "DELETE_PASSED_RETESTS", isset( $options["delete-passed-retests"] ) );
00295 
00296 // Do we want to rerun old saved tests at script startup?
00297 // Set to true to help catch regressions, or false if you only want new stuff.
00298 define( "RERUN_OLD_TESTS", isset( $options["rerun-failed-tests"] ) );
00299 
00300 // File where the database errors are logged. Should be defined in LocalSettings.php.
00301 define( "DB_ERROR_LOG_FILE", $wgDBerrorLog );
00302 
00303 // Run in chatty mode (all output, default), or run in quiet mode (only prints out details of failed tests)?
00304 define( "QUIET", isset( $options["quiet"] ) );
00305 
00306 // Keep all test files, even those that pass. Potentially useful to tracking input that causes something
00307 // unusual to happen, if you don't know what "unusual" is until later.
00308 define( "KEEP_PASSED_TESTS", isset( $options["keep-passed-tests"] ) );
00309 
00310 // The maximum runtime, if specified.
00311 if ( !empty( $options["max-runtime"] ) && intval( $options["max-runtime"] ) > 0 ) {
00312     define( "MAX_RUNTIME", intval( $options["max-runtime"] ) );
00313 }
00314 
00315 // The maximum number of problems to find, if specified. Excludes retest errors.
00316 if ( !empty( $options["max-errors"] ) && intval( $options["max-errors"] ) > 0 ) {
00317     define( "MAX_ERRORS", intval( $options["max-errors"] ) );
00318 }
00319 
00320 // if the user has requested a specific test (instead of all tests), and the test they asked for looks valid.
00321 if ( !empty( $options["specific-test"] ) ) {
00322     if ( class_exists( $options["specific-test"] ) && get_parent_class( $options["specific-test"] ) == "pageTest" ) {
00323         define( "SPECIFIC_TEST", $options["specific-test"] );
00324     }
00325     else {
00326         print "Ignoring invalid --specific-test\n";
00327     }
00328 }
00329 
00330 // Define the file extensions we'll use:
00331 define( "PHP_TEST" , ".test.php" );
00332 define( "CURL_TEST", ".curl.sh" );
00333 define( "DATA_FILE", ".data.bin" );
00334 define( "INFO_FILE", ".info.txt" );
00335 define( "HTML_FILE", ".wiki_preview.html" );
00336 
00337 // If it goes wrong, we want to know about it.
00338 error_reporting( E_ALL | E_STRICT );
00339 
00340 // //////////////  A CLASS THAT GENERATES RANDOM NASTY WIKI & HTML STRINGS  //////////////////////
00341 
00342 class wikiFuzz {
00343 
00344     // Only some HTML tags are understood with params by MediaWiki, the rest are ignored.
00345     // List the tags that accept params below, as well as what those params are.
00346     public static $data = array(
00347             "B"          => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
00348             "CAPTION"    => array( "CLASS", "ID", "STYLE", "align", "lang", "dir", "title" ),
00349             "CENTER"     => array( "CLASS", "STYLE", "ID", "lang", "dir", "title" ),
00350             "DIV"        => array( "CLASS", "STYLE", "ID", "align", "lang", "dir", "title" ),
00351             "FONT"       => array( "CLASS", "STYLE", "ID", "lang", "dir", "title", "face", "size", "color" ),
00352             "H1"         => array( "STYLE", "CLASS", "ID", "align", "lang", "dir", "title" ),
00353             "H2"         => array( "STYLE", "CLASS", "ID", "align", "lang", "dir", "title" ),
00354             "HR"         => array( "STYLE", "CLASS", "ID", "WIDTH", "lang", "dir", "title", "size", "noshade" ),
00355             "LI"         => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "type", "value" ),
00356             "TABLE"      => array( "STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "BORDER", "CELLPADDING",
00357                                    "CELLSPACING", "lang", "dir", "title", "summary", "frame", "rules" ),
00358             "TD"         => array( "STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "COLSPAN", "ROWSPAN",
00359                                   "VALIGN", "abbr", "axis", "headers", "scope", "nowrap", "height", "lang",
00360                                   "dir", "title", "char", "charoff" ),
00361             "TH"         => array( "STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "COLSPAN", "ROWSPAN",
00362                                   "VALIGN", "abbr", "axis", "headers", "scope", "nowrap", "height", "lang",
00363                                   "dir", "title", "char", "charoff" ),
00364             "TR"         => array( "CLASS", "STYLE", "ID", "BGCOLOR", "ALIGN", "VALIGN", "lang", "dir", "title", "char", "charoff" ),
00365             "UL"         => array( "CLASS", "STYLE", "ID", "lang", "dir", "title", "type" ),
00366             "P"          => array( "style", "class", "id", "align", "lang", "dir", "title" ),
00367             "blockquote" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "cite" ),
00368             "span"       => array( "CLASS", "ID", "STYLE", "align", "lang", "dir", "title" ),
00369             "code"       => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
00370             "tt"         => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
00371             "small"      => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
00372             "big"        => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
00373             "s"          => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
00374             "u"          => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
00375             "del"        => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "datetime", "cite" ),
00376             "ins"        => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "datetime", "cite" ),
00377             "sub"        => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
00378             "sup"        => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
00379             "ol"         => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "type", "start" ),
00380             "br"         => array( "CLASS", "ID", "STYLE", "title", "clear" ),
00381             "cite"       => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
00382             "var"        => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
00383             "ruby"       => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
00384             "rt"         => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
00385             "rp"         => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
00386             "dt"         => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
00387             "dl"         => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
00388             "em"         => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
00389             "strong"     => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
00390             "i"          => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ),
00391             "thead"      => array( "CLASS", "ID", "STYLE", "lang", "dir", "title",  'align', 'char', 'charoff', 'valign' ),
00392             "tfoot"      => array( "CLASS", "ID", "STYLE", "lang", "dir", "title",  'align', 'char', 'charoff', 'valign' ),
00393             "tbody"      => array( "CLASS", "ID", "STYLE", "lang", "dir", "title",  'align', 'char', 'charoff', 'valign' ),
00394             "colgroup"   => array( "CLASS", "ID", "STYLE", "lang", "dir", "title",  'align', 'char', 'charoff', 'valign', 'span', 'width' ),
00395             "col"        => array( "CLASS", "ID", "STYLE", "lang", "dir", "title",  'align', 'char', 'charoff', 'valign', 'span', 'width' ),
00396             "pre"        => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "width" ),
00397 
00398             // extension tags that accept parameters:
00399             "sort"         => array( "order", "class" ),
00400             "ref"          => array( "name" ),
00401             "categorytree" => array( "hideroot", "mode", "style" ),
00402             "chemform"     => array( "link", "wikilink", "query" ),
00403             "section"      => array( "begin", "new" ),
00404 
00405             // older MW transclusion.
00406             "transclude"   => array( "page" ),
00407                 );
00408 
00409     // The types of the HTML that we will be testing were defined above
00410     // Note: this needs to be initialized later to be equal to: array_keys(wikiFuzz::$data);
00411     // as such, it also needs to also be publicly modifiable.
00412     public static $types;
00413 
00414 
00415     // Some attribute values.
00416     static private $other = array( "&", "=", ":", "?", "\"", "\n", "%n%n%n%n%n%n%n%n%n%n%n%n", "\\" );
00417     static private $ints  = array(
00418             // various numbers
00419             "0", "-1", "127", "-7897", "89000", "808080", "90928345",
00420             "0xfffffff", "ffff",
00421 
00422             // Different ways of saying: '
00423             "&#0000039;", // Long UTF-8 Unicode encoding
00424             "&#39;",  // dec version.
00425             "&#x27;", // hex version.
00426             "&#xA7;", // malformed hex variant, MSB not zero.
00427 
00428             // Different ways of saying: "
00429             "&#0000034;", // Long UTF-8 Unicode encoding
00430             "&#34;",
00431             "&#x22;", // hex version.
00432             "&#xA2;", // malformed hex variant, MSB not zero.
00433 
00434             // Different ways of saying: <
00435             "<",
00436             "&#0000060",  // Long UTF-8 Unicode encoding without semicolon (Mediawiki wants the colon)
00437             "&#0000060;", // Long UTF-8 Unicode encoding with semicolon
00438             "&#60;",
00439             "&#x3C;",     // hex version.
00440             "&#xBC;",     // malformed hex variant, MSB not zero.
00441             "&#x0003C;",  // mid-length hex version
00442             "&#X00003C;", // slightly longer hex version, with capital "X"
00443 
00444             // Different ways of saying: >
00445             ">",
00446             "&#0000062;", // Long UTF-8 Unicode encoding
00447             "&#62;",
00448             "&#x3E;",     // hex version.
00449             "&#xBE;",     // malformed variant, MSB not zero.
00450 
00451             // Different ways of saying: [
00452             "&#0000091;", // Long UTF-8 Unicode encoding
00453             "&#91;",
00454             "&#x5B;",     // hex version.
00455 
00456             // Different ways of saying: {{
00457             "&#0000123;&#0000123;", // Long UTF-8 Unicode encoding
00458             "&#123;&#123;",
00459             "&#x7B;&#x7B;",         // hex version.
00460 
00461             // Different ways of saying: |
00462             "&#0000124;", // Long UTF-8 Unicode encoding
00463             "&#124;",
00464             "&#x7C;",     // hex version.
00465             "&#xFC;",     // malformed hex variant, MSB not zero.
00466 
00467             // a "lignature" - http://www.robinlionheart.com/stds/html4/spchars#ligature
00468             // &#8204; == &zwnj;
00469             "&#8204;"
00470                 );
00471 
00472     // Defines various wiki-related bits of syntax, that can potentially cause
00473     // MediaWiki to do something other than just print that literal text.
00474     static private $ext = array(
00475             // links, templates, parameters.
00476             "[[", "]]", "{{", "}}", "|", "[", "]", "{{{", "}}}", "|]]",
00477 
00478             // wiki tables.
00479             "\n{|", "\n|}",
00480             "!",
00481             "\n!",
00482             "!!",
00483             "||",
00484             "\n|-", "| ", "\n|",
00485 
00486             // section headings.
00487             "=", "==", "===", "====", "=====", "======",
00488 
00489             // lists (ordered and unordered) and indentation.
00490             "\n*", "*", "\n:", ":",
00491             "\n#", "#",
00492 
00493             // definition lists (dl, dt, dd), newline, and newline with pre, and a tab.
00494             "\n;", ";", "\n ",
00495 
00496             // Whitespace: newline, tab, space.
00497             "\n", "\t", " ",
00498 
00499             // Some XSS attack vectors from http://ha.ckers.org/xss.html
00500             "&#x09;", // tab
00501             "&#x0A;", // newline
00502             "&#x0D;", // carriage return
00503             "\0",     // null character
00504             " &#14; ", // spaces and meta characters
00505             "'';!--\"<XSS>=&{()}", // compact injection of XSS & SQL tester
00506 
00507             // various NULL fields
00508             "%00",
00509             "&#00;",
00510             "\0",
00511 
00512             // horizontal rule.
00513             "-----", "\n-----",
00514 
00515             // signature, redirect, bold, italics.
00516             "~~~~", "#REDIRECT [[", "'''", "''",
00517 
00518             // comments.
00519             "<!--", "-->",
00520 
00521             // quotes.
00522             "\"", "'",
00523 
00524             // tag start and tag end.
00525             "<", ">",
00526 
00527             // implicit link creation on URIs.
00528             "http://",
00529             "https://",
00530             "ftp://",
00531             "irc://",
00532             "news:",
00533             'gopher://',
00534             'telnet://',
00535             'nntp://',
00536             'worldwind://',
00537             'mailto:',
00538 
00539             // images.
00540             "[[image:",
00541             ".gif",
00542             ".png",
00543             ".jpg",
00544             ".jpeg",
00545             'thumbnail=',
00546             'thumbnail',
00547             'thumb=',
00548             'thumb',
00549             'right',
00550             'none',
00551             'left',
00552             'framed',
00553             'frame',
00554             'enframed',
00555             'centre',
00556             'center',
00557             "Image:",
00558             "[[:Image",
00559             'px',
00560             'upright=',
00561             'border',
00562 
00563             // misc stuff to throw at the Parser.
00564             '%08X',
00565             '/',
00566             ":x{|",
00567             "\n|+",
00568             "<noinclude>",
00569             "</noinclude>",
00570             " \302\273",
00571             " :",
00572             " !",
00573             " ;",
00574             "\302\253",
00575             "[[category:",
00576             "?=",
00577             "(",
00578             ")",
00579             "]]]",
00580             "../",
00581             "{{{{",
00582             "}}}}",
00583             "[[Special:",
00584             "<includeonly>",
00585             "</includeonly>",
00586             "<!--MWTEMPLATESECTION=",
00587             '<!--MWTOC-->',
00588 
00589             // implicit link creation on booknum, RFC, and PubMed ID usage (both with and without IDs)
00590             "ISBN 2",
00591             "RFC 000",
00592             "PMID 000",
00593             "ISBN ",
00594             "RFC ",
00595             "PMID ",
00596 
00597             // magic words:
00598             '__NOTOC__',
00599             '__FORCETOC__',
00600             '__NOEDITSECTION__',
00601             '__START__',
00602             '__NOTITLECONVERT__',
00603             '__NOCONTENTCONVERT__',
00604             '__END__',
00605             '__TOC__',
00606             '__NOTC__',
00607             '__NOCC__',
00608             "__FORCETOC__",
00609             "__NEWSECTIONLINK__",
00610             "__NOGALLERY__",
00611 
00612             // more magic words / internal templates.
00613             '{{PAGENAME}}',
00614             '{{PAGENAMEE}}',
00615             '{{NAMESPACE}}',
00616             "{{MSG:",
00617             "}}",
00618             "{{MSGNW:",
00619             "}}",
00620             "{{INT:",
00621             "}}",
00622             '{{SITENAME}}',
00623             "{{NS:",
00624             "}}",
00625             "{{LOCALURL:",
00626             "}}",
00627             "{{LOCALURLE:",
00628             "}}",
00629             "{{SCRIPTPATH}}",
00630             "{{GRAMMAR:gentiv|",
00631             "}}",
00632             "{{REVISIONID}}",
00633             "{{SUBPAGENAME}}",
00634             "{{SUBPAGENAMEE}}",
00635             "{{ns:0}}",
00636             "{{fullurle:",
00637             "}}",
00638             "{{subst::",
00639             "}}",
00640             "{{UCFIRST:",
00641             "}}",
00642             "{{UC:",
00643             '{{SERVERNAME}}',
00644             '{{SERVER}}',
00645             "{{RAW:",
00646             "}}",
00647             "{{PLURAL:",
00648             "}}",
00649             "{{LCFIRST:",
00650             "}}",
00651             "{{LC:",
00652             "}}",
00653             '{{CURRENTWEEK}}',
00654             '{{CURRENTDOW}}',
00655             "{{INT:{{LC:contribs-showhideminor}}|",
00656             "}}",
00657             "{{INT:googlesearch|",
00658             "}}",
00659                         "{{ROOTPAGENAME}}",
00660             "{{BASEPAGENAME}}",
00661             "{{CONTENTLANGUAGE}}",
00662             "{{PAGESINNAMESPACE:}}",
00663             "{{#language:",
00664             "}}",
00665             "{{#special:",
00666             "}}",
00667             "{{#special:emailuser",
00668             "}}",
00669 
00670             // Some raw link for magic words.
00671             "{{NUMBEROFPAGES:R",
00672             "}}",
00673             "{{NUMBEROFUSERS:R",
00674             "}}",
00675             "{{NUMBEROFARTICLES:R",
00676             "}}",
00677             "{{NUMBEROFFILES:R",
00678             "}}",
00679             "{{NUMBEROFADMINS:R",
00680             "}}",
00681             "{{padleft:",
00682             "}}",
00683             "{{padright:",
00684             "}}",
00685             "{{DEFAULTSORT:",
00686             "}}",
00687 
00688             // internal Math "extension":
00689             "<math>",
00690             "</math>",
00691 
00692             // Parser extension functions:
00693             "{{#expr:",
00694             "{{#if:",
00695             "{{#ifeq:",
00696             "{{#ifexist:",
00697             "{{#ifexpr:",
00698             "{{#switch:",
00699             "{{#time:",
00700             "}}",
00701 
00702             // references table for the Cite extension.
00703             "<references/>",
00704 
00705             // Internal Parser tokens - try inserting some of these.
00706             "UNIQ25f46b0524f13e67NOPARSE",
00707             "UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002",
00708             "\x07UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002-QINU",
00709 
00710             // Inputbox extension:
00711             "<inputbox>\ntype=search\nsearchbuttonlabel=\n",
00712             "</inputbox>",
00713 
00714             // charInsert extension:
00715             "<charInsert>",
00716             "</charInsert>",
00717 
00718             // wikiHiero extension:
00719             "<hiero>",
00720             "</hiero>",
00721 
00722             // Image gallery:
00723             "<gallery>",
00724             "</gallery>",
00725 
00726             // FixedImage extension.
00727             "<fundraising/>",
00728 
00729             // Timeline extension: currently untested.
00730 
00731             // Nowiki:
00732             "<nOwIkI>",
00733             "</nowiki>",
00734 
00735             // an external image to test the external image displaying code
00736             "http://debian.org/Pics/debian.png",
00737 
00738             // LabeledSectionTransclusion extension.
00739             "{{#lstx:",
00740             "}}",
00741             "{{#lst:",
00742             "}}",
00743             "{{#lst:Main Page|",
00744             "}}"
00745             );
00746 
00750     public static function chooseInput( array $input ) {
00751         $randindex = wikiFuzz::randnum( count( $input ) - 1 );
00752         return $input[$randindex];
00753     }
00754 
00755     // Max number of parameters for HTML attributes.
00756     static private $maxparams = 10;
00757 
00764     public static function randnum( $finish, $start = 0 ) {
00765         return mt_rand( $start, $finish );
00766     }
00767 
00772     private static function randstring() {
00773         $thestring = "";
00774 
00775         for ( $i = 0; $i < 40; $i++ ) {
00776             $what = wikiFuzz::randnum( 1 );
00777 
00778             if ( $what == 0 ) { // include some random wiki syntax
00779                 $which = wikiFuzz::randnum( count( wikiFuzz::$ext ) - 1 );
00780                 $thestring .= wikiFuzz::$ext[$which];
00781             }
00782             else { // include some random text
00783                 $char = INCLUDE_BINARY
00784                     // Decimal version:
00785                     // "&#" . wikiFuzz::randnum(255) . ";"
00786                     // Hex version:
00787                     ? "&#x" . str_pad( dechex( wikiFuzz::randnum( 255 ) ), wikiFuzz::randnum( 2, 7 ), "0", STR_PAD_LEFT ) . ";"
00788                     // A truly binary version:
00789                     // ? chr(wikiFuzz::randnum(0,255))
00790                     : chr( wikiFuzz::randnum( 126, 32 ) );
00791 
00792                 $length = wikiFuzz::randnum( 8 );
00793                 $thestring .= str_repeat ( $char, $length );
00794             }
00795         }
00796         return $thestring;
00797     }
00798 
00804     private static function makestring() {
00805         $what = wikiFuzz::randnum( 2 );
00806         if ( $what == 0 ) {
00807             return wikiFuzz::randstring();
00808         } elseif ( $what == 1 ) {
00809             return wikiFuzz::$ints[wikiFuzz::randnum( count( wikiFuzz::$ints ) - 1 )];
00810         } else {
00811             return wikiFuzz::$other[wikiFuzz::randnum( count( wikiFuzz::$other ) - 1 )];
00812         }
00813     }
00814 
00821     private static function stringEscape( $matches ) {
00822         return sprintf( "\\x%02x", ord( $matches[1] ) );
00823     }
00824 
00831     public static function makeTitleSafe( $str ) {
00832         $legalTitleChars = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF";
00833         return preg_replace_callback(
00834                 "/([^$legalTitleChars])/", 'wikiFuzz::stringEscape',
00835                 $str );
00836     }
00837 
00842     private static function loop() {
00843         switch ( wikiFuzz::randnum( 3 ) ) {
00844             case 1: // an opening tag, with parameters.
00845                 $string = "";
00846                 $i = wikiFuzz::randnum( count( wikiFuzz::$types ) - 1 );
00847                 $t = wikiFuzz::$types[$i];
00848                 $arr = wikiFuzz::$data[$t];
00849                 $string .= "<" . $t . " ";
00850                 $num_params = min( wikiFuzz::$maxparams, count( $arr ) );
00851                 for ( $z = 0; $z < $num_params; $z++ ) {
00852                     $badparam = $arr[wikiFuzz::randnum( count( $arr ) - 1 )];
00853                     $badstring = wikiFuzz::makestring();
00854                     $string .= $badparam . "=" . wikiFuzz::getRandQuote() . $badstring . wikiFuzz::getRandQuote() . " ";
00855                 }
00856                 $string .= ">\n";
00857                 return $string;
00858             case 2: // a closing tag.
00859                 $i = wikiFuzz::randnum( count( wikiFuzz::$types ) - 1 );
00860                 return "</" . wikiFuzz::$types[$i] . ">";
00861             case 3: // a random string, between tags.
00862                 return wikiFuzz::makeString();
00863         }
00864         return "";    // catch-all, should never be called.
00865     }
00866 
00871     private static function getRandQuote() {
00872         switch ( wikiFuzz::randnum( 3 ) ) {
00873             case 1 : return "'";
00874             case 2 : return "\"";
00875             default: return "";
00876         }
00877     }
00878 
00884     public static function makeFuzz( $maxtypes = 2 ) {
00885         $page = "";
00886         for ( $k = 0; $k < $maxtypes; $k++ ) {
00887             $page .= wikiFuzz::loop();
00888         }
00889         return $page;
00890     }
00891 }
00892 
00893 
00894 // //////   MEDIAWIKI PAGES TO TEST, AND HOW TO TEST THEM  ///////
00895 
00905 abstract class pageTest {
00906     protected $params;
00907     protected $pagePath;
00908     protected $cookie = "";
00909     protected $tidyValidate = true;
00910 
00911     public function getParams() {
00912         return $this->params;
00913     }
00914 
00915     public function getPagePath() {
00916         return $this->pagePath;
00917     }
00918 
00919     public function getCookie() {
00920         return $this->cookie;
00921     }
00922 
00923     public function tidyValidate() {
00924         return $this->tidyValidate;
00925     }
00926 }
00927 
00928 
00932 class editPageTest extends pageTest {
00933     function __construct() {
00934         $this->pagePath = "index.php?title=WIKIFUZZ";
00935 
00936         $this->params = array (
00937                 "action"        => "submit",
00938                 "wpMinoredit"   => wikiFuzz::makeFuzz( 2 ),
00939                 "wpPreview"     => wikiFuzz::makeFuzz( 2 ),
00940                 "wpSection"     => wikiFuzz::makeFuzz( 2 ),
00941                 "wpEdittime"    => wikiFuzz::makeFuzz( 2 ),
00942                 "wpSummary"     => wikiFuzz::makeFuzz( 2 ),
00943                 "wpScrolltop"   => wikiFuzz::makeFuzz( 2 ),
00944                 "wpStarttime"   => wikiFuzz::makeFuzz( 2 ),
00945                 "wpAutoSummary" => wikiFuzz::makeFuzz( 2 ),
00946                 "wpTextbox1"    => wikiFuzz::makeFuzz( 40 )  // the main wiki text, need lots of this.
00947                 );
00948 
00949         // sometimes we don't want to specify certain parameters.
00950         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpSection"] );
00951         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpEdittime"] );
00952         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpSummary"] );
00953         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpScrolltop"] );
00954         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpStarttime"] );
00955         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpAutoSummary"] );
00956         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpTextbox1"] );
00957     }
00958 }
00959 
00960 
00964 class listusersTest extends pageTest {
00965     function __construct() {
00966         $this->pagePath = "index.php?title=Special:Listusers";
00967 
00968         $this->params = array (
00969                 "title"        => wikiFuzz::makeFuzz( 2 ),
00970                 "group"        => wikiFuzz::makeFuzz( 2 ),
00971                 "username"     => wikiFuzz::makeFuzz( 2 ),
00972                 "Go"           => wikiFuzz::makeFuzz( 2 ),
00973                 "limit"        => wikiFuzz::chooseInput( array( "0", "-1", "---'----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
00974                 "offset"       => wikiFuzz::chooseInput( array( "0", "-1", "--------'-----0", "+1", "81343242346234234", wikiFuzz::makeFuzz( 2 ) ) )
00975                 );
00976     }
00977 }
00978 
00979 
00983 class searchTest extends pageTest {
00984     function __construct() {
00985         $this->pagePath = "index.php?title=Special:Search";
00986 
00987         $this->params = array (
00988                 "action"        => "index.php?title=Special:Search",
00989                 "ns0"           => wikiFuzz::makeFuzz( 2 ),
00990                 "ns1"           => wikiFuzz::makeFuzz( 2 ),
00991                 "ns2"           => wikiFuzz::makeFuzz( 2 ),
00992                 "ns3"           => wikiFuzz::makeFuzz( 2 ),
00993                 "ns4"           => wikiFuzz::makeFuzz( 2 ),
00994                 "ns5"           => wikiFuzz::makeFuzz( 2 ),
00995                 "ns6"           => wikiFuzz::makeFuzz( 2 ),
00996                 "ns7"           => wikiFuzz::makeFuzz( 2 ),
00997                 "ns8"           => wikiFuzz::makeFuzz( 2 ),
00998                 "ns9"           => wikiFuzz::makeFuzz( 2 ),
00999                 "ns10"          => wikiFuzz::makeFuzz( 2 ),
01000                 "ns11"          => wikiFuzz::makeFuzz( 2 ),
01001                 "ns12"          => wikiFuzz::makeFuzz( 2 ),
01002                 "ns13"          => wikiFuzz::makeFuzz( 2 ),
01003                 "ns14"          => wikiFuzz::makeFuzz( 2 ),
01004                 "ns15"          => wikiFuzz::makeFuzz( 2 ),
01005                 "redirs"        => wikiFuzz::makeFuzz( 2 ),
01006                 "search"        => wikiFuzz::makeFuzz( 2 ),
01007                 "offset"        => wikiFuzz::chooseInput( array( "", "0", "-1", "--------'-----0", "+1", "81343242346234234", wikiFuzz::makeFuzz( 2 ) ) ),
01008                 "fulltext"      => wikiFuzz::chooseInput( array( "", "0", "1", "--------'-----0", "+1", wikiFuzz::makeFuzz( 2 ) ) ),
01009                 "searchx"       => wikiFuzz::chooseInput( array( "", "0", "1", "--------'-----0", "+1", wikiFuzz::makeFuzz( 2 ) ) )
01010                     );
01011     }
01012 }
01013 
01014 
01018 class recentchangesTest extends pageTest {
01019     function __construct() {
01020         $this->pagePath = "index.php?title=Special:Recentchanges";
01021 
01022         $this->params = array (
01023                 "action"        => wikiFuzz::makeFuzz( 2 ),
01024                 "title"         => wikiFuzz::makeFuzz( 2 ),
01025                 "namespace"     => wikiFuzz::chooseInput( range( -1, 15 ) ),
01026                 "Go"            => wikiFuzz::makeFuzz( 2 ),
01027                 "invert"        => wikiFuzz::chooseInput( array( "-1", "---'----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
01028                 "hideanons"     => wikiFuzz::chooseInput( array( "-1", "------'-------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
01029                 'limit'         => wikiFuzz::chooseInput( array( "0", "-1", "---------'----0", "+1", "81340909772349234",  wikiFuzz::makeFuzz( 2 ) ) ),
01030                 "days"          => wikiFuzz::chooseInput( array( "-1", "----------'---0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
01031                 "hideminor"     => wikiFuzz::chooseInput( array( "-1", "-----------'--0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
01032                 "hidebots"      => wikiFuzz::chooseInput( array( "-1", "---------'----0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
01033                 "hideliu"       => wikiFuzz::chooseInput( array( "-1", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
01034                 "hidepatrolled" => wikiFuzz::chooseInput( array( "-1", "-----'--------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
01035                 "hidemyself"    => wikiFuzz::chooseInput( array( "-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
01036                 'categories_any' => wikiFuzz::chooseInput( array( "-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
01037                 'categories'    => wikiFuzz::chooseInput( array( "-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
01038                 'feed'          => wikiFuzz::chooseInput( array( "-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) )
01039                 );
01040     }
01041 }
01042 
01043 
01047 class prefixindexTest extends pageTest {
01048     function __construct() {
01049         $this->pagePath = "index.php?title=Special:Prefixindex";
01050 
01051         $this->params = array (
01052                 "title"         => "Special:Prefixindex",
01053                 "namespace"     => wikiFuzz::randnum( 101, -10 ),
01054                 "Go"            => wikiFuzz::makeFuzz( 2 )
01055                 );
01056 
01057         // sometimes we want 'prefix', sometimes we want 'from', and sometimes we want nothing.
01058         if ( wikiFuzz::randnum( 3 ) == 0 ) {
01059             $this->params["prefix"] = wikiFuzz::chooseInput( array( "-1", "-----'--------0", "+++--+1",
01060                                                  wikiFuzz::randnum( 8134, -10 ), wikiFuzz::makeFuzz( 2 ) ) );
01061         }
01062         if ( wikiFuzz::randnum( 3 ) == 0 ) {
01063             $this->params["from"]   = wikiFuzz::chooseInput( array( "-1", "-----'--------0", "+++--+1",
01064                                                 wikiFuzz::randnum( 8134, -10 ), wikiFuzz::makeFuzz( 2 ) ) );
01065         }
01066     }
01067 }
01068 
01069 
01073 class mimeSearchTest extends pageTest {
01074     function __construct() {
01075         $this->pagePath = "index.php?title=Special:MIMEsearch";
01076 
01077         $this->params = array (
01078                 "action"        => "index.php?title=Special:MIMEsearch",
01079                 "mime"          => wikiFuzz::makeFuzz( 3 ),
01080                 'limit'         => wikiFuzz::chooseInput( array( "0", "-1", "-------'------0", "+1", "81342321351235325",  wikiFuzz::makeFuzz( 2 ) ) ),
01081                 'offset'        => wikiFuzz::chooseInput( array( "0", "-1", "-----'--------0", "+1", "81341231235365252234324",  wikiFuzz::makeFuzz( 2 ) ) )
01082                 );
01083     }
01084 }
01085 
01086 
01090 class specialLogTest extends pageTest {
01091     function __construct() {
01092         $this->pagePath = "index.php?title=Special:Log";
01093 
01094         $this->params = array (
01095                 "type"        => wikiFuzz::chooseInput( array( "", wikiFuzz::makeFuzz( 2 ) ) ),
01096                 "par"         => wikiFuzz::makeFuzz( 2 ),
01097                 "user"        => wikiFuzz::makeFuzz( 2 ),
01098                 "page"        => wikiFuzz::makeFuzz( 2 ),
01099                 "from"        => wikiFuzz::makeFuzz( 2 ),
01100                 "until"       => wikiFuzz::makeFuzz( 2 ),
01101                 "title"       => wikiFuzz::makeFuzz( 2 )
01102                 );
01103     }
01104 }
01105 
01106 
01110 class successfulUserLoginTest extends pageTest {
01111     function __construct() {
01112         $this->pagePath = "index.php?title=Special:Userlogin&action=submitlogin&type=login&returnto=" . wikiFuzz::makeFuzz( 2 );
01113 
01114         $this->params = array (
01115                 "wpName"          => USER_ON_WIKI,
01116                 // sometimes real password, sometimes not:
01117                 'wpPassword'      => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), USER_PASSWORD ) ),
01118                 'wpRemember'      => wikiFuzz::makeFuzz( 2 )
01119                 );
01120 
01121         $this->cookie = "wikidb_session=" .  wikiFuzz::chooseInput( array( "1" , wikiFuzz::makeFuzz( 2 ) ) );
01122     }
01123 }
01124 
01125 
01129 class userLoginTest extends pageTest {
01130     function __construct() {
01131 
01132         $this->pagePath = "index.php?title=Special:Userlogin";
01133 
01134         $this->params = array (
01135                 'wpRetype'        => wikiFuzz::makeFuzz( 2 ),
01136                 'wpRemember'      => wikiFuzz::makeFuzz( 2 ),
01137                 'wpRealName'      => wikiFuzz::makeFuzz( 2 ),
01138                 'wpPassword'      => wikiFuzz::makeFuzz( 2 ),
01139                 'wpName'          => wikiFuzz::makeFuzz( 2 ),
01140                 'wpMailmypassword' => wikiFuzz::makeFuzz( 2 ),
01141                 'wpLoginattempt'  => wikiFuzz::makeFuzz( 2 ),
01142                 'wpEmail'         => wikiFuzz::makeFuzz( 2 ),
01143                 'wpDomain'        => wikiFuzz::chooseInput( array( "", "local", wikiFuzz::makeFuzz( 2 ) ) ),
01144                 'wpCreateaccountMail' => wikiFuzz::chooseInput( array( "", wikiFuzz::makeFuzz( 2 ) ) ),
01145                 'wpCreateaccount' => wikiFuzz::chooseInput( array( "", wikiFuzz::makeFuzz( 2 ) ) ),
01146                 'wpCookieCheck'   => wikiFuzz::chooseInput( array( "", wikiFuzz::makeFuzz( 2 ) ) ),
01147                 'type'            => wikiFuzz::chooseInput( array( "signup", "login", "", wikiFuzz::makeFuzz( 2 ) ) ),
01148                 'returnto'        => wikiFuzz::makeFuzz( 2 ),
01149                 'action'          => wikiFuzz::chooseInput( array( "", "submitlogin", wikiFuzz::makeFuzz( 2 ) ) )
01150                 );
01151 
01152         $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array( "1" , wikiFuzz::makeFuzz( 2 ) ) );
01153     }
01154 }
01155 
01156 
01160 class ipblocklistTest extends pageTest {
01161     function __construct() {
01162         $this->pagePath = "index.php?title=Special:Ipblocklist";
01163 
01164         $this->params = array (
01165                 'wpUnblockAddress' => wikiFuzz::makeFuzz( 2 ),
01166                 'ip'              => wikiFuzz::chooseInput( array( "20398702394", "", "Nickj2", wikiFuzz::makeFuzz( 2 ),
01167                                      // something like an IP address, sometimes invalid:
01168                                      ( wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) . "."
01169                                        . wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) ) ) ),
01170                 'id'              => wikiFuzz::makeFuzz( 2 ),
01171                 'wpUnblockReason' => wikiFuzz::makeFuzz( 2 ),
01172                 'action'          => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), "success", "submit", "unblock" ) ),
01173                 'wpEditToken'     => wikiFuzz::makeFuzz( 2 ),
01174                 'wpBlock'         => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), "" ) ),
01175                 'limit'           => wikiFuzz::chooseInput( array( "0", "-1", "--------'-----0", "+1",
01176                                                  "09700982312351132098234",  wikiFuzz::makeFuzz( 2 ) ) ),
01177                 'offset'          => wikiFuzz::chooseInput( array( "0", "-1", "------'-------0", "+1",
01178                                                  "09700980982341535324234234", wikiFuzz::makeFuzz( 2 ) ) )
01179                 );
01180 
01181         // sometimes we don't want to specify certain parameters.
01182         if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["action"] );
01183         if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["ip"] );
01184         if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["id"] );
01185         if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["wpUnblockAddress"] );
01186     }
01187 }
01188 
01189 
01193 class newImagesTest extends  pageTest {
01194     function __construct() {
01195         $this->pagePath = "index.php?title=Special:Newimages";
01196 
01197         $this->params = array (
01198                 'hidebots'  => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), "1", "", "-1" ) ),
01199                 'wpIlMatch' => wikiFuzz::makeFuzz( 2 ),
01200                 'until'     => wikiFuzz::makeFuzz( 2 ),
01201                 'from'      => wikiFuzz::makeFuzz( 2 )
01202                 );
01203 
01204         // sometimes we don't want to specify certain parameters.
01205         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["until"] );
01206         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["from"] );
01207     }
01208 }
01209 
01210 
01214 class imagelistTest extends pageTest {
01215     function __construct() {
01216         $this->pagePath = "index.php?title=Special:Imagelist";
01217 
01218         $this->params = array (
01219                 'sort'      => wikiFuzz::chooseInput( array( "bysize", "byname" , "bydate", wikiFuzz::makeFuzz( 2 ) ) ),
01220                 'limit'     => wikiFuzz::chooseInput( array( "0", "-1", "--------'-----0", "+1", "09700982312351132098234",  wikiFuzz::makeFuzz( 2 ) ) ),
01221                 'offset'    => wikiFuzz::chooseInput( array( "0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz( 2 ) ) ),
01222                 'wpIlMatch' => wikiFuzz::makeFuzz( 2 )
01223                 );
01224     }
01225 }
01226 
01227 
01231 class specialExportTest extends pageTest {
01232     function __construct() {
01233         $this->pagePath = "index.php?title=Special:Export";
01234 
01235         $this->params = array (
01236                 'action'      => wikiFuzz::chooseInput( array( "submit", "", wikiFuzz::makeFuzz( 2 ) ) ),
01237                 'pages'       => wikiFuzz::makeFuzz( 2 ),
01238                 'curonly'     => wikiFuzz::chooseInput( array( "", "0", "-1", wikiFuzz::makeFuzz( 2 ) ) ),
01239                 'listauthors' => wikiFuzz::chooseInput( array( "", "0", "-1", wikiFuzz::makeFuzz( 2 ) ) ),
01240                 'history'     => wikiFuzz::chooseInput( array( "0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz( 2 ) ) ),
01241 
01242                 );
01243 
01244         // For the time being, need to disable "submit" action as Tidy barfs on MediaWiki's XML export.
01245         if ( $this->params['action'] == 'submit' ) $this->params['action'] = '';
01246 
01247         // Sometimes remove the history field.
01248         if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["history"] );
01249 
01250         // page does not produce HTML.
01251         $this->tidyValidate = false;
01252     }
01253 }
01254 
01255 
01259 class specialBooksourcesTest extends pageTest {
01260     function __construct() {
01261         $this->pagePath = "index.php?title=Special:Booksources";
01262 
01263         $this->params = array (
01264                 'go'    => wikiFuzz::makeFuzz( 2 ),
01265                 // ISBN codes have to contain some semi-numeric stuff or will be ignored:
01266                 'isbn'  => "0X0" . wikiFuzz::makeFuzz( 2 )
01267                 );
01268     }
01269 }
01270 
01271 
01275 class specialAllpagesTest extends pageTest {
01276     function __construct() {
01277         $this->pagePath = "index.php?title=Special%3AAllpages";
01278 
01279         $this->params = array (
01280                 'from'      => wikiFuzz::makeFuzz( 2 ),
01281                 'namespace' => wikiFuzz::chooseInput( range( -1, 15 ) ),
01282                 'go'        => wikiFuzz::makeFuzz( 2 )
01283                 );
01284     }
01285 }
01286 
01287 
01291 class pageHistoryTest extends pageTest {
01292     function __construct() {
01293         $this->pagePath = "index.php?title=Main_Page&action=history";
01294 
01295         $this->params = array (
01296                 'limit'     => wikiFuzz::chooseInput( array( "-1", "0", "-------'------0", "+1", "8134",  wikiFuzz::makeFuzz( 2 ) ) ),
01297                 'offset'    => wikiFuzz::chooseInput( array( "-1", "0", "------'-------0", "+1", "9823412312312412435", wikiFuzz::makeFuzz( 2 ) ) ),
01298                 "go"        => wikiFuzz::chooseInput( array( "first", "last", wikiFuzz::makeFuzz( 2 ) ) ),
01299                 "dir"       => wikiFuzz::chooseInput( array( "prev", "next", wikiFuzz::makeFuzz( 2 ) ) ),
01300                 "diff"      => wikiFuzz::chooseInput( array( "-1", "--------'-----0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
01301                 "oldid"     => wikiFuzz::chooseInput( array( "prev", "-1", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ),
01302                 "feed"      => wikiFuzz::makeFuzz( 2 )
01303                 );
01304     }
01305 }
01306 
01307 
01311 class contributionsTest extends pageTest {
01312     function __construct() {
01313         $this->pagePath = "index.php?title=Special:Contributions/" . USER_ON_WIKI;
01314 
01315         $this->params = array (
01316                 'target'    => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), "newbies", USER_ON_WIKI ) ),
01317                 'namespace' => wikiFuzz::chooseInput( array( -1, 15, 1, wikiFuzz::makeFuzz( 2 ) ) ),
01318                 'offset'    => wikiFuzz::chooseInput( array( "0", "-1", "------'-------0", "+1", "982342131232131231241", wikiFuzz::makeFuzz( 2 ) ) ),
01319                 'bot'       => wikiFuzz::chooseInput( array( "", "-1", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
01320                 'go'        => wikiFuzz::chooseInput( array( "-1", 'prev', 'next', wikiFuzz::makeFuzz( 2 ) ) )
01321                 );
01322     }
01323 }
01324 
01325 
01329 class viewPageTest extends pageTest {
01330     function __construct() {
01331         $this->pagePath = "index.php?title=Main_Page";
01332 
01333         $this->params = array (
01334                 "useskin"        => wikiFuzz::chooseInput( array( "chick", "cologneblue", "myskin",
01335                                         "nostalgia", "simple", "standard", wikiFuzz::makeFuzz( 2 ) ) ),
01336                 "uselang"        => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ),
01337                         "ab", "af", "an", "ar", "arc", "as", "ast", "av", "ay", "az", "ba",
01338                         "bat-smg", "be", "bg", "bm", "bn", "bo", "bpy", "br", "bs", "ca",
01339                         "ce", "cs", "csb", "cv", "cy", "da", "de", "dv", "dz", "el", "en",
01340                         "eo", "es", "et", "eu", "fa", "fi", "fo", "fr", "fur", "fy", "ga",
01341                         "gn", "gsw", "gu", "he", "hi", "hr", "hu", "ia", "id", "ii", "is",
01342                         "it", "ja", "jv", "ka", "km", "kn", "ko", "ks", "ku", "kv", "la",
01343                         "li", "lo", "lt", "lv", "mk", "ml", "ms", "nah", "nap", "nds",
01344                         "nds-nl", "nl", "nn", "no", "non", "nv", "oc", "or", "os", "pa",
01345                         "pl", "pms", "ps", "pt", "pt-br", "qu", "rmy", "ro", "ru", "sc",
01346                         "sd", "sk", "sl", "sq", "sr", "sr-ec", "sr-el",
01347                         "su", "sv", "ta", "te", "th", "tr", "tt", "ty", "tyv", "udm",
01348                         "ug", "uk", "ur", "utf8", "vec", "vi", "wa", "xal", "yi", "za",
01349                         "zh", "zh-cn", "zh-hk", "zh-sg", "zh-tw", "zh-tw" ) ),
01350                 "returnto"       => wikiFuzz::makeFuzz( 2 ),
01351                 "feed"           => wikiFuzz::chooseInput( array( "atom", "rss", wikiFuzz::makeFuzz( 2 ) ) ),
01352                 "rcid"           => wikiFuzz::makeFuzz( 2 ),
01353                 "action"         => wikiFuzz::chooseInput( array( "view", "raw", "render", wikiFuzz::makeFuzz( 2 ), "markpatrolled" ) ),
01354                 "printable"      => wikiFuzz::makeFuzz( 2 ),
01355                 "oldid"          => wikiFuzz::makeFuzz( 2 ),
01356                 "redirect"       => wikiFuzz::makeFuzz( 2 ),
01357                 "diff"           => wikiFuzz::makeFuzz( 2 ),
01358                 "search"         => wikiFuzz::makeFuzz( 2 ),
01359                 "rdfrom"         => wikiFuzz::makeFuzz( 2 ),  // things from Article.php from here on:
01360                 "token"          => wikiFuzz::makeFuzz( 2 ),
01361                 "tbid"           => wikiFuzz::makeFuzz( 2 ),
01362                 // @todo FIXME: Duplicate array key.
01363                 "action"         => wikiFuzz::chooseInput( array( "purge", wikiFuzz::makeFuzz( 2 ) ) ),
01364                 "wpReason"       => wikiFuzz::makeFuzz( 2 ),
01365                 "wpEditToken"    => wikiFuzz::makeFuzz( 2 ),
01366                 "from"           => wikiFuzz::makeFuzz( 2 ),
01367                 "bot"            => wikiFuzz::makeFuzz( 2 ),
01368                 "summary"        => wikiFuzz::makeFuzz( 2 ),
01369                 "direction"      => wikiFuzz::chooseInput( array( "next", "prev", wikiFuzz::makeFuzz( 2 ) ) ),
01370                 "section"        => wikiFuzz::makeFuzz( 2 ),
01371                 "preload"        => wikiFuzz::makeFuzz( 2 ),
01372 
01373                 );
01374 
01375         // Tidy does not know how to valid atom or rss, so exclude from testing for the time being.
01376         if ( $this->params["feed"] == "atom" )     { unset( $this->params["feed"] ); }
01377         elseif ( $this->params["feed"] == "rss" ) { unset( $this->params["feed"] ); }
01378 
01379         // Raw pages cannot really be validated
01380         if ( $this->params["action"] == "raw" ) unset( $this->params["action"] );
01381 
01382         // sometimes we don't want to specify certain parameters.
01383         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["rcid"] );
01384         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["diff"] );
01385         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["rdfrom"] );
01386         if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["oldid"] );
01387 
01388         // usually don't want action == purge.
01389         if ( wikiFuzz::randnum( 6 ) > 1 ) unset( $this->params["action"] );
01390     }
01391 }
01392 
01393 
01397 class specialAllmessagesTest extends pageTest {
01398     function __construct() {
01399         $this->pagePath = "index.php?title=Special:Allmessages";
01400 
01401         // only really has one parameter
01402         $this->params = array (
01403                 "ot"     => wikiFuzz::chooseInput( array( "php", "html", wikiFuzz::makeFuzz( 2 ) ) )
01404                 );
01405     }
01406 }
01407 
01411 class specialNewpagesPageTest extends pageTest {
01412     function __construct() {
01413         $this->pagePath = "index.php?title=Special:Newpages";
01414 
01415         $this->params = array (
01416                 "namespace" => wikiFuzz::chooseInput( range( -1, 15 ) ),
01417                 "feed"      => wikiFuzz::chooseInput( array( "atom", "rss", wikiFuzz::makeFuzz( 2 ) ) ),
01418                 'limit'     => wikiFuzz::chooseInput( array( "-1", "0", "-------'------0", "+1", "8134",  wikiFuzz::makeFuzz( 2 ) ) ),
01419                 'offset'    => wikiFuzz::chooseInput( array( "-1", "0", "------'-------0", "+1", "9823412312312412435", wikiFuzz::makeFuzz( 2 ) ) )
01420                 );
01421 
01422         // Tidy does not know how to valid atom or rss, so exclude from testing for the time being.
01423         if ( $this->params["feed"] == "atom" )     { unset( $this->params["feed"] ); }
01424         elseif ( $this->params["feed"] == "rss" ) { unset( $this->params["feed"] ); }
01425     }
01426 }
01427 
01431 class redirectTest extends pageTest {
01432     function __construct() {
01433         $this->pagePath = "redirect.php";
01434 
01435         $this->params = array (
01436                 "wpDropdown" => wikiFuzz::makeFuzz( 2 )
01437                 );
01438 
01439         // sometimes we don't want to specify certain parameters.
01440         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpDropdown"] );
01441     }
01442 }
01443 
01444 
01448 class confirmEmail extends pageTest {
01449     function __construct() {
01450         // sometimes we send a bogus confirmation code, and sometimes we don't.
01451         $this->pagePath = "index.php?title=Special:Confirmemail" . wikiFuzz::chooseInput( array( "", "/" . wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 1 ) ) ) );
01452 
01453         $this->params = array (
01454                 "token" => wikiFuzz::makeFuzz( 2 )
01455                 );
01456     }
01457 }
01458 
01459 
01464 class watchlistTest extends pageTest {
01465     function __construct() {
01466         $this->pagePath = "index.php?title=Special:Watchlist";
01467 
01468         $this->params = array (
01469                 "remove"   => wikiFuzz::chooseInput( array( "Remove checked items from watchlist", wikiFuzz::makeFuzz( 2 ) ) ),
01470                 'days'     => wikiFuzz::chooseInput( array( 0, -1, -230, "--", 3, 9, wikiFuzz::makeFuzz( 2 ) ) ),
01471                 'hideOwn'  => wikiFuzz::chooseInput( array( "", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
01472                 'hideBots' => wikiFuzz::chooseInput( array( "", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
01473                 'namespace' => wikiFuzz::chooseInput( array( "", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
01474                 'action'   => wikiFuzz::chooseInput( array( "submit", "clear", wikiFuzz::makeFuzz( 2 ) ) ),
01475                 'id[]'     => wikiFuzz::makeFuzz( 2 ),
01476                 'edit'     => wikiFuzz::makeFuzz( 2 ),
01477                 'token'    => wikiFuzz::chooseInput( array( "", "1243213", wikiFuzz::makeFuzz( 2 ) ) )
01478                 );
01479 
01480         // sometimes we specifiy "reset", and sometimes we don't.
01481         if ( wikiFuzz::randnum( 3 ) == 0 ) $this->params["reset"] = wikiFuzz::chooseInput( array( "", "0", "1", wikiFuzz::makeFuzz( 2 ) ) );
01482     }
01483 }
01484 
01488 class specialMovePage extends pageTest {
01489     function __construct() {
01490         $this->pagePath = "index.php?title=Special:Movepage";
01491 
01492         $this->params = array (
01493                 "action"      => wikiFuzz::chooseInput( array( "success", "submit", "", wikiFuzz::makeFuzz( 2 ) ) ),
01494                 'wpEditToken' => wikiFuzz::chooseInput( array( '', 0, 34987987, wikiFuzz::makeFuzz( 2 ) ) ),
01495                 'target'      => wikiFuzz::chooseInput( array( "x", wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 2 ) ) ) ),
01496                 'wpOldTitle'  => wikiFuzz::chooseInput( array( "z", wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 2 ) ), wikiFuzz::makeFuzz( 2 ) ) ),
01497                 'wpNewTitle'  => wikiFuzz::chooseInput( array( "y", wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 2 ) ), wikiFuzz::makeFuzz( 2 ) ) ),
01498                 'wpReason'    => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ) ) ),
01499                 'wpMovetalk'  => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
01500                 'wpDeleteAndMove'  => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
01501                 'wpConfirm'   => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
01502                 'talkmoved'   => wikiFuzz::chooseInput( array( "1", wikiFuzz::makeFuzz( 2 ), "articleexists", 'notalkpage' ) ),
01503                 'oldtitle'    => wikiFuzz::makeFuzz( 2 ),
01504                 'newtitle'    => wikiFuzz::makeFuzz( 2 ),
01505                 'wpMovetalk'  => wikiFuzz::chooseInput( array( "1", "0", wikiFuzz::makeFuzz( 2 ) ) )
01506                 );
01507 
01508         // sometimes we don't want to specify certain parameters.
01509         if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["wpEditToken"] );
01510         if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["target"] );
01511         if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["wpNewTitle"] );
01512         if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpReason"] );
01513         if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpOldTitle"] );
01514     }
01515 }
01516 
01517 
01521 class specialUndeletePageTest extends pageTest {
01522     function __construct() {
01523         $this->pagePath = "index.php?title=Special:Undelete";
01524 
01525         $this->params = array (
01526                 "action"      => wikiFuzz::chooseInput( array( "submit", "", wikiFuzz::makeFuzz( 2 ) ) ),
01527                 'wpEditToken' => wikiFuzz::chooseInput( array( '', 0, 34987987, wikiFuzz::makeFuzz( 2 ) ) ),
01528                 'target'      => wikiFuzz::chooseInput( array( "x", wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 2 ) ) ) ),
01529                 'timestamp'   => wikiFuzz::chooseInput( array( "125223", wikiFuzz::makeFuzz( 2 ) ) ),
01530                 'file'        => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
01531                 'restore'     => wikiFuzz::chooseInput( array( "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
01532                 'preview'     => wikiFuzz::chooseInput( array( "0", "1", wikiFuzz::makeFuzz( 2 ) ) ),
01533                 'wpComment'   => wikiFuzz::makeFuzz( 2 )
01534                 );
01535 
01536         // sometimes we don't want to specify certain parameters.
01537         if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["wpEditToken"] );
01538         if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["target"] );
01539         if ( wikiFuzz::randnum( 1 ) == 0 ) unset( $this->params["restore"] );
01540         if ( wikiFuzz::randnum( 1 ) == 0 ) unset( $this->params["preview"] );
01541     }
01542 }
01543 
01544 
01548 class specialUnlockdbPageTest extends pageTest {
01549     function __construct() {
01550         $this->pagePath = "index.php?title=Special:Unlockdb";
01551 
01552         $this->params = array (
01553                 "action"        => wikiFuzz::chooseInput( array( "submit", "success", "", wikiFuzz::makeFuzz( 2 ) ) ),
01554                 'wpEditToken'   => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
01555                 'wpLockConfirm' => wikiFuzz::chooseInput( array( "0", "1", wikiFuzz::makeFuzz( 2 ) ) )
01556                 );
01557 
01558         // sometimes we don't want to specify certain parameters.
01559         if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpEditToken"] );
01560         if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["action"] );
01561         if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpLockConfirm"] );
01562     }
01563 }
01564 
01565 
01569 class specialLockdbPageTest extends pageTest {
01570     function __construct() {
01571         $this->pagePath = "index.php?title=Special:Lockdb";
01572 
01573         $this->params = array (
01574                 "action"       => wikiFuzz::chooseInput( array( "submit", "success", "", wikiFuzz::makeFuzz( 2 ) ) ),
01575                 'wpEditToken'  => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
01576                 'wpLockReason' => wikiFuzz::makeFuzz( 2 ),
01577                 'wpLockConfirm' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) )
01578                 );
01579 
01580         // sometimes we don't want to specify certain parameters.
01581         if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpEditToken"] );
01582         if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["action"] );
01583         if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpLockConfirm"] );
01584     }
01585 }
01586 
01587 
01591 class specialUserrights extends pageTest {
01592     function __construct() {
01593         $this->pagePath = "index.php?title=Special:Userrights";
01594 
01595         $this->params = array (
01596                 'wpEditToken'   => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
01597                 'user-editname' => wikiFuzz::chooseInput( array( "Nickj2", "Nickj2\n<xyz>", wikiFuzz::makeFuzz( 2 ) ) ),
01598                 'ssearchuser'   => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
01599                 'saveusergroups' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ), "Save User Groups" ),
01600                 'member[]'      => wikiFuzz::chooseInput( array( "0", "bot", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
01601                 "available[]"   => wikiFuzz::chooseInput( array( "0", "sysop", "bureaucrat", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) )
01602                 );
01603 
01604         // sometimes we don't want to specify certain parameters.
01605         if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params['ssearchuser'] );
01606         if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params['saveusergroups'] );
01607     }
01608 }
01609 
01610 
01614 class pageProtectionForm extends pageTest {
01615     function __construct() {
01616         $this->pagePath = "index.php?title=Main_Page";
01617 
01618         $this->params = array (
01619                 "action"               => "protect",
01620                 'wpEditToken'          => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
01621                 "mwProtect-level-edit" => wikiFuzz::chooseInput( array( '', 'autoconfirmed', 'sysop', wikiFuzz::makeFuzz( 2 ) ) ),
01622                 "mwProtect-level-move" => wikiFuzz::chooseInput( array( '', 'autoconfirmed', 'sysop', wikiFuzz::makeFuzz( 2 ) ) ),
01623                 "mwProtectUnchained"   => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
01624                 'mwProtect-reason'     => wikiFuzz::chooseInput( array( "because it was there", wikiFuzz::makeFuzz( 2 ) ) )
01625                 );
01626 
01627 
01628         // sometimes we don't want to specify certain parameters.
01629         if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["mwProtectUnchained"] );
01630         if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params['mwProtect-reason'] );
01631     }
01632 }
01633 
01634 
01638 class specialBlockip extends pageTest {
01639     function __construct() {
01640         $this->pagePath = "index.php?title=Special:Blockip";
01641 
01642         $this->params = array (
01643                 "action"          => wikiFuzz::chooseInput( array( "submit", "",  wikiFuzz::makeFuzz( 2 ) ) ),
01644                 'wpEditToken'     => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
01645                 "wpBlockAddress"  => wikiFuzz::chooseInput( array( "20398702394", "", "Nickj2", wikiFuzz::makeFuzz( 2 ),
01646                                       // something like an IP address, sometimes invalid:
01647                                      ( wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) . "."
01648                                       . wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) ) ) ),
01649                 "ip"              => wikiFuzz::chooseInput( array( "20398702394", "", "Nickj2", wikiFuzz::makeFuzz( 2 ),
01650                                       // something like an IP address, sometimes invalid:
01651                                      ( wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) . "."
01652                                       . wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) ) ) ),
01653                 "wpBlockOther"    => wikiFuzz::chooseInput( array( '', 'Nickj2', wikiFuzz::makeFuzz( 2 ) ) ),
01654                 "wpBlockExpiry"   => wikiFuzz::chooseInput( array( "other", "2 hours", "1 day", "3 days", "1 week", "2 weeks",
01655                                           "1 month", "3 months", "6 months", "1 year", "infinite", wikiFuzz::makeFuzz( 2 ) ) ),
01656                 "wpBlockReason"   => wikiFuzz::chooseInput( array( "because it was there", wikiFuzz::makeFuzz( 2 ) ) ),
01657                 "wpAnonOnly"      => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
01658                 "wpCreateAccount" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
01659                 "wpBlock"         => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) )
01660                 );
01661 
01662         // sometimes we don't want to specify certain parameters.
01663         if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpBlockOther"] );
01664         if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpBlockExpiry"] );
01665         if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpBlockReason"] );
01666         if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpAnonOnly"] );
01667         if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpCreateAccount"] );
01668         if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpBlockAddress"] );
01669         if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["ip"] );
01670     }
01671 }
01672 
01673 
01677 class imagepageTest extends pageTest {
01678     function __construct() {
01679         $this->pagePath = "index.php?title=Image:Small-email.png";
01680 
01681         $this->params = array (
01682                 "image"         => wikiFuzz::chooseInput( array( "Small-email.png", wikiFuzz::makeFuzz( 2 ) ) ),
01683                 "wpReason"      => wikiFuzz::makeFuzz( 2 ),
01684                 "oldimage"      => wikiFuzz::chooseInput( array( "Small-email.png", wikiFuzz::makeFuzz( 2 ) ) ),
01685                 "wpEditToken"   => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
01686                 );
01687 
01688         // sometimes we don't want to specify certain parameters.
01689         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["image"] );
01690         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpReason"] );
01691         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["oldimage"] );
01692         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpEditToken"] );
01693     }
01694 }
01695 
01696 
01700 class pageDeletion extends pageTest {
01701     function __construct() {
01702         $this->pagePath = "index.php?title=Main_Page&action=delete";
01703 
01704         $this->params = array (
01705                 "wpEditToken" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
01706                 "wpReason"    => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
01707                 "wpConfirm"   => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
01708                 );
01709 
01710         // sometimes we don't want to specify certain parameters.
01711         if ( wikiFuzz::randnum( 5 ) == 0 ) unset( $this->params["wpReason"] );
01712         if ( wikiFuzz::randnum( 5 ) == 0 ) unset( $this->params["wpEditToken"] );
01713         if ( wikiFuzz::randnum( 5 ) == 0 ) unset( $this->params["wpConfirm"] );
01714     }
01715 }
01716 
01717 
01718 
01722 class specialRevisionDeletePageTest extends pageTest {
01723     function __construct() {
01724         $this->pagePath = "index.php?title=Special:Revisiondelete";
01725 
01726         $this->params = array (
01727                 "target"               => wikiFuzz::chooseInput( array( "Main Page", wikiFuzz::makeFuzz( 2 ) ) ),
01728                 "oldid"                => wikiFuzz::makeFuzz( 2 ),
01729                 "oldid[]"              => wikiFuzz::makeFuzz( 2 ),
01730                 "wpReason"             => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
01731                 "revdelete-hide-text"  => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
01732                 "revdelete-hide-comment" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
01733                 "revdelete-hide-user"  => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
01734                 "revdelete-hide-restricted" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
01735                 );
01736 
01737         // sometimes we don't want to specify certain parameters.
01738         if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["target"] );
01739         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["oldid"] );
01740         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["oldid[]"] );
01741         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpReason"] );
01742         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["revdelete-hide-text"] );
01743         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["revdelete-hide-comment"] );
01744         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["revdelete-hide-user"] );
01745         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["revdelete-hide-restricted"] );
01746     }
01747 }
01748 
01749 
01753 class specialImportPageTest extends pageTest {
01754     function __construct() {
01755         $this->pagePath = "index.php?title=Special:Import";
01756 
01757         $this->params = array (
01758                 "action"         => "submit",
01759                 "source"         => wikiFuzz::chooseInput( array( "upload", "interwiki", wikiFuzz::makeFuzz( 2 ) ) ),
01760                 "MAX_FILE_SIZE"  => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
01761                 "xmlimport"      => wikiFuzz::chooseInput( array( "/var/www/hosts/mediawiki/wiki/AdminSettings.php", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ),
01762                 "namespace"      => wikiFuzz::chooseInput( array( wikiFuzz::randnum( 30, -6 ), wikiFuzz::makeFuzz( 2 ) ) ),
01763                 "interwiki"      => wikiFuzz::makeFuzz( 2 ),
01764                 "interwikiHistory" => wikiFuzz::makeFuzz( 2 ),
01765                 "frompage"       => wikiFuzz::makeFuzz( 2 ),
01766                 );
01767 
01768         // sometimes we don't want to specify certain parameters.
01769         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["action"] );
01770         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["source"] );
01771         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["MAX_FILE_SIZE"] );
01772         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["xmlimport"] );
01773         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["interwiki"] );
01774         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["interwikiHistory"] );
01775         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["frompage"] );
01776 
01777         // Note: Need to do a file upload to fully test this Special page.
01778     }
01779 }
01780 
01781 
01785 class thumbTest extends pageTest {
01786     function __construct() {
01787         $this->pagePath = "thumb.php";
01788 
01789         $this->params = array (
01790                 "f"  => wikiFuzz::chooseInput( array( "..", "\\", "small-email.png", wikiFuzz::makeFuzz( 2 ) ) ),
01791                 "w"  => wikiFuzz::chooseInput( array( "80", wikiFuzz::randnum( 6000, -200 ), wikiFuzz::makeFuzz( 2 ) ) ),
01792                 "r"  => wikiFuzz::chooseInput( array( "0", wikiFuzz::makeFuzz( 2 ) ) ),
01793                 );
01794 
01795         // sometimes we don't want to specify certain parameters.
01796         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["f"] );
01797         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["w"] );
01798         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["r"] );
01799     }
01800 }
01801 
01805 class profileInfo extends pageTest {
01806     function __construct() {
01807         $this->pagePath = "profileinfo.php";
01808 
01809         $this->params = array (
01810                 "expand"  => wikiFuzz::makeFuzz( 2 ),
01811                 "sort"    => wikiFuzz::chooseInput( array( "time", "count", "name", wikiFuzz::makeFuzz( 2 ) ) ),
01812                 "filter"  => wikiFuzz::chooseInput( array( "Main Page", wikiFuzz::makeFuzz( 2 ) ) ),
01813                 );
01814 
01815         // sometimes we don't want to specify certain parameters.
01816         if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["sort"] );
01817         if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["filter"] );
01818     }
01819 }
01820 
01821 
01825 class specialCitePageTest extends pageTest {
01826     function __construct() {
01827         $this->pagePath = "index.php?title=Special:Cite";
01828 
01829         $this->params = array (
01830                 "page"    => wikiFuzz::chooseInput( array( "\" onmouseover=\"alert(1);\"", "Main Page", wikiFuzz::makeFuzz( 2 ) ) ),
01831                 "id"      => wikiFuzz::chooseInput( array( "-1", "0", "------'-------0", "+1", "-9823412312312412435", wikiFuzz::makeFuzz( 2 ) ) ),
01832                 );
01833 
01834         // sometimes we don't want to specify certain parameters.
01835         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["page"] );
01836         if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["id"] );
01837     }
01838 }
01839 
01840 
01844 class specialFilepathPageTest extends pageTest {
01845     function __construct() {
01846         $this->pagePath = "index.php?title=Special:Filepath";
01847 
01848         $this->params = array (
01849                 "file"    => wikiFuzz::chooseInput( array( "Small-email.png", "Small-email.png" . wikiFuzz::makeFuzz( 1 ), wikiFuzz::makeFuzz( 2 ) ) ),
01850                 );
01851     }
01852 }
01853 
01854 
01858 class specialRenameuserPageTest extends pageTest {
01859     function __construct() {
01860         $this->pagePath = "index.php?title=Special:Renameuser";
01861 
01862         $this->params = array (
01863                 "oldusername"   => wikiFuzz::chooseInput( array( "Nickj2", "192.168.0.2", wikiFuzz::makeFuzz( 1 ) ) ),
01864                 "newusername"   => wikiFuzz::chooseInput( array( "Nickj2", "192.168.0.2", wikiFuzz::makeFuzz( 1 ) ) ),
01865                 "token"         => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ),
01866                 );
01867     }
01868 }
01869 
01870 
01874 class specialLinksearch extends pageTest {
01875     function __construct() {
01876         $this->pagePath = "index.php?title=Special%3ALinksearch";
01877 
01878         $this->params = array (
01879                 "target" => wikiFuzz::makeFuzz( 2 ),
01880                 );
01881 
01882         // sometimes we don't want to specify certain parameters.
01883         if ( wikiFuzz::randnum( 10 ) == 0 ) unset( $this->params["target"] );
01884     }
01885 }
01886 
01887 
01891 class specialCategoryTree extends pageTest {
01892     function __construct() {
01893         $this->pagePath = "index.php?title=Special:CategoryTree";
01894 
01895         $this->params = array (
01896                 "target" => wikiFuzz::makeFuzz( 2 ),
01897                 "from"   => wikiFuzz::makeFuzz( 2 ),
01898                 "until"  => wikiFuzz::makeFuzz( 2 ),
01899                 "showas" => wikiFuzz::makeFuzz( 2 ),
01900                 "mode"   => wikiFuzz::chooseInput( array( "pages", "categories", "all", wikiFuzz::makeFuzz( 2 ) ) ),
01901                 );
01902 
01903         // sometimes we do want to specify certain parameters.
01904         if ( wikiFuzz::randnum( 5 ) == 0 ) $this->params["notree"] = wikiFuzz::chooseInput( array( "1", 0, "", wikiFuzz::makeFuzz( 2 ) ) );
01905     }
01906 }
01907 
01908 
01912 class specialChemicalsourcesTest extends pageTest {
01913     function __construct() {
01914         $this->pagePath = "index.php?title=Special:Chemicalsources";
01915 
01916         // choose an input format to use.
01917         $format =  wikiFuzz::chooseInput(
01918                     array(  'go',
01919                             'CAS',
01920                             'EINECS',
01921                             'CHEBI',
01922                             'PubChem',
01923                             'SMILES',
01924                             'InChI',
01925                             'ATCCode',
01926                             'KEGG',
01927                             'RTECS',
01928                             'ECNumber',
01929                             'DrugBank',
01930                             'Formula',
01931                             'Name'
01932                          )
01933                     );
01934 
01935         // values for different formats usually start with either letters or numbers.
01936         switch ( $format ) {
01937             case 'Name'   : $value = "A"; break;
01938             case 'InChI'  :
01939             case 'SMILES' :
01940             case 'Formula': $value = "C"; break;
01941             default       : $value = "0"; break;
01942         }
01943 
01944         // and then we append the fuzz input.
01945         $this->params = array ( $format => $value . wikiFuzz::makeFuzz( 2 ) );
01946     }
01947 }
01948 
01949 
01959 class api extends pageTest {
01960 
01961     // API login mode.
01962     private static function loginMode() {
01963         $arr =  array ( "lgname"        => wikiFuzz::makeFuzz( 2 ),
01964                         "lgpassword"    => wikiFuzz::makeFuzz( 2 ),
01965                        );
01966         // sometimes we want to specify the extra "lgdomain" parameter.
01967         if ( wikiFuzz::randnum( 3 ) == 0 ) {
01968             $arr["lgdomain"] = wikiFuzz::chooseInput( array( "1", 0, "", wikiFuzz::makeFuzz( 2 ) ) );
01969         }
01970 
01971         return $arr;
01972     }
01973 
01974     // API OpenSearch mode.
01975     private static function opensearchMode() {
01976         return array ( "search"        => wikiFuzz::makeFuzz( 2 ) );
01977     }
01978 
01979     // API watchlist feed mode.
01980     private static function feedwatchlistMode() {
01981         // @todo FIXME: Add "wikiFuzz::makeFuzz(2)" as possible value below?
01982         return array ( "feedformat"    => wikiFuzz::chooseInput( array( "rss", "atom" ) ) );
01983     }
01984 
01985     // API query mode.
01986     private static function queryMode() {
01987         // @todo FIXME: Add "wikiFuzz::makeFuzz(2)" as possible params for the elements below?
01988         //        Suspect this will stuff up the tests more, but need to check.
01989         $params = array (
01990                      // @todo FIXME: More titles.
01991                      "titles"        => wikiFuzz::chooseInput( array( "Main Page" ) ),
01992                      // @todo FIXME: More pageids.
01993                      "pageids"       => 1,
01994                      "prop"          => wikiFuzz::chooseInput( array( "info", "revisions", "watchlist" ) ),
01995                      "list"          => wikiFuzz::chooseInput( array( "allpages", "logevents", "watchlist", "usercontribs", "recentchanges", "backlinks", "embeddedin", "imagelinks" ) ),
01996                      "meta"          => wikiFuzz::chooseInput( array( "siteinfo" ) ),
01997                      "generator"     => wikiFuzz::chooseInput( array( "allpages", "logevents", "watchlist", "info", "revisions" ) ),
01998                      "siprop"        => wikiFuzz::chooseInput( array( "general", "namespaces", "general|namespaces" ) ),
01999                    );
02000 
02001          // Add extra parameters based on what list choice we got.
02002          switch ( $params["list"] ) {
02003             case "usercontribs" : self::addListParams ( $params, "uc", array( "limit", "start", "end", "user", "dir" ) ); break;
02004             case "allpages"     : self::addListParams ( $params, "ap", array( "from", "prefix", "namespace", "filterredir", "limit" ) ); break;
02005             case "watchlist"    : self::addListParams ( $params, "wl", array( "allrev", "start", "end", "namespace", "dir", "limit", "prop" ) ); break;
02006             case "logevents"    : self::addListParams ( $params, "le", array( "limit", "type", "start", "end", "user", "dir" ) ); break;
02007             case "recentchanges": self::addListParams ( $params, "rc", array( "limit", "prop", "show", "namespace", "start", "end", "dir" ) ); break;
02008             case "backlinks"    : self::addListParams ( $params, "bl", array( "continue", "namespace", "redirect", "limit" ) ); break;
02009             case "embeddedin"   : self::addListParams ( $params, "ei", array( "continue", "namespace", "redirect", "limit" ) ); break;
02010             case "imagelinks"   : self::addListParams ( $params, "il", array( "continue", "namespace", "redirect", "limit" ) ); break;
02011          }
02012 
02013          if ( $params["prop"] == "revisions" ) {
02014             self::addListParams ( $params, "rv", array( "prop", "limit", "startid", "endid", "end", "dir" ) );
02015          }
02016 
02017          // Sometimes we want redirects, sometimes we don't.
02018          if ( wikiFuzz::randnum( 3 ) == 0 ) {
02019             $params["redirects"] = wikiFuzz::chooseInput( array( "1", 0, "", wikiFuzz::makeFuzz( 2 ) ) );
02020          }
02021 
02022          return $params;
02023     }
02024 
02025     // Adds all the elements to the array, using the specified prefix.
02026     private static function addListParams( &$array, $prefix, $elements ) {
02027         foreach ( $elements as $element ) {
02028             $array[$prefix . $element] = self::getParamDetails( $element );
02029         }
02030     }
02031 
02032     // For a given element name, returns the data for that element.
02033     private static function getParamDetails( $element ) {
02034         switch ( $element ) {
02035             case 'startid'    :
02036             case 'endid'      :
02037             case 'start'      :
02038             case 'end'        :
02039             case 'limit'      : return wikiFuzz::chooseInput( array( "0", "-1", "---'----------0", "+1", "8134", "320742734234235", "20060230121212", wikiFuzz::randnum( 9000, -100 ), wikiFuzz::makeFuzz( 2 ) ) );
02040             case 'dir'        : return wikiFuzz::chooseInput( array( "newer", "older", wikiFuzz::makeFuzz( 2 ) ) );
02041             case 'user'       : return wikiFuzz::chooseInput( array( USER_ON_WIKI, wikiFuzz::makeFuzz( 2 ) ) );
02042             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 ) ) );
02043             case 'filterredir': return wikiFuzz::chooseInput( array( "all", "redirects", "nonredirectsallpages", wikiFuzz::makeFuzz( 2 ) ) );
02044             case 'allrev'     : return wikiFuzz::chooseInput( array( "1", 0, "", wikiFuzz::makeFuzz( 2 ) ) );
02045             case 'prop'       : return wikiFuzz::chooseInput( array( "user", "comment", "timestamp", "patrol", "flags", "user|user|comment|flags", wikiFuzz::makeFuzz( 2 ) ) );
02046             case 'type'       : return wikiFuzz::chooseInput( array( "block", "protect", "rights", "delete", "upload", "move", "import", "renameuser", "newusers", "makebot", wikiFuzz::makeFuzz( 2 ) ) );
02047             case 'hide'       : return wikiFuzz::chooseInput( array( "minor", "bots", "anons", "liu", "liu|bots|", wikiFuzz::makeFuzz( 2 ) ) );
02048             case 'show'       : return wikiFuzz::chooseInput( array( 'minor', '!minor', 'bot', '!bot', 'anon', '!anon', wikiFuzz::makeFuzz( 2 ) ) );
02049             default           : return wikiFuzz::makeFuzz( 2 );
02050         }
02051     }
02052 
02053     // Entry point.
02054     function __construct() {
02055         $this->pagePath = "api.php";
02056 
02057         $modes = array ( "help",
02058                         "login",
02059                         "opensearch",
02060                         "feedwatchlist",
02061                         "query" );
02062         $action = wikiFuzz::chooseInput( array_merge ( $modes, array( wikiFuzz::makeFuzz( 2 ) ) ) );
02063 
02064         switch ( $action ) {
02065             case "login"         : $this->params = self::loginMode();
02066                                    break;
02067             case "opensearch"    : $this->params = self::opensearchMode();
02068                                    break;
02069             case "feedwatchlist" : $this->params = self::feedwatchlistMode();
02070                                    break;
02071             case "query"         : $this->params = self::queryMode();
02072                                    break;
02073             case "help"         :
02074             default             :  // Do something random - "Crazy Ivan" mode.
02075                                    $random_mode = wikiFuzz::chooseInput( $modes ) . "Mode";
02076                                    // There is no "helpMode".
02077                                    if ( $random_mode == "helpMode" ) $random_mode = "queryMode";
02078                                    $this->params = self::$random_mode();
02079                                    break;
02080         }
02081 
02082         // Save the selected action.
02083         $this->params["action"] = $action;
02084 
02085         // Set the cookie:
02086         // @todo FIXME: Need to get this cookie dynamically set, rather than hard-coded.
02087         $this->cookie = "wikidbUserID=10001; wikidbUserName=Test; wikidb_session=178df0fe68c75834643af65dec9ec98a; wikidbToken=1adc6753d62c44aec950c024d7ae0540";
02088 
02089         // Output format
02090         $this->params["format"] = wikiFuzz::chooseInput( array( "json", "jsonfm", "php", "phpfm",
02091                                                                "wddx", "wddxfm", "xml", "xmlfm",
02092                                                                "yaml", "yamlfm", "raw", "rawfm",
02093                                                                wikiFuzz::makeFuzz( 2 ) ) );
02094 
02095         // Page does not produce HTML (sometimes).
02096         $this->tidyValidate = false;
02097     }
02098 }
02099 
02100 
02104 class GeSHi_Test extends pageTest {
02105 
02106     private function getGeSHiContent() {
02107         return "<source lang=\"" . $this->getLang() . "\" "
02108                . ( wikiFuzz::randnum( 2 ) == 0 ? "line " : "" )
02109                . ( wikiFuzz::randnum( 2 ) == 0 ? "strict " : "" )
02110                . "start=" . wikiFuzz::chooseInput( array( wikiFuzz::randnum( 6000, -6000 ), wikiFuzz::makeFuzz( 2 ) ) )
02111                . ">"
02112                . wikiFuzz::makeFuzz( 2 )
02113                . "</source>";
02114     }
02115 
02116     private function getLang() {
02117     return wikiFuzz::chooseInput( array( "actionscript", "ada", "apache", "applescript", "asm", "asp", "autoit", "bash", "blitzbasic", "bnf", "c", "c_mac", "caddcl", "cadlisp",
02118                 "cfdg", "cfm", "cpp", "cpp-qt", "csharp", "css", "d", "delphi", "diff", "div", "dos", "eiffel", "fortran", "freebasic", "gml", "groovy", "html4strict", "idl",
02119                 "ini", "inno", "io", "java", "java5", "javascript", "latex", "lisp", "lua", "matlab", "mirc", "mpasm", "mysql", "nsis", "objc", "ocaml", "ocaml-brief", "oobas",
02120                 "oracle8", "pascal", "perl", "php", "php-brief", "plsql", "python", "qbasic", "rails", "reg", "robots", "ruby", "sas", "scheme", "sdlbasic", "smalltalk", "smarty",
02121                 "sql", "tcl", "text", "thinbasic", "tsql", "vb", "vbnet", "vhdl", "visualfoxpro", "winbatch", "xml", "xpp", "z80", wikiFuzz::makeFuzz( 1 ) ) );
02122     }
02123 
02124     function __construct() {
02125         $this->pagePath = "index.php?title=WIKIFUZZ";
02126 
02127         $this->params = array (
02128                 "action"        => "submit",
02129                 "wpMinoredit"   => "test",
02130                 "wpPreview"     => "test",
02131                 "wpSection"     => "test",
02132                 "wpEdittime"    => "test",
02133                 "wpSummary"     => "test",
02134                 "wpScrolltop"   => "test",
02135                 "wpStarttime"   => "test",
02136                 "wpAutoSummary" => "test",
02137                 "wpTextbox1"    => $this->getGeSHiContent() // the main wiki text, contains fake GeSHi content.
02138                 );
02139     }
02140 }
02141 
02147 function selectPageTest( $count ) {
02148 
02149     // if the user only wants a specific test, then only ever give them that.
02150     if ( defined( "SPECIFIC_TEST" ) ) {
02151         $testType = SPECIFIC_TEST;
02152         return new $testType ();
02153     }
02154 
02155     // Some of the time we test Special pages, the remaining
02156     // time we test using the standard edit page.
02157     switch ( $count % 100 ) {
02158         case 0 : return new successfulUserLoginTest();
02159         case 1 : return new listusersTest();
02160         case 2 : return new searchTest();
02161         case 3 : return new recentchangesTest();
02162         case 4 : return new prefixindexTest();
02163         case 5 : return new mimeSearchTest();
02164         case 6 : return new specialLogTest();
02165         case 7 : return new userLoginTest();
02166         case 8 : return new ipblocklistTest();
02167         case 9 : return new newImagesTest();
02168         case 10: return new imagelistTest();
02169         case 11: return new specialExportTest();
02170         case 12: return new specialBooksourcesTest();
02171         case 13: return new specialAllpagesTest();
02172         case 14: return new pageHistoryTest();
02173         case 15: return new contributionsTest();
02174         case 16: return new viewPageTest();
02175         case 17: return new specialAllmessagesTest();
02176         case 18: return new specialNewpagesPageTest();
02177         case 19: return new searchTest();
02178         case 20: return new redirectTest();
02179         case 21: return new confirmEmail();
02180         case 22: return new watchlistTest();
02181         case 24: return new specialUndeletePageTest();
02182         case 25: return new specialMovePage();
02183         case 26: return new specialUnlockdbPageTest();
02184         case 27: return new specialLockdbPageTest();
02185         case 28: return new specialUserrights();
02186         case 29: return new pageProtectionForm();
02187         case 30: return new specialBlockip();
02188         case 31: return new imagepageTest();
02189         case 32: return new pageDeletion();
02190         case 33: return new specialRevisionDeletePageTest();
02191         case 34: return new specialImportPageTest();
02192         case 35: return new thumbTest();
02193         case 37: return new profileInfo();
02194         case 38: return new specialCitePageTest();
02195         case 39: return new specialFilepathPageTest();
02196         case 40: return new specialRenameuserPageTest();
02197         case 41: return new specialLinksearch();
02198         case 42: return new specialCategoryTree();
02199         case 43: return new api();
02200         case 44: return new specialChemicalsourcesTest();
02201         default: return new editPageTest();
02202     }
02203 }
02204 
02205 
02206 // /////////////////////  SAVING OUTPUT  /////////////////////////
02207 
02211 function saveFile( $data, $name ) {
02212     file_put_contents( $name, $data );
02213 }
02214 
02222 function getAsURL( pageTest $test ) {
02223     $used_question_mark = ( strpos( $test->getPagePath(), "?" ) !== false );
02224     $retval = "http://get-to-post.nickj.org/?" . WIKI_BASE_URL . $test->getPagePath();
02225     foreach ( $test->getParams() as $param => $value ) {
02226         if ( !$used_question_mark ) {
02227             $retval .= "?";
02228             $used_question_mark = true;
02229         }
02230         else {
02231             $retval .= "&";
02232         }
02233         $retval .= $param . "=" . urlencode( $value );
02234     }
02235     return $retval;
02236 }
02237 
02238 
02242 function saveTestAsText( pageTest $test, $filename ) {
02243     $str = "Test: " . $test->getPagePath();
02244     foreach ( $test->getParams() as $param => $value ) {
02245         $str .= "\n$param: $value";
02246     }
02247     $str .= "\nGet-to-post URL: " . getAsURL( $test ) . "\n";
02248     saveFile( $str, $filename );
02249 }
02250 
02251 
02256 function saveTestAsPHP( pageTest $test, $filename ) {
02257     $str = "<?php\n"
02258         . "\$params = " . var_export( escapeForCurl( $test->getParams() ), true ) . ";\n"
02259         . "\$ch = curl_init();\n"
02260         . "curl_setopt(\$ch, CURLOPT_POST, 1);\n"
02261         . "curl_setopt(\$ch, CURLOPT_POSTFIELDS, \$params );\n"
02262         . "curl_setopt(\$ch, CURLOPT_URL, " . var_export( WIKI_BASE_URL . $test->getPagePath(), true ) . ");\n"
02263         . "curl_setopt(\$ch, CURLOPT_RETURNTRANSFER,1);\n"
02264         .  ( $test->getCookie() ? "curl_setopt(\$ch, CURLOPT_COOKIE, " . var_export( $test->getCookie(), true ) . ");\n" : "" )
02265         . "\$result=curl_exec(\$ch);\n"
02266         . "curl_close (\$ch);\n"
02267         . "print \$result;\n"
02268         . "\n";
02269     saveFile( $str, $filename );
02270 }
02271 
02279 function escapeForCurl( array $input_params ) {
02280     $output_params = array();
02281     foreach ( $input_params as $param => $value ) {
02282         if ( strlen( $value ) > 0 && ( $value[0] == "@" || $value[0] == "<" ) ) {
02283             $value = "\\" . $value;
02284         }
02285         $output_params[$param] = $value;
02286     }
02287     return $output_params;
02288 }
02289 
02290 
02295 function saveTestAsCurl( pageTest $test, $filename ) {
02296     $str = "#!/bin/bash\n"
02297         . "curl --silent --include --globoff \\\n"
02298         . ( $test->getCookie() ? " --cookie " . escapeshellarg( $test->getCookie() ) . " \\\n" : "" );
02299     foreach ( escapeForCurl( $test->getParams() ) as $param => $value ) {
02300         $str .= " -F " . escapeshellarg( $param ) . "=" . escapeshellarg( $value ) . " \\\n";
02301     }
02302     $str .= " " . escapeshellarg( WIKI_BASE_URL . $test->getPagePath() ); // beginning space matters.
02303     $str .= "\n";
02304     saveFile( $str, $filename );
02305     chmod( $filename, 0755 ); // make executable
02306 }
02307 
02308 
02312 function saveTestData ( pageTest $test, $filename ) {
02313     saveFile( serialize( $test ),  $filename );
02314 }
02315 
02316 
02320 function saveTest( pageTest $test, $testname ) {
02321     $base_name = DIRECTORY . "/" . $testname;
02322     saveTestAsText( $test, $base_name . INFO_FILE );
02323     saveTestAsPHP ( $test, $base_name . PHP_TEST );
02324     saveTestAsCurl( $test, $base_name . CURL_TEST );
02325     saveTestData  ( $test, $base_name . DATA_FILE );
02326 }
02327 
02328 // ////////////////// MEDIAWIKI OUTPUT /////////////////////////
02329 
02335 function wikiTestOutput( pageTest $test ) {
02336 
02337     $ch = curl_init();
02338 
02339     // specify the cookie, if required.
02340     if ( $test->getCookie() ) {
02341         curl_setopt( $ch, CURLOPT_COOKIE, $test->getCookie() );
02342     }
02343     curl_setopt( $ch, CURLOPT_POST, 1 );                          // save form using a POST
02344 
02345     $params = escapeForCurl( $test->getParams() );
02346     curl_setopt( $ch, CURLOPT_POSTFIELDS, $params );             // load the POST variables
02347 
02348     curl_setopt( $ch, CURLOPT_URL, WIKI_BASE_URL . $test->getPagePath() );  // set url to post to
02349     curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );                 // return into a variable
02350 
02351     $result = curl_exec ( $ch );
02352 
02353     // if we encountered an error, then say so, and return an empty string.
02354     if ( curl_error( $ch ) ) {
02355         print "\nCurl error #: " . curl_errno( $ch ) . " - " . curl_error ( $ch );
02356         $result = "";
02357     }
02358 
02359     curl_close ( $ch );
02360 
02361     return $result;
02362 }
02363 
02364 
02365 // ////////////////// HTML VALIDATION /////////////////////////
02366 
02372 function validateHTML( $text ) {
02373 
02374     $params = array ( "fragment"   => $text );
02375 
02376     $ch = curl_init();
02377 
02378     curl_setopt( $ch, CURLOPT_POST, 1 );                    // save form using a POST
02379     curl_setopt( $ch, CURLOPT_POSTFIELDS, $params );        // load the POST variables
02380     curl_setopt( $ch, CURLOPT_URL, VALIDATOR_URL );         // set url to post to
02381     curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );           // return into a variable
02382 
02383     $result = curl_exec ( $ch );
02384 
02385     // if we encountered an error, then log it, and exit.
02386     if ( curl_error( $ch ) ) {
02387         trigger_error( "Curl error #: " . curl_errno( $ch ) . " - " . curl_error ( $ch ) );
02388         print "Curl error #: " . curl_errno( $ch ) . " - " . curl_error ( $ch ) . " - exiting.\n";
02389         exit( 1 );
02390     }
02391 
02392     curl_close ( $ch );
02393 
02394     $valid = ( strpos( $result, "Failed validation" ) === false );
02395 
02396     return array( $valid, $result );
02397 }
02398 
02404 function tidyCheckFile( $name ) {
02405     $file = DIRECTORY . "/" . $name;
02406     $command = PATH_TO_TIDY . " -output /tmp/out.html -quiet $file 2>&1";
02407     $x = `$command`;
02408 
02409     // Look for the most interesting Tidy errors and warnings.
02410     if (   strpos( $x, "end of file while parsing attributes" ) !== false
02411             || strpos( $x, "attribute with missing trailing quote mark" ) !== false
02412             || strpos( $x, "missing '>' for end of tag" ) !== false
02413             || strpos( $x, "Error:" ) !== false ) {
02414         print "\nTidy found something - view details with: $command";
02415         return false;
02416     } else {
02417         return true;
02418     }
02419 }
02420 
02426 function dbErrorLogged() {
02427     static $filesize;
02428 
02429     // first time running this function
02430     if ( !isset( $filesize ) ) {
02431         // create log if it does not exist
02432         if ( DB_ERROR_LOG_FILE && !file_exists( DB_ERROR_LOG_FILE ) ) {
02433             saveFile( '', DB_ERROR_LOG_FILE );
02434         }
02435         $filesize = filesize( DB_ERROR_LOG_FILE );
02436         return false;
02437     }
02438 
02439     $newsize = filesize( DB_ERROR_LOG_FILE );
02440     // if the log has grown, then assume the current test caused it.
02441     if ( $newsize != $filesize ) {
02442         $filesize = $newsize;
02443         return true;
02444     }
02445 
02446     return false;
02447 }
02448 
02449 // //////////////// TOP-LEVEL PROBLEM-FINDING FUNCTION ////////////////////////
02450 
02459 function runWikiTest( pageTest $test, &$testname, $can_overwrite = false ) {
02460 
02461     // by default don't overwrite a previous test of the same name.
02462     while ( ! $can_overwrite && file_exists( DIRECTORY . "/" . $testname . DATA_FILE ) ) {
02463         $testname .= "-" . mt_rand( 0, 9 );
02464     }
02465 
02466     $filename = DIRECTORY . "/" . $testname . DATA_FILE;
02467 
02468     // Store the time before and after, to find slow pages.
02469     $before = microtime( true );
02470 
02471     // Get MediaWiki to give us the output of this test.
02472     $wiki_preview = wikiTestOutput( $test );
02473 
02474     $after = microtime( true );
02475 
02476     // if we received no response, then that's interesting.
02477     if ( $wiki_preview == "" ) {
02478         print "\nNo response received for: $filename";
02479         return false;
02480     }
02481 
02482     // save output HTML to file.
02483     $html_file = DIRECTORY . "/" . $testname . HTML_FILE;
02484     saveFile( $wiki_preview,  $html_file );
02485 
02486     // if there were PHP errors in the output, then that's interesting too.
02487     if (       strpos( $wiki_preview, "<b>Warning</b>: "        ) !== false
02488             || strpos( $wiki_preview, "<b>Fatal error</b>: "    ) !== false
02489             || strpos( $wiki_preview, "<b>Notice</b>: "         ) !== false
02490             || strpos( $wiki_preview, "<b>Error</b>: "          ) !== false
02491             || strpos( $wiki_preview, "<b>Strict Standards:</b>" ) !== false
02492             ) {
02493         $error = substr( $wiki_preview, strpos( $wiki_preview, "</b>:" ) + 7, 50 );
02494         // Avoid probable PHP bug with bad session ids; http://bugs.php.net/bug.php?id=38224
02495         if ( $error != "Unknown: The session id contains illegal character" ) {
02496             print "\nPHP error/warning/notice in HTML output: $html_file ; $error";
02497             return false;
02498         }
02499     }
02500 
02501     // if there was a MediaWiki Backtrace message in the output, then that's also interesting.
02502     if ( strpos( $wiki_preview, "Backtrace:" ) !== false ) {
02503         print "\nInternal MediaWiki error in HTML output: $html_file";
02504         return false;
02505     }
02506 
02507     // if there was a Parser error comment in the output, then that's potentially interesting.
02508     if ( strpos( $wiki_preview, "!-- ERR" ) !== false ) {
02509         print "\nParser Error comment in HTML output: $html_file";
02510         return false;
02511     }
02512 
02513     // if a database error was logged, then that's definitely interesting.
02514     if ( dbErrorLogged() ) {
02515         print "\nDatabase Error logged for: $filename";
02516         return false;
02517     }
02518 
02519     // validate result
02520     $valid = true;
02521     if ( VALIDATE_ON_WEB ) {
02522         list ( $valid, $validator_output ) = validateHTML( $wiki_preview );
02523         if ( !$valid ) print "\nW3C web validation failed - view details with: html2text " . DIRECTORY . "/" . $testname . ".validator_output.html";
02524     }
02525 
02526     // Get tidy to check the page, unless we already know it produces non-(X)HTML output.
02527     if ( $test->tidyValidate() ) {
02528         $valid = tidyCheckFile( $testname . HTML_FILE ) && $valid;
02529     }
02530 
02531     // if it took more than 2 seconds to render, then it may be interesting too. (Possible DoS attack?)
02532     if ( ( $after - $before ) >= 2 ) {
02533         print "\nParticularly slow to render (" . round( $after - $before, 2 ) . " seconds): $filename";
02534         return false;
02535     }
02536 
02537     if ( $valid ) {
02538         // Remove temp HTML file if test was valid:
02539         unlink( $html_file );
02540     } elseif ( VALIDATE_ON_WEB ) {
02541         saveFile( $validator_output,   DIRECTORY . "/" . $testname . ".validator_output.html" );
02542     }
02543 
02544     return $valid;
02545 }
02546 
02547 
02548 // ///////////////// RERUNNING OLD TESTS ///////////////////
02549 
02554 function rerunPreviousTests() {
02555     print "Retesting previously found problems.\n";
02556 
02557     $dir_contents = scandir ( DIRECTORY );
02558 
02559     // sort file into the order a normal person would use.
02560     natsort ( $dir_contents );
02561 
02562     foreach ( $dir_contents as $file ) {
02563 
02564         // if file is not a test, then skip it.
02565         // Note we need to escape any periods or will be treated as "any character".
02566         $matches = array();
02567         if ( !preg_match( "/(.*)" . str_replace( ".", "\.", DATA_FILE ) . "$/", $file, $matches ) ) continue;
02568 
02569         // reload the test.
02570         $full_path = DIRECTORY . "/" . $file;
02571         $test = unserialize( file_get_contents( $full_path ) );
02572 
02573         // if this is not a valid test, then skip it.
02574         if ( ! $test instanceof pageTest ) {
02575             print "\nSkipping invalid test - $full_path";
02576             continue;
02577         }
02578 
02579         // The date format is in Apache log format, which makes it easier to locate
02580         // which retest caused which error in the Apache logs (only happens usually if
02581         // apache segfaults).
02582         if ( !QUIET ) print "[" . date ( "D M d H:i:s Y" ) . "] Retesting $file (" . get_class( $test ) . ")";
02583 
02584         // run test
02585         $testname = $matches[1];
02586         $valid = runWikiTest( $test, $testname, true );
02587 
02588         if ( !$valid ) {
02589             saveTest( $test, $testname );
02590             if ( QUIET ) {
02591                 print "\nTest: " . get_class( $test ) . " ; Testname: $testname\n------";
02592             } else {
02593                 print "\n";
02594             }
02595         }
02596         else {
02597             if ( !QUIET ) print "\r";
02598             if ( DELETE_PASSED_RETESTS ) {
02599                 $prefix = DIRECTORY . "/" . $testname;
02600                 if ( is_file( $prefix . DATA_FILE ) ) unlink( $prefix . DATA_FILE );
02601                 if ( is_file( $prefix . PHP_TEST ) ) unlink( $prefix . PHP_TEST );
02602                 if ( is_file( $prefix . CURL_TEST ) ) unlink( $prefix . CURL_TEST );
02603                 if ( is_file( $prefix . INFO_FILE ) ) unlink( $prefix . INFO_FILE );
02604             }
02605         }
02606     }
02607 
02608     print "\nDone retesting.\n";
02609 }
02610 
02611 
02612 // ////////////////////  MAIN LOOP  ////////////////////////
02613 
02614 
02615 // first check whether CURL is installed, because sometimes it's not.
02616 if ( ! function_exists( 'curl_init' ) ) {
02617     die( "Could not find 'curl_init' function. Is the curl extension compiled into PHP?\n" );
02618 }
02619 
02620 // Initialization of types. wikiFuzz doesn't have a constructor because we want to
02621 // access it staticly and not have any globals.
02622 wikiFuzz::$types = array_keys( wikiFuzz::$data );
02623 
02624 // Make directory if doesn't exist
02625 if ( !is_dir( DIRECTORY ) ) {
02626     mkdir ( DIRECTORY, 0700 );
02627 }
02628 // otherwise, we first retest the things that we have found in previous runs
02629 elseif ( RERUN_OLD_TESTS ) {
02630     rerunPreviousTests();
02631 }
02632 
02633 // main loop.
02634 $start_time = date( "U" );
02635 $num_errors = 0;
02636 if ( !QUIET ) {
02637     print "Beginning main loop. Results are stored in the " . DIRECTORY . " directory.\n";
02638     print "Press CTRL+C to stop testing.\n";
02639 }
02640 
02641 for ( $count = 0; true; $count++ ) {
02642     if ( !QUIET ) {
02643         // spinning progress indicator.
02644         switch( $count % 4 ) {
02645             case '0': print "\r/";  break;
02646             case '1': print "\r-";  break;
02647             case '2': print "\r\\"; break;
02648             case '3': print "\r|";  break;
02649         }
02650         print " $count";
02651     }
02652 
02653     // generate a page test to run.
02654     $test = selectPageTest( $count );
02655 
02656     $mins = ( date( "U" ) - $start_time ) / 60;
02657     if ( !QUIET && $mins > 0 ) {
02658         print ".  $num_errors poss errors. "
02659             . floor( $mins ) . " mins. "
02660             . round ( $count / $mins, 0 ) . " tests/min. "
02661             . get_class( $test ); // includes the current test name.
02662     }
02663 
02664     // run this test against MediaWiki, and see if the output was valid.
02665     $testname = $count;
02666     $valid = runWikiTest( $test, $testname, false );
02667 
02668     // save the failed test
02669     if ( ! $valid ) {
02670         if ( QUIET ) {
02671             print "\nTest: " . get_class( $test ) . " ; Testname: $testname\n------";
02672         } else {
02673             print "\n";
02674         }
02675         saveTest( $test, $testname );
02676         $num_errors += 1;
02677     } elseif ( KEEP_PASSED_TESTS ) {
02678         // print current time, with microseconds (matches "strace" format), and the test name.
02679         print " " . date( "H:i:s." ) . substr( current( explode( " ", microtime() ) ), 2 ) . " " . $testname;
02680         saveTest( $test, $testname );
02681     }
02682 
02683     // stop if we have reached max number of errors.
02684     if ( defined( "MAX_ERRORS" ) && $num_errors >= MAX_ERRORS ) {
02685         break;
02686     }
02687 
02688     // stop if we have reached max number of mins runtime.
02689     if ( defined( "MAX_RUNTIME" ) && $mins >= MAX_RUNTIME ) {
02690         break;
02691     }
02692 }