] [--directory=] [--include-binary] [--w3c-validate] [--delete-passed-retests] [--help] [--user=] [--password=] [--rerun-failed-tests] [--max-errors=] [--max-runtime=] [--specific-test=] Options: --quiet : Hides passed tests, shows only failed tests. --base-url : URL to a wiki on which to run the tests. The "http://" is optional and can be omitted. --directory : Full path to directory for storing failed tests. Will be created if it does not exist. --include-binary : Includes non-alphanumeric characters in the tests. --w3c-validate : Validates pages using the W3C's web validator. Slow. Currently many pages fail validation. --user : Login name of a valid user on your test wiki. --password : Password for the valid user on your test wiki. --delete-passed-retests : Will delete retests that now pass. Requires --rerun-failed-tests to be meaningful. --rerun-failed-tests : Whether to rerun any previously failed tests. --max-errors : Maximum number of errors to report before exiting. Does not include errors from --rerun-failed-tests --max-runtime : Maximum runtime, in minutes, to run before exiting. Only applies to new tests, not --rerun-failed-tests --specific-test : Runs only the specified fuzz test. Only applies to new tests, not --rerun-failed-tests --help : Show this help message. Example: If you wanted to fuzz test a nightly MediaWiki checkout using cron for 1 hour, and only wanted to be informed of errors, and did not want to redo previously failed tests, and wanted a maximum of 100 errors, then you could do: php {$_SERVER["SCRIPT_NAME"]} --quiet --max-errors=100 --max-runtime=60 ENDS; exit( 0 ); } // if we got command line options, check they look valid. $validOptions = array ("quiet", "base-url", "directory", "include-binary", "w3c-validate", "user", "password", "delete-passed-retests", "rerun-failed-tests", "max-errors", "max-runtime", "specific-test", "help" ); if (!empty($options)) { $unknownArgs = array_diff (array_keys($options), $validOptions); foreach ($unknownArgs as $invalidArg) { print "Ignoring invalid command-line option: --$invalidArg\n"; } } ///////////////////////////// CONFIGURATION //////////////////////////////////// // URL to some wiki on which we can run our tests. if (!empty($options["base-url"])) { define("WIKI_BASE_URL", $options["base-url"]); } else { define("WIKI_BASE_URL", $wgServer . $wgScriptPath . '/'); } // The directory name where we store the output. // Example for Windows: "c:\\temp\\wiki-fuzz" if (!empty($options["directory"])) { define("DIRECTORY", $options["directory"] ); } else { define("DIRECTORY", "{$wgUploadDirectory}/fuzz-tests"); } // Should our test fuzz data include binary strings? define("INCLUDE_BINARY", isset($options["include-binary"]) ); // Whether we want to validate HTML output on the web. // At the moment very few generated pages will validate, so not recommended. define("VALIDATE_ON_WEB", isset($options["w3c-validate"]) ); // URL to use to validate our output: define("VALIDATOR_URL", "http://validator.w3.org/check"); // Location of Tidy standalone executable. define("PATH_TO_TIDY", "/usr/bin/tidy"); // The name of a user who has edited on your wiki. Used // when testing the Special:Contributions and Special:Userlogin page. if (!empty($options["user"])) { define("USER_ON_WIKI", $options["user"] ); } else { define("USER_ON_WIKI", "nickj"); } // The password of the above user. Used when testing the login page, // and to do this we sometimes need to login successfully. if (!empty($options["password"])) { define("USER_PASSWORD", $options["password"] ); } else { // And no, this is not a valid password on any public wiki. define("USER_PASSWORD", "nickj"); } // If we have a test that failed, and then we run it again, and it passes, // do you want to delete it or keep it? define("DELETE_PASSED_RETESTS", isset($options["delete-passed-retests"]) ); // Do we want to rerun old saved tests at script startup? // Set to true to help catch regressions, or false if you only want new stuff. define("RERUN_OLD_TESTS", isset($options["rerun-failed-tests"]) ); // File where the database errors are logged. Should be defined in LocalSettings.php. define("DB_ERROR_LOG_FILE", $wgDBerrorLog ); // Run in chatty mode (all output, default), or run in quiet mode (only prints out details of failed tests)? define("QUIET", isset($options["quiet"]) ); // The maximum runtime, if specified. if (!empty($options["max-runtime"]) && intval($options["max-runtime"])>0) { define("MAX_RUNTIME", intval($options["max-runtime"]) ); } // The maximum number of problems to find, if specified. Excludes retest errors. if (!empty($options["max-errors"]) && intval($options["max-errors"])>0) { define("MAX_ERRORS", intval($options["max-errors"]) ); } // if the user has requested a specific test (instead of all tests), and the test they asked for looks valid. if (!empty($options["specific-test"])) { if (class_exists($options["specific-test"]) && get_parent_class($options["specific-test"])=="pageTest") { define("SPECIFIC_TEST", $options["specific-test"] ); } else { print "Ignoring invalid --specific-test\n"; } } // Define the file extensions we'll use: define("PHP_TEST" , ".test.php"); define("CURL_TEST", ".curl.sh" ); define("DATA_FILE", ".data.bin"); define("INFO_FILE", ".info.txt"); define("HTML_FILE", ".wiki_preview.html"); // If it goes wrong, we want to know about it. error_reporting(E_ALL | E_STRICT); //////////////// A CLASS THAT GENERATES RANDOM NASTY WIKI & HTML STRINGS ////////////////////// class wikiFuzz { // Only some HTML tags are understood with params by MediaWiki, the rest are ignored. // List the tags that accept params below, as well as what those params are. public static $data = array( "B" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"), "CAPTION" => array("CLASS", "ID", "STYLE", "align", "lang", "dir", "title"), "CENTER" => array("CLASS", "STYLE", "ID", "lang", "dir", "title"), "DIV" => array("CLASS", "STYLE", "ID", "align", "lang", "dir", "title"), "FONT" => array("CLASS", "STYLE", "ID", "lang", "dir", "title", "face", "size", "color"), "H1" => array("STYLE", "CLASS", "ID", "align", "lang", "dir", "title"), "H2" => array("STYLE", "CLASS", "ID", "align", "lang", "dir", "title"), "HR" => array("STYLE", "CLASS", "ID", "WIDTH", "lang", "dir", "title", "size", "noshade"), "LI" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "type", "value"), "TABLE" => array("STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "BORDER", "CELLPADDING", "CELLSPACING", "lang", "dir", "title", "summary", "frame", "rules"), "TD" => array("STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "COLSPAN", "ROWSPAN", "VALIGN", "abbr", "axis", "headers", "scope", "nowrap", "height", "lang", "dir", "title", "char", "charoff"), "TH" => array("STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "COLSPAN", "ROWSPAN", "VALIGN", "abbr", "axis", "headers", "scope", "nowrap", "height", "lang", "dir", "title", "char", "charoff"), "TR" => array("CLASS", "STYLE", "ID", "BGCOLOR", "ALIGN", "VALIGN", "lang", "dir", "title", "char", "charoff"), "UL" => array("CLASS", "STYLE", "ID", "lang", "dir", "title", "type"), "P" => array("style", "class", "id", "align", "lang", "dir", "title"), "blockquote" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "cite"), "span" => array("CLASS", "ID", "STYLE", "align", "lang", "dir", "title"), "code" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"), "tt" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"), "small" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"), "big" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"), "s" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"), "u" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"), "del" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "datetime", "cite"), "ins" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "datetime", "cite"), "sub" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"), "sup" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"), "ol" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "type", "start"), "br" => array("CLASS", "ID", "STYLE", "title", "clear"), "cite" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"), "var" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"), "dl" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"), "ruby" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"), "rt" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"), "rp" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"), "dt" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"), "dl" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"), "em" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"), "strong" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"), "i" => array("CLASS", "ID", "STYLE", "lang", "dir", "title"), "thead" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign'), "tfoot" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign'), "tbody" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign'), "colgroup" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign', 'span', 'width'), "col" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign', 'span', 'width'), "pre" => array("CLASS", "ID", "STYLE", "lang", "dir", "title", "width"), // extension tags that accept parameters: "sort" => array("order", "class"), "ref" => array("name"), "categorytree" => array("hideroot", "mode", "style"), ); // The types of the HTML that we will be testing were defined above // Note: this needs to be initialized later to be equal to: array_keys(wikiFuzz::$data); // as such, it also needs to also be publicly modifiable. public static $types; // Some attribute values. static private $other = array("&","=",":","?","\"","\n","%n%n%n%n%n%n%n%n%n%n%n%n","\\"); static private $ints = array( // various numbers "0","-1","127","-7897","89000","808080","90928345", "0xfffffff","ffff", // Different ways of saying: ' "'", // Long UTF-8 Unicode encoding "'", // dec version. "'", // hex version. "§", // malformed hex variant, MSB not zero. // Different ways of saying: " """, // Long UTF-8 Unicode encoding """, """, // hex version. "¢", // malformed hex variant, MSB not zero. // Different ways of saying: < "<", "<", // Long UTF-8 Unicode encoding without semicolon (Mediawiki wants the colon) "<", // Long UTF-8 Unicode encoding with semicolon "<", "<", // hex version. "¼", // malformed hex variant, MSB not zero. "<", // mid-length hex version "<", // slightly longer hex version, with capital "X" // Different ways of saying: > ">", ">", // Long UTF-8 Unicode encoding ">", ">", // hex version. "¾", // malformed variant, MSB not zero. // Different ways of saying: [ "[", // Long UTF-8 Unicode encoding "[", "[", // hex version. // Different ways of saying: {{ "{{", // Long UTF-8 Unicode encoding "{{", "{{", // hex version. // Different ways of saying: | "|", // Long UTF-8 Unicode encoding "|", "|", // hex version. "ü", // malformed hex variant, MSB not zero. // a "lignature" - http://www.robinlionheart.com/stds/html4/spchars#ligature "‌" ); // Defines various wiki-related bits of syntax, that can potentially cause // MediaWiki to do something other than just print that literal text. static private $ext = array( // links, templates, parameters. "[[", "]]", "{{", "}}", "|", "[", "]", "{{{", "}}}", "|]]", // wiki tables. "\n{|", "\n|}", "!", "\n!", "!!", "||", "\n|-", "| ", "\n|", // section headings. "=", "==", "===", "====", "=====", "======", // lists (ordered and unordered) and indentation. "\n*", "*", "\n:", ":", "\n#", "#", // definition lists (dl, dt, dd), newline, and newline with pre, and a tab. "\n;", ";", "\n ", // Whitespace: newline, tab, space. "\n", "\t", " ", // Some XSS attack vectors from http://ha.ckers.org/xss.html " ", // tab " ", // newline " ", // carriage return "\0", // null character "  ", // spaces and meta characters "'';!--\"=&{()}", // compact injection of XSS & SQL tester // various NULL fields "%00", "�", "\0", // horizontal rule. "-----", "\n-----", // signature, redirect, bold, italics. "~~~~", "#REDIRECT [[", "'''", "''", // comments. "", // quotes. "\"", "'", // tag start and tag end. "<", ">", // implicit link creation on URIs. "http://", "https://", "ftp://", "irc://", "news:", 'gopher://', 'telnet://', 'nntp://', 'worldwind://', 'mailto:', // images. "[[image:", ".gif", ".png", ".jpg", ".jpeg", 'thumbnail=', 'thumbnail', 'thumb=', 'thumb', 'right', 'none', 'left', 'framed', 'frame', 'enframed', 'centre', 'center', "Image:", "[[:Image", 'px', // misc stuff to throw at the Parser. '%08X', '/', ":x{|", "\n|+", "", "", " \302\273", " :", " !", " ;", "\302\253", "[[category:", "?=", "(", ")", "]]]", "../", "{{{{", "}}}}", "[[Special:", "", "", "', // implicit link creation on booknum, RFC, and PubMed ID usage (both with and without IDs) "ISBN 2", "RFC 000", "PMID 000", "ISBN ", "RFC ", "PMID ", // magic words: '__NOTOC__', '__FORCETOC__', '__NOEDITSECTION__', '__START__', '__NOTITLECONVERT__', '__NOCONTENTCONVERT__', '__END__', '__TOC__', '__NOTC__', '__NOCC__', "__FORCETOC__", "__NEWSECTIONLINK__", "__NOGALLERY__", // more magic words / internal templates. '{{PAGENAME}}', '{{PAGENAMEE}}', '{{NAMESPACE}}', "{{MSG:", "}}", "{{MSGNW:", "}}", "{{INT:", "}}", '{{SITENAME}}', "{{NS:", "}}", "{{LOCALURL:", "}}", "{{LOCALURLE:", "}}", "{{SCRIPTPATH}}", "{{GRAMMAR:gentiv|", "}}", "{{REVISIONID}}", "{{SUBPAGENAME}}", "{{SUBPAGENAMEE}}", "{{ns:0}}", "{{fullurle:", "}}", "{{subst:", "}}", "{{UCFIRST:", "}}", "{{UC:", '{{SERVERNAME}}', '{{SERVER}}', "{{RAW:", "}}", "{{PLURAL:", "}}", "{{LCFIRST:", "}}", "{{LC:", "}}", '{{CURRENTWEEK}}', '{{CURRENTDOW}}', "{{INT:{{LC:contribs-showhideminor}}|", "}}", "{{INT:googlesearch|", "}}", "{{BASEPAGENAME}}", "{{CONTENTLANGUAGE}}", "{{PAGESINNAMESPACE:}}", "{{#language:", "}}", // Some raw link for magic words. "{{NUMBEROFPAGES:R", "}}", "{{NUMBEROFUSERS:R", "}}", "{{NUMBEROFARTICLES:R", "}}", "{{NUMBEROFFILES:R", "}}", "{{NUMBEROFADMINS:R", "}}", "{{padleft:", "}}", "{{padright:", "}}", // internal Math "extension": "", "", // Parser extension functions: "{{#expr:", "{{#if:", "{{#ifeq:", "{{#ifexist:", "{{#ifexpr:", "{{#switch:", "{{#time:", "}}", // references table for the Cite extension. "", // Internal Parser tokens - try inserting some of these. "UNIQ25f46b0524f13e67NOPARSE", "UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002", "\x07UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002-QINU", // Inputbox extension: "\ntype=search\nsearchbuttonlabel=\n", "", // charInsert extension: "", "", // wikiHiero extension: "", "", // Image gallery: "", "", // FixedImage: "", // Timeline extension: currently untested. // Nowiki: "", "", // an external image to test the external image displaying code "http://debian.org/Pics/debian.png", ); /** ** @desc: Randomly returns one element of the input array. */ static public function chooseInput(array $input) { $randindex = wikiFuzz::randnum(count($input) - 1); return $input[$randindex]; } // Max number of parameters for HTML attributes. static private $maxparams = 10; /** ** @desc: Returns random number between finish and start. */ static public function randnum($finish,$start=0) { return mt_rand($start,$finish); } /** ** @desc: Returns a mix of random text and random wiki syntax. */ static private function randstring() { $thestring = ""; for ($i=0; $i<40; $i++) { $what = wikiFuzz::randnum(1); if ($what == 0) { // include some random wiki syntax $which = wikiFuzz::randnum(count(wikiFuzz::$ext) - 1); $thestring .= wikiFuzz::$ext[$which]; } else { // include some random text $char = INCLUDE_BINARY // Decimal version: // "&#" . wikiFuzz::randnum(255) . ";" // Hex version: ? "&#x" . str_pad(dechex(wikiFuzz::randnum(255)), wikiFuzz::randnum(2, 7), "0", STR_PAD_LEFT) . ";" : chr(wikiFuzz::randnum(126,32)); $length = wikiFuzz::randnum(8); $thestring .= str_repeat ($char, $length); } } return $thestring; } /** ** @desc: Returns either random text, or random wiki syntax, or random data from "ints", ** or random data from "other". */ static private function makestring() { $what = wikiFuzz::randnum(2); if ($what == 0) { return wikiFuzz::randstring(); } elseif ($what == 1) { return wikiFuzz::$ints[wikiFuzz::randnum(count(wikiFuzz::$ints) - 1)]; } else { return wikiFuzz::$other[wikiFuzz::randnum(count(wikiFuzz::$other) - 1)]; } } /** ** @desc: Strips out the stuff that Mediawiki balks at in a page's title. ** Implementation copied/pasted from cleanupTable.inc & cleanupImages.php */ static public function makeTitleSafe($str) { $legalTitleChars = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF"; return preg_replace_callback( "/([^$legalTitleChars])/", create_function( // single quotes are essential here, // or alternative escape all $ as \$ '$matches', 'return sprintf( "\\x%02x", ord( $matches[1] ) );' ), $str ); } /** ** @desc: Returns a string of fuzz text. */ static private function loop() { switch ( wikiFuzz::randnum(3) ) { case 1: // an opening tag, with parameters. $string = ""; $i = wikiFuzz::randnum(count(wikiFuzz::$types) - 1); $t = wikiFuzz::$types[$i]; $arr = wikiFuzz::$data[$t]; $string .= "<" . $t . " "; $num_params = min(wikiFuzz::$maxparams, count($arr)); for ($z=0; $z<$num_params; $z++) { $badparam = $arr[wikiFuzz::randnum(count($arr) - 1)]; $badstring = wikiFuzz::makestring(); $string .= $badparam . "=" . wikiFuzz::getRandQuote() . $badstring . wikiFuzz::getRandQuote() . " "; } $string .= ">\n"; return $string; case 2: // a closing tag. $i = wikiFuzz::randnum(count(wikiFuzz::$types) - 1); return ""; case 3: // a random string, between tags. return wikiFuzz::makeString(); } return ""; // catch-all, should never be called. } /** ** @desc: Returns one of the three styles of random quote: ', ", and nothing. */ static private function getRandQuote() { switch ( wikiFuzz::randnum(3) ) { case 1 : return "'"; case 2 : return "\""; default: return ""; } } /** ** @desc: Returns fuzz text, with the parameter indicating approximately how many lines of text you want. */ static public function makeFuzz($maxtypes = 2) { $page = ""; for ($k=0; $k<$maxtypes; $k++) { $page .= wikiFuzz::loop(); } return $page; } } //////// MEDIAWIKI PAGES TO TEST, AND HOW TO TEST THEM /////// /** ** @desc: A page test has just these things: ** 1) Form parameters. ** 2) the URL we are going to test those parameters on. ** 3) Any cookies required for the test. ** Declared abstract because it should be extended by a class ** that supplies these parameters. */ abstract class pageTest { protected $params; protected $pagePath; protected $cookie = ""; public function getParams() { return $this->params; } public function getPagePath() { return $this->pagePath; } public function getCookie() { return $this->cookie; } } /** ** @desc: a page test for the "Edit" page. Tests Parser.php and Sanitizer.php. */ class editPageTest extends pageTest { function __construct() { $this->pagePath = "index.php?title=WIKIFUZZ"; $this->params = array ( "action" => "submit", "wpMinoredit" => wikiFuzz::makeFuzz(2), "wpPreview" => wikiFuzz::makeFuzz(2), "wpSection" => wikiFuzz::makeFuzz(2), "wpEdittime" => wikiFuzz::makeFuzz(2), "wpSummary" => wikiFuzz::makeFuzz(2), "wpScrolltop" => wikiFuzz::makeFuzz(2), "wpStarttime" => wikiFuzz::makeFuzz(2), "wpAutoSummary" => wikiFuzz::makeFuzz(2), "wpTextbox1" => wikiFuzz::makeFuzz(40) // the main wiki text, need lots of this. ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(6) == 0) unset($this->params["wpSection"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["wpEdittime"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["wpSummary"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["wpScrolltop"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["wpStarttime"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["wpAutoSummary"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["wpTextbox1"]); } } /** ** @desc: a page test for "Special:Listusers". */ class listusersTest extends pageTest { function __construct() { $this->pagePath = "index.php/Special:Listusers"; $this->params = array ( "title" => wikiFuzz::makeFuzz(2), "group" => wikiFuzz::makeFuzz(2), "username" => wikiFuzz::makeFuzz(2), "Go" => wikiFuzz::makeFuzz(2), "limit" => wikiFuzz::chooseInput( array("0", "-1", "---'----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ), "offset" => wikiFuzz::chooseInput( array("0", "-1", "--------'-----0", "+1", "81343242346234234", wikiFuzz::makeFuzz(2)) ) ); } } /** ** @desc: a page test for "Special:Search". */ class searchTest extends pageTest { function __construct() { $this->pagePath = "index.php/Special:Search"; $this->params = array ( "action" => "index.php/Special:Search", "ns0" => wikiFuzz::makeFuzz(2), "ns1" => wikiFuzz::makeFuzz(2), "ns2" => wikiFuzz::makeFuzz(2), "ns3" => wikiFuzz::makeFuzz(2), "ns4" => wikiFuzz::makeFuzz(2), "ns5" => wikiFuzz::makeFuzz(2), "ns6" => wikiFuzz::makeFuzz(2), "ns7" => wikiFuzz::makeFuzz(2), "ns8" => wikiFuzz::makeFuzz(2), "ns9" => wikiFuzz::makeFuzz(2), "ns10" => wikiFuzz::makeFuzz(2), "ns11" => wikiFuzz::makeFuzz(2), "ns12" => wikiFuzz::makeFuzz(2), "ns13" => wikiFuzz::makeFuzz(2), "ns14" => wikiFuzz::makeFuzz(2), "ns15" => wikiFuzz::makeFuzz(2), "redirs" => wikiFuzz::makeFuzz(2), "search" => wikiFuzz::makeFuzz(2), "offset" => wikiFuzz::chooseInput( array("", "0", "-1", "--------'-----0", "+1", "81343242346234234", wikiFuzz::makeFuzz(2)) ), "fulltext" => wikiFuzz::chooseInput( array("", "0", "1", "--------'-----0", "+1", wikiFuzz::makeFuzz(2)) ), "searchx" => wikiFuzz::chooseInput( array("", "0", "1", "--------'-----0", "+1", wikiFuzz::makeFuzz(2)) ) ); } } /** ** @desc: a page test for "Special:Recentchanges". */ class recentchangesTest extends pageTest { function __construct() { $this->pagePath = "index.php/Special:Recentchanges"; $this->params = array ( "action" => wikiFuzz::makeFuzz(2), "title" => wikiFuzz::makeFuzz(2), "namespace" => wikiFuzz::chooseInput( range(-1, 15) ), "Go" => wikiFuzz::makeFuzz(2), "invert" => wikiFuzz::chooseInput( array("-1", "---'----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ), "hideanons" => wikiFuzz::chooseInput( array("-1", "------'-------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ), 'limit' => wikiFuzz::chooseInput( array("0", "-1", "---------'----0", "+1", "81340909772349234", wikiFuzz::makeFuzz(2)) ), "days" => wikiFuzz::chooseInput( array("-1", "----------'---0", "+1", "8134", wikiFuzz::makeFuzz(2)) ), "hideminor" => wikiFuzz::chooseInput( array("-1", "-----------'--0", "+1", "8134", wikiFuzz::makeFuzz(2)) ), "hidebots" => wikiFuzz::chooseInput( array("-1", "---------'----0", "+1", "8134", wikiFuzz::makeFuzz(2)) ), "hideliu" => wikiFuzz::chooseInput( array("-1", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ), "hidepatrolled" => wikiFuzz::chooseInput( array("-1", "-----'--------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ), "hidemyself" => wikiFuzz::chooseInput( array("-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ), 'categories_any'=> wikiFuzz::chooseInput( array("-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ), 'categories' => wikiFuzz::chooseInput( array("-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ), 'feed' => wikiFuzz::chooseInput( array("-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ) ); } } /** ** @desc: a page test for "Special:Prefixindex". */ class prefixindexTest extends pageTest { function __construct() { $this->pagePath = "index.php/Special:Prefixindex"; $this->params = array ( "title" => "Special:Prefixindex", "namespace" => wikiFuzz::randnum(-10,101), "Go" => wikiFuzz::makeFuzz(2) ); // sometimes we want 'prefix', sometimes we want 'from', and sometimes we want nothing. if (wikiFuzz::randnum(3) == 0) { $this->params["prefix"] = wikiFuzz::chooseInput( array("-1", "-----'--------0", "+++--+1", wikiFuzz::randnum(-10,8134), wikiFuzz::makeFuzz(2)) ); } if (wikiFuzz::randnum(3) == 0) { $this->params["from"] = wikiFuzz::chooseInput( array("-1", "-----'--------0", "+++--+1", wikiFuzz::randnum(-10,8134), wikiFuzz::makeFuzz(2)) ); } } } /** ** @desc: a page test for "Special:MIMEsearch". */ class mimeSearchTest extends pageTest { function __construct() { $this->pagePath = "index.php/Special:MIMEsearch"; $this->params = array ( "action" => "/wiki/index.php/Special:MIMEsearch", "mime" => wikiFuzz::makeFuzz(3), 'limit' => wikiFuzz::chooseInput( array("0", "-1", "-------'------0", "+1", "81342321351235325", wikiFuzz::makeFuzz(2)) ), 'offset' => wikiFuzz::chooseInput( array("0", "-1", "-----'--------0", "+1", "81341231235365252234324", wikiFuzz::makeFuzz(2)) ) ); } } /** ** @desc: a page test for "Special:Log". */ class specialLogTest extends pageTest { function __construct() { $this->pagePath = "index.php/Special:Log"; $this->params = array ( "type" => wikiFuzz::chooseInput( array("", wikiFuzz::makeFuzz(2)) ), "par" => wikiFuzz::makeFuzz(2), "user" => wikiFuzz::makeFuzz(2), "page" => wikiFuzz::makeFuzz(2), "from" => wikiFuzz::makeFuzz(2), "until" => wikiFuzz::makeFuzz(2), "title" => wikiFuzz::makeFuzz(2) ); } } /** ** @desc: a page test for "Special:Userlogin", with a successful login. */ class successfulUserLoginTest extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special:Userlogin&action=submitlogin&type=login&returnto=" . wikiFuzz::makeFuzz(2); $this->params = array ( "wpName" => USER_ON_WIKI, // sometimes real password, sometimes not: 'wpPassword' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz(2), USER_PASSWORD ) ), 'wpRemember' => wikiFuzz::makeFuzz(2) ); $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array("1" , wikiFuzz::makeFuzz(2) ) ); } } /** ** @desc: a page test for "Special:Userlogin". */ class userLoginTest extends pageTest { function __construct() { $this->pagePath = "index.php/Special:Userlogin"; $this->params = array ( 'wpRetype' => wikiFuzz::makeFuzz(2), 'wpRemember' => wikiFuzz::makeFuzz(2), 'wpRealName' => wikiFuzz::makeFuzz(2), 'wpPassword' => wikiFuzz::makeFuzz(2), 'wpName' => wikiFuzz::makeFuzz(2), 'wpMailmypassword'=> wikiFuzz::makeFuzz(2), 'wpLoginattempt' => wikiFuzz::makeFuzz(2), 'wpEmail' => wikiFuzz::makeFuzz(2), 'wpDomain' => wikiFuzz::chooseInput( array("", "local", wikiFuzz::makeFuzz(2)) ), 'wpCreateaccountMail' => wikiFuzz::chooseInput( array("", wikiFuzz::makeFuzz(2)) ), 'wpCreateaccount' => wikiFuzz::chooseInput( array("", wikiFuzz::makeFuzz(2)) ), 'wpCookieCheck' => wikiFuzz::chooseInput( array("", wikiFuzz::makeFuzz(2)) ), 'type' => wikiFuzz::chooseInput( array("signup", "login", "", wikiFuzz::makeFuzz(2)) ), 'returnto' => wikiFuzz::makeFuzz(2), 'action' => wikiFuzz::chooseInput( array("", "submitlogin", wikiFuzz::makeFuzz(2)) ) ); $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array("1" , wikiFuzz::makeFuzz(2) ) ); } } /** ** @desc: a page test for "Special:Ipblocklist" (also includes unblocking) */ class ipblocklistTest extends pageTest { function __construct() { $this->pagePath = "index.php/Special:Ipblocklist"; $this->params = array ( 'wpUnblockAddress'=> wikiFuzz::makeFuzz(2), 'ip' => wikiFuzz::chooseInput( array("20398702394", "", "Nickj2", wikiFuzz::makeFuzz(2), // something like an IP address, sometimes invalid: ( wikiFuzz::randnum(300,-20) . "." . wikiFuzz::randnum(300,-20) . "." . wikiFuzz::randnum(300,-20) . "." .wikiFuzz::randnum(300,-20) ) ) ), 'id' => wikiFuzz::makeFuzz(2), 'wpUnblockReason' => wikiFuzz::makeFuzz(2), 'action' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2), "success", "submit", "unblock") ), 'wpEditToken' => wikiFuzz::makeFuzz(2), 'wpBlock' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2), "") ), 'limit' => wikiFuzz::chooseInput( array("0", "-1", "--------'-----0", "+1", "09700982312351132098234", wikiFuzz::makeFuzz(2)) ), 'offset' => wikiFuzz::chooseInput( array("0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz(2)) ) ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(4) == 0) unset($this->params["action"]); if (wikiFuzz::randnum(3) == 0) unset($this->params["ip"]); if (wikiFuzz::randnum(2) == 0) unset($this->params["id"]); if (wikiFuzz::randnum(3) == 0) unset($this->params["wpUnblockAddress"]); } } /** ** @desc: a page test for "Special:Newimages". */ class newImagesTest extends pageTest { function __construct() { $this->pagePath = "index.php/Special:Newimages"; $this->params = array ( 'hidebots' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2), "1", "", "-1") ), 'wpIlMatch' => wikiFuzz::makeFuzz(2), 'until' => wikiFuzz::makeFuzz(2), 'from' => wikiFuzz::makeFuzz(2) ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(6) == 0) unset($this->params["until"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["from"]); } } /** ** @desc: a page test for the "Special:Imagelist" page. */ class imagelistTest extends pageTest { function __construct() { $this->pagePath = "index.php/Special:Imagelist"; $this->params = array ( 'sort' => wikiFuzz::chooseInput( array("bysize", "byname" , "bydate", wikiFuzz::makeFuzz(2)) ), 'limit' => wikiFuzz::chooseInput( array("0", "-1", "--------'-----0", "+1", "09700982312351132098234", wikiFuzz::makeFuzz(2)) ), 'offset' => wikiFuzz::chooseInput( array("0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz(2)) ), 'wpIlMatch' => wikiFuzz::makeFuzz(2) ); } } /** ** @desc: a page test for "Special:Export". */ class specialExportTest extends pageTest { function __construct() { $this->pagePath = "index.php/Special:Export"; $this->params = array ( 'action' => wikiFuzz::chooseInput( array("submit", "", wikiFuzz::makeFuzz(2)) ), 'pages' => wikiFuzz::makeFuzz(2), 'curonly' => wikiFuzz::chooseInput( array("", "0", "-1", wikiFuzz::makeFuzz(2)) ), 'listauthors' => wikiFuzz::chooseInput( array("", "0", "-1", wikiFuzz::makeFuzz(2)) ), 'history' => wikiFuzz::chooseInput( array("0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz(2)) ), ); // For the time being, need to disable "submit" action as Tidy barfs on MediaWiki's XML export. if ($this->params['action'] == 'submit') $this->params['action'] = ''; // Sometimes remove the history field. if (wikiFuzz::randnum(2) == 0) unset($this->params["history"]); } } /** ** @desc: a page test for "Special:Booksources". */ class specialBooksourcesTest extends pageTest { function __construct() { $this->pagePath = "index.php/Special:Booksources"; $this->params = array ( 'go' => wikiFuzz::makeFuzz(2), // ISBN codes have to contain some semi-numeric stuff or will be ignored: 'isbn' => "0X0" . wikiFuzz::makeFuzz(2) ); } } /** ** @desc: a page test for "Special:Allpages". */ class specialAllpagesTest extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special%3AAllpages"; $this->params = array ( 'from' => wikiFuzz::makeFuzz(2), 'namespace' => wikiFuzz::chooseInput( range(-1, 15) ), 'go' => wikiFuzz::makeFuzz(2) ); } } /** ** @desc: a page test for the page History. */ class pageHistoryTest extends pageTest { function __construct() { $this->pagePath = "index.php?title=Main_Page&action=history"; $this->params = array ( 'limit' => wikiFuzz::chooseInput( array("-1", "0", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ), 'offset' => wikiFuzz::chooseInput( array("-1", "0", "------'-------0", "+1", "9823412312312412435", wikiFuzz::makeFuzz(2)) ), "go" => wikiFuzz::chooseInput( array("first", "last", wikiFuzz::makeFuzz(2)) ), "dir" => wikiFuzz::chooseInput( array("prev", "next", wikiFuzz::makeFuzz(2)) ), "diff" => wikiFuzz::chooseInput( array("-1", "--------'-----0", "+1", "8134", wikiFuzz::makeFuzz(2)) ), "oldid" => wikiFuzz::chooseInput( array("prev", "-1", "+1", "8134", wikiFuzz::makeFuzz(2)) ), "feed" => wikiFuzz::makeFuzz(2) ); } } /** ** @desc: a page test for the Special:Contributions". */ class contributionsTest extends pageTest { function __construct() { $this->pagePath = "index.php/Special:Contributions/" . USER_ON_WIKI; $this->params = array ( 'target' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2), "newbies") ), 'namespace' => wikiFuzz::chooseInput( array(-1, 15, 1, wikiFuzz::makeFuzz(2)) ), 'offset' => wikiFuzz::chooseInput( array("0", "-1", "------'-------0", "+1", "982342131232131231241", wikiFuzz::makeFuzz(2)) ), 'bot' => wikiFuzz::chooseInput( array("", "-1", "0", "1", wikiFuzz::makeFuzz(2)) ), 'go' => wikiFuzz::chooseInput( array("-1", 'prev', 'next', wikiFuzz::makeFuzz(2)) ) ); } } /** ** @desc: a page test for viewing a normal page, whilst posting various params. */ class viewPageTest extends pageTest { function __construct() { $this->pagePath = "index.php/Main_Page"; $this->params = array ( "useskin" => wikiFuzz::chooseInput( array("chick", "cologneblue", "myskin", "nostalgia", "simple", "standard", wikiFuzz::makeFuzz(2)) ), "uselang" => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz(2), "ab", "af", "an", "ar", "arc", "as", "ast", "av", "ay", "az", "ba", "bat-smg", "be", "bg", "bm", "bn", "bo", "bpy", "br", "bs", "ca", "ce", "cs", "csb", "cv", "cy", "da", "de", "dv", "dz", "el", "en", "eo", "es", "et", "eu", "fa", "fi", "fo", "fr", "fur", "fy", "ga", "gn", "gsw", "gu", "he", "hi", "hr", "hu", "ia", "id", "ii", "is", "it", "ja", "jv", "ka", "km", "kn", "ko", "ks", "ku", "kv", "la", "li", "lo", "lt", "lv", "mk", "ml", "ms", "nah", "nap", "nds", "nds-nl", "nl", "nn", "no", "non", "nv", "oc", "or", "os", "pa", "pl", "pms", "ps", "pt", "pt-br", "qu", "rmy", "ro", "ru", "sc", "sd", "sk", "sl", "sq", "sr", "sr-ec", "sr-el", "sr-jc", "sr-jl", "su", "sv", "ta", "te", "th", "tlh", "tr", "tt", "ty", "tyv", "udm", "ug", "uk", "ur", "utf8", "vec", "vi", "wa", "xal", "yi", "za", "zh", "zh-cn", "zh-hk", "zh-sg", "zh-tw", "zh-tw") ), "returnto" => wikiFuzz::makeFuzz(2), "feed" => wikiFuzz::chooseInput( array("atom", "rss", wikiFuzz::makeFuzz(2)) ), "rcid" => wikiFuzz::makeFuzz(2), "action" => wikiFuzz::chooseInput( array("view", "raw", "render", wikiFuzz::makeFuzz(2), "markpatrolled") ), "printable" => wikiFuzz::makeFuzz(2), "oldid" => wikiFuzz::makeFuzz(2), "redirect" => wikiFuzz::makeFuzz(2), "diff" => wikiFuzz::makeFuzz(2), "search" => wikiFuzz::makeFuzz(2), "rdfrom" => wikiFuzz::makeFuzz(2), // things from Article.php from here on: "token" => wikiFuzz::makeFuzz(2), "tbid" => wikiFuzz::makeFuzz(2), "action" => wikiFuzz::chooseInput( array("purge", wikiFuzz::makeFuzz(2)) ), "wpReason" => wikiFuzz::makeFuzz(2), "wpEditToken" => wikiFuzz::makeFuzz(2), "from" => wikiFuzz::makeFuzz(2), "bot" => wikiFuzz::makeFuzz(2), "summary" => wikiFuzz::makeFuzz(2), "direction" => wikiFuzz::chooseInput( array("next", "prev", wikiFuzz::makeFuzz(2)) ), "section" => wikiFuzz::makeFuzz(2), "preload" => wikiFuzz::makeFuzz(2), ); // Tidy does not know how to valid atom or rss, so exclude from testing for the time being. if ($this->params["feed"] == "atom") unset($this->params["feed"]); else if ($this->params["feed"] == "rss") unset($this->params["feed"]); // Raw pages cannot really be validated if ($this->params["action"] == "raw") unset($this->params["action"]); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(6) == 0) unset($this->params["rcid"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["diff"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["rdfrom"]); if (wikiFuzz::randnum(3) == 0) unset($this->params["oldid"]); // usually don't want action == purge. if (wikiFuzz::randnum(6) > 1) unset($this->params["action"]); } } /** ** @desc: a page test for "Special:Allmessages". */ class specialAllmessagesTest extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special:Allmessages"; // only really has one parameter $this->params = array ( "ot" => wikiFuzz::chooseInput( array("php", "html", wikiFuzz::makeFuzz(2)) ) ); } } /** ** @desc: a page test for "Special:Newpages". */ class specialNewpages extends pageTest { function __construct() { $this->pagePath = "index.php/Special:Newpages"; $this->params = array ( "namespace" => wikiFuzz::chooseInput( range(-1, 15) ), "feed" => wikiFuzz::chooseInput( array("atom", "rss", wikiFuzz::makeFuzz(2)) ), 'limit' => wikiFuzz::chooseInput( array("-1", "0", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz(2)) ), 'offset' => wikiFuzz::chooseInput( array("-1", "0", "------'-------0", "+1", "9823412312312412435", wikiFuzz::makeFuzz(2)) ) ); // Tidy does not know how to valid atom or rss, so exclude from testing for the time being. if ($this->params["feed"] == "atom") unset($this->params["feed"]); else if ($this->params["feed"] == "rss") unset($this->params["feed"]); } } /** ** @desc: a page test for "redirect.php" */ class redirectTest extends pageTest { function __construct() { $this->pagePath = "redirect.php"; $this->params = array ( "wpDropdown" => wikiFuzz::makeFuzz(2) ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(6) == 0) unset($this->params["wpDropdown"]); } } /** ** @desc: a page test for "Special:Confirmemail" */ class confirmEmail extends pageTest { function __construct() { // sometimes we send a bogus confirmation code, and sometimes we don't. $this->pagePath = "index.php?title=Special:Confirmemail" . wikiFuzz::chooseInput( array("", "/" . wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(1)) ) ); $this->params = array ( "token" => wikiFuzz::makeFuzz(2) ); } } /** ** @desc: a page test for "Special:Watchlist" ** Note: this test would be better if we were logged in. */ class watchlistTest extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special:Watchlist"; $this->params = array ( "remove" => wikiFuzz::chooseInput( array("Remove checked items from watchlist", wikiFuzz::makeFuzz(2))), 'days' => wikiFuzz::chooseInput( array(0, -1, -230, "--", 3, 9, wikiFuzz::makeFuzz(2)) ), 'hideOwn' => wikiFuzz::chooseInput( array("", "0", "1", wikiFuzz::makeFuzz(2)) ), 'hideBots' => wikiFuzz::chooseInput( array("", "0", "1", wikiFuzz::makeFuzz(2)) ), 'namespace'=> wikiFuzz::chooseInput( array("", "0", "1", wikiFuzz::makeFuzz(2)) ), 'action' => wikiFuzz::chooseInput( array("submit", "clear", wikiFuzz::makeFuzz(2)) ), 'id[]' => wikiFuzz::makeFuzz(2), 'edit' => wikiFuzz::makeFuzz(2), 'token' => wikiFuzz::chooseInput( array("", "1243213", wikiFuzz::makeFuzz(2)) ) ); // sometimes we specifiy "reset", and sometimes we don't. if (wikiFuzz::randnum(3) == 0) $this->params["reset"] = wikiFuzz::chooseInput( array("", "0", "1", wikiFuzz::makeFuzz(2)) ); } } /** ** @desc: a page test for "Special:Blockme" */ class specialBlockmeTest extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special:Blockme"; $this->params = array ( ); // sometimes we specify "ip", and sometimes we don't. if (wikiFuzz::randnum(1) == 0) { $this->params["ip"] = wikiFuzz::chooseInput( array("10.12.41.213", wikiFuzz::randnum(-10,8134), wikiFuzz::makeFuzz(2)) ); } } } /** ** @desc: a page test for "Special:Movepage" */ class specialMovePage extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special:Movepage"; $this->params = array ( "action" => wikiFuzz::chooseInput( array("success", "submit", "", wikiFuzz::makeFuzz(2)) ), 'wpEditToken' => wikiFuzz::chooseInput( array('', 0, 34987987, wikiFuzz::makeFuzz(2)) ), 'target' => wikiFuzz::chooseInput( array("x", wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(2)) ) ), 'wpOldTitle' => wikiFuzz::chooseInput( array("z", wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(2)), wikiFuzz::makeFuzz(2) ) ), 'wpNewTitle' => wikiFuzz::chooseInput( array("y", wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(2)), wikiFuzz::makeFuzz(2) ) ), 'wpReason' => wikiFuzz::chooseInput( array(wikiFuzz::makeFuzz(2)) ), 'wpMovetalk' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ), 'wpDeleteAndMove' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ), 'wpConfirm' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ), 'talkmoved' => wikiFuzz::chooseInput( array("1", wikiFuzz::makeFuzz(2), "articleexists", 'notalkpage') ), 'oldtitle' => wikiFuzz::makeFuzz(2), 'newtitle' => wikiFuzz::makeFuzz(2), 'wpMovetalk' => wikiFuzz::chooseInput( array("1", "0", wikiFuzz::makeFuzz(2)) ) ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(2) == 0) unset($this->params["wpEditToken"]); if (wikiFuzz::randnum(3) == 0) unset($this->params["target"]); if (wikiFuzz::randnum(3) == 0) unset($this->params["wpNewTitle"]); if (wikiFuzz::randnum(4) == 0) unset($this->params["wpReason"]); if (wikiFuzz::randnum(4) == 0) unset($this->params["wpOldTitle"]); } } /** ** @desc: a page test for "Special:Undelete" */ class specialUndelete extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special:Undelete"; $this->params = array ( "action" => wikiFuzz::chooseInput( array("submit", "", wikiFuzz::makeFuzz(2)) ), 'wpEditToken' => wikiFuzz::chooseInput( array('', 0, 34987987, wikiFuzz::makeFuzz(2)) ), 'target' => wikiFuzz::chooseInput( array("x", wikiFuzz::makeTitleSafe(wikiFuzz::makeFuzz(2)) ) ), 'timestamp' => wikiFuzz::chooseInput( array("125223", wikiFuzz::makeFuzz(2) ) ), 'file' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ), 'restore' => wikiFuzz::chooseInput( array("0", "1", wikiFuzz::makeFuzz(2)) ), 'preview' => wikiFuzz::chooseInput( array("0", "1", wikiFuzz::makeFuzz(2)) ), 'wpComment' => wikiFuzz::makeFuzz(2) ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(2) == 0) unset($this->params["wpEditToken"]); if (wikiFuzz::randnum(4) == 0) unset($this->params["target"]); if (wikiFuzz::randnum(1) == 0) unset($this->params["restore"]); if (wikiFuzz::randnum(1) == 0) unset($this->params["preview"]); } } /** ** @desc: a page test for "Special:Unlockdb" */ class specialUnlockdb extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special:Unlockdb"; $this->params = array ( "action" => wikiFuzz::chooseInput( array("submit", "success", "", wikiFuzz::makeFuzz(2)) ), 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ), 'wpLockConfirm' => wikiFuzz::chooseInput( array("0", "1", wikiFuzz::makeFuzz(2)) ) ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(4) == 0) unset($this->params["wpEditToken"]); if (wikiFuzz::randnum(4) == 0) unset($this->params["action"]); if (wikiFuzz::randnum(4) == 0) unset($this->params["wpLockConfirm"]); } } /** ** @desc: a page test for "Special:Lockdb" */ class specialLockdb extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special:Lockdb"; $this->params = array ( "action" => wikiFuzz::chooseInput( array("submit", "success", "", wikiFuzz::makeFuzz(2)) ), 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ), 'wpLockReason' => wikiFuzz::makeFuzz(2), 'wpLockConfirm'=> wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ) ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(4) == 0) unset($this->params["wpEditToken"]); if (wikiFuzz::randnum(4) == 0) unset($this->params["action"]); if (wikiFuzz::randnum(4) == 0) unset($this->params["wpLockConfirm"]); } } /** ** @desc: a page test for "Special:Userrights" */ class specialUserrights extends pageTest { function __construct() { $this->pagePath = "index.php/Special:Userrights"; $this->params = array ( 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ), 'user-editname' => wikiFuzz::chooseInput( array("Nickj2", "Nickj2\n", wikiFuzz::makeFuzz(2)) ), 'ssearchuser' => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ), 'saveusergroups'=> wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)), "Save User Groups"), 'member[]' => wikiFuzz::chooseInput( array("0", "bot", "1", "++--34234", wikiFuzz::makeFuzz(2)) ), "available[]" => wikiFuzz::chooseInput( array("0", "sysop", "bureaucrat", "1", "++--34234", wikiFuzz::makeFuzz(2)) ) ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(3) == 0) unset($this->params['ssearchuser']); if (wikiFuzz::randnum(3) == 0) unset($this->params['saveusergroups']); } } /** ** @desc: a test for page protection and unprotection. */ class pageProtectionForm extends pageTest { function __construct() { $this->pagePath = "index.php?title=Main_Page"; $this->params = array ( "action" => "protect", 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ), "mwProtect-level-edit" => wikiFuzz::chooseInput( array('', 'autoconfirmed', 'sysop', wikifuzz::makeFuzz(2)) ), "mwProtect-level-move" => wikiFuzz::chooseInput( array('', 'autoconfirmed', 'sysop', wikifuzz::makeFuzz(2)) ), "mwProtectUnchained" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ), 'mwProtect-reason' => wikiFuzz::chooseInput( array("because it was there", wikifuzz::makeFuzz(2)) ) ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(3) == 0) unset($this->params["mwProtectUnchained"]); if (wikiFuzz::randnum(3) == 0) unset($this->params['mwProtect-reason']); } } /** ** @desc: a page test for "Special:Blockip". */ class specialBlockip extends pageTest { function __construct() { $this->pagePath = "index.php/Special:Blockip"; $this->params = array ( "action" => wikiFuzz::chooseInput( array("submit", "", wikiFuzz::makeFuzz(2)) ), 'wpEditToken' => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ), "wpBlockAddress" => wikiFuzz::chooseInput( array("20398702394", "", "Nickj2", wikiFuzz::makeFuzz(2), // something like an IP address, sometimes invalid: ( wikiFuzz::randnum(300,-20) . "." . wikiFuzz::randnum(300,-20) . "." . wikiFuzz::randnum(300,-20) . "." .wikiFuzz::randnum(300,-20) ) ) ), "ip" => wikiFuzz::chooseInput( array("20398702394", "", "Nickj2", wikiFuzz::makeFuzz(2), // something like an IP address, sometimes invalid: ( wikiFuzz::randnum(300,-20) . "." . wikiFuzz::randnum(300,-20) . "." . wikiFuzz::randnum(300,-20) . "." .wikiFuzz::randnum(300,-20) ) ) ), "wpBlockOther" => wikiFuzz::chooseInput( array('', 'Nickj2', wikifuzz::makeFuzz(2)) ), "wpBlockExpiry" => wikiFuzz::chooseInput( array("other", "2 hours", "1 day", "3 days", "1 week", "2 weeks", "1 month", "3 months", "6 months", "1 year", "infinite", wikiFuzz::makeFuzz(2)) ), "wpBlockReason" => wikiFuzz::chooseInput( array("because it was there", wikifuzz::makeFuzz(2)) ), "wpAnonOnly" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ), "wpCreateAccount" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ), "wpBlock" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ) ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(4) == 0) unset($this->params["wpBlockOther"]); if (wikiFuzz::randnum(4) == 0) unset($this->params["wpBlockExpiry"]); if (wikiFuzz::randnum(4) == 0) unset($this->params["wpBlockReason"]); if (wikiFuzz::randnum(4) == 0) unset($this->params["wpAnonOnly"]); if (wikiFuzz::randnum(4) == 0) unset($this->params["wpCreateAccount"]); if (wikiFuzz::randnum(4) == 0) unset($this->params["wpBlockAddress"]); if (wikiFuzz::randnum(4) == 0) unset($this->params["ip"]); } } /** ** @desc: a test for the imagepage. */ class imagepageTest extends pageTest { function __construct() { $this->pagePath = "index.php/Image:Small-email.png"; $this->params = array ( "image" => wikiFuzz::chooseInput( array("Small-email.png", wikifuzz::makeFuzz(2)) ), "wpReason" => wikifuzz::makeFuzz(2), "oldimage" => wikiFuzz::chooseInput( array("Small-email.png", wikifuzz::makeFuzz(2)) ), "wpEditToken" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ), ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(6) == 0) unset($this->params["image"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["wpReason"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["oldimage"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["wpEditToken"]); } } /** ** @desc: a test for page deletion form. */ class pageDeletion extends pageTest { function __construct() { $this->pagePath = "index.php?title=Main_Page&action=delete"; $this->params = array ( "wpEditToken" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ), "wpReason" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ), "wpConfirm" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ), ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(5) == 0) unset($this->params["wpReason"]); if (wikiFuzz::randnum(5) == 0) unset($this->params["wpEditToken"]); if (wikiFuzz::randnum(5) == 0) unset($this->params["wpConfirm"]); } } /** ** @desc: a test for Revision Deletion. */ class specialRevisionDelete extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special:Revisiondelete"; $this->params = array ( "target" => wikiFuzz::chooseInput( array("Main Page", wikifuzz::makeFuzz(2)) ), "oldid" => wikifuzz::makeFuzz(2), "oldid[]" => wikifuzz::makeFuzz(2), "wpReason" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ), "revdelete-hide-text" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ), "revdelete-hide-comment" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ), "revdelete-hide-user" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ), "revdelete-hide-restricted" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikiFuzz::makeFuzz(2)) ), ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(3) == 0) unset($this->params["target"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["oldid"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["oldid[]"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["wpReason"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["revdelete-hide-text"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["revdelete-hide-comment"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["revdelete-hide-user"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["revdelete-hide-restricted"]); } } /** ** @desc: a test for Special:Import. */ class specialImport extends pageTest { function __construct() { $this->pagePath = "index.php/Special:Import"; $this->params = array ( "action" => "submit", "source" => wikiFuzz::chooseInput( array("upload", "interwiki", wikifuzz::makeFuzz(2)) ), "MAX_FILE_SIZE" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ), "xmlimport" => wikiFuzz::chooseInput( array("/var/www/hosts/mediawiki/wiki/AdminSettings.php", "1", "++--34234", wikiFuzz::makeFuzz(2)) ), "namespace" => wikiFuzz::chooseInput( array(wikiFuzz::randnum(30,-6), wikiFuzz::makeFuzz(2)) ), "interwiki" => wikiFuzz::makeFuzz(2), "interwikiHistory" => wikiFuzz::makeFuzz(2), "frompage" => wikiFuzz::makeFuzz(2), ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(6) == 0) unset($this->params["action"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["source"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["MAX_FILE_SIZE"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["xmlimport"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["interwiki"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["interwikiHistory"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["frompage"]); // Note: Need to do a file upload to fully test this Special page. } } /** ** @desc: a test for thumb.php */ class thumbTest extends pageTest { function __construct() { $this->pagePath = "thumb.php"; $this->params = array ( "f" => wikiFuzz::chooseInput( array("..", "\\", "small-email.png", wikifuzz::makeFuzz(2)) ), "w" => wikiFuzz::chooseInput( array("80", wikiFuzz::randnum(6000,-200), wikifuzz::makeFuzz(2)) ), "r" => wikiFuzz::chooseInput( array("0", wikifuzz::makeFuzz(2)) ), ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(6) == 0) unset($this->params["f"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["w"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["r"]); } } /** ** @desc: a test for trackback.php */ class trackbackTest extends pageTest { function __construct() { $this->pagePath = "trackback.php"; $this->params = array ( "url" => wikifuzz::makeFuzz(2), "blog_name" => wikiFuzz::chooseInput( array("80", wikiFuzz::randnum(6000,-200), wikifuzz::makeFuzz(2)) ), "article" => wikiFuzz::chooseInput( array("Main Page", wikifuzz::makeFuzz(2)) ), "title" => wikiFuzz::chooseInput( array("Main Page", wikifuzz::makeFuzz(2)) ), "excerpt" => wikifuzz::makeFuzz(2), ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(3) == 0) unset($this->params["title"]); if (wikiFuzz::randnum(3) == 0) unset($this->params["excerpt"]); } } /** ** @desc: a test for profileinfo.php */ class profileInfo extends pageTest { function __construct() { $this->pagePath = "profileinfo.php"; $this->params = array ( "expand" => wikifuzz::makeFuzz(2), "sort" => wikiFuzz::chooseInput( array("time", "count", "name", wikifuzz::makeFuzz(2)) ), "filter" => wikiFuzz::chooseInput( array("Main Page", wikifuzz::makeFuzz(2)) ), ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(3) == 0) unset($this->params["sort"]); if (wikiFuzz::randnum(3) == 0) unset($this->params["filter"]); } } /** ** @desc: a test for Special:Cite (extension Special page). */ class specialCite extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special:Cite"; $this->params = array ( "page" => wikiFuzz::chooseInput( array("\" onmouseover=\"alert(1);\"", "Main Page", wikifuzz::makeFuzz(2)) ), "id" => wikiFuzz::chooseInput( array("-1", "0", "------'-------0", "+1", "-9823412312312412435", wikiFuzz::makeFuzz(2)) ), ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(6) == 0) unset($this->params["page"]); if (wikiFuzz::randnum(6) == 0) unset($this->params["id"]); } } /** ** @desc: a test for Special:Filepath (extension Special page). */ class specialFilepath extends pageTest { function __construct() { $this->pagePath = "index.php/Special:Filepath"; $this->params = array ( "file" => wikiFuzz::chooseInput( array("Small-email.png", "Small-email.png" . wikifuzz::makeFuzz(1), wikiFuzz::makeFuzz(2)) ), ); } } /** ** @desc: a test for Special:Makebot (extension Special page). */ class specialMakebot extends pageTest { function __construct() { $this->pagePath = "index.php/Special:Makebot"; $this->params = array ( "username" => wikiFuzz::chooseInput( array("Nickj2", "192.168.0.2", wikifuzz::makeFuzz(1) ) ), "dosearch" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ), "grant" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ), "comment" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ), "token" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ), ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(2) == 0) unset($this->params["dosearch"]); if (wikiFuzz::randnum(2) == 0) unset($this->params["grant"]); if (wikiFuzz::randnum(5) == 0) unset($this->params["token"]); } } /** ** @desc: a test for Special:Makesysop (extension Special page). */ class specialMakesysop extends pageTest { function __construct() { $this->pagePath = "index.php/Special:Makesysop"; $this->params = array ( "wpMakesysopUser" => wikiFuzz::chooseInput( array("Nickj2", "192.168.0.2", wikifuzz::makeFuzz(1) ) ), "action" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ), "wpMakesysopSubmit" => wikiFuzz::chooseInput( array("0", "1", "++--34234", wikifuzz::makeFuzz(2)) ), "wpEditToken" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ), "wpSetBureaucrat" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ), ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(3) == 0) unset($this->params["wpMakesysopSubmit"]); if (wikiFuzz::randnum(3) == 0) unset($this->params["wpEditToken"]); if (wikiFuzz::randnum(3) == 0) unset($this->params["wpSetBureaucrat"]); } } /** ** @desc: a test for Special:Renameuser (extension Special page). */ class specialRenameuser extends pageTest { function __construct() { $this->pagePath = "index.php/Special:Renameuser"; $this->params = array ( "oldusername" => wikiFuzz::chooseInput( array("Nickj2", "192.168.0.2", wikifuzz::makeFuzz(1) ) ), "newusername" => wikiFuzz::chooseInput( array("Nickj2", "192.168.0.2", wikifuzz::makeFuzz(1) ) ), "token" => wikiFuzz::chooseInput( array("20398702394", "", wikiFuzz::makeFuzz(2)) ), ); } } /** ** @desc: a test for Special:Linksearch (extension Special page). */ class specialLinksearch extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special%3ALinksearch"; $this->params = array ( "target" => wikifuzz::makeFuzz(2), ); // sometimes we don't want to specify certain parameters. if (wikiFuzz::randnum(10) == 0) unset($this->params["target"]); } } /** ** @desc: a test for Special:CategoryTree (extension Special page). */ class specialCategoryTree extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special:CategoryTree"; $this->params = array ( "target" => wikifuzz::makeFuzz(2), "from" => wikifuzz::makeFuzz(2), "until" => wikifuzz::makeFuzz(2), "showas" => wikifuzz::makeFuzz(2), "mode" => wikiFuzz::chooseInput( array("pages", "categories", "all", wikifuzz::makeFuzz(2)) ), ); // sometimes we do want to specify certain parameters. if (wikiFuzz::randnum(5) == 0) $this->params["notree"] = wikiFuzz::chooseInput( array("1", 0, "", wikiFuzz::makeFuzz(2)) ); } } /** ** @desc: selects a page test to run. */ function selectPageTest($count) { // if the user only wants a specific test, then only ever give them that. if (defined("SPECIFIC_TEST")) { $testType = SPECIFIC_TEST; return new $testType (); } // Some of the time we test Special pages, the remaining // time we test using the standard edit page. switch ($count % 100) { case 0 : return new successfulUserLoginTest(); case 1 : return new listusersTest(); case 2 : return new searchTest(); case 3 : return new recentchangesTest(); case 4 : return new prefixindexTest(); case 5 : return new mimeSearchTest(); case 6 : return new specialLogTest(); case 7 : return new userLoginTest(); case 8 : return new ipblocklistTest(); case 9 : return new newImagesTest(); case 10: return new imagelistTest(); case 11: return new specialExportTest(); case 12: return new specialBooksourcesTest(); case 13: return new specialAllpagesTest(); case 14: return new pageHistoryTest(); case 15: return new contributionsTest(); case 16: return new viewPageTest(); case 17: return new specialAllmessagesTest(); case 18: return new specialNewpages(); case 19: return new searchTest(); case 20: return new redirectTest(); case 21: return new confirmEmail(); case 22: return new watchlistTest(); case 23: return new specialBlockmeTest(); case 24: return new specialUndelete(); case 25: return new specialMovePage(); case 26: return new specialUnlockdb(); case 27: return new specialLockdb(); case 28: return new specialUserrights(); case 29: return new pageProtectionForm(); case 30: return new specialBlockip(); case 31: return new imagepageTest(); case 32: return new pageDeletion(); case 33: return new specialRevisionDelete(); case 34: return new specialImport(); case 35: return new thumbTest(); case 36: return new trackbackTest(); case 37: return new profileInfo(); case 38: return new specialCite(); case 39: return new specialFilepath(); case 40: return new specialMakebot(); case 41: return new specialMakesysop(); case 42: return new specialRenameuser(); case 43: return new specialLinksearch(); case 44: return new specialCategoryTree(); default: return new editPageTest(); } } /////////////////////// SAVING OUTPUT ///////////////////////// /** ** @desc: Utility function for saving a file. Currently has no error checking. */ function saveFile($data, $name) { file_put_contents($name, $data); } /** ** @desc: Returns a test as an experimental GET-to-POST URL. ** This doesn't seem to always work though, and sometimes the output is too long ** to be a valid GET URL, so we also save in other formats. */ function getAsURL(pageTest $test) { $used_question_mark = (strpos($test->getPagePath(), "?") !== false); $retval = "http://get-to-post.nickj.org/?http://" . WIKI_BASE_URL . $test->getPagePath(); foreach ($test->getParams() as $param => $value) { if (!$used_question_mark) { $retval .= "?"; $used_question_mark = true; } else { $retval .= "&"; } $retval .= $param . "=" . urlencode($value); } return $retval; } /** ** @desc: Saves a plain-text human-readable version of a test. */ function saveTestAsText(pageTest $test, $filename) { $str = "Test: " . $test->getPagePath(); foreach ($test->getParams() as $param => $value) { $str .= "\n$param: $value"; } $str .= "\nGet-to-post URL: " . getAsURL($test) . "\n"; saveFile($str, $filename); } /** ** @desc: Saves a test as a standalone basic PHP script that shows this one problem. ** Resulting script requires PHP-Curl be installed in order to work. */ function saveTestAsPHP(pageTest $test, $filename) { $str = "getParams()), true) . ";\n" . "\$ch = curl_init();\n" . "curl_setopt(\$ch, CURLOPT_POST, 1);\n" . "curl_setopt(\$ch, CURLOPT_POSTFIELDS, \$params );\n" . "curl_setopt(\$ch, CURLOPT_URL, " . var_export(WIKI_BASE_URL . $test->getPagePath(), true) . ");\n" . "curl_setopt(\$ch, CURLOPT_RETURNTRANSFER,1);\n" . ($test->getCookie() ? "curl_setopt(\$ch, CURLOPT_COOKIE, " . var_export($test->getCookie(), true) . ");\n" : "") . "\$result=curl_exec(\$ch);\n" . "curl_close (\$ch);\n" . "print \$result;\n" . "?>\n"; saveFile($str, $filename); } /** ** @desc: Escapes a value so that it can be used on the command line by Curl. ** Specifically, "<" and "@" need to be escaped if they are the first character, ** otherwise curl interprets these as meaning that we want to insert a file. */ function escapeForCurl(array $input_params) { $output_params = array(); foreach ($input_params as $param => $value) { if (strlen($value) > 0 && ( $value[0] == "@" || $value[0] == "<")) { $value = "\\" . $value; } $output_params[$param] = $value; } return $output_params; } /** ** @desc: Saves a test as a standalone CURL shell script that shows this one problem. ** Resulting script requires standalone Curl be installed in order to work. */ function saveTestAsCurl(pageTest $test, $filename) { $str = "#!/bin/bash\n" . "curl --silent --include --globoff \\\n" . ($test->getCookie() ? " --cookie " . escapeshellarg($test->getCookie()) . " \\\n" : ""); foreach (escapeForCurl($test->getParams()) as $param => $value) { $str .= " -F " . escapeshellarg($param) . "=" . escapeshellarg($value) . " \\\n"; } $str .= " " . escapeshellarg(WIKI_BASE_URL . $test->getPagePath()); // beginning space matters. $str .= "\n"; saveFile($str, $filename); chmod($filename, 0755); // make executable } /** ** @desc: Saves the internal data structure to file. */ function saveTestData (pageTest $test, $filename) { saveFile(serialize($test), $filename); } /** ** @desc: saves a test in the various formats. */ function saveTest(pageTest $test, $testname) { $base_name = DIRECTORY . "/" . $testname; saveTestAsText($test, $base_name . INFO_FILE); saveTestAsPHP ($test, $base_name . PHP_TEST ); saveTestAsCurl($test, $base_name . CURL_TEST); saveTestData ($test, $base_name . DATA_FILE); } //////////////////// MEDIAWIKI OUTPUT ///////////////////////// /** ** @desc: Asks MediaWiki for the HTML output of a test. */ function wikiTestOutput(pageTest $test) { $ch = curl_init(); // specify the cookie, if required. if ($test->getCookie()) curl_setopt($ch, CURLOPT_COOKIE, $test->getCookie()); curl_setopt($ch, CURLOPT_POST, 1); // save form using a POST $params = escapeForCurl($test->getParams()); curl_setopt($ch, CURLOPT_POSTFIELDS, $params ); // load the POST variables curl_setopt($ch, CURLOPT_URL, WIKI_BASE_URL . $test->getPagePath() ); // set url to post to curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); // return into a variable $result=curl_exec ($ch); // if we encountered an error, then say so, and return an empty string. if (curl_error($ch)) { print "\nCurl error #: " . curl_errno($ch) . " - " . curl_error ($ch); $result = ""; } curl_close ($ch); return $result; } //////////////////// HTML VALIDATION ///////////////////////// /* ** @desc: Asks the validator whether this is valid HTML, or not. */ function validateHTML($text) { $params = array ("fragment" => $text); $ch = curl_init(); curl_setopt($ch, CURLOPT_POST, 1); // save form using a POST curl_setopt($ch, CURLOPT_POSTFIELDS, $params); // load the POST variables curl_setopt($ch, CURLOPT_URL, VALIDATOR_URL); // set url to post to curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); // return into a variable $result=curl_exec ($ch); // if we encountered an error, then log it, and exit. if (curl_error($ch)) { trigger_error("Curl error #: " . curl_errno($ch) . " - " . curl_error ($ch) ); print "Curl error #: " . curl_errno($ch) . " - " . curl_error ($ch) . " - exiting.\n"; exit(); } curl_close ($ch); $valid = (strpos($result, "Failed validation") === false ? true : false); return array($valid, $result); } /** ** @desc: Get tidy to check for no HTML errors in the output file (e.g. unescaped strings). */ function tidyCheckFile($name) { $file = DIRECTORY . "/" . $name; $command = PATH_TO_TIDY . " -output /tmp/out.html -quiet $file 2>&1"; $x = `$command`; // Look for the most interesting Tidy errors and warnings. if ( strpos($x,"end of file while parsing attributes") !== false || strpos($x,"attribute with missing trailing quote mark") !== false || strpos($x,"missing '>' for end of tag") !== false || strpos($x,"Error:") !== false) { print "\nTidy found something - view details with: $command"; return false; } else { return true; } } /** ** @desc: Returns whether or not an database error log file has changed in size since ** the last time this was run. This is used to tell if a test caused a DB error. */ function dbErrorLogged() { static $filesize; // first time running this function if (!isset($filesize)) { // create log if it does not exist if (!file_exists(DB_ERROR_LOG_FILE)) { saveFile("", DB_ERROR_LOG_FILE); } $filesize = filesize(DB_ERROR_LOG_FILE); return false; } $newsize = filesize(DB_ERROR_LOG_FILE); // if the log has grown, then assume the current test caused it. if ($newsize != $filesize) { $filesize = $newsize; return true; } return false; } ////////////////// TOP-LEVEL PROBLEM-FINDING FUNCTION //////////////////////// /** ** @desc: takes a page test, and runs it and tests it for problems in the output. ** Returns: False on finding a problem, or True on no problems being found. */ function runWikiTest(pageTest $test, &$testname, $can_overwrite = false) { // by default don't overwrite a previous test of the same name. while ( ! $can_overwrite && file_exists(DIRECTORY . "/" . $testname . DATA_FILE)) { $testname .= "-" . mt_rand(0,9); } $filename = DIRECTORY . "/" . $testname . DATA_FILE; // Store the time before and after, to find slow pages. $before = microtime(true); // Get MediaWiki to give us the output of this test. $wiki_preview = wikiTestOutput($test); $after = microtime(true); // if we received no response, then that's interesting. if ($wiki_preview == "") { print "\nNo response received for: $filename"; return false; } // save output HTML to file. $html_file = DIRECTORY . "/" . $testname . HTML_FILE; saveFile($wiki_preview, $html_file); // if there were PHP errors in the output, then that's interesting too. if ( strpos($wiki_preview, "Warning: " ) !== false || strpos($wiki_preview, "Fatal error: ") !== false || strpos($wiki_preview, "Notice: " ) !== false || strpos($wiki_preview, "Error: " ) !== false ) { $error = substr($wiki_preview, strpos($wiki_preview, ":") + 7, 50); // Avoid probable PHP bug with bad session ids; http://bugs.php.net/bug.php?id=38224 if ($error != "Unknown: The session id contains illegal character") { print "\nPHP error/warning/notice in HTML output: $html_file ; $error"; return false; } } // if there was a MediaWiki Backtrace message in the output, then that's also interesting. if (strpos($wiki_preview, "Backtrace:") !== false) { print "\nInternal MediaWiki error in HTML output: $html_file"; return false; } // if there was a Parser error comment in the output, then that's potentially interesting. if (strpos($wiki_preview, "!-- ERR") !== false) { print "\nParser Error comment in HTML output: $html_file"; return false; } // if a database error was logged, then that's definitely interesting. if (dbErrorLogged()) { print "\nDatabase Error logged for: $filename"; return false; } // validate result $valid = true; if (VALIDATE_ON_WEB) { list ($valid, $validator_output) = validateHTML($wiki_preview); if (!$valid) print "\nW3C web validation failed - view details with: html2text " . DIRECTORY . "/" . $testname . ".validator_output.html"; } // Get tidy to check the page, unless it is a test which produces XML. if (!$test instanceof trackbackTest && !$test instanceof specialExportTest) { $valid = tidyCheckFile( $testname . HTML_FILE ) && $valid; } // if it took more than 2 seconds to render, then it may be interesting too. (Possible DoS attack?) if (($after - $before) >= 2) { print "\nParticularly slow to render (" . round($after - $before, 2) . " seconds): $filename"; return false; } if( $valid ) { // Remove temp HTML file if test was valid: unlink( $html_file ); } elseif( VALIDATE_ON_WEB ) { saveFile($validator_output, DIRECTORY . "/" . $testname . ".validator_output.html"); } return $valid; } /////////////////// RERUNNING OLD TESTS /////////////////// /** ** @desc: We keep our failed tests so that they can be rerun. ** This function does that retesting. */ function rerunPreviousTests() { print "Retesting previously found problems.\n"; $dir_contents = scandir (DIRECTORY); // sort file into the order a normal person would use. natsort ($dir_contents); foreach ($dir_contents as $file) { // if file is not a test, then skip it. // Note we need to escape any periods or will be treated as "any character". $matches = array(); if (!ereg("(.*)" . str_replace(".", "\.", DATA_FILE) . "$", $file, $matches)) continue; // reload the test. $full_path = DIRECTORY . "/" . $file; $test = unserialize(file_get_contents($full_path)); // if this is not a valid test, then skip it. if (! $test instanceof pageTest) { print "\nSkipping invalid test - $full_path"; continue; } // The date format is in Apache log format, which makes it easier to locate // which retest caused which error in the Apache logs (only happens usually if // apache segfaults). if (!QUIET) print "[" . date ("D M d H:i:s Y") . "] Retesting $file (" . get_class($test) . ")"; // run test $testname = $matches[1]; $valid = runWikiTest($test, $testname, true); if (!$valid) { saveTest($test, $testname); if (QUIET) { print "\nTest: " . get_class($test) . " ; Testname: $testname\n------"; } else { print "\n"; } } else { if (!QUIET) print "\r"; if (DELETE_PASSED_RETESTS) { $prefix = DIRECTORY . "/" . $testname; if (is_file($prefix . DATA_FILE)) unlink($prefix . DATA_FILE); if (is_file($prefix . PHP_TEST )) unlink($prefix . PHP_TEST ); if (is_file($prefix . CURL_TEST)) unlink($prefix . CURL_TEST); if (is_file($prefix . INFO_FILE)) unlink($prefix . INFO_FILE); } } } print "\nDone retesting.\n"; } ////////////////////// MAIN LOOP //////////////////////// // first check whether CURL is installed, because sometimes it's not. if( ! function_exists('curl_init') ) { die("Could not find 'curl_init' function. Is the curl extension compiled into PHP?\n"); } // Initialization of types. wikiFuzz doesn't have a constructor because we want to // access it staticly and not have any globals. wikiFuzz::$types = array_keys(wikiFuzz::$data); // Make directory if doesn't exist if (!is_dir(DIRECTORY)) { mkdir (DIRECTORY, 0700 ); } // otherwise, we first retest the things that we have found in previous runs else if (RERUN_OLD_TESTS) { rerunPreviousTests(); } // seed the random number generator mt_srand(crc32(microtime())); // main loop. $start_time = date("U"); $num_errors = 0; if (!QUIET) print "Beginning main loop. Results are stored in the " . DIRECTORY . " directory.\n"; if (!QUIET) print "Press CTRL+C to stop testing.\n"; for ($count=0; true; $count++) { if (!QUIET) { // spinning progress indicator. switch( $count % 4 ) { case '0': print "\r/"; break; case '1': print "\r-"; break; case '2': print "\r\\"; break; case '3': print "\r|"; break; } print " $count"; } // generate a page test to run. $test = selectPageTest($count); $mins = ( date("U") - $start_time ) / 60; if (!QUIET && $mins > 0) { print ". $num_errors poss errors. " . floor($mins) . " mins. " . round ($count / $mins, 0) . " tests/min. " . get_class($test); // includes the current test name. } // run this test against MediaWiki, and see if the output was valid. $testname = $count; $valid = runWikiTest($test, $testname, false); // save the failed test if (!$valid) { if (QUIET) { print "\nTest: " . get_class($test) . " ; Testname: $testname\n------"; } else { print "\n"; } saveTest($test, $testname); $num_errors += 1; } // stop if we have reached max number of errors. if (defined("MAX_ERRORS") && $num_errors>=MAX_ERRORS) { break; } // stop if we have reached max number of mins runtime. if (defined("MAX_RUNTIME") && $mins>=MAX_RUNTIME) { break; } } ?>