| <?php |
| |
| /** |
| * [Bug 474734] [security] xss vulnerability on mmt website |
| * |
| * SQL injection is a code injection technique, |
| * used to attack data-driven applications, in which malicious |
| * SQL statements are inserted into an entry field for execution |
| * (e.g. to dump the database contents to the attacker). |
| * |
| * Cross-Site Scripting (XSS) vulnerabilities are a type of |
| * computer security vulnerability typically found in Web applications. |
| * XSS vulnerabilities enable attackers to inject client-side script |
| * into Web pages viewed by other users. |
| * |
| * Given the severity of this bug, we added an exit() at the top |
| * of this file to stop it from being executed on our servers. |
| * |
| * The owner(s) of this website should review every request to MYSQL before |
| * removing the exit() on this page. |
| * |
| */ |
| exit(); |
| |
| /* |
| * A plugin/feature version checking auditor with support for cli and www mode. |
| */ |
| define("LOGGER_FAIL", 0); |
| define("LOGGER_OK", 1); |
| define("LOGGER_INFO", 2); |
| define("LOGGER_SQL", 10); |
| |
| require_once ("buildServer-common.php"); |
| |
| $verbosity = 0; |
| $cli = isset($argv); // $argv is only defined when running in cli mode |
| |
| /* in cli mode, you'll need to have the includes directory (with db.php) in the current directory, then invoke this script directly, rather than one of the placeholders */ |
| if ($cli) |
| { |
| $require_db = "includes/db.php"; |
| |
| $dirs = array(); |
| for ($i = 1; $i < sizeof($argv); $i++) |
| { |
| $m = null; |
| if (preg_match("/^-(v+)$/", $argv[$i], $m)) |
| { |
| $verbosity = strlen($m[1]); |
| } |
| else if (is_dir($argv[$i])) |
| { |
| $dirs[] = $argv[$i]; |
| } |
| else |
| { |
| print "$argv[$i] wasn't a directory or a verbosity flag, I don't know what to do with it!\n"; |
| exit(-4); |
| } |
| } |
| |
| $html = false; |
| } |
| else |
| { |
| require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/app.class.php"); require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/nav.class.php"); require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/menu.class.php"); $App = new App(); $Nav = new Nav(); $Menu = new Menu(); include($App->getProjectCommon()); |
| |
| $require_db = $_SERVER["DOCUMENT_ROOT"] . "/modeling/includes/db.php"; |
| |
| if ((isset($_GET["debug"]) && preg_match("/(^\d+$)/", $_GET["debug"], $m)) || (isset($_GET["verbosity"]) && preg_match("/(^\d+$)/", $_GET["verbosity"], $m))) |
| { |
| $verbosity = $m[1]; |
| } |
| |
| /* here we inherit $dirs from the placeholder file that includes us */ |
| if (isset($_GET["branch"])) |
| { |
| $b = $_GET["branch"]; |
| if (isset($dirs[$b])) |
| { |
| $dirs = array($dirs[$b]); |
| } |
| else |
| { |
| header("Content-type: text/html"); |
| print "<pre>$b wasn't a valid branch, please try again with a valid branch, such as:\n"; |
| print join("\n", preg_replace("/^(.+)$/", "- <a href=\"?branch=$1\">$1</a>", array_keys($dirs))) . "</pre>\n"; |
| exit(-5); |
| } |
| } |
| |
| foreach ($dirs as $dir) |
| { |
| if (!is_dir($dir)) |
| { |
| print "$dir wasn't a directory, please amend the definition of \$dirs in {$_SERVER["PHP_SELF"]}\n"; |
| exit(-4); |
| } |
| } |
| |
| if (isset($_GET["html"])) |
| { |
| $html = true; |
| header("Content-type: text/html"); |
| } |
| else |
| { |
| $html = false; |
| header("Content-type: text/plain"); |
| } |
| } |
| |
| if (!isset($dirs) || !is_array($dirs) || sizeof($dirs) == 0) |
| { |
| print "I need to know what project/component you'd like me to audit!\n\n"; |
| if ($cli) |
| { |
| print "For example:\n"; |
| print "\tphp $argv[0] org.eclipse.emf\n\n"; |
| print "You may also add -v or -vv for greater verbosity.\n"; |
| } |
| else |
| { |
| print "Make sure \$dirs is defined in {$_SERVER["PHP_SELF"]} and contains a list of branches => directories you'd like to process.\n"; |
| } |
| exit(-1); |
| } |
| |
| ob_start(); |
| |
| foreach ($dirs as $dir) |
| { |
| $r = trim(file_get_contents("$dir/CVS/Repository")); |
| $m = null; |
| if (preg_match("#^(org\.eclipse\.[^/]+)(?:/([^/]+))?$#", $r, $m)) |
| { |
| $proj = $m[1]; |
| $com = (isset($m[2]) ? $m[2] : ""); |
| logger(LOGGER_INFO, "found $proj/$com\n"); |
| } |
| else |
| { |
| $msg = "I couldn't figure out what project/component that is, quitting...\n"; |
| $msg .= " --> make sure $dir/CVS/Repository exists and is correct\n"; |
| logger(LOGGER_FAIL, $msg); |
| exit(-3); |
| } |
| |
| $branch = "HEAD"; |
| if (is_file("$dir/CVS/Tag")) |
| { |
| $branch = preg_replace("/^./", "", trim(file_get_contents("$dir/CVS/Tag"))); |
| logger(LOGGER_INFO, "detected branch $branch\n"); |
| } |
| else |
| { |
| logger(LOGGER_INFO, "no CVS/Tag file found, assuming HEAD branch\n"); |
| } |
| |
| $branchfails = 0; |
| $issues = array(); |
| require_once($require_db); |
| foreach (glob("$dir/{plugins,tests,examples}/org.eclipse.*", GLOB_BRACE) as $plugdir) |
| { |
| $plugin = basename($plugdir); |
| $type = preg_replace("#^.+/([^/]+)/$plugin$#", "$1", $plugdir); //plugins, tests, or examples |
| |
| $deps = array(); |
| $checked = array(); |
| $queue = array($plugin); |
| $versions = array(); |
| $lastversions = array(); |
| $vcache = array(); |
| $fails = 0; |
| |
| if (preg_match("/-feature$/", $plugin)) |
| { |
| logger(LOGGER_INFO, "skipping $plugdir as it looks like a feature, not a plugin\n\n"); |
| continue; |
| } |
| |
| if ($tmp = plugin_version($plugdir)) |
| { |
| $vanityname = preg_replace("/\.qualifier$/", "", $tmp); |
| $version = convert_version($tmp); |
| |
| $lastdir = preg_replace("#cvssrc(?:_branches)?(/" . basename($dir) . ")#", "cvssrc_branches$1-latest", $dir); |
| $lastplugdir = preg_replace("#cvssrc(?:_branches)?(/" . basename($dir) . ")#", "cvssrc_branches$1-latest", $plugdir); |
| if (!is_dir($lastplugdir)) |
| { |
| logger(LOGGER_INFO, "$lastplugdir does not exist, skipping all checks on $plugdir\n"); |
| continue; |
| } |
| $lastversion = preg_replace("/\.qualifier$/", "", plugin_version($lastplugdir)); |
| if (is_dir($lastplugdir)) |
| { |
| logger(LOGGER_INFO, "Last released version of $lastplugdir is $lastversion\n"); |
| } |
| |
| $p = ($com == "" ? $proj : "$proj/$com"); |
| |
| /* it's quite possible for us to end up with changes in a branch (say R2_1_maintenance) with the plugin only being versioned at 2.1.0, of course 2.1.0 wasn't released from the R2_1_maintenance branch, so we'll never find it there |
| * keep trying to find the last build in progressively less picky ways... */ |
| $lastbuild = array( |
| "(SELECT MAX(`buildtime`) FROM `releases` WHERE `project` = '$proj' AND `component` = '$com' AND `branch` = '$branch' AND `type` = 'R')", |
| "(SELECT `buildtime` FROM `releases` WHERE `project` = '$proj' AND `component` = '$com' AND `vanityname` = '$vanityname' AND `type` = 'R')", |
| "(SELECT MIN(`buildtime`) FROM `releases` WHERE `project` = '$proj' AND `component` = '$com' AND `type` = 'R')", |
| "'2000-01-01'" |
| ); |
| $query = "SELECT `bugid`, `cvsname`, `date` FROM `cvsfiles` NATURAL JOIN `commits` NATURAL LEFT JOIN `bugs` WHERE `project` = '$proj' AND `branch` = '$branch' AND `cvsname` LIKE '/cvsroot/modeling/$p/$type/$plugin/%' AND `date` >= COALESCE(" . join(", ", $lastbuild) . ")"; |
| $result = wmysql_query($query); |
| if (mysql_num_rows($result) == 0) |
| { |
| logger(LOGGER_SQL, "$query\n"); |
| logger(LOGGER_OK, "no commits found >= $p/$type/$plugin/ $vanityname\n"); |
| } |
| else |
| { |
| $plugtext = $plugin; |
| if ($html) |
| { |
| $query = "SELECT MIN(`date`) FROM `cvsfiles` NATURAL JOIN `commits` WHERE `project` = '$proj' AND `branch` = '$branch' AND `cvsname` LIKE '/cvsroot/modeling/$p/$type/$plugin/%' AND `date` >= COALESCE(" . join(", ", $lastbuild) . ")"; |
| $result2 = wmysql_query($query); |
| logger(LOGGER_SQL, "$query\n"); |
| |
| $row2 = mysql_fetch_row($result2); |
| $plugtext = "<a href=\"http://www.eclipse.org/modeling/emf/searchcvs.php?q=" . urlencode("file: $p/$type/$plugin/ startdate: $row2[0] branch: $branch") . "\">$plugin</a>"; |
| } |
| $msg = mysql_num_rows($result) . " commit(s) found >= $plugtext $lastversion, currently at $vanityname\n"; |
| while ($row = mysql_fetch_row($result)) |
| { |
| $msg .= " ref: http://www.eclipse.org/modeling/emf/searchcvs.php?q="; |
| if ($row[0]) |
| { |
| $msg .= "$row[0]\n"; |
| } |
| else |
| { |
| $msg .= urlencode("file: $row[1] startdate: $row[2] enddate: $row[2]") . "\n"; |
| } |
| } |
| if ($version > convert_version($lastversion)) |
| { |
| logger(LOGGER_OK, $msg); |
| } |
| else |
| { |
| $msg .= " --> $plugin must be > $vanityname\n"; |
| logger(LOGGER_FAIL, $msg); |
| $fails++; |
| } |
| } |
| |
| while (sizeof($queue) > 0) |
| { |
| foreach (array_keys($queue) as $z) |
| { |
| $actual = preg_replace("/-feature$/", "", $queue[$z]); |
| $fails += depgrep($dir, $actual, $vanityname, $plugin); |
| unset($queue[$z]); |
| } |
| } |
| |
| $f = "doc/$proj.doc-feature/feature.xml"; |
| if (is_file("$dir/$f")) |
| { |
| $deps["$proj.doc-feature"] = "$dir/$f"; |
| } |
| else |
| { |
| logger(LOGGER_INFO, "couldn't find $f\n"); |
| } |
| |
| foreach (array_keys($deps) as $z) |
| { |
| $versions[$z] = convert_version(feature_version($deps[$z])); |
| $lastversions[$z] = convert_version(feature_version(preg_replace("#cvssrc(?:_branches)?(/" . basename($dir) . ")#", "cvssrc_branches$1-latest", $deps[$z]))); |
| } |
| //print_r($deps); |
| |
| $f = "doc/$proj.doc/build.xml"; |
| if (is_file("$dir/$f")) |
| { |
| $versions["$proj.doc"] = doc_version($dir, $proj); |
| $lastversions["$proj.doc"] = doc_version($lastdir, $proj); |
| } |
| else |
| { |
| logger(LOGGER_INFO, "couldn't find $f\n"); |
| } |
| |
| if ($version > convert_version($lastversion)) |
| { |
| foreach (array_keys($versions) as $z) |
| { |
| if (preg_match("/\.doc(?:-feature)?$/", $z) && $version - $versions[$z] <= 999) |
| { |
| $v1 = $vcache[$versions[$z]]; |
| $v2 = $vcache[$version]; |
| logger(LOGGER_OK, "$z (" . preg_replace("/\.\d+$/", "", $v1) . ") >= $plugin (" . preg_replace("/\.\d+$/", "", $v2) . "), ignoring service versions (actual versions were $v1 and $v2, respectively)\n"); |
| } |
| else if ($versions[$z] > $lastversions[$z]) |
| { |
| if ($versions[$z] - $lastversions[$z] == 1 || $versions[$z] - $lastversions[$z] == 1000) |
| { |
| logger(LOGGER_OK, "$z last released at " . $vcache[$lastversions[$z]] . ", currently at " . $vcache[$versions[$z]] . "\n"); |
| } |
| else if (!isset($vcache[$lastversions[$z]]) || !$vcache[$lastversions[$z]]) // if feature is new, no previous version will exist |
| { |
| logger(LOGGER_INFO, "$z appears to be new, skipping checks\n"); |
| } |
| else |
| { |
| $msg = "$z last released at " . $vcache[$lastversions[$z]] . ", currently at " . $vcache[$versions[$z]] . "\n"; |
| $msg .= " --> $z must be incremented only once per release cycle (last released at " . $vcache[$lastversions[$z]] . ", currently at " . $vcache[$versions[$z]] . ")\n"; |
| logger(LOGGER_FAIL, $msg); |
| $fails++; |
| } |
| } |
| else |
| { |
| $msg = "$z last released at " . $vcache[$lastversions[$z]] . ", currently at " . $vcache[$versions[$z]] . ", but $plugin has been incremented\n"; |
| $msg .= " --> $z must be > " . $vcache[$versions[$z]] . "\n"; |
| logger(LOGGER_FAIL, $msg); |
| $fails++; |
| } |
| } |
| } |
| else |
| { |
| logger(LOGGER_OK, "$plugin last released at $lastversion, currently at $vcache[$version]\n"); |
| } |
| |
| if ($fails == 0) |
| { |
| logger(LOGGER_OK, "$plugdir appears to be fine\n\n"); |
| } |
| else if ($verbosity >= LOGGER_OK) |
| { |
| print "\n"; |
| } |
| } |
| else if (!preg_match("/-feature$/", $plugdir)) |
| { |
| logger(LOGGER_INFO, "couldn't find a MANIFEST.MF for $plugdir\n"); |
| } |
| $branchfails += $fails; |
| } |
| |
| /* we don't use the logger() interface here because we always want these to show, and we don't want a prepended tag */ |
| if ($branchfails == 0) |
| { |
| print "$branch: ok\n\n"; |
| } |
| else |
| { |
| print "$branch: $branchfails failure(s): commit plugin/feature fixes to CVS, then run http://build.eclipse.org/modeling/build/updateSearchCVS.php to refresh database.\n" . |
| " Or, if you recently released an R build, update the searchcvs/cvssrc_branches/*-latest folder to the newer tag.\n"; |
| ksort($issues); |
| print join("\n", array_keys($issues)) . "\n\n"; |
| $issues = array(); |
| } |
| } |
| |
| $content = ob_get_contents(); |
| ob_end_clean(); |
| |
| if ($html) |
| { |
| print "<pre>\n"; |
| print preg_replace("#(?<!href=\")(https?://[^ \n\t]+)#", "<a href=\"$1\">$1</a>", $content); |
| print "</pre>\n"; |
| print "<p> </p>\n"; |
| print '<p align="right"><i><small><a href="http://wiki.eclipse.org/Plugin_Version_Auditing">about this tool</a></small></i></p>'."\n"; |
| } |
| else |
| { |
| print $content; |
| } |
| |
| /* find features which include or depend on the given plugin within the given directory */ |
| function depgrep($dir, $plugin, $vanityname, $origplugin) |
| { |
| global $deps, $checked, $queue; |
| |
| foreach (glob("$dir/{plugins,features,tests,examples}/*-feature/{,org.eclipse.*.sdk/}feature.xml", GLOB_BRACE) as $z) |
| { |
| $regs = null; |
| if (preg_match("/<(?:includes|plugin)[^>]+id=\"\Q$plugin\E\"[^>]+version=\"([^\"]+)\"/s", file_get_contents($z), $regs)) |
| { |
| if ($plugin === $origplugin) |
| { |
| if ($vanityname !== $regs[1] && $regs[1] !== "0.0.0") |
| { |
| $msg = "$plugin is at $vanityname, but $z wants $regs[1]\n"; |
| $msg .= " --> $plugin must be $vanityname in $z\n"; |
| logger(LOGGER_FAIL, $msg); |
| } |
| else |
| { |
| logger(LOGGER_OK, "$z wants $regs[1]\n"); |
| } |
| } |
| |
| $m = null; |
| if (preg_match("#/([^/]+)/feature\.xml$#", $z, $m)) |
| { |
| $deps[$m[1]] = $z; |
| if (!isset($checked[$m[1]])) |
| { |
| $checked[$m[1]] = true; |
| $queue[] = $m[1]; |
| } |
| } |
| } |
| } |
| } |
| |
| /* parse the feature version out of a feature.xml file */ |
| function feature_version($file) |
| { |
| $m = null; |
| if (is_file($file) && is_readable($file)) |
| { |
| if (preg_match("/<feature[^>]+version=\"([^\"]+)\"/s", file_get_contents($file), $m)) |
| { |
| return $m[1]; |
| } |
| } |
| return null; |
| } |
| |
| /* parse the bundle version out of a MANIFEST.MF file (or plugin.xml if we can't find a MANIFEST.MF) */ |
| function plugin_version($plugdir) |
| { |
| $ret = false; |
| |
| $m = null; |
| if (is_file("$plugdir/META-INF/MANIFEST.MF")) |
| { |
| if (preg_match("#^Bundle-Version:\s+(.+)$#m", file_get_contents("$plugdir/META-INF/MANIFEST.MF"), $m)) |
| { |
| $ret = $m[1]; |
| } |
| } |
| else if (is_file("$plugdir/plugin.xml")) |
| { |
| if (preg_match("#<plugin[^>]+version\s*=\s*\"([^\"]+)\"[^>]+>#s", file_get_contents("$plugdir/plugin.xml"), $m)) |
| { |
| $ret = $m[1]; |
| } |
| } |
| else |
| { |
| logger(LOGGER_FAIL, "couldn't find a MANIFEST.MF or plugin.xml for $plugdir!\n"); |
| } |
| |
| return $ret; |
| } |
| |
| /* convert a version number like 2.3.0.qualifier to 2*10^6 + 3*10^3 + 0, so they can be compared numerically |
| * also associate the two values in $vcache so the operation can be reversed easily */ |
| function convert_version($version) |
| { |
| global $vcache; |
| $num = null; |
| if ($version) |
| { |
| $version = preg_replace("/\.qualifier$/", "", $version); |
| list($major, $minor, $patch) = split("\.", $version); |
| |
| $num = ($major * pow(10, 6) + $minor * pow(10, 3) + $patch); |
| $vcache[$num] = $version; |
| } |
| return $num; |
| } |
| |
| /* we need to check 2 different files for the doc plugin, so we'll just add it's version directly */ |
| function doc_version($dir, $proj) |
| { |
| global $fail; |
| |
| $v1 = null; |
| $v2 = null; |
| |
| $m = null; |
| if (preg_match("#<property\s+name=\"pluginVersion\"\s+value=\"([^\"]+)\"\s*/>#", file_get_contents("$dir/doc/$proj.doc/build.xml"), $m)) |
| { |
| $v1 = $m[1]; |
| } |
| |
| $v2 = plugin_version("$dir/doc/$proj.doc"); |
| |
| if ($v1 !== null && $v2 !== null) |
| { |
| if ("$v1.qualifier" === $v2 || $v1 === $v2) |
| { |
| return convert_version($v2); |
| } |
| else |
| { |
| $msg = "$dir/doc/$proj.doc is not internally consistent! ($v1 != $v2)\n"; |
| $msg .= " --> the versions in build.xml (currently $v1) and META-INF/MANIFEST.MF (currently $v2) must match\n"; |
| logger(LOGGER_FAIL, $msg); |
| } |
| } |
| else |
| { |
| $msg = "couldn't find all version information for $dir/doc/$proj.doc!\n"; |
| if ($v1 === null) |
| { |
| $msg .= " --> build.xml must have a <property name=\"pluginVersion\" value=\"x.x.x\"/>\n"; |
| } |
| if ($v2 === null) |
| { |
| $msg .= " --> META-INF/MANIFEST.MF must have a Bundle-Version: x.x.x\n"; |
| } |
| logger(LOGGER_FAIL, $msg); |
| } |
| } |
| |
| /* wrapper for output, prepends a label and controls output based on the verbosity level */ |
| function logger($type, $msg) |
| { |
| global $verbosity, $issues; |
| |
| $labels = array( |
| LOGGER_FAIL => "[ fail ]", |
| LOGGER_OK => "[ ok ]", |
| LOGGER_INFO => "[ info ]", |
| LOGGER_SQL => "[ sql ]", |
| ); |
| |
| if ($type <= $verbosity) |
| { |
| print $labels[$type] . " $msg"; |
| $m = null; |
| if (preg_match_all("/^( -->.+)$/m", $msg, $m)) |
| { |
| foreach ($m[1] as $z) |
| { |
| $issues[$z] = true; |
| } |
| } |
| } |
| } |
| ?> |