*/ /** * The default language setting if none is set/retrievable * */ define ('PHPSECINFO_LANG_DEFAULT', 'en'); /** * a general version string to differentiate releases * */ define ('PHPSECINFO_VERSION', '0.2.2'); /** * a YYYYMMDD date string to indicate "build" date * */ define ('PHPSECINFO_BUILD', '20080723'); /** * Homepage for phpsecinfo project * */ define ('PHPSECINFO_URL', 'http://phpsecinfo.com'); /** * The base folder where views are stored. Include trailing slash * */ define('PHPSECINFO_VIEW_DIR_DEFAULT', 'View/'); /** * The default format, used to load the proper view. */ define('PHPSECINFO_FORMAT_DEFAULT', 'Html'); /** * The base directory, used to resolve requires and includes */ define('PHPSECINFO_BASE_DIR', dirname(__FILE__)); /** * This is the main class for the phpsecinfo system. It's responsible for * dynamically loading tests, running those tests, and generating the results * output * * Example: * * * * * * If you want to capture the output, or just grab the test results and display them * in your own way, you'll need to do slightly more work. * * Example: * * require_once(PHPSECINFO_BASE_DIR.'/PhpSecInfo.php'); * // instantiate the class * $psi = new PhpSecInfo(); * * // load and run all tests * $psi->loadAndRun(); * * // grab the results as a multidimensional array * $results = $psi->getResultsAsArray(); * echo "
"; echo print_r($results, true); echo "
"; * * // grab the standard results output as a string * $html = $psi->getOutput(); * * // send it to the browser * echo $html; *
* * * The procedural function "phpsecinfo" is defined below this class. * @see phpsecinfo() * * @author Ed Finkler * * see CHANGELOG for changes * */ class PhpSecInfo { /** * An array of tests to run * * @var array PhpSecInfo_Test */ var $tests_to_run = array(); /** * An array of results. Each result is an associative array: * * $result['result'] = PHPSECINFO_TEST_RESULT_NOTICE; * $result['message'] = "a string describing the test results and what they mean"; * * * @var array */ var $test_results = array(); /** * An array of tests that were not run * * * $result['result'] = PHPSECINFO_TEST_RESULT_NOTRUN; * $result['message'] = "a string explaining why the test was not run"; * * * @var array */ var $tests_not_run = array(); /** * The language code used. Defaults to PHPSECINFO_LANG_DEFAULT, which * is 'en' * * @var string * @see PHPSECINFO_LANG_DEFAULT */ var $language = PHPSECINFO_LANG_DEFAULT; /** * An array of integers recording the number of test results in each category. Categories can include * some or all of the PHPSECINFO_TEST_* constants. Constants are the keys, # of results are the values. * * @var array */ var $result_counts = array(); /** * The number of tests that have been run * * @var integer */ var $num_tests_run = 0; /** * The base directory for phpsecinfo. Set within the constructor. Paths are resolved from this. * @var string */ var $_base_dir; /** * The directory PHPSecInfo will look for views. It defaults to the value * in PHPSECINFO_VIEW_DIR_DEFAULT, but can be changed with the setViewDirectory() * method. * * @var string */ var $_view_directory; /** * The output format, used to load the proper view * * @var string **/ var $_format; /** * Constructor * * @return PhpSecInfo */ function PhpSecInfo($opts = null) { $this->_base_dir = dirname(__FILE__); if ($opts) { if (isset($opts['view_directory'])) { $this->setViewDirectory($opts['view_directory']); } else { $this->setViewDirectory(dirname(__FILE__).DIRECTORY_SEPARATOR . PHPSECINFO_VIEW_DIR_DEFAULT); } if (isset($opts['format'])) { $this->setFormat($opts['format']); } else { if (strtolower(php_sapi_name()) == 'cli' ) { $this->setFormat('Cli'); } else { $this->setFormat(PHPSECINFO_FORMAT_DEFAULT); } } } else { /* Use defaults */ $this->setViewDirectory(dirname(__FILE__).DIRECTORY_SEPARATOR . PHPSECINFO_VIEW_DIR_DEFAULT); if (strtolower(php_sapi_name()) == 'cli' ) { $this->setFormat('Cli'); } else { $this->setFormat(PHPSECINFO_FORMAT_DEFAULT); } } } /** * recurses through the Test subdir and includes classes in each test group subdir, * then builds an array of classnames for the tests that will be run * */ function loadTests() { $test_root = dir(dirname(__FILE__).DIRECTORY_SEPARATOR.'Test'); //echo "
"; echo print_r($test_root, true); echo "
"; while (false !== ($entry = $test_root->read())) { if ( is_dir($test_root->path.DIRECTORY_SEPARATOR.$entry) && !preg_match('|^\.(.*)$|', $entry) ) { $test_dirs[] = $entry; } } //echo "
"; echo print_r($test_dirs, true); echo "
"; // include_once all files in each test dir foreach ($test_dirs as $test_dir) { $this_dir = dir($test_root->path.DIRECTORY_SEPARATOR.$test_dir); while (false !== ($entry = $this_dir->read())) { if (!is_dir($this_dir->path.DIRECTORY_SEPARATOR.$entry)) { include_once $this_dir->path.DIRECTORY_SEPARATOR.$entry; $classNames[] = "PhpSecInfo_Test_".$test_dir."_".basename($entry, '.php'); } } } // modded this to not throw a PHP5 STRICT notice, although I don't like passing by value here $this->tests_to_run = $classNames; } /** * This runs the tests in the tests_to_run array and * places returned data in the following arrays/scalars: * - $this->test_results * - $this->result_counts * - $this->num_tests_run * - $this->tests_not_run; * */ function runTests() { // initialize a bunch of arrays $this->test_results = array(); $this->result_counts = array(); $this->result_counts[PHPSECINFO_TEST_RESULT_NOTRUN] = 0; $this->num_tests_run = 0; foreach ($this->tests_to_run as $testClass) { /** * @var $test PhpSecInfo_Test */ $test = new $testClass(); if ($test->isTestable()) { $test->test(); $rs = array( 'result' => $test->getResult(), 'message' => $test->getMessage(), 'value_current' => $test->getCurrentTestValue(), 'value_recommended' => $test->getRecommendedTestValue(), 'moreinfo_url' => $test->getMoreInfoURL(), ); $this->test_results[$test->getTestGroup()][$test->getTestName()] = $rs; // initialize if not yet set if (!isset ($this->result_counts[$rs['result']]) ) { $this->result_counts[$rs['result']] = 0; } $this->result_counts[$rs['result']]++; $this->num_tests_run++; } else { $rs = array( 'result' => $test->getResult(), 'message' => $test->getMessage(), 'value_current' => NULL, 'value_recommended' => NULL, 'moreinfo_url' => $test->getMoreInfoURL(), ); $this->result_counts[PHPSECINFO_TEST_RESULT_NOTRUN]++; $this->tests_not_run[$test->getTestGroup()."::".$test->getTestName()] = $rs; } } } /** * This is the main output method. The look and feel mimics phpinfo() * */ function renderOutput($page_title="Security Information About PHP") { /** * We need to use PhpSecInfo_Test::getBooleanIniValue() below * @see PhpSecInfo_Test::getBooleanIniValue() */ if (!class_exists('PhpSecInfo_Test')) { include( dirname(__FILE__).DIRECTORY_SEPARATOR.'Test'.DIRECTORY_SEPARATOR.'Test.php'); } $this->loadView($this->_format); } /** * This is a helper method that makes it easy to output tables of test results * for a given test group * * @param string $group_name * @param array $group_results */ function _outputRenderTable($group_name, $group_results) { // exit out if $group_results was empty or not an array. This sorta seems a little hacky... if (!is_array($group_results) || sizeof($group_results) < 1) { return false; } ksort($group_results); $this->loadView($this->_format.'/Result', array('group_name'=>$group_name, 'group_results'=>$group_results)); return true; } /** * This outputs a table containing a summary of the test results (counts and % in each result type) * * @see PHPSecInfo::_outputRenderTable() * @see PHPSecInfo::_outputGetResultTypeFromCode() */ function _outputRenderStatsTable() { foreach($this->result_counts as $code=>$val) { if ($code != PHPSECINFO_TEST_RESULT_NOTRUN) { $percentage = round($val/$this->num_tests_run * 100,2); $result_type = $this->_outputGetResultTypeFromCode($code); $stats[$result_type] = array( 'count' => $val, 'result' => $code, 'message' => "$val out of {$this->num_tests_run} ($percentage%)"); } } $this->_outputRenderTable('Test Results Summary', $stats); } /** * This outputs a table containing a summary or test that were not executed, and the reasons why they were skipped * * @see PHPSecInfo::_outputRenderTable() */ function _outputRenderNotRunTable() { $this->_outputRenderTable('Tests Not Run', $this->tests_not_run); } /** * This is a helper function that returns a CSS class corresponding to * the result code the test returned. This allows us to color-code * results * * @param integer $code * @return string */ function _outputGetCssClassFromResult($code) { switch ($code) { case PHPSECINFO_TEST_RESULT_OK: return 'value-ok'; break; case PHPSECINFO_TEST_RESULT_NOTICE: return 'value-notice'; break; case PHPSECINFO_TEST_RESULT_WARN: return 'value-warn'; break; case PHPSECINFO_TEST_RESULT_NOTRUN: return 'value-notrun'; break; case PHPSECINFO_TEST_RESULT_ERROR: return 'value-error'; break; default: return 'value-notrun'; break; } } /** * This is a helper function that returns a label string corresponding to * the result code the test returned. This is mainly used for the Test * Results Summary table. * * @see PHPSecInfo::_outputRenderStatsTable() * @param integer $code * @return string */ function _outputGetResultTypeFromCode($code) { switch ($code) { case PHPSECINFO_TEST_RESULT_OK: return 'Pass'; break; case PHPSECINFO_TEST_RESULT_NOTICE: return 'Notice'; break; case PHPSECINFO_TEST_RESULT_WARN: return 'Warning'; break; case PHPSECINFO_TEST_RESULT_NOTRUN: return 'Not Run'; break; case PHPSECINFO_TEST_RESULT_ERROR: return 'Error'; break; default: return 'Invalid Result Code'; break; } } /** * Loads and runs all the tests * * As loading, then running, is a pretty common process, this saves a extra method call * * @since 0.1.1 * */ function loadAndRun() { $this->loadTests(); $this->runTests(); } /** * returns an associative array of test data. Four keys are set: * - test_results (array) * - tests_not_run (array) * - result_counts (array) * - num_tests_run (integer) * * note that this must be called after tests are loaded and run * * @since 0.1.1 * @return array */ function getResultsAsArray() { $results = array(); $results['test_results'] = $this->test_results; $results['tests_not_run'] = $this->tests_not_run; $results['result_counts'] = $this->result_counts; $results['num_tests_run'] = $this->num_tests_run; return $results; } /** * returns the standard output as a string instead of echoing it to the browser * * note that this must be called after tests are loaded and run * * @since 0.1.1 * * @return string */ function getOutput() { ob_start(); $this->renderOutput(); $output = ob_get_clean(); return $output; } /** * A very, very simple "view" system * */ function loadView($view_name, $data=null) { if ($data != null) { extract($data); } $view_file = $this->getViewDirectory().$view_name.".php"; if ( file_exists($view_file) && is_readable($view_file) ) { ob_start(); include $view_file; echo ob_get_clean(); } else { user_error("The view '{$view_file}' either does not exist or is not readable", E_USER_WARNING); } } /** * Returns the current view directory * * @return string */ function getViewDirectory() { return $this->_view_directory; } /** * Sets the directory that PHPSecInfo will look in for views * * @param string $newdir */ function setViewDirectory($newdir) { $this->_view_directory = $newdir; } function getFormat() { return $this->_format; } function setFormat($format) { $this->_format = $format; } } /** * A globally-available function that runs the tests and creates the result page * */ function phpsecinfo() { // modded this to not throw a PHP5 STRICT notice, although I don't like passing by value here $psi = new PhpSecInfo(); $psi->loadAndRun(); $psi->renderOutput(); }