summaryrefslogtreecommitdiff
path: root/maintenance/fuzz-tester.php
diff options
context:
space:
mode:
Diffstat (limited to 'maintenance/fuzz-tester.php')
-rw-r--r--maintenance/fuzz-tester.php2458
1 files changed, 2458 insertions, 0 deletions
diff --git a/maintenance/fuzz-tester.php b/maintenance/fuzz-tester.php
new file mode 100644
index 00000000..23c3cd7c
--- /dev/null
+++ b/maintenance/fuzz-tester.php
@@ -0,0 +1,2458 @@
+<?php
+/**
+* @package MediaWiki
+* @subpackage Maintainance
+* @author Nick Jenkins ( http://nickj.org/ ).
+* @copyright 2006 Nick Jenkins
+* @licence GNU General Public Licence 2.0
+
+Started: 18 May 2006.
+
+Description:
+ Performs fuzz-style testing of MediaWiki's parser and forms.
+
+How:
+ - Generate lots of nasty wiki text.
+ - Ask the Parser to render that wiki text to HTML, or ask MediaWiki's forms
+ to deal with that wiki text.
+ - Check MediaWiki's output for problems.
+ - Repeat.
+
+Why:
+ - To help find bugs.
+ - To help find security issues, or potential security issues.
+
+What type of problems are being checked for:
+ - Unclosed tags.
+ - Errors or interesting warnings from Tidy.
+ - PHP errors / warnings / notices.
+ - MediaWiki internal errors.
+ - Very slow responses.
+ - No response from apache.
+ - Optionally checking for malformed HTML using the W3C validator.
+
+Background:
+ Many of the wikiFuzz class methods are a modified PHP port,
+ of a "shameless" Python port, of LCAMTUF'S MANGELME:
+ - http://www.securiteam.com/tools/6Z00N1PBFK.html
+ - http://www.securityfocus.com/archive/1/378632/2004-10-15/2004-10-21/0
+
+Video:
+ There's an XviD video discussing this fuzz tester. You can get it from:
+ http://files.nickj.org/MediaWiki/Fuzz-Testing-MediaWiki-xvid.avi
+
+Requirements:
+ To run this, you will need:
+ - Command-line PHP5, with PHP-curl enabled (not all installations have this
+ enabled - try "apt-get install php5-curl" if you're on Debian to install).
+ - the Tidy standalone executable. ("apt-get install tidy").
+
+Optional:
+ - If you want to run the curl scripts, you'll need standalone curl installed
+ ("apt-get install curl")
+ - For viewing the W3C validator output on a command line, the "html2text"
+ program may be useful ("apt-get install html2text")
+
+Saving tests and test results:
+ Any of the fuzz tests which find problems are saved for later review.
+ In order to help track down problems, tests are saved in a number of
+ different formats. The default filename extensions and their meanings are:
+ - ".test.php" : PHP script that reproduces just that one problem using PHP-Curl.
+ - ".curl.sh" : Shell script that reproduces that problem using standalone curl.
+ - ".data.bin" : The serialized PHP data so that this script can re-run the test.
+ - ".info.txt" : A human-readable text file with details of the field contents.
+
+Wiki configuration for testing:
+ You should make some additions to LocalSettings.php in order to catch the most
+ errors. Note this configuration is for **TESTING PURPOSES ONLY**, and is IN NO
+ WAY, SHAPE, OR FORM suitable for deployment on a hostile network. That said,
+ personally I find these additions to be the most helpful for testing purposes:
+
+ // --------- Start ---------
+ // Everyone can do everything. Very useful for testing, yet useless for deployment.
+ $wgGroupPermissions['*']['autoconfirmed'] = true;
+ $wgGroupPermissions['*']['block'] = true;
+ $wgGroupPermissions['*']['bot'] = true;
+ $wgGroupPermissions['*']['delete'] = true;
+ $wgGroupPermissions['*']['deletedhistory'] = true;
+ $wgGroupPermissions['*']['deleterevision'] = true;
+ $wgGroupPermissions['*']['editinterface'] = true;
+ $wgGroupPermissions['*']['hiderevision'] = true;
+ $wgGroupPermissions['*']['import'] = true;
+ $wgGroupPermissions['*']['importupload'] = true;
+ $wgGroupPermissions['*']['minoredit'] = true;
+ $wgGroupPermissions['*']['move'] = true;
+ $wgGroupPermissions['*']['patrol'] = true;
+ $wgGroupPermissions['*']['protect'] = true;
+ $wgGroupPermissions['*']['proxyunbannable'] = true;
+ $wgGroupPermissions['*']['renameuser'] = true;
+ $wgGroupPermissions['*']['reupload'] = true;
+ $wgGroupPermissions['*']['reupload-shared'] = true;
+ $wgGroupPermissions['*']['rollback'] = true;
+ $wgGroupPermissions['*']['siteadmin'] = true;
+ $wgGroupPermissions['*']['trackback'] = true;
+ $wgGroupPermissions['*']['unwatchedpages'] = true;
+ $wgGroupPermissions['*']['upload'] = true;
+ $wgGroupPermissions['*']['userrights'] = true;
+ $wgGroupPermissions['*']['renameuser'] = true;
+ $wgGroupPermissions['*']['makebot'] = true;
+ $wgGroupPermissions['*']['makesysop'] = true;
+
+ // Enable weird and wonderful options:
+ // Increase default error reporting level.
+ error_reporting (E_ALL); // At a later date could be increased to E_ALL | E_STRICT
+ $wgBlockOpenProxies = true; // Some block pages require this to be true in order to test.
+ $wgEnableUploads = true; // enable uploads.
+ //$wgUseTrackbacks = true; // enable trackbacks; However this breaks the viewPageTest, so currently disabled.
+ $wgDBerrorLog = "/root/mediawiki-db-error-log.txt"; // log DB errors, replace with suitable path.
+ $wgShowSQLErrors = true; // Show SQL errors (instead of saying the query was hidden).
+
+ // Install & enable Parser Hook extensions to increase code coverage. E.g.:
+ require_once("extensions/ParserFunctions/ParserFunctions.php");
+ require_once("extensions/Cite/Cite.php");
+ require_once("extensions/inputbox/inputbox.php");
+ require_once("extensions/Sort/Sort.php");
+ require_once("extensions/wikihiero/wikihiero.php");
+ require_once("extensions/CharInsert/CharInsert.php");
+ require_once("extensions/FixedImage/FixedImage.php");
+
+ // Install & enable Special Page extensions to increase code coverage. E.g.:
+ require_once("extensions/Cite/SpecialCite.php");
+ require_once("extensions/Filepath/SpecialFilepath.php");
+ require_once("extensions/Makebot/Makebot.php");
+ require_once("extensions/Makesysop/SpecialMakesysop.php");
+ require_once("extensions/Renameuser/SpecialRenameuser.php");
+ require_once("extensions/LinkSearch/LinkSearch.php");
+ // --------- End ---------
+
+ Also add/change this in AdminSettings.php:
+ // --------- Start ---------
+ $wgEnableProfileInfo = true;
+ $wgDBserver = "localhost"; // replace with DB server hostname
+ // --------- End ---------
+
+Usage:
+ Run with "php fuzz-tester.php".
+ To see the various command-line options, run "php fuzz-tester.php --help".
+ To stop the script, press Ctrl-C.
+
+Console output:
+ - If requested, first any previously failed tests will be rerun.
+ - Then new tests will be generated and run. Any tests that fail will be saved,
+ and a brief message about why they failed will be printed on the console.
+ - The console will show the number of tests run, time run, number of tests
+ failed, number of tests being done per minute, and the name of the current test.
+
+TODO:
+ Some known things that could improve this script:
+ - Logging in with cookie jar storage needed for some tests (as there are some
+ pages that cannot be tested without being logged in, and which are currently
+ untested - e.g. Special:Emailuser, Special:Preferences, adding to Watchist).
+ - Testing of Timeline extension (I cannot test as ploticus has/had issues on
+ my architecture).
+
+*/
+
+/////////////////////////// COMMAND LINE HELP ////////////////////////////////////
+
+// This is a command line script, load MediaWiki env (gives command line options);
+include('commandLine.inc');
+
+// if the user asked for an explanation of command line options.
+if ( isset( $options["help"] ) ) {
+ print <<<ENDS
+MediaWiki $wgVersion fuzz tester
+Usage: php {$_SERVER["SCRIPT_NAME"]} [--quiet] [--base-url=<url-to-test-wiki>]
+ [--directory=<failed-test-path>] [--include-binary]
+ [--w3c-validate] [--delete-passed-retests] [--help]
+ [--user=<username>] [--password=<password>]
+ [--rerun-failed-tests] [--max-errors=<int>]
+ [--max-runtime=<num-minutes>]
+ [--specific-test=<test-name>]
+
+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: '
+ "&#0000039;", // Long UTF-8 Unicode encoding
+ "&#39;", // dec version.
+ "&#x27;", // hex version.
+ "&#xA7;", // malformed hex variant, MSB not zero.
+
+ // Different ways of saying: "
+ "&#0000034;", // Long UTF-8 Unicode encoding
+ "&#34;",
+ "&#x22;", // hex version.
+ "&#xA2;", // malformed hex variant, MSB not zero.
+
+ // Different ways of saying: <
+ "<",
+ "&#0000060", // Long UTF-8 Unicode encoding without semicolon (Mediawiki wants the colon)
+ "&#0000060;", // Long UTF-8 Unicode encoding with semicolon
+ "&#60;",
+ "&#x3C;", // hex version.
+ "&#xBC;", // malformed hex variant, MSB not zero.
+ "&#x0003C;", // mid-length hex version
+ "&#X00003C;", // slightly longer hex version, with capital "X"
+
+ // Different ways of saying: >
+ ">",
+ "&#0000062;", // Long UTF-8 Unicode encoding
+ "&#62;",
+ "&#x3E;", // hex version.
+ "&#xBE;", // malformed variant, MSB not zero.
+
+ // Different ways of saying: [
+ "&#0000091;", // Long UTF-8 Unicode encoding
+ "&#91;",
+ "&#x5B;", // hex version.
+
+ // Different ways of saying: {{
+ "&#0000123;&#0000123;", // Long UTF-8 Unicode encoding
+ "&#123;&#123;",
+ "&#x7B;&#x7B;", // hex version.
+
+ // Different ways of saying: |
+ "&#0000124;", // Long UTF-8 Unicode encoding
+ "&#124;",
+ "&#x7C;", // hex version.
+ "&#xFC;", // malformed hex variant, MSB not zero.
+
+ // a "lignature" - http://www.robinlionheart.com/stds/html4/spchars#ligature
+ "&zwnj;"
+ );
+
+ // 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
+ "&#x09;", // tab
+ "&#x0A;", // newline
+ "&#x0D;", // carriage return
+ "\0", // null character
+ " &#14; ", // spaces and meta characters
+ "'';!--\"<XSS>=&{()}", // compact injection of XSS & SQL tester
+
+ // various NULL fields
+ "%00",
+ "&#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|+",
+ "<noinclude>",
+ "</noinclude>",
+ " \302\273",
+ " :",
+ " !",
+ " ;",
+ "\302\253",
+ "[[category:",
+ "?=",
+ "(",
+ ")",
+ "]]]",
+ "../",
+ "{{{{",
+ "}}}}",
+ "[[Special:",
+ "<includeonly>",
+ "</includeonly>",
+ "<!--MWTEMPLATESECTION=",
+ '<!--MWTOC-->',
+
+ // 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":
+ "<math>",
+ "</math>",
+
+ // Parser extension functions:
+ "{{#expr:",
+ "{{#if:",
+ "{{#ifeq:",
+ "{{#ifexist:",
+ "{{#ifexpr:",
+ "{{#switch:",
+ "{{#time:",
+ "}}",
+
+ // references table for the Cite extension.
+ "<references/>",
+
+ // Internal Parser tokens - try inserting some of these.
+ "UNIQ25f46b0524f13e67NOPARSE",
+ "UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002",
+ "\x07UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002-QINU",
+
+ // Inputbox extension:
+ "<inputbox>\ntype=search\nsearchbuttonlabel=\n",
+ "</inputbox>",
+
+ // charInsert extension:
+ "<charInsert>",
+ "</charInsert>",
+
+ // wikiHiero extension:
+ "<hiero>",
+ "</hiero>",
+
+ // Image gallery:
+ "<gallery>",
+ "</gallery>",
+
+ // FixedImage:
+ "<fundraising/>",
+
+ // Timeline extension: currently untested.
+
+ // Nowiki:
+ "<nOwIkI>",
+ "</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 "</". wikiFuzz::$types[$i] . ">";
+ 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<xyz>", 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 = "<?php\n"
+ . "\$params = " . var_export(escapeForCurl($test->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, "<b>Warning</b>: " ) !== false
+ || strpos($wiki_preview, "<b>Fatal error</b>: ") !== false
+ || strpos($wiki_preview, "<b>Notice</b>: " ) !== false
+ || strpos($wiki_preview, "<b>Error</b>: " ) !== false ) {
+ $error = substr($wiki_preview, strpos($wiki_preview, "</b>:") + 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;
+ }
+}
+
+?>