398 lines
12 KiB
PHP
398 lines
12 KiB
PHP
<?php
|
|
/**
|
|
* Security Audit Script for GitHub Code Viewer Plugin
|
|
* Run this to verify all security measures are in place
|
|
*/
|
|
|
|
// Prevent direct access
|
|
if (!defined('WPINC')) {
|
|
die('Direct access not permitted.');
|
|
}
|
|
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
class MCB_Security_Audit {
|
|
|
|
private $issues = array();
|
|
private $passed = array();
|
|
|
|
/**
|
|
* Run complete security audit
|
|
*/
|
|
public function run_audit() {
|
|
$this->check_file_permissions();
|
|
$this->check_index_files();
|
|
$this->check_htaccess_files();
|
|
$this->check_php_files_protection();
|
|
$this->check_input_validation();
|
|
$this->check_output_escaping();
|
|
$this->check_nonce_usage();
|
|
$this->check_capability_checks();
|
|
$this->check_ssl_usage();
|
|
$this->check_rate_limiting();
|
|
|
|
return $this->generate_report();
|
|
}
|
|
|
|
/**
|
|
* Check file permissions
|
|
*/
|
|
private function check_file_permissions() {
|
|
$plugin_dir = plugin_dir_path(__FILE__);
|
|
|
|
// Check directory permissions (should be 755 or stricter)
|
|
if (is_readable($plugin_dir)) {
|
|
$perms = fileperms($plugin_dir);
|
|
$octal = substr(sprintf('%o', $perms), -3);
|
|
|
|
if ($octal > '755') {
|
|
$this->issues[] = 'Directory permissions too permissive: ' . $octal;
|
|
} else {
|
|
$this->passed[] = 'Directory permissions OK: ' . $octal;
|
|
}
|
|
}
|
|
|
|
// Check file permissions (should be 644 or stricter)
|
|
$files = glob($plugin_dir . '*.php');
|
|
foreach ($files as $file) {
|
|
$perms = fileperms($file);
|
|
$octal = substr(sprintf('%o', $perms), -3);
|
|
|
|
if ($octal > '644') {
|
|
$this->issues[] = 'File permissions too permissive for ' . basename($file) . ': ' . $octal;
|
|
}
|
|
}
|
|
|
|
if (empty($this->issues)) {
|
|
$this->passed[] = 'All file permissions are secure';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check for index.php files in all directories
|
|
*/
|
|
private function check_index_files() {
|
|
$plugin_dir = plugin_dir_path(__FILE__);
|
|
$directories = array(
|
|
$plugin_dir,
|
|
$plugin_dir . 'admin/',
|
|
$plugin_dir . 'includes/',
|
|
$plugin_dir . 'assets/',
|
|
$plugin_dir . 'assets/css/',
|
|
$plugin_dir . 'assets/js/'
|
|
);
|
|
|
|
foreach ($directories as $dir) {
|
|
if (is_dir($dir) && !file_exists($dir . 'index.php')) {
|
|
$this->issues[] = 'Missing index.php in ' . str_replace($plugin_dir, '', $dir);
|
|
} else {
|
|
$this->passed[] = 'index.php present in ' . str_replace($plugin_dir, '', $dir);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check for .htaccess files
|
|
*/
|
|
private function check_htaccess_files() {
|
|
$plugin_dir = plugin_dir_path(__FILE__);
|
|
$required_htaccess = array(
|
|
$plugin_dir => 'root',
|
|
$plugin_dir . 'admin/' => 'admin',
|
|
$plugin_dir . 'includes/' => 'includes',
|
|
$plugin_dir . 'assets/' => 'assets'
|
|
);
|
|
|
|
foreach ($required_htaccess as $dir => $name) {
|
|
if (file_exists($dir . '.htaccess')) {
|
|
$this->passed[] = '.htaccess present in ' . $name . ' directory';
|
|
} else {
|
|
$this->issues[] = 'Missing .htaccess in ' . $name . ' directory';
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check PHP files have direct access protection
|
|
*/
|
|
private function check_php_files_protection() {
|
|
$plugin_dir = plugin_dir_path(__FILE__);
|
|
$php_files = $this->get_all_php_files($plugin_dir);
|
|
|
|
foreach ($php_files as $file) {
|
|
$content = file_get_contents($file);
|
|
|
|
// Skip index.php files
|
|
if (basename($file) === 'index.php') {
|
|
continue;
|
|
}
|
|
|
|
// Check for ABSPATH or WPINC checks
|
|
if (!strpos($content, 'ABSPATH') && !strpos($content, 'WPINC')) {
|
|
$this->issues[] = 'No direct access protection in ' . str_replace($plugin_dir, '', $file);
|
|
} else {
|
|
$this->passed[] = 'Direct access protected: ' . basename($file);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check input validation
|
|
*/
|
|
private function check_input_validation() {
|
|
$checks = array(
|
|
'sanitize_text_field' => 'Text sanitization',
|
|
'wp_verify_nonce' => 'Nonce verification',
|
|
'esc_attr' => 'Attribute escaping',
|
|
'esc_html' => 'HTML escaping',
|
|
'esc_js' => 'JavaScript escaping',
|
|
'absint' => 'Integer validation',
|
|
'filter_var' => 'Input filtering'
|
|
);
|
|
|
|
$plugin_dir = plugin_dir_path(__FILE__);
|
|
$php_files = $this->get_all_php_files($plugin_dir);
|
|
|
|
foreach ($checks as $function => $description) {
|
|
$found = false;
|
|
foreach ($php_files as $file) {
|
|
$content = file_get_contents($file);
|
|
if (strpos($content, $function) !== false) {
|
|
$found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($found) {
|
|
$this->passed[] = $description . ' implemented (' . $function . ')';
|
|
} else {
|
|
$this->issues[] = $description . ' not found (' . $function . ')';
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check output escaping
|
|
*/
|
|
private function check_output_escaping() {
|
|
$escaping_functions = array(
|
|
'esc_html',
|
|
'esc_attr',
|
|
'esc_url',
|
|
'esc_js',
|
|
'htmlspecialchars'
|
|
);
|
|
|
|
$plugin_dir = plugin_dir_path(__FILE__);
|
|
$php_files = $this->get_all_php_files($plugin_dir);
|
|
|
|
$escaping_found = false;
|
|
foreach ($php_files as $file) {
|
|
$content = file_get_contents($file);
|
|
foreach ($escaping_functions as $func) {
|
|
if (strpos($content, $func) !== false) {
|
|
$escaping_found = true;
|
|
break 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($escaping_found) {
|
|
$this->passed[] = 'Output escaping implemented';
|
|
} else {
|
|
$this->issues[] = 'No output escaping functions found';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check nonce usage
|
|
*/
|
|
private function check_nonce_usage() {
|
|
$nonce_functions = array(
|
|
'wp_create_nonce',
|
|
'wp_verify_nonce',
|
|
'wp_nonce_field',
|
|
'check_admin_referer'
|
|
);
|
|
|
|
$plugin_dir = plugin_dir_path(__FILE__);
|
|
$php_files = $this->get_all_php_files($plugin_dir);
|
|
|
|
$nonce_found = 0;
|
|
foreach ($php_files as $file) {
|
|
$content = file_get_contents($file);
|
|
foreach ($nonce_functions as $func) {
|
|
if (strpos($content, $func) !== false) {
|
|
$nonce_found++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($nonce_found >= 2) { // Should have both create and verify
|
|
$this->passed[] = 'Nonce protection implemented';
|
|
} else {
|
|
$this->issues[] = 'Insufficient nonce protection';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check capability checks
|
|
*/
|
|
private function check_capability_checks() {
|
|
$capability_functions = array(
|
|
'current_user_can',
|
|
'user_can',
|
|
'is_admin'
|
|
);
|
|
|
|
$plugin_dir = plugin_dir_path(__FILE__);
|
|
$php_files = $this->get_all_php_files($plugin_dir);
|
|
|
|
$capability_found = false;
|
|
foreach ($php_files as $file) {
|
|
$content = file_get_contents($file);
|
|
foreach ($capability_functions as $func) {
|
|
if (strpos($content, $func) !== false) {
|
|
$capability_found = true;
|
|
break 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($capability_found) {
|
|
$this->passed[] = 'Capability checks implemented';
|
|
} else {
|
|
$this->issues[] = 'No capability checks found';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check SSL usage
|
|
*/
|
|
private function check_ssl_usage() {
|
|
$plugin_dir = plugin_dir_path(__FILE__);
|
|
$php_files = $this->get_all_php_files($plugin_dir);
|
|
|
|
$https_enforced = false;
|
|
foreach ($php_files as $file) {
|
|
$content = file_get_contents($file);
|
|
if (strpos($content, 'https://api.github.com') !== false) {
|
|
$https_enforced = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($https_enforced) {
|
|
$this->passed[] = 'HTTPS enforced for API calls';
|
|
} else {
|
|
$this->issues[] = 'HTTPS not enforced for API calls';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check rate limiting
|
|
*/
|
|
private function check_rate_limiting() {
|
|
$plugin_dir = plugin_dir_path(__FILE__);
|
|
$php_files = $this->get_all_php_files($plugin_dir);
|
|
|
|
$rate_limiting = false;
|
|
foreach ($php_files as $file) {
|
|
$content = file_get_contents($file);
|
|
if (strpos($content, 'rate_limit') !== false || strpos($content, 'throttl') !== false) {
|
|
$rate_limiting = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($rate_limiting) {
|
|
$this->passed[] = 'Rate limiting implemented';
|
|
} else {
|
|
$this->issues[] = 'No rate limiting found';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get all PHP files recursively
|
|
*/
|
|
private function get_all_php_files($dir) {
|
|
$files = array();
|
|
$iterator = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($dir)
|
|
);
|
|
|
|
foreach ($iterator as $file) {
|
|
if ($file->isFile() && $file->getExtension() === 'php') {
|
|
$files[] = $file->getPathname();
|
|
}
|
|
}
|
|
|
|
return $files;
|
|
}
|
|
|
|
/**
|
|
* Generate audit report
|
|
*/
|
|
private function generate_report() {
|
|
$report = array(
|
|
'timestamp' => current_time('mysql'),
|
|
'passed_count' => count($this->passed),
|
|
'issues_count' => count($this->issues),
|
|
'passed' => $this->passed,
|
|
'issues' => $this->issues,
|
|
'score' => $this->calculate_score(),
|
|
'status' => empty($this->issues) ? 'SECURE' : 'NEEDS ATTENTION'
|
|
);
|
|
|
|
return $report;
|
|
}
|
|
|
|
/**
|
|
* Calculate security score
|
|
*/
|
|
private function calculate_score() {
|
|
$total = count($this->passed) + count($this->issues);
|
|
if ($total === 0) {
|
|
return 0;
|
|
}
|
|
|
|
return round((count($this->passed) / $total) * 100);
|
|
}
|
|
}
|
|
|
|
// Run audit if requested
|
|
if (isset($_GET['mcb_security_audit']) && current_user_can('manage_options')) {
|
|
$audit = new MCB_Security_Audit();
|
|
$report = $audit->run_audit();
|
|
|
|
echo '<div class="wrap">';
|
|
echo '<h1>GitHub Code Viewer Security Audit</h1>';
|
|
echo '<div class="notice notice-' . (empty($report['issues']) ? 'success' : 'warning') . '">';
|
|
echo '<p><strong>Security Score: ' . $report['score'] . '%</strong></p>';
|
|
echo '<p>Status: ' . $report['status'] . '</p>';
|
|
echo '</div>';
|
|
|
|
if (!empty($report['passed'])) {
|
|
echo '<h2>✅ Passed Checks (' . $report['passed_count'] . ')</h2>';
|
|
echo '<ul>';
|
|
foreach ($report['passed'] as $pass) {
|
|
echo '<li style="color: green;">✓ ' . esc_html($pass) . '</li>';
|
|
}
|
|
echo '</ul>';
|
|
}
|
|
|
|
if (!empty($report['issues'])) {
|
|
echo '<h2>⚠️ Issues Found (' . $report['issues_count'] . ')</h2>';
|
|
echo '<ul>';
|
|
foreach ($report['issues'] as $issue) {
|
|
echo '<li style="color: red;">✗ ' . esc_html($issue) . '</li>';
|
|
}
|
|
echo '</ul>';
|
|
}
|
|
|
|
echo '<p><em>Audit completed at ' . $report['timestamp'] . '</em></p>';
|
|
echo '</div>';
|
|
}
|