http://cyberspark.net/webmasters
This is a standalone, not to be confused with the Cloudkick 'agent' plugin
version, which has some similar functionality.
/help
Describes what options are available.
/path
Reports the filesystem path to this script. Does nothing else.
/report
/report=n
Spiders the site and builds a baseline set of hashes or lengths. Does nothing active.
If "n" (a number) is present the site is spidered only to this maximum depth.
It's best to start with 1 or 2 so as not to overburden your server.
/report=n&base=xxxxxxx
Prepares a report using "xxxxxxx" as the base subdirectory and a depth of "n"
the base is relative to the directory containing this file and must start and end with '/'
/exclude=aaa,bbb,ccc
/except=aaa,bbb,ccc
/ignore=aaa,bbb,ccc
Causes file(s) or directory(ies) containing strings 'aaa' or 'bbb' or 'ccc' to be ignored
/wordpress
Causes certain subdirectories/files to be ignored - good for wordpress installations
/disk=dev
Reports space on specific device/volume "/dev" whatever you specify
/disk=none
Turns off the disk capacity and space checking. (actually, just makes it fail)
/cpu=n
Artificially waits 'n' seconds of wait time to measure overall CPU load
REPAIR CAPABILITIES ARE NOT PRESENT IN THIS SCRIPT.
Create a directory /cyberspark within the docroot of the web server
Make this directory world-writeable (chmod 777 or chmod a+rwx )
Within your Apache (or other) web server configuration, add:
\n";
$logEntry .= $string."\r\n";
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//// ifGetOrPost()
//// Looks for a parameter (in $_GET) or input (in $_POST) by name.
//// Returns 'null' if none found.
function ifGetOrPost($name) {
if (isset($_GET[$name])) {
return $_GET[$name];
}
if (isset($_POST[$name])) {
return $_POST[$name];
}
return null;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//// paramValue()
//// Gets the integer value of a GET or POST parameter or input.
function paramValue($paramName) {
if (isset($_GET[$paramName])) {
try {
if (($md = intval($_GET[$paramName])) > 0)
return $md;
}
catch (Exception $pvx) {
}
}
if (isset($_POST[$paramName])) {
try {
if (($md = intval($_POST[$paramName])) > 0)
return $md;
}
catch (Exception $pvx) {
}
}
// No parameter, return "0" which is "failure"
return 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//// paramString()
//// Gets the string value of a GET or POST parameter or input.
function paramString($paramName) {
if (isset($_GET[$paramName])) {
return $_GET[$paramName];
}
if (isset($_POST[$paramName])) {
return $_POST[$paramName];
}
// No parameter, return "" which is usually ignorable
return "";
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//// nValue()
//// Gets the value of the 'report=n' parameter. (Spidering depth)
function nValue() {
// Several options allow "=n" to specify the spidering depth. Such as:
// cyberspark-utility.php?report=3
// This function looks for the "n" (which means really the value of the
// parameter "repair" "remove" or "report") and sets $maxDepth accordingly.
// $maxDepth remains untouched if there is no "=n"
global $maxDepth;
if (($md = paramValue('report')) > 0) {
return $md;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//// stripos_array()
//// Searches an array of strings to see if a particular string is present.
//// Returns only 'true' or 'false'
function stripos_array($haystack, $needleArray) {
foreach ($needleArray as $needle) {
if (stripos($haystack, $needle) !== false) {
return true;
}
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//// spiderThis()
//// Performs the main spidering function.
function spiderThis($baseDirectory, $maxDepth)
{
global $depth; // current depth of spidering
global $results; // current 'signatures' of files
global $status; // previous 'signatures' of files
global $newFiles; // number of new files found during this scan
global $newSizes; // number of files that changed size
global $newSuspect; // number of files containing 'suspect' PHP functions eval() base64_decode() etc.
global $maxFileSize;
global $phpFiles;
global $totalFiles;
global $logEntry;
global $exclude;
global $checkSignatures;
global $myName; // "just the filename" of this script
// Be sure we're working with a directory
if (is_dir($baseDirectory) && ($maxDepth>$depth)) {
try {
$depth++;
$dirContents = dir($baseDirectory);
// Run through this directory
// WARNING: Under circumstances I have been unable to understand, sometimes the
// 'read()' below just fails and the PHP script stops executing. No exception
// is caught here. The script just dies. It may be related to directories
// containing a zero-length file, or perhaps some filesystem corruption. I have
// not really found the cause, nor a way to avoid it. [SKY 2012-03-02]
while (($entry = $dirContents->read()) !== false) {
// Get an entry from the directory
$thisEntry = $baseDirectory.$entry;
// Check whether this file or directory is to be excluded from scanning
if ((count($exclude) > 0) && stripos_array($thisEntry, $exclude)) {
echoAndLog ("Excluding: $thisEntry ");
continue;
}
// Look for '.' or '..' and ignore these entries
if ((strcmp('.',$entry)<>0) && (strcmp('..',$entry)<>0) && is_dir($thisEntry)) {
// Next entry is a directory, dive into it
spiderThis($thisEntry."/", $maxDepth);
}
else if (is_link($thisEntry)) {
// Skip a 'link' (not directory, not file) avoids recursion, but might
// miss something that you want to examine. You can always set up separate
// scan that uses 'base=' to target the actual directory you want to examine.
// (This also means you can't scan outside the web space. Guess you could
// regard this as a "feature.")
}
else if (is_file($thisEntry)) {
// It's a file
$stat = stat($thisEntry);
$totalFiles++;
// MD5: use this to record md5 hashes of files rather than lengths
// but this will be much more time-consuming than just looking at lengths.
// $filemd5s[] = md5_file($thisentry);
// Record file lengths
$fileSize = $stat['size'];
$results[$thisEntry] = $fileSize;
if ($status[$thisEntry] <> $fileSize) {
if ($status[$thisEntry] == 0) {
// New file
// Report the presence of a new file
echoAndLog("New file: ".$status[$thisEntry]." -> [".$fileSize."] $thisEntry ");
$newFiles++;
}
else {
$len = strlen($thisEntry);
if(($len > 3) and !(strripos($thisEntry, "log") == ($len-3)) and !(strripos($thisEntry, SPIDERFILE) == ($len-strlen(SPIDERFILE))) and !(strripos($thisEntry, STOREFILE) == ($len-strlen(STOREFILE)))) {
// Note: Ignore files ending in "log"
// Note: Ignore files ending with the name of our data file
// Otherwise, note a changed size
echoAndLog("New size: ".$status[$thisEntry]." -> [$fileSize] $thisEntry ");
$newSizes++;
}
}
}
// And scan PHP/HTML/HTM/JS files for eval and gzinflate and base64
// Note: skips self ($myName)
try {
$len = strlen($thisEntry);
if(($len > 4) and ((strripos($thisEntry, ".php") == ($len-4))
|| (strripos($thisEntry, ".htm") == ($len-4))
|| (strripos($thisEntry, ".html") == ($len-5))
|| (strripos($thisEntry, ".js") == ($len-3))
) and (stripos($thisEntry, '/'.$myName)===false)) {
$thisFile = fopen($thisEntry,"r");
$thisContents = fread($thisFile, $maxFileSize);
fclose($thisFile);
$phpFiles++;
if (strlen($thisContents) > 0) {
// check for PHP and javascript active code
foreach ($checkSignatures as $signature=>$value) {
if (stripos($thisContents, $signature) !== false) {
echoAndLog("Found '$signature' $value: -> $thisEntry");
$newSuspect++;
}
}
}
}
}
catch (Exception $egbx) {
}
// Remove from 'previous status' array. When we finish, anything left in
// this array will be a file that has disappeared.
unset($status[$thisEntry]);
}
// Otherwise ignore ("." and ".." for instance)
}
$depth--;
}
catch (Exception $x) {
echoAndLog( "Exception: ".$x->getMessage());
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// MAIN script - this is executed when the file is invoked by HTTP GET or HTTP PUT
// Get the filesystem path to this file (only the PATH) including an ending "/"
$path = substr(__FILE__,0,strrpos(__FILE__,'/',0)+1); // including the last "/"
$myName = $_SERVER['SCRIPT_FILENAME'];
$myName = substr($myName, strrpos($myName, '/')+1);
// Set up to calculate CPU utilization
$cpuStat = ifGetOrPost('cpu');
if (!isset($cpuStat) || $cpuStat == null || $cpuStat == 0) {
unset($cpuStat);
}
if (isset($cpuStat)) {
$statFile = file_get_contents('/proc/stat');
try{
$statLines = explode("\n", $statFile);
foreach ($statLines as $statLine) {
$stat = explode(' ', $statLine);
if (strcasecmp('cpu', $stat[0]) == 0) {
while ($stat[1]=='') {
array_splice($stat, 1, 1);
}
$cpu = $stat[1];
}
}
}
catch (Exception $slx) {
}
$cpuStart = time(); // this is seconds
}
// 'disk=none' or 'disk=filesystembase
if (($disk = ifGetOrPost('disk')) != null) {
if (strcasecmp($disk, 'none') == 0) {
$fsPath = null; // causes silent fail
}
else {
$fsPath = '/' . $disk; // note ADD LEADING '/' to help find mount point because '/' can't be in the URL !
}
}
// Disk/filesystem space used
$diskTotalSpace = 0;
$diskUsedSpace = 0;
if (($fsPath != null) && is_dir($fsPath)) {
if (function_exists('disk_total_space')) {
$diskTotalSpace = disk_total_space($fsPath);
}
if (function_exists('disk_free_space')) {
$diskUsedSpace = $diskTotalSpace - disk_free_space($fsPath);
}
if ($diskTotalSpace > 0) {
// Note: $diskPercentUsed is only defined if total space > 0
$diskPercentUsed = $diskUsedSpace/$diskTotalSpace * 100;
}
}
header('Content-Type: text/html; charset=UTF-8');
// '/help' - - - - - - - - - - - -
if (isset($_GET['help']) || isset($_POST['help'])) {
// /help
echo "\n
Produces a report on status of all PHP files. If 'n' (a number) is present the site is spidered only to this maximum depth. (Start by using 1 or 2 for the depth until you know how quickly your server can perform this task.)
Run several times to establish a baseline, then in the future you can watch for any significant changes.
If base=xxxxxxx is specified then the report starts at directory /xxxxxxx with respect to where the CyberSpark PHP is located
/ignore=aaa,bbb,cccExcludes/ignores directories and files containing any of the specified strings ('aaa' 'bbb' 'ccc' separated by commas).
/disk=devReports disk (filesystem) usage and capacity if a 'dev' is specified or Skips disk (filesystem) capacity reporting if =none is specified. Common usages are disk=dev/sda or disk=home/username
\n\n"; return; } // '/path' - - - - - - - - - - - - - if (isset($_GET['path'])) { // /path echo "\r\n\r\n
CyberSpark local agent report
Base directory is $fullPath
My name is $myName
Spidering depth will be $maxDepth
Any changes reported below are 'since the last time this script was run.'
Do not stop this script or leave this page until it finishes.