Subversion Repositories ggsysinfo

[/] [modules/] [sysinfo/] [lib/] [PhpSecInfo/] [PhpSecInfo.php] - Blame information for rev 123

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 123 gg
<?php
2
/**
3
 * Main class file
4
 *
5
 * @package PhpSecInfo
6
 * @author Ed Finkler <coj@funkatron.com>
7
 */
8
 
9
// gg 2012.3.26 avoid double inclusion of PhpSecInfo.php file
10
if ( !defined( 'PHPSECINFO_VERSION' ) )
11
{
12
 
13
 
14
/**
15
 * The default language setting if none is set/retrievable
16
 *
17
 */
18
define ('PHPSECINFO_LANG_DEFAULT', 'en');
19
 
20
/**
21
 * a general version string to differentiate releases
22
 *
23
 */
24
define ('PHPSECINFO_VERSION', '0.2.2');
25
 
26
/**
27
 * a YYYYMMDD date string to indicate "build" date
28
 *
29
 */
30
define ('PHPSECINFO_BUILD', '20080723');
31
 
32
/**
33
 * Homepage for phpsecinfo project
34
 *
35
 */
36
define ('PHPSECINFO_URL', 'http://phpsecinfo.com');
37
 
38
/**
39
 * The base folder where views are stored.  Include trailing slash
40
 *
41
 */
42
define('PHPSECINFO_VIEW_DIR_DEFAULT', 'View/');
43
 
44
 
45
/**
46
 * The default format, used to load the proper view.
47
 */
48
define('PHPSECINFO_FORMAT_DEFAULT', 'Html');
49
 
50
 
51
/**
52
 * The base directory, used to resolve requires and includes
53
 */
54
define('PHPSECINFO_BASE_DIR', dirname(__FILE__));
55
 
56
/**
57
 * This is the main class for the phpsecinfo system.  It's responsible for
58
 * dynamically loading tests, running those tests, and generating the results
59
 * output
60
 *
61
 * Example:
62
 * <code>
63
 * <?php require_once(PHPSECINFO_BASE_DIR.'/PhpSecInfo.php'); ?>
64
 * <?php phpsecinfo(); ?>
65
 * </code>
66
 *
67
 * If you want to capture the output, or just grab the test results and display them
68
 * in your own way, you'll need to do slightly more work.
69
 *
70
 * Example:
71
 * <code>
72
 * require_once(PHPSECINFO_BASE_DIR.'/PhpSecInfo.php');
73
 * // instantiate the class
74
 * $psi = new PhpSecInfo();
75
 *
76
 * // load and run all tests
77
 * $psi->loadAndRun();
78
 *
79
 * // grab the results as a multidimensional array
80
 * $results = $psi->getResultsAsArray();
81
 * echo "<pre>"; echo print_r($results, true); echo "</pre>";
82
 *
83
 * // grab the standard results output as a string
84
 * $html = $psi->getOutput();
85
 *
86
 * // send it to the browser
87
 * echo $html;
88
 * </code>
89
 *
90
 *
91
 * The procedural function "phpsecinfo" is defined below this class.
92
 * @see phpsecinfo()
93
 *
94
 * @author Ed Finkler <coj@funkatron.com>
95
 *
96
 * see CHANGELOG for changes
97
 *
98
 */
99
class PhpSecInfo
100
{
101
 
102
        /**
103
         * An array of tests to run
104
         *
105
         * @var array PhpSecInfo_Test
106
         */
107
        var $tests_to_run = array();
108
 
109
 
110
        /**
111
         * An array of results.  Each result is an associative array:
112
         * <code>
113
         * $result['result'] = PHPSECINFO_TEST_RESULT_NOTICE;
114
         * $result['message'] = "a string describing the test results and what they mean";
115
         * </code>
116
         *
117
         * @var array
118
         */
119
        var $test_results = array();
120
 
121
 
122
        /**
123
         * An array of tests that were not run
124
         *
125
         * <code>
126
         * $result['result'] = PHPSECINFO_TEST_RESULT_NOTRUN;
127
         * $result['message'] = "a string explaining why the test was not run";
128
         * </code>
129
         *
130
         * @var array
131
         */
132
        var $tests_not_run = array();
133
 
134
 
135
        /**
136
         * The language code used.  Defaults to PHPSECINFO_LANG_DEFAULT, which
137
         * is 'en'
138
         *
139
         * @var string
140
         * @see PHPSECINFO_LANG_DEFAULT
141
         */
142
        var $language = PHPSECINFO_LANG_DEFAULT;
143
 
144
 
145
        /**
146
         * An array of integers recording the number of test results in each category.  Categories can include
147
         * some or all of the PHPSECINFO_TEST_* constants.  Constants are the keys, # of results are the values.
148
         *
149
         * @var array
150
         */
151
        var $result_counts = array();
152
 
153
 
154
        /**
155
         * The number of tests that have been run
156
         *
157
         * @var integer
158
         */
159
        var $num_tests_run = 0;
160
 
161
 
162
        /**
163
         * The base directory for phpsecinfo. Set within the constructor. Paths are resolved from this.
164
         * @var string
165
         */
166
        var $_base_dir;
167
 
168
 
169
        /**
170
         * The directory PHPSecInfo will look for views.  It defaults to the value
171
         * in PHPSECINFO_VIEW_DIR_DEFAULT, but can be changed with the setViewDirectory()
172
         * method.
173
         *
174
         * @var string
175
         */
176
        var $_view_directory;
177
 
178
 
179
        /**
180
         * The output format, used to load the proper view
181
         *
182
         * @var string
183
         **/
184
        var $_format;
185
 
186
        /**
187
         * Constructor
188
         *
189
         * @return PhpSecInfo
190
         */
191
        function PhpSecInfo($opts = null) {
192
 
193
                $this->_base_dir = dirname(__FILE__);
194
 
195
                if ($opts) {
196
                        if (isset($opts['view_directory'])) {
197
                                $this->setViewDirectory($opts['view_directory']);
198
                        } else {
199
                                $this->setViewDirectory(dirname(__FILE__).DIRECTORY_SEPARATOR . PHPSECINFO_VIEW_DIR_DEFAULT);
200
                        }
201
 
202
                        if (isset($opts['format'])) {
203
                                $this->setFormat($opts['format']);
204
                        } else {
205
                                if (!strcasecmp(PHP_SAPI, 'cli')) {
206
                                        $this->setFormat('Cli');
207
                                } else {
208
                                        $this->setFormat(PHPSECINFO_FORMAT_DEFAULT);
209
                                }
210
                        }
211
 
212
                } else { /* Use defaults */
213
                        $this->setViewDirectory(dirname(__FILE__).DIRECTORY_SEPARATOR . PHPSECINFO_VIEW_DIR_DEFAULT);
214
                        if (!strcasecmp(PHP_SAPI, 'cli')) {
215
                                $this->setFormat('Cli');
216
                        } else {
217
                                $this->setFormat(PHPSECINFO_FORMAT_DEFAULT);
218
                        }
219
                }
220
        }
221
 
222
 
223
        /**
224
         * recurses through the Test subdir and includes classes in each test group subdir,
225
         * then builds an array of classnames for the tests that will be run
226
         *
227
         */
228
        function loadTests() {
229
 
230
                $test_root = dir(dirname(__FILE__).DIRECTORY_SEPARATOR.'Test');
231
 
232
                //echo "<pre>"; echo print_r($test_root, true); echo "</pre>";
233
 
234
                while (false !== ($entry = $test_root->read())) {
235
                        if ( is_dir($test_root->path.DIRECTORY_SEPARATOR.$entry) && !preg_match('~^(\.|_vti)(.*)$~', $entry) ) {
236
                                $test_dirs[] = $entry;
237
                        }
238
                }
239
                //echo "<pre>"; echo print_r($test_dirs, true); echo "</pre>";
240
 
241
                // include_once all files in each test dir
242
                foreach ($test_dirs as $test_dir) {
243
                        $this_dir = dir($test_root->path.DIRECTORY_SEPARATOR.$test_dir);
244
 
245
                        while (false !== ($entry = $this_dir->read())) {
246
                                if (!is_dir($this_dir->path.DIRECTORY_SEPARATOR.$entry)) {
247
                                        include_once $this_dir->path.DIRECTORY_SEPARATOR.$entry;
248
                                        $classNames[] = "PhpSecInfo_Test_".$test_dir."_".basename($entry, '.php');
249
                                }
250
                        }
251
 
252
                }
253
 
254
                // modded this to not throw a PHP5 STRICT notice, although I don't like passing by value here
255
                $this->tests_to_run = $classNames;
256
        }
257
 
258
 
259
        /**
260
         * This runs the tests in the tests_to_run array and
261
         * places returned data in the following arrays/scalars:
262
         * - $this->test_results
263
         * - $this->result_counts
264
         * - $this->num_tests_run
265
         * - $this->tests_not_run;
266
         *
267
         */
268
        function runTests() {
269
                // initialize a bunch of arrays
270
                $this->test_results  = array();
271
                $this->result_counts = array();
272
                $this->result_counts[PHPSECINFO_TEST_RESULT_NOTRUN] = 0;
273
                $this->num_tests_run = 0;
274
 
275
                foreach ($this->tests_to_run as $testClass) {
276
 
277
                        /**
278
                         * @var $test PhpSecInfo_Test
279
                         */
280
                        $test = new $testClass();
281
 
282
                        if ($test->isTestable()) {
283
                                $test->test();
284
                                $rs = array(    'result' => $test->getResult(),
285
                                                        'message' => $test->getMessage(),
286
                                                        'value_current' => $test->getCurrentTestValue(),
287
                                                        'value_recommended' => $test->getRecommendedTestValue(),
288
                                                        'moreinfo_url' => $test->getMoreInfoURL(),
289
                                                );
290
                                $this->test_results[$test->getTestGroup()][$test->getTestName()] = $rs;
291
 
292
                                // initialize if not yet set
293
                                if (!isset ($this->result_counts[$rs['result']]) ) {
294
                                        $this->result_counts[$rs['result']] = 0;
295
                                }
296
 
297
                                $this->result_counts[$rs['result']]++;
298
                                $this->num_tests_run++;
299
                        } else {
300
                                $rs = array(    'result' => $test->getResult(),
301
                                                        'message' => $test->getMessage(),
302
                                                        'value_current' => NULL,
303
                                                        'value_recommended' => NULL,
304
                                                        'moreinfo_url' => $test->getMoreInfoURL(),
305
                                                );
306
                                $this->result_counts[PHPSECINFO_TEST_RESULT_NOTRUN]++;
307
                                $this->tests_not_run[$test->getTestGroup()."::".$test->getTestName()] = $rs;
308
                        }
309
                }
310
        }
311
 
312
 
313
        /**
314
         * This is the main output method.  The look and feel mimics phpinfo()
315
         *
316
         */
317
        function renderOutput($page_title="Security Information About PHP") {
318
                /**
319
                 * We need to use PhpSecInfo_Test::getBooleanIniValue() below
320
                 * @see PhpSecInfo_Test::getBooleanIniValue()
321
                 */
322
                if (!class_exists('PhpSecInfo_Test')) {
323
                        include( dirname(__FILE__).DIRECTORY_SEPARATOR.'Test'.DIRECTORY_SEPARATOR.'Test.php');
324
                }
325
                $this->loadView($this->_format);
326
        }
327
 
328
 
329
        /**
330
         * This is a helper method that makes it easy to output tables of test results
331
         * for a given test group
332
         *
333
         * @param string $group_name
334
         * @param array $group_results
335
         */
336
        function _outputRenderTable($group_name, $group_results) {
337
 
338
                // exit out if $group_results was empty or not an array.  This sorta seems a little hacky...
339
                if (!is_array($group_results) || sizeof($group_results) < 1) {
340
                        return false;
341
                }
342
 
343
                ksort($group_results);
344
 
345
                $this->loadView($this->_format.'/Result', array('group_name'=>$group_name, 'group_results'=>$group_results));
346
 
347
                return true;
348
        }
349
 
350
 
351
 
352
        /**
353
         * This outputs a table containing a summary of the test results (counts and % in each result type)
354
         *
355
         * @see PHPSecInfo::_outputRenderTable()
356
         * @see PHPSecInfo::_outputGetResultTypeFromCode()
357
         */
358
        function _outputRenderStatsTable() {
359
 
360
                foreach($this->result_counts as $code=>$val) {
361
                        if ($code != PHPSECINFO_TEST_RESULT_NOTRUN) {
362
                                $percentage = round($val/$this->num_tests_run * 100,2);
363
                                $result_type = $this->_outputGetResultTypeFromCode($code);
364
                                $stats[$result_type] = array( 'count' => $val,
365
                                                                                        'result' => $code,
366
                                                                                        'message' => "$val out of {$this->num_tests_run} ($percentage%)");
367
                        }
368
                }
369
 
370
                $this->_outputRenderTable('Test Results Summary', $stats);
371
 
372
        }
373
 
374
 
375
 
376
        /**
377
         * This outputs a table containing a summary or test that were not executed, and the reasons why they were skipped
378
         *
379
         * @see PHPSecInfo::_outputRenderTable()
380
         */
381
        function _outputRenderNotRunTable() {
382
 
383
                $this->_outputRenderTable('Tests Not Run', $this->tests_not_run);
384
 
385
        }
386
 
387
 
388
 
389
 
390
        /**
391
         * This is a helper function that returns a CSS class corresponding to
392
         * the result code the test returned.  This allows us to color-code
393
         * results
394
         *
395
         * @param integer $code
396
         * @return string
397
         */
398
        function _outputGetCssClassFromResult($code) {
399
 
400
                switch ($code) {
401
                        case PHPSECINFO_TEST_RESULT_OK:
402
                                return 'value-ok';
403
                                break;
404
 
405
                        case PHPSECINFO_TEST_RESULT_NOTICE:
406
                                return 'value-notice';
407
                                break;
408
 
409
                        case PHPSECINFO_TEST_RESULT_WARN:
410
                                return 'value-warn';
411
                                break;
412
 
413
                        case PHPSECINFO_TEST_RESULT_NOTRUN:
414
                                return 'value-notrun';
415
                                break;
416
 
417
                        case PHPSECINFO_TEST_RESULT_ERROR:
418
                                return 'value-error';
419
                                break;
420
 
421
                        default:
422
                                return 'value-notrun';
423
                                break;
424
                }
425
 
426
        }
427
 
428
 
429
 
430
        /**
431
         * This is a helper function that returns a label string corresponding to
432
         * the result code the test returned.  This is mainly used for the Test
433
         * Results Summary table.
434
         *
435
         * @see PHPSecInfo::_outputRenderStatsTable()
436
         * @param integer $code
437
         * @return string
438
         */
439
        function _outputGetResultTypeFromCode($code) {
440
 
441
                switch ($code) {
442
                        case PHPSECINFO_TEST_RESULT_OK:
443
                                return 'Pass';
444
                                break;
445
 
446
                        case PHPSECINFO_TEST_RESULT_NOTICE:
447
                                return 'Notice';
448
                                break;
449
 
450
                        case PHPSECINFO_TEST_RESULT_WARN:
451
                                return 'Warning';
452
                                break;
453
 
454
                        case PHPSECINFO_TEST_RESULT_NOTRUN:
455
                                return 'Not Run';
456
                                break;
457
 
458
                        case PHPSECINFO_TEST_RESULT_ERROR:
459
                                return 'Error';
460
                                break;
461
 
462
                        default:
463
                                return 'Invalid Result Code';
464
                                break;
465
                }
466
 
467
        }
468
 
469
 
470
        /**
471
         * Loads and runs all the tests
472
         *
473
         * As loading, then running, is a pretty common process, this saves a extra method call
474
         *
475
         * @since 0.1.1
476
         *
477
         */
478
        function loadAndRun() {
479
                $this->loadTests();
480
                $this->runTests();
481
        }
482
 
483
 
484
        /**
485
         * returns an associative array of test data.  Four keys are set:
486
         * - test_results  (array)
487
         * - tests_not_run (array)
488
         * - result_counts (array)
489
         * - num_tests_run (integer)
490
         *
491
         * note that this must be called after tests are loaded and run
492
         *
493
         * @since 0.1.1
494
         * @return array
495
         */
496
        function getResultsAsArray() {
497
                $results = array();
498
 
499
                $results['test_results'] = $this->test_results;
500
                $results['tests_not_run'] = $this->tests_not_run;
501
                $results['result_counts'] = $this->result_counts;
502
                $results['num_tests_run'] = $this->num_tests_run;
503
 
504
                return $results;
505
        }
506
 
507
 
508
 
509
        /**
510
         * returns the standard output as a string instead of echoing it to the browser
511
         *
512
         * note that this must be called after tests are loaded and run
513
         *
514
         * @since 0.1.1
515
         *
516
         * @return string
517
         */
518
        function getOutput() {
519
                ob_start();
520
                $this->renderOutput();
521
                $output = ob_get_clean();
522
                return $output;
523
        }
524
 
525
 
526
        /**
527
         * A very, very simple "view" system
528
         *
529
         */
530
        function loadView($view_name, $data=null) {
531
                if ($data != null) {
532
                        extract($data);
533
                }
534
 
535
                $view_file = $this->getViewDirectory().$view_name.".php";
536
 
537
                if ( file_exists($view_file) && is_readable($view_file) ) {
538
                        ob_start();
539
                        include $view_file;
540
                        echo ob_get_clean();
541
                } else {
542
                        user_error("The view '{$view_file}' either does not exist or is not readable", E_USER_WARNING);
543
                }
544
 
545
 
546
        }
547
 
548
 
549
        /**
550
         * Returns the current view directory
551
         *
552
         * @return string
553
         */
554
        function getViewDirectory() {
555
                return $this->_view_directory;
556
        }
557
 
558
 
559
        /**
560
         * Sets the directory that PHPSecInfo will look in for views
561
         *
562
         * @param string $newdir
563
         */
564
        function setViewDirectory($newdir) {
565
                $this->_view_directory = $newdir;
566
        }
567
 
568
 
569
 
570
 
571
        function getFormat() {
572
                return $this->_format;
573
        }
574
 
575
 
576
        function setFormat($format) {
577
                $this->_format = $format;
578
        }
579
 
580
}
581
 
582
 
583
 
584
 
585
/**
586
 * A globally-available function that runs the tests and creates the result page
587
 *
588
 */
589
function phpsecinfo() {
590
        // modded this to not throw a PHP5 STRICT notice, although I don't like passing by value here
591
        $psi = new PhpSecInfo();
592
        $psi->loadAndRun();
593
        $psi->renderOutput();
594
}
595
 
596
 
597
}