added additional plugins

This commit is contained in:
Rodolfo Martinez 2025-12-12 19:05:48 -05:00
parent c85895d306
commit 00e60ec1b7
132 changed files with 27514 additions and 0 deletions

View file

@ -0,0 +1,322 @@
<?php
/**
* Security Handler Class
* Implements OWASP security best practices
*/
// Prevent direct access
if (!defined('WPINC')) {
die('Direct access not permitted.');
}
if (!defined('ABSPATH')) {
exit;
}
class MCB_Security {
/**
* Initialize security features
*/
public static function init() {
// Add security headers
add_action('send_headers', array(__CLASS__, 'add_security_headers'));
// Add Content Security Policy
add_action('wp_head', array(__CLASS__, 'add_csp_meta'));
// Note: Global input sanitization removed - sanitize data at point of use instead
// This prevents conflicts with other plugins (e.g., WPForms)
}
/**
* Add security headers
*/
public static function add_security_headers() {
// Only add headers on pages with our plugin
if (!self::is_plugin_active_on_page()) {
return;
}
// X-Content-Type-Options
header('X-Content-Type-Options: nosniff');
// X-Frame-Options
header('X-Frame-Options: SAMEORIGIN');
// X-XSS-Protection (legacy but still useful)
header('X-XSS-Protection: 1; mode=block');
// Referrer Policy
header('Referrer-Policy: strict-origin-when-cross-origin');
}
/**
* Add Content Security Policy meta tag
*/
public static function add_csp_meta() {
if (!self::is_plugin_active_on_page()) {
return;
}
// Strict CSP for code viewer areas
$csp = "default-src 'self'; " .
"script-src 'self' 'unsafe-inline' 'unsafe-eval'; " . // Needed for Prism.js
"style-src 'self' 'unsafe-inline'; " . // Needed for inline styles
"img-src 'self' data: https:; " .
"connect-src 'self'; " .
"font-src 'self' data:; " .
"object-src 'none'; " .
"base-uri 'self'; " .
"form-action 'self'; " .
"frame-ancestors 'self';";
echo '<meta http-equiv="Content-Security-Policy" content="' . esc_attr($csp) . '">' . "\n";
}
/**
* Check if plugin is active on current page
*/
private static function is_plugin_active_on_page() {
global $post;
if (!is_a($post, 'WP_Post')) {
return false;
}
return has_shortcode($post->post_content, 'maple_code_block');
}
/**
* Sanitize global input arrays
*/
public static function sanitize_global_input() {
// Sanitize $_GET
if (!empty($_GET)) {
$_GET = self::sanitize_array($_GET);
}
// Sanitize $_POST
if (!empty($_POST)) {
$_POST = self::sanitize_array($_POST);
}
// Sanitize $_REQUEST
if (!empty($_REQUEST)) {
$_REQUEST = self::sanitize_array($_REQUEST);
}
// Sanitize $_COOKIE
if (!empty($_COOKIE)) {
$_COOKIE = self::sanitize_array($_COOKIE);
}
}
/**
* Recursively sanitize array
*/
private static function sanitize_array($array) {
foreach ($array as $key => $value) {
if (is_array($value)) {
$array[$key] = self::sanitize_array($value);
} else {
// Remove null bytes
$value = str_replace(chr(0), '', $value);
// Strip tags and encode special chars
$array[$key] = htmlspecialchars(strip_tags($value), ENT_QUOTES, 'UTF-8');
}
}
return $array;
}
/**
* Validate repository format
* Supports: owner/repo, platform:owner/repo, or full URLs
*/
public static function validate_repo_format($repo) {
// Remove any whitespace
$repo = trim($repo);
// If it's a full URL, validate it
if (strpos($repo, 'https://') === 0 || strpos($repo, 'http://') === 0) {
// Check if it's from a supported platform
$supported_domains = array(
'github.com',
'gitlab.com',
'bitbucket.org',
'codeberg.org'
);
$parsed = parse_url($repo);
if (!$parsed || !isset($parsed['host'])) {
return false;
}
$domain_valid = false;
foreach ($supported_domains as $domain) {
if (strpos($parsed['host'], $domain) !== false) {
$domain_valid = true;
break;
}
}
if (!$domain_valid) {
return false;
}
// Extract path and validate format
if (!isset($parsed['path'])) {
return false;
}
$path = trim($parsed['path'], '/');
$parts = explode('/', $path);
// Need at least owner/repo
if (count($parts) < 2) {
return false;
}
// Validate owner and repo names
$owner = $parts[0];
$repo_name = $parts[1];
if (!preg_match('/^[a-zA-Z0-9\-_]+$/', $owner) ||
!preg_match('/^[a-zA-Z0-9\-_\.]+$/', $repo_name)) {
return false;
}
return true;
}
// Check for platform prefix (e.g., gitlab:owner/repo)
if (strpos($repo, ':') !== false) {
list($platform, $repo_path) = explode(':', $repo, 2);
// Validate platform
$valid_platforms = array('github', 'gitlab', 'bitbucket', 'codeberg');
if (!in_array($platform, $valid_platforms)) {
return false;
}
// Validate repo path
if (!preg_match('/^[a-zA-Z0-9\-_]+\/[a-zA-Z0-9\-_\.]+$/', $repo_path)) {
return false;
}
return true;
}
// Standard format: owner/repo
if (!preg_match('/^[a-zA-Z0-9\-_]+\/[a-zA-Z0-9\-_\.]+$/', $repo)) {
return false;
}
// Check length limits
$parts = explode('/', $repo);
if (count($parts) !== 2) {
return false;
}
// Owner: 1-39 characters (GitHub limit)
if (strlen($parts[0]) < 1 || strlen($parts[0]) > 39) {
return false;
}
// Repo name: 1-100 characters (GitHub limit)
if (strlen($parts[1]) < 1 || strlen($parts[1]) > 100) {
return false;
}
return true;
}
/**
* Validate file path
*/
public static function validate_file_path($path) {
// Remove leading/trailing slashes
$path = trim($path, '/');
// Check for path traversal attempts
$dangerous_patterns = array(
'..',
'//',
'\\',
'.git',
'.env',
'wp-config',
'.htaccess',
'.htpasswd'
);
foreach ($dangerous_patterns as $pattern) {
if (stripos($path, $pattern) !== false) {
return false;
}
}
// Only allow safe characters
if (!preg_match('/^[a-zA-Z0-9\-_\.\/]+$/', $path)) {
return false;
}
// Check path depth (max 10 levels)
if (substr_count($path, '/') > 10) {
return false;
}
return true;
}
/**
* Generate secure random token
*/
public static function generate_token($length = 32) {
if (function_exists('random_bytes')) {
return bin2hex(random_bytes($length));
} elseif (function_exists('openssl_random_pseudo_bytes')) {
return bin2hex(openssl_random_pseudo_bytes($length));
} else {
// Fallback to less secure method
return wp_generate_password($length * 2, false, false);
}
}
/**
* Log security events
*/
public static function log_security_event($event_type, $details = array()) {
$log_entry = array(
'timestamp' => current_time('mysql'),
'event_type' => $event_type,
'ip_address' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '',
'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '',
'details' => $details
);
// Store in WordPress transient for review (expires in 7 days)
$logs = get_transient('mcb_security_logs');
if (!is_array($logs)) {
$logs = array();
}
// Keep only last 100 entries
if (count($logs) >= 100) {
array_shift($logs);
}
$logs[] = $log_entry;
set_transient('mcb_security_logs', $logs, 7 * DAY_IN_SECONDS);
// For critical events, also log to error log
if (in_array($event_type, array('invalid_nonce', 'rate_limit_exceeded', 'path_traversal_attempt'))) {
error_log('GCV Security Event: ' . json_encode($log_entry));
}
}
}
// Initialize security features
MCB_Security::init();