| 1 |
<?php |
| 2 |
|
| 3 |
/* |
| 4 |
Plugin Name: Security by CleanTalk |
| 5 |
Plugin URI: https://wordpress.org/plugins/security-malware-firewall/ |
| 6 |
Description: Security & Malware scan by CleanTalk to protect your website from online threats and viruses. IP/Country FireWall, Web application FireWall. Detailed stats and logs to have full control. |
| 7 |
Author: CleanTalk Security |
| 8 |
Version: 2.187 |
| 9 |
Author URI: https://cleantalk.org |
| 10 |
Text Domain: security-malware-firewall |
| 11 |
Domain Path: /i18n |
| 12 |
*/ |
| 13 |
|
| 14 |
use CleantalkSP\Security\LoginCollectingProtector; |
| 15 |
use CleantalkSP\SpbctWP\Activator; |
| 16 |
use CleantalkSP\SpbctWP\DTO\SecurityLogsDataRowDTO; |
| 17 |
use CleantalkSP\SpbctWP\DTO\SecurityLogsDTO; |
| 18 |
use CleantalkSP\SpbctWP\FSWatcher\Controller as FSWatcherController; |
| 19 |
use CleantalkSP\SpbctWP\DB; |
| 20 |
use CleantalkSP\SpbctWP\Firewall\BFP; |
| 21 |
use CleantalkSP\SpbctWP\Firewall\FW; |
| 22 |
use CleantalkSP\SpbctWP\Cron as SpbcCron; |
| 23 |
use CleantalkSP\SpbctWP\HTTP\CDNHeadersChecker; |
| 24 |
use CleantalkSP\SpbctWP\RemoteCalls as SpbcRemoteCalls; |
| 25 |
use CleantalkSP\SpbctWP\RenameLoginPage; |
| 26 |
use CleantalkSP\SpbctWP\Sanitize; |
| 27 |
use CleantalkSP\SpbctWP\SpbcDevLogger; |
| 28 |
use CleantalkSP\SpbctWP\Scanner\Services\SendFileToCloudService; |
| 29 |
use CleantalkSP\SpbctWP\Scanner\Stages\SignatureAnalysis\SignatureAnalysisFacade; |
| 30 |
use CleantalkSP\SpbctWP\Settings\FilesScanPathExclusion; |
| 31 |
use CleantalkSP\SpbctWP\Settings\FrontendScanDomainExclusion; |
| 32 |
use CleantalkSP\SpbctWP\State; |
| 33 |
use CleantalkSP\SpbctWP\Sync as SpbcSync; |
| 34 |
use CleantalkSP\SpbctWP\SpbcEarlyTranslation as SpbcET; |
| 35 |
use CleantalkSP\SpbctWP\Transaction; |
| 36 |
use CleantalkSP\SpbctWP\Variables\Cookie; |
| 37 |
use CleantalkSP\SpbctWP\VulnerabilityAlarm\VulnerabilityAlarmService; |
| 38 |
use CleantalkSP\Updater\Updater; |
| 39 |
use CleantalkSP\Updater\UpdaterScripts; |
| 40 |
use CleantalkSP\Variables\Get; |
| 41 |
use CleantalkSP\Variables\Post; |
| 42 |
use CleantalkSP\Variables\Server; |
| 43 |
use CleantalkSP\SpbctWP\Helpers\IP; |
| 44 |
use CleantalkSP\SpbctWP\Helpers\HTTP; |
| 45 |
use CleantalkSP\SpbctWP\API as SpbcAPI; |
| 46 |
use CleantalkSP\SpbctWP\Scanner\ScanRepository; |
| 47 |
use CleantalkSP\SpbctWP\Scanner\ScanStorage; |
| 48 |
use CleantalkSP\SpbctWP\VulnerabilityAlarm\VulnerabilityAlarm; |
| 49 |
use CleantalkSP\SpbctWP\UsersPassCheckModule\UsersPassCheckCron; |
| 50 |
use CleantalkSP\SpbctWP\DoingItWrongHandler; |
| 51 |
use CleantalkSP\SpbctWP\Scanner\ScannerAjaxEndpoints; |
| 52 |
use CleantalkSP\SpbctWP\UploadDirPreventPhpExecutionModule\UploadDirPreventPhpExecution; |
| 53 |
|
| 54 |
// Prevent direct call |
| 55 |
if ( ! defined('WPINC') ) { |
| 56 |
die('Not allowed!'); |
| 57 |
} |
| 58 |
|
| 59 |
// Getting version form main file (look above) |
| 60 |
$plugin_info = get_file_data(__FILE__, array('Version' => 'Version', 'Name' => 'Plugin Name', 'Description' => 'Description')); |
| 61 |
$plugin_version__agent = $plugin_info['Version']; |
| 62 |
// Converts xxx.xxx.xx-dev to xxx.xxx.2xx |
| 63 |
// And xxx.xxx.xx-fix to xxx.xxx.1xx |
| 64 |
if ( preg_match('@^(\d+)\.(\d+)\.(\d{1,2})-(dev|fix)$@', $plugin_version__agent, $m) ) { |
| 65 |
$plugin_version__agent = $m[1] . '.' . $m[2] . '.' . ($m[4] === 'dev' ? '2' : '1') . str_pad($m[3], 2, '0', STR_PAD_LEFT); |
| 66 |
} |
| 67 |
|
| 68 |
// Common params |
| 69 |
define('SPBC_NAME', $plugin_info['Name']); |
| 70 |
define('SPBC_VERSION', $plugin_info['Version']); |
| 71 |
define('SPBC_AGENT', 'wordpress-security-' . $plugin_version__agent); |
| 72 |
define('SPBC_USER_AGENT', 'Cleantalk-Security-Wordpress-Plugin/' . $plugin_info['Version']); |
| 73 |
define('SPBC_API_URL', 'https://api.cleantalk.org'); //Api URL |
| 74 |
define('SPBC_PLUGIN_DIR', dirname(__FILE__) . DIRECTORY_SEPARATOR); //System path. Plugin root folder with '/'. |
| 75 |
define('SPBC_PLUGIN_BASE_NAME', plugin_basename(__FILE__)); //Plugin base name. |
| 76 |
define( |
| 77 |
'SPBC_PATH', |
| 78 |
is_ssl() |
| 79 |
? preg_replace('/^http(s)?/', 'https', plugins_url('', __FILE__)) |
| 80 |
: plugins_url('', __FILE__) |
| 81 |
); //HTTP(S)? path. Plugin root folder without '/'. |
| 82 |
|
| 83 |
// SSL Serttificate path |
| 84 |
if ( ! defined('CLEANTALK_CASERT_PATH') ) { |
| 85 |
define('CLEANTALK_CASERT_PATH', file_exists(ABSPATH . WPINC . '/certificates/ca-bundle.crt') ? ABSPATH . WPINC . '/certificates/ca-bundle.crt' : ''); |
| 86 |
} |
| 87 |
|
| 88 |
// Options names |
| 89 |
define('SPBC_DATA', 'spbc_data'); //Option name with different plugin data. |
| 90 |
define('SPBC_SETTINGS', 'spbc_settings'); //Option name with plugin settings. |
| 91 |
define('SPBC_NETWORK_SETTINGS', 'spbc_network_settings'); //Option name with plugin network settings. |
| 92 |
define('SPBC_CRON', 'spbc_cron'); //Option name with scheduled tasks. |
| 93 |
define('SPBC_ERRORS', 'spbc_errors'); //Option name with errors. |
| 94 |
define('SPBC_DEBUG', 'spbc_debug'); //Option name with a debug data. Empty by default. |
| 95 |
define('SPBC_PLUGINS', 'spbc_plugins'); //Option name with a debug data. Empty by default. |
| 96 |
define('SPBC_THEMES', 'spbc_themes'); //Option name with a debug data. Empty by default. |
| 97 |
|
| 98 |
// Different params |
| 99 |
define('SPBC_REMOTE_CALL_SLEEP', 10); //Minimum time between remote call |
| 100 |
define('SPBC_LAST_ACTIONS_TO_VIEW', 20); //Nubmer of last actions to show in plugin settings page. |
| 101 |
|
| 102 |
// Auth params |
| 103 |
define('SPBC_2FA_KEY_TTL', 600); // 2fa key lifetime in seconds |
| 104 |
|
| 105 |
/** |
| 106 |
* Set SPBC_BFP_DISABLE to true to disable Brute Force Protection module. |
| 107 |
* Kept for backward compatibility; prefer settings option bfp__enabled. |
| 108 |
*/ |
| 109 |
if (!defined('SPBC_BFP_DISABLE')) { |
| 110 |
define('SPBC_BFP_DISABLE', false); |
| 111 |
} |
| 112 |
|
| 113 |
// DataBase params |
| 114 |
global $wpdb; |
| 115 |
|
| 116 |
define('SPBC_TBL_FIREWALL_DATA', $wpdb->base_prefix . 'spbc_firewall_data'); |
| 117 |
define('SPBC_TBL_FIREWALL_DATA_V4', SPBC_TBL_FIREWALL_DATA . '_v4'); |
| 118 |
define('SPBC_TBL_FIREWALL_DATA_V6', SPBC_TBL_FIREWALL_DATA . '_v6'); |
| 119 |
define('SPBC_TBL_FIREWALL_DATA__IPS', $wpdb->prefix . 'spbc_firewall__personal_ips'); |
| 120 |
define('SPBC_TBL_FIREWALL_DATA__IPS_V4', SPBC_TBL_FIREWALL_DATA__IPS . '_v4'); // Table with firewall IPS v4 |
| 121 |
define('SPBC_TBL_FIREWALL_DATA__IPS_V6', SPBC_TBL_FIREWALL_DATA__IPS . '_v6'); // Table with firewall IPS v6 |
| 122 |
define('SPBC_TBL_FIREWALL_DATA__COUNTRIES', $wpdb->prefix . 'spbc_firewall__personal_countries'); // Table with firewall countries. |
| 123 |
define('SPBC_TBL_FIREWALL_LOG', $wpdb->prefix . 'spbc_firewall_logs'); // Table with firewall logs. |
| 124 |
define('SPBC_TBL_SESSIONS', $wpdb->prefix . 'spbc_sessions'); // Alternative sessions table |
| 125 |
|
| 126 |
define('SPBC_TBL_MONITORING_USERS', $wpdb->prefix . 'spbc_monitoring_users'); // Table with users monitoring data |
| 127 |
define('SPBC_TBL_SECURITY_LOG', $wpdb->prefix . 'spbc_auth_logs'); // Table with security logs. |
| 128 |
define('SPBC_TBL_SECURITY_LOG_HOSTNAMES', $wpdb->base_prefix . 'spbc_security_log_hostnames'); // Table with security logs hostnames. |
| 129 |
define('SPBC_TBL_TC_LOG', $wpdb->prefix . 'spbc_traffic_control_logs'); // Table with traffic control logs. |
| 130 |
define('SPBC_TBL_BFP_BLOCKED', $wpdb->prefix . 'spbc_bfp_blocked'); // Table with traffic control logs. |
| 131 |
define('SPBC_TBL_SCAN_FILES', $wpdb->base_prefix . 'spbc_scan_results'); // Table with scan results. |
| 132 |
define('SPBC_TBL_SCAN_RESULTS_LOG', $wpdb->base_prefix . 'spbc_scan_results_log'); // Table with log of scan results. |
| 133 |
define('SPBC_TBL_SCAN_LINKS', $wpdb->prefix . 'spbc_scan_links_logs'); // For links scanner. Results of scan. |
| 134 |
define('SPBC_TBL_SCAN_FRONTEND', $wpdb->base_prefix . 'spbc_scan_frontend'); // For frontend scanner. Results of scan. |
| 135 |
define('SPBC_TBL_SCAN_SIGNATURES', $wpdb->base_prefix . 'spbc_scan_signatures'); // For malware signatures. |
| 136 |
define('SPBC_TBL_BACKUPED_FILES', $wpdb->prefix . 'spbc_backuped_files'); // Contains backuped files |
| 137 |
define('SPBC_TBL_BACKUPS', $wpdb->prefix . 'spbc_backups'); // Contains backup info. |
| 138 |
define('SPBC_TBL_CURE_LOG', $wpdb->base_prefix . 'spbc_cure_log'); // Table with scan results. |
| 139 |
define('SPBC_TBL_RATE_LIMITS', $wpdb->base_prefix . 'spbc_rate_limits'); // Table with scan results. |
| 140 |
define('SPBC_SURFACE_COMPLETED_DIRS', $wpdb->base_prefix . 'spbc_surface_completed_dirs'); // Table with scan results. |
| 141 |
define('SPBC_TBL_PSC_PLUGINS_CACHE', $wpdb->prefix . 'spbc_psc_plugins_cache'); // PSC certified plugins cache. |
| 142 |
define('SPBC_SELECT_LIMIT', 1500); // Select limit for logs. |
| 143 |
define('SPBC_WRITE_LIMIT', 5000); // Write limit for firewall data. |
| 144 |
|
| 145 |
// Multisite |
| 146 |
define('SPBC_WPMS', (is_multisite() ? true : false)); // WMPS is enabled |
| 147 |
|
| 148 |
// Scanner params for background scanning |
| 149 |
define('SPBC_SCAN_SURFACE_AMOUNT', 1000); // Surface scan amount for 1 iteration |
| 150 |
define('SPBC_SCAN_SURFACE_PERIOD', 30); // Surface scan call period |
| 151 |
define('SPBC_SCAN_MODIFIED_AMOUNT', 5); // Deep scan amount for 1 iteration |
| 152 |
define('SPBC_SCAN_SIGNATURE_AMOUNT', 20); // Deep scan amount for 1 iteration |
| 153 |
define('SPBC_SCAN_MODIFIED_PERIOD', 30); // Deep scan call period |
| 154 |
define('SPBC_SCAN_LINKS_AMOUNT', 10); // Links scan amount for 1 iteration |
| 155 |
define('SPBC_SCAN_FRONTEND_AMOUNT', 10); // Links scan amount for 1 iteration |
| 156 |
define('SPBC_SCAN_LINKS_PERIOD', 30); // Links scan call period |
| 157 |
define('SPBC_PSCAN_UPDATE_FILES_STATUS_PERIOD', 60); // Check cloud analysis files status period |
| 158 |
define('SPBC_PSCAN_RESEND_FILES_STATUS_PERIOD', 300); // Resend files |
| 159 |
|
| 160 |
// brief data limits |
| 161 |
define('SPBC_BRIEF_DATA_DAYS_LIMIT', 7); // how many days will be logs looked for |
| 162 |
define('SPBC_BRIEF_DATA_ACTIONS_LIMIT', 10); // how many actions will be logs looked for |
| 163 |
|
| 164 |
|
| 165 |
require_once SPBC_PLUGIN_DIR . 'lib/spbc-php-patch.php'; // PHP functions patches |
| 166 |
require_once SPBC_PLUGIN_DIR . 'lib/autoloader.php'; // Autoloader |
| 167 |
|
| 168 |
/** |
| 169 |
* Early translation call. |
| 170 |
* @param string $text text to translate |
| 171 |
* @return mixed|string|null |
| 172 |
*/ |
| 173 |
function __spbc($text) |
| 174 |
{ |
| 175 |
static $et; |
| 176 |
if (!isset($et) || !$et instanceof SpbcET) { |
| 177 |
$et = new SpbcET( |
| 178 |
SPBC_PLUGIN_DIR, |
| 179 |
WP_CONTENT_DIR |
| 180 |
); |
| 181 |
if (did_action('init')) { |
| 182 |
$et->checkWPTranslationReady(); |
| 183 |
} else { |
| 184 |
add_action('init', [$et, 'checkWPTranslationReady']); |
| 185 |
} |
| 186 |
} |
| 187 |
return $et->translate($text); |
| 188 |
} |
| 189 |
|
| 190 |
require_once SPBC_PLUGIN_DIR . 'inc/fw-update.php'; |
| 191 |
|
| 192 |
// Misc libs |
| 193 |
require_once SPBC_PLUGIN_DIR . 'inc/spbc-tools.php'; // Different helper functions |
| 194 |
require_once SPBC_PLUGIN_DIR . 'inc/spbc-pluggable.php'; // WordPress functions |
| 195 |
require_once SPBC_PLUGIN_DIR . 'inc/spbc-scanner.php'; |
| 196 |
require_once(SPBC_PLUGIN_DIR . 'inc/spbc-wpcli.php'); |
| 197 |
|
| 198 |
// ArrayObject with settings and other global variables |
| 199 |
global $spbc; |
| 200 |
$spbc = new State( |
| 201 |
'spbc', |
| 202 |
array( |
| 203 |
'settings', |
| 204 |
'data', |
| 205 |
'remote_calls', |
| 206 |
'debug', |
| 207 |
'installing', |
| 208 |
'errors', |
| 209 |
'fw_stats', |
| 210 |
'scan_plugins_info', |
| 211 |
'scan_themes_info' |
| 212 |
), |
| 213 |
is_multisite(), |
| 214 |
is_main_site() |
| 215 |
); |
| 216 |
|
| 217 |
require_once SPBC_PLUGIN_DIR . 'inc/spbc-auth.php'; |
| 218 |
|
| 219 |
// Update plugin's data to current version |
| 220 |
spbc_update_actions(); |
| 221 |
|
| 222 |
// Collect and partially suppress doing_it_wrong_errors |
| 223 |
new DoingItWrongHandler($spbc); |
| 224 |
|
| 225 |
add_action('init', function () { |
| 226 |
// Cron |
| 227 |
global $spbc_cron, $spbc; // Letting know functions that they are running under spbc_cron |
| 228 |
$spbc_cron = new SpbcCron(); |
| 229 |
! SpbcRemoteCalls::check() && $spbc_cron->execute(); |
| 230 |
unset($spbc_cron); |
| 231 |
|
| 232 |
// Remote calls |
| 233 |
if ( SpbcRemoteCalls::check() ) { |
| 234 |
try { |
| 235 |
if ( Get::getString('spbc_remote_call_action') === 'run_service_template_get' ) { |
| 236 |
require_once(SPBC_PLUGIN_DIR . 'inc/spbc-settings.php'); |
| 237 |
} |
| 238 |
$rc = new SpbcRemoteCalls($spbc); |
| 239 |
$rc->process(); |
| 240 |
} catch ( Exception $e ) { |
| 241 |
die(json_encode(array('ERROR:' => $e->getMessage()))); |
| 242 |
} |
| 243 |
} |
| 244 |
}); |
| 245 |
|
| 246 |
//First start |
| 247 |
// Do recheck the settings key |
| 248 |
if ( !$spbc->key_is_ok || !empty($spbc->errors['apikey']) ) { |
| 249 |
!empty($spbc->settings['api_key']) && spbc_check_account_status($spbc->settings['api_key']); |
| 250 |
} |
| 251 |
|
| 252 |
if ( $spbc->settings && $spbc->key_is_ok) { |
| 253 |
require_once SPBC_PLUGIN_DIR . 'inc/spbc-firewall.php'; |
| 254 |
add_action('init', function () use ($spbc) { |
| 255 |
// protect author login enumeration and password reset confirmation text |
| 256 |
$login_protector = new LoginCollectingProtector($spbc); |
| 257 |
$login_protector->init(); |
| 258 |
if ( is_admin() && spbc_is_user_logged_in() ) { |
| 259 |
//do this if in admin area and user is logged in - check only admin area (WAF run) |
| 260 |
if ( ! spbc_firewall_skip_check()) { |
| 261 |
spbc_firewall_check_admin_area(); |
| 262 |
} |
| 263 |
|
| 264 |
if ( ! spbc_firewall_skip_check_uploadchecker()) { |
| 265 |
spbc_upload_checker__check(); |
| 266 |
} |
| 267 |
} else { |
| 268 |
//if not in admin area and user is not logged in - check with all modules |
| 269 |
if ( ! spbc_firewall_skip_check()) { |
| 270 |
spbc_firewall__check(); |
| 271 |
} |
| 272 |
} |
| 273 |
}); |
| 274 |
} |
| 275 |
|
| 276 |
// Disable XMLRPC if setting is enabled |
| 277 |
if ( $spbc->settings['wp__disable_xmlrpc'] ) { |
| 278 |
add_filter('xmlrpc_enabled', '__return_false'); |
| 279 |
} |
| 280 |
|
| 281 |
// Disable WordPress REST API for non-authenticated |
| 282 |
if ( $spbc->settings['wp__disable_rest_api'] == '2' ) { |
| 283 |
add_filter( |
| 284 |
'rest_authentication_errors', |
| 285 |
function ($result) { |
| 286 |
if ( empty($result) && ! is_user_logged_in() ) { |
| 287 |
return new WP_Error( |
| 288 |
'rest_not_logged_in', |
| 289 |
'You are not currently logged in. (Security by CleanTalk)', |
| 290 |
array('status' => 401) |
| 291 |
); |
| 292 |
} |
| 293 |
return $result; |
| 294 |
} |
| 295 |
); |
| 296 |
} |
| 297 |
|
| 298 |
// Disable the WordPress endpoint "users" REST API |
| 299 |
if ($spbc->settings['wp__disable_rest_api'] == '1') { |
| 300 |
add_filter( |
| 301 |
'rest_authentication_errors', |
| 302 |
function ($result) { |
| 303 |
if ( |
| 304 |
Server::inUriCanonical('/wp/') && |
| 305 |
Server::inUriCanonical('users') && |
| 306 |
!is_user_logged_in() // do not check if user logged in |
| 307 |
) { |
| 308 |
return new WP_Error( |
| 309 |
'access_denied', |
| 310 |
__('Current REST route access denied. (Security by CleanTalk)'), |
| 311 |
array( 'status' => 401 ) |
| 312 |
); |
| 313 |
} |
| 314 |
return $result; |
| 315 |
} |
| 316 |
); |
| 317 |
} |
| 318 |
|
| 319 |
/** |
| 320 |
* This is the Cron handler for the `spbc_security_check_vulnerabilities` task |
| 321 |
* |
| 322 |
* @return array|void |
| 323 |
*/ |
| 324 |
function spbc_security_check_vulnerabilities() |
| 325 |
{ |
| 326 |
global $spbc; |
| 327 |
try { |
| 328 |
VulnerabilityAlarm::updateWPModulesVulnerabilities(); |
| 329 |
$spbc->data['spbc_security_check_vulnerabilities_last_call'] = time(); |
| 330 |
$spbc->save('data'); |
| 331 |
// Send found vulnerabilities to the cloud |
| 332 |
VulnerabilityAlarmService::sendReport(); |
| 333 |
} catch ( \Exception $exception ) { |
| 334 |
return ['error' => $exception->getMessage()]; |
| 335 |
} |
| 336 |
} |
| 337 |
|
| 338 |
/** |
| 339 |
* This is the Cron handler for the `spbc_upload_dir_prevent_php_execution` task |
| 340 |
* |
| 341 |
* @return array|bool |
| 342 |
*/ |
| 343 |
function spbc_upload_dir_prevent_php_execution() |
| 344 |
{ |
| 345 |
global $spbc; |
| 346 |
|
| 347 |
if ($spbc->settings['wp__upload_dir_prevent_php_execution']) { |
| 348 |
return UploadDirPreventPhpExecution::handle(); |
| 349 |
} |
| 350 |
} |
| 351 |
|
| 352 |
/** |
| 353 |
* This is the Cron handler for the `spbc_cron__users_pass_check_routine` task |
| 354 |
* |
| 355 |
* @return void |
| 356 |
*/ |
| 357 |
function spbc_cron__users_pass_check_routine() |
| 358 |
{ |
| 359 |
global $spbc; |
| 360 |
|
| 361 |
if ($spbc->settings['check_pass__enable'] == '0') { |
| 362 |
return; |
| 363 |
} |
| 364 |
|
| 365 |
UsersPassCheckCron::handle(); |
| 366 |
} |
| 367 |
|
| 368 |
/** |
| 369 |
* This is the Cron handler for the `spbc_cron__users_pass_check_worker` task |
| 370 |
* |
| 371 |
* @return void |
| 372 |
*/ |
| 373 |
function spbc_cron__users_pass_check_worker() |
| 374 |
{ |
| 375 |
global $spbc; |
| 376 |
|
| 377 |
if ($spbc->settings['check_pass__enable'] == '0') { |
| 378 |
return; |
| 379 |
} |
| 380 |
|
| 381 |
UsersPassCheckCron::worker(); |
| 382 |
} |
| 383 |
|
| 384 |
/** |
| 385 |
* Update scanner exclusions from external files. |
| 386 |
* @return array|true array on error, true otherwise |
| 387 |
*/ |
| 388 |
function spbc_update_scan_settings_exclusions() |
| 389 |
{ |
| 390 |
global $spbc; |
| 391 |
|
| 392 |
$path_exclusions_view = $spbc->settings['scanner__path_exclusions_view']; |
| 393 |
$domains_exclusions_view = $spbc->settings['scanner__frontend_analysis__domains_exclusions_view']; |
| 394 |
|
| 395 |
$settings = clone $spbc->settings; |
| 396 |
$settings['scanner__path_exclusions_view'] = $path_exclusions_view; |
| 397 |
$settings['scanner__frontend_analysis__domains_exclusions_view'] = $domains_exclusions_view; |
| 398 |
|
| 399 |
try { |
| 400 |
// update files exclusion from external |
| 401 |
$pathExclusion = new FilesScanPathExclusion(); |
| 402 |
$path_exclusions_view = $pathExclusion->pathExclusionsView($path_exclusions_view); |
| 403 |
$settings['scanner__path_exclusions_view'] = $path_exclusions_view; |
| 404 |
$path_exclusions = $pathExclusion->pathExclusions($path_exclusions_view); |
| 405 |
$settings['scanner__path_exclusions'] = $path_exclusions; |
| 406 |
|
| 407 |
// update domains exclusion from external |
| 408 |
$domainExclusion = new FrontendScanDomainExclusion(); |
| 409 |
$domains_exclusions_view = $domainExclusion->frontendScanDomainExclusionsView($domains_exclusions_view); |
| 410 |
$settings['scanner__frontend_analysis__domains_exclusions_view'] = $domains_exclusions_view; |
| 411 |
$domains_exclusions = $domainExclusion->domainExclusions($domains_exclusions_view); |
| 412 |
$settings['scanner__frontend_analysis__domains_exclusions'] = $domains_exclusions; |
| 413 |
|
| 414 |
// reset frontend results |
| 415 |
$domainExclusion->resetScannerFrontendResult($settings); |
| 416 |
|
| 417 |
$spbc->settings = $settings; |
| 418 |
$spbc->save('settings'); |
| 419 |
} catch ( \Exception $exception ) { |
| 420 |
return ['error' => $exception->getMessage()]; |
| 421 |
} |
| 422 |
|
| 423 |
return true; |
| 424 |
} |
| 425 |
|
| 426 |
function spbc_change_author_name($link, $_author_id, $_author_nicename) |
| 427 |
{ |
| 428 |
$link = preg_replace('@(.*?)([\w-]+\/)$@', '$1honeypot_login_' . microtime(true), $link); |
| 429 |
wp_redirect($link); |
| 430 |
die(); |
| 431 |
} |
| 432 |
|
| 433 |
if ( $spbc->settings['monitoring__users'] ) { |
| 434 |
add_action('admin_head', array( '\CleantalkSP\Monitoring\User', 'record' )); |
| 435 |
add_action('wp_head', array( '\CleantalkSP\Monitoring\User', 'record' )); |
| 436 |
} |
| 437 |
|
| 438 |
//Password-protected pages also uses wp-login page, we should not break it |
| 439 |
if ( $spbc->settings['login_page_rename__enabled'] ) { |
| 440 |
new RenameLoginPage( |
| 441 |
$spbc->settings['login_page_rename__name'], |
| 442 |
$spbc->settings['login_page_rename__redirect'] |
| 443 |
); |
| 444 |
} |
| 445 |
|
| 446 |
// Logged hooks |
| 447 |
register_activation_hook(__FILE__, 'spbc_activation'); |
| 448 |
register_deactivation_hook(__FILE__, 'spbc_deactivation'); |
| 449 |
register_uninstall_hook(__FILE__, 'spbc_uninstall'); |
| 450 |
|
| 451 |
// Hook for newly added blog |
| 452 |
Activator::addActionForNetworkBlogLegacy(get_bloginfo('version')); |
| 453 |
|
| 454 |
add_action('plugins_loaded', 'spbc_plugin_loaded', 1); // Main hook |
| 455 |
|
| 456 |
// Posts hooks |
| 457 |
add_action('wp_insert_post', 'spbc_update_postmeta_links', 10, 3); |
| 458 |
add_action('wp_insert_comment', 'spbc_update_postmeta_links__by_comment', 10, 2); |
| 459 |
|
| 460 |
// Set headers |
| 461 |
add_action('init', 'spbc_set_headers'); |
| 462 |
add_action('login_enqueue_scripts', 'spbc_attach_public_css'); |
| 463 |
|
| 464 |
if ( $spbc->settings['spbc_trusted_and_affiliate__footer'] === '1' ) { |
| 465 |
add_action('wp_enqueue_scripts', 'spbc_attach_public_css'); |
| 466 |
add_action('wp_footer', 'spbc_hook__wp_footer_trusted_text', 998); |
| 467 |
} |
| 468 |
|
| 469 |
if ( is_admin() || is_network_admin() ) { |
| 470 |
include_once SPBC_PLUGIN_DIR . 'inc/spbc-admin.php'; |
| 471 |
add_action('init', function () { |
| 472 |
include_once SPBC_PLUGIN_DIR . 'templates/spbc_settings_main.php'; // Templates for settings pgae |
| 473 |
}); |
| 474 |
include_once SPBC_PLUGIN_DIR . 'inc/spbct-sync-react.php'; |
| 475 |
|
| 476 |
// Async loading for JavaScript |
| 477 |
add_filter('script_loader_tag', array('CleantalkSP\SpbctWP\SpbcEnqueue', 'addScriptAttributes'), 10, 3); |
| 478 |
add_action('admin_init', array('CleantalkSP\SpbctWP\Activator', 'redirectAfterActivation'), 1); // Redirect after activation |
| 479 |
add_action('admin_init', 'spbc_admin_init', 1, 1); // Main admin hook |
| 480 |
add_action('admin_menu', 'spbc_admin_add_page'); // Admin pages |
| 481 |
add_action('network_admin_menu', 'spbc_admin_add_page'); // Network admin pages |
| 482 |
add_action('admin_enqueue_scripts', array('CleantalkSP\SpbctWP\SpbcEnqueue', 'handleEnqueueHook')); // Scripts |
| 483 |
|
| 484 |
if ( Post::getInt('spbc_brief_refresh') === 1 ) { |
| 485 |
add_action('admin_init', 'spbc_set_brief_data', 1); |
| 486 |
} |
| 487 |
|
| 488 |
if ( $spbc->settings['wp__dashboard_widget__show'] ) { |
| 489 |
add_action('wp_dashboard_setup', 'spbc_widget_scripts_init'); |
| 490 |
add_action('wp_dashboard_setup', 'spbc_dashboard_statistics_widget'); |
| 491 |
} |
| 492 |
|
| 493 |
|
| 494 |
add_action('admin_init', function () { |
| 495 |
global $spbc; |
| 496 |
$admin_banners_handler = new \CleantalkSP\SpbctWP\AdminBannersModule\AdminBannersHandler($spbc); |
| 497 |
$admin_banners_handler->handle(); |
| 498 |
}); |
| 499 |
|
| 500 |
// Customize row with the plugin on plugins list page. |
| 501 |
if ( ( isset($pagenow) && $pagenow === 'plugins.php' ) || ( isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], 'plugins.php') !== false ) ) { |
| 502 |
add_filter('plugin_action_links_' . SPBC_PLUGIN_BASE_NAME, 'spbc_plugin_action_links', 10, 2); |
| 503 |
add_filter('network_admin_plugin_action_links_' . SPBC_PLUGIN_BASE_NAME, 'spbc_plugin_action_links', 10, 2); |
| 504 |
add_filter('all_plugins', 'spbc_admin__change_plugin_description'); |
| 505 |
add_filter('plugin_row_meta', 'spbc_plugin_links_meta', 10, 2); |
| 506 |
} |
| 507 |
} |
| 508 |
|
| 509 |
add_action('init', function () use ($spbc) { |
| 510 |
if ( $spbc->feature_restrictions->getState($spbc, 'fswatcher')->is_active && $spbc->settings['scanner__fs_watcher'] ) { |
| 511 |
FSWatcherController::getInstance(); |
| 512 |
} |
| 513 |
}); |
| 514 |
|
| 515 |
function spbc_set_headers() |
| 516 |
{ |
| 517 |
global $spbc; |
| 518 |
if ( ! headers_sent() ) { |
| 519 |
// Additional headers |
| 520 |
if ( $spbc->settings['data__additional_headers'] ) { |
| 521 |
header('X-XSS-Protection: 1; mode=block'); |
| 522 |
header('X-Content-Type-Options: nosniff'); |
| 523 |
header('Strict-Transport-Security: max-age=31536000; includeSubDomains'); |
| 524 |
header('Referrer-Policy: strict-origin-when-cross-origin'); |
| 525 |
} |
| 526 |
|
| 527 |
// Forbid to show in iframes |
| 528 |
if ( $spbc->settings['misc__forbid_to_show_in_iframes'] ) { |
| 529 |
header('X-Frame-Options: sameorigin', false); |
| 530 |
} |
| 531 |
|
| 532 |
// Set cookie to detect any logged in user |
| 533 |
if ( |
| 534 |
spbc_is_user_logged_in() && |
| 535 |
! empty($spbc->settings['data__set_cookies']) && |
| 536 |
( |
| 537 |
! Cookie::getString('spbc_is_logged_in') || |
| 538 |
Cookie::getString('spbc_is_logged_in') !== md5($spbc->data['salt'] . get_option('home')) |
| 539 |
) |
| 540 |
) { |
| 541 |
// skip rewriting spbc_is_logged_in cookie on favicon request for WPMS - its always run on main site url and returns it`s home url |
| 542 |
if ( |
| 543 |
is_multisite() && |
| 544 |
strpos(Server::get('REQUEST_URI', null, 'url'), 'favicon.ico') !== false |
| 545 |
) { |
| 546 |
return; |
| 547 |
} |
| 548 |
//rewrite spbc_is_logged_in cookie |
| 549 |
Cookie::set('spbc_is_logged_in', md5($spbc->data['salt'] . get_option('home')), time() + 86400 * 365, '/'); |
| 550 |
} |
| 551 |
} |
| 552 |
} |
| 553 |
|
| 554 |
function spbc_update_actions() |
| 555 |
{ |
| 556 |
global $spbc; |
| 557 |
|
| 558 |
//Update logic |
| 559 |
$current_version = $spbc->data['plugin_version']; |
| 560 |
|
| 561 |
if ( $current_version != SPBC_VERSION ) { |
| 562 |
//Migrate DB data on updating to 2.128.1 |
| 563 |
add_action('ColumnCreator_before_drop_column_analysis_status', [UpdaterScripts::class, 'migrateDbData_2_128_1']); |
| 564 |
add_action('ColumnCreator_before_change_column_event', [UpdaterScripts::class, 'migrateDbData_2_141_0']); |
| 565 |
|
| 566 |
// Perform a transaction and exit transaction ID isn't match |
| 567 |
if ( ! Transaction::get('updater', 5)->perform() ) { |
| 568 |
return; |
| 569 |
} |
| 570 |
|
| 571 |
Updater::runUpdateScripts($current_version, SPBC_VERSION); |
| 572 |
|
| 573 |
$spbc->data['plugin_version'] = SPBC_VERSION; |
| 574 |
$spbc->save('data'); |
| 575 |
|
| 576 |
Transaction::get('updater')->clearTransactionTimer(); |
| 577 |
} |
| 578 |
} |
| 579 |
|
| 580 |
/** |
| 581 |
* Plugin activation |
| 582 |
* |
| 583 |
* @param $network |
| 584 |
* @param $redirect |
| 585 |
* |
| 586 |
* @return void |
| 587 |
* @throws Exception |
| 588 |
*/ |
| 589 |
function spbc_activation($network, $redirect = true) |
| 590 |
{ |
| 591 |
Activator::activation($network, $redirect); |
| 592 |
} |
| 593 |
|
| 594 |
|
| 595 |
/** |
| 596 |
* A code during plugin deactivation. |
| 597 |
* |
| 598 |
* @param $network |
| 599 |
* |
| 600 |
* @return void |
| 601 |
*/ |
| 602 |
function spbc_deactivation($network) |
| 603 |
{ |
| 604 |
\CleantalkSP\SpbctWP\Deactivator::deactivation($network); |
| 605 |
} |
| 606 |
|
| 607 |
/** |
| 608 |
* Run deactivation process (complete deactivation forced) for hook register_uninstall_hook. |
| 609 |
* Notice: this hook returns no callback arg! |
| 610 |
* @return void |
| 611 |
*/ |
| 612 |
function spbc_uninstall() |
| 613 |
{ |
| 614 |
\CleantalkSP\SpbctWP\Deactivator::deactivation(true, false, true); |
| 615 |
} |
| 616 |
|
| 617 |
/** |
| 618 |
* @deprecated 2.125 use Deactivator::deleteBlogTables() |
| 619 |
* @return void |
| 620 |
*/ |
| 621 |
function spbc_deactivation__delete_blog_tables() //deprecated |
| 622 |
{ |
| 623 |
\CleantalkSP\SpbctWP\Deactivator::deleteBlogTables(); |
| 624 |
} |
| 625 |
|
| 626 |
/** |
| 627 |
* @deprecated 2.125 use Deactivator::deleteCommonTables() |
| 628 |
* @return void |
| 629 |
*/ |
| 630 |
function spbc_deactivation__delete_common_tables() //deprecated |
| 631 |
{ |
| 632 |
\CleantalkSP\SpbctWP\Deactivator::deleteCommonTables(); |
| 633 |
} |
| 634 |
|
| 635 |
// Misc functions to test the plugin. |
| 636 |
function spbc_plugin_loaded() |
| 637 |
{ |
| 638 |
global $spbc; |
| 639 |
|
| 640 |
if ( is_admin() || is_network_admin() ) { |
| 641 |
$dir = plugin_basename(dirname(__FILE__)) . '/i18n'; |
| 642 |
load_plugin_textdomain('security-malware-firewall', false, $dir); |
| 643 |
} |
| 644 |
|
| 645 |
if ( $spbc->settings['spbc_trusted_and_affiliate__shortcode'] === '1' ) { |
| 646 |
add_action('wp_enqueue_scripts', 'spbc_attach_public_css'); |
| 647 |
add_shortcode('cleantalk_security_affiliate_link', 'spbc_trusted_text_shortcode_handler'); |
| 648 |
} |
| 649 |
} |
| 650 |
|
| 651 |
/** |
| 652 |
* Check brute force attack |
| 653 |
* |
| 654 |
* @return void |
| 655 |
*/ |
| 656 |
function spbc_authenticate__check_brute_force() |
| 657 |
{ |
| 658 |
global $spbc; |
| 659 |
|
| 660 |
$bfp = new BFP( |
| 661 |
array( |
| 662 |
'api_key' => $spbc->api_key, |
| 663 |
'state' => $spbc, |
| 664 |
'is_login_page' => spbc_is_login_page_request(), |
| 665 |
'is_logged_in' => Cookie::getString('spbc_is_logged_in') === md5($spbc->data['salt'] . get_option('home')), |
| 666 |
'bf_limit' => $spbc->settings['bfp__allowed_wrong_auths'], |
| 667 |
'block_period' => $spbc->settings['bfp__block_period__5_fails'], |
| 668 |
'count_period' => $spbc->settings['bfp__count_interval'], |
| 669 |
) |
| 670 |
); |
| 671 |
|
| 672 |
$bfp->setDb(new DB()); |
| 673 |
$bfp->setIpArray([IP::get()]); |
| 674 |
$bfp_result = $bfp->check(); |
| 675 |
$bfp->middleAction(); |
| 676 |
|
| 677 |
if (!empty($bfp_result)) { |
| 678 |
$bfp->_die($bfp_result[0]); |
| 679 |
} |
| 680 |
} |
| 681 |
|
| 682 |
// |
| 683 |
// Sorts some data. |
| 684 |
// |
| 685 |
function spbc_usort_desc($a, $b) |
| 686 |
{ |
| 687 |
return $b->datetime_ts - $a->datetime_ts; |
| 688 |
} |
| 689 |
|
| 690 |
/** |
| 691 |
* Function to get the countries by IPs list. |
| 692 |
* |
| 693 |
* @param $ips_data |
| 694 |
* |
| 695 |
* @return array |
| 696 |
*/ |
| 697 |
function spbc_get_countries_by_ips($ips_data = '') |
| 698 |
{ |
| 699 |
$ips_c = array(); |
| 700 |
|
| 701 |
if ( $ips_data === '' ) { |
| 702 |
return $ips_c; |
| 703 |
} |
| 704 |
|
| 705 |
$result = SpbcAPI::method__ip_info($ips_data); |
| 706 |
|
| 707 |
if ( empty($result['error']) ) { |
| 708 |
foreach ( $result as $ip_dec => $v2 ) { |
| 709 |
if ( isset($v2['country_code']) ) { |
| 710 |
$ips_c[ $ip_dec ]['country_code'] = $v2['country_code']; |
| 711 |
} |
| 712 |
if ( isset($v2['country_name']) ) { |
| 713 |
$ips_c[ $ip_dec ]['country_name'] = $v2['country_name']; |
| 714 |
} |
| 715 |
if ( isset($v2['subdivision']) && is_string($v2['subdivision']) && $v2['subdivision'] !== '' ) { |
| 716 |
$ips_c[ $ip_dec ]['subdivision'] = $v2['subdivision']; |
| 717 |
} |
| 718 |
if ( isset($v2['city']) && is_string($v2['city']) && $v2['city'] !== '' ) { |
| 719 |
$ips_c[ $ip_dec ]['city'] = $v2['city']; |
| 720 |
} |
| 721 |
} |
| 722 |
} |
| 723 |
|
| 724 |
return $ips_c; |
| 725 |
} |
| 726 |
|
| 727 |
/** |
| 728 |
* Gets and write new signatures in local database |
| 729 |
* @param bool $force_update if true, ignores last update and overwrite current signatures |
| 730 |
* @return bool|array |
| 731 |
* @global State $spbc |
| 732 |
* @global WPDB $wpdb |
| 733 |
*/ |
| 734 |
function spbc_scanner__signatures_update($force_update = false) |
| 735 |
{ |
| 736 |
global $spbc; |
| 737 |
|
| 738 |
/** |
| 739 |
* @psalm-suppress InvalidScalarArgument |
| 740 |
*/ |
| 741 |
$spbc->error_delete('scanner_update_signatures_bad_signatures', 'save'); |
| 742 |
|
| 743 |
$latest_signature_submitted_time = $force_update ? 0 : SignatureAnalysisFacade::getLatestSignatureSubmittedTime(); |
| 744 |
|
| 745 |
$signatures_from_cloud = SignatureAnalysisFacade::getSignaturesFromCloud($latest_signature_submitted_time); |
| 746 |
|
| 747 |
// Signatures updated |
| 748 |
if (!$force_update && isset($signatures_from_cloud['error']) && $signatures_from_cloud['error'] === 'UP_TO_DATE') { |
| 749 |
return array('success' => 'UP_TO_DATE'); |
| 750 |
} |
| 751 |
|
| 752 |
// There is errors |
| 753 |
if (isset($signatures_from_cloud['error'])) { |
| 754 |
return $signatures_from_cloud; |
| 755 |
} |
| 756 |
|
| 757 |
$signatures = $signatures_from_cloud['values']; |
| 758 |
$map = $signatures_from_cloud['map']; |
| 759 |
|
| 760 |
SignatureAnalysisFacade::clearSignaturesTable(); |
| 761 |
|
| 762 |
$signatures_added = SignatureAnalysisFacade::addSignaturesToDb($map, $signatures); |
| 763 |
|
| 764 |
if (!$signatures_added) { |
| 765 |
// Attempt to record one at a time |
| 766 |
$signatures_added = SignatureAnalysisFacade::addSignaturesToDbOneByOne($map, $signatures); |
| 767 |
|
| 768 |
if (isset($signatures_added['bad_signatures'])) { |
| 769 |
$spbc->error_add('scanner_update_signatures_bad_signatures', $signatures_added['bad_signatures']); |
| 770 |
} |
| 771 |
} |
| 772 |
|
| 773 |
$spbc->data['scanner']['last_signature_update'] = current_time('timestamp'); |
| 774 |
$spbc->data['scanner']['signature_count'] = count($signatures); |
| 775 |
$spbc->save('data'); |
| 776 |
|
| 777 |
return true; |
| 778 |
} |
| 779 |
|
| 780 |
/** |
| 781 |
* Ajax handler for sending Security FireWall logs |
| 782 |
*/ |
| 783 |
function spbc_send_firewall_logs_ajax_handler() |
| 784 |
{ |
| 785 |
spbc_check_ajax_referer('spbc_secret_nonce', 'security'); |
| 786 |
$result = spbc_send_firewall_logs(); |
| 787 |
wp_send_json($result); |
| 788 |
} |
| 789 |
|
| 790 |
/** |
| 791 |
* Sending Security FireWall logs |
| 792 |
* |
| 793 |
* @param $api_key |
| 794 |
* |
| 795 |
* @return array|int |
| 796 |
*/ |
| 797 |
function spbc_send_firewall_logs($api_key = false) |
| 798 |
{ |
| 799 |
global $spbc; |
| 800 |
|
| 801 |
$api_key = ! empty($api_key) ? $api_key : $spbc->api_key; |
| 802 |
|
| 803 |
if ( ! empty($api_key) ) { |
| 804 |
$result = FW::sendLog( |
| 805 |
DB::getInstance(), |
| 806 |
SPBC_TBL_FIREWALL_LOG, |
| 807 |
$api_key |
| 808 |
); |
| 809 |
|
| 810 |
if ( empty($result['error']) ) { |
| 811 |
$spbc->fw_stats['last_send'] = current_time('timestamp'); |
| 812 |
$spbc->fw_stats['last_send_count'] = $result; |
| 813 |
$spbc->save('fw_stats', true, false); |
| 814 |
|
| 815 |
return $result; |
| 816 |
} |
| 817 |
|
| 818 |
return $result; |
| 819 |
} |
| 820 |
|
| 821 |
return array( |
| 822 |
'error' => 'KEY_EMPTY' |
| 823 |
); |
| 824 |
} |
| 825 |
|
| 826 |
/** |
| 827 |
* Drop Security FireWall data |
| 828 |
* |
| 829 |
* @return bool|string[] |
| 830 |
*/ |
| 831 |
function spbc_security_firewall_drop() |
| 832 |
{ |
| 833 |
global $wpdb; |
| 834 |
|
| 835 |
// @psalm-suppress WpdbUnsafeMethodsIssue |
| 836 |
$result = $wpdb->query('DELETE FROM `' . SPBC_TBL_FIREWALL_DATA . '`;'); |
| 837 |
|
| 838 |
if ( $result !== false ) { |
| 839 |
return true; |
| 840 |
} |
| 841 |
|
| 842 |
return array( 'error' => 'DELETE_ERROR' ); |
| 843 |
} |
| 844 |
|
| 845 |
/** |
| 846 |
* Handle firewall private_records remote call. |
| 847 |
* @param $action string 'add','delete' |
| 848 |
* @param $test_data string JSON string used in test cases |
| 849 |
* @return string JSON string of results |
| 850 |
* @throws Exception |
| 851 |
*/ |
| 852 |
function spbct_sfw_private_records_handler($action, $test_data = null) |
| 853 |
{ |
| 854 |
|
| 855 |
$error = 'secfw_private_records_handler: '; |
| 856 |
|
| 857 |
if ( !empty($action) && (in_array($action, array('add', 'delete'))) ) { |
| 858 |
$metadata = !empty($test_data) ? $test_data : Post::getString('metadata'); // validation is done next line |
| 859 |
|
| 860 |
/** |
| 861 |
* Validate JSON |
| 862 |
*/ |
| 863 |
if ( !empty($metadata) ) { |
| 864 |
$metadata = json_decode(stripslashes($metadata), true); |
| 865 |
if ( $metadata === 'NULL' || $metadata === null ) { |
| 866 |
throw new InvalidArgumentException($error . 'metadata JSON decoding failed'); |
| 867 |
} |
| 868 |
} else { |
| 869 |
throw new InvalidArgumentException($error . 'metadata is empty'); |
| 870 |
} |
| 871 |
|
| 872 |
foreach ( $metadata as $_key => &$row ) { |
| 873 |
$row = explode(',', $row); |
| 874 |
|
| 875 |
/** |
| 876 |
* Validation of JSON decoded array data |
| 877 |
*/ |
| 878 |
$ip_validated = false; |
| 879 |
$validation_error = ''; |
| 880 |
|
| 881 |
//validate IP |
| 882 |
if ( IP::validate($row[0]) === 'v6' ) { |
| 883 |
$ip_validated = $row[0]; |
| 884 |
} elseif ( |
| 885 |
IP::validate(long2ip((int)$row[0])) === 'v4' |
| 886 |
&& (int)($row[0]) === ip2long(long2ip((int)$row[0])) |
| 887 |
) { |
| 888 |
$ip_validated = (int)$row[0]; |
| 889 |
} else { |
| 890 |
$validation_error = 'network value does not look like IP address '; |
| 891 |
} |
| 892 |
|
| 893 |
//do this to get info more obvious |
| 894 |
$metadata_assoc_array = array( |
| 895 |
'network' => $ip_validated ?: null, |
| 896 |
'mask' => (int)$row[1], |
| 897 |
'status' => isset($row[2]) && $row[2] !== '' ? (int)$row[2] : null, |
| 898 |
); |
| 899 |
|
| 900 |
//validate mask and status |
| 901 |
if ( $metadata_assoc_array['mask'] === 0 |
| 902 |
|| $metadata_assoc_array['mask'] > 4294967295 |
| 903 |
) { |
| 904 |
$validation_error = 'metadata validate failed on "mask" value'; |
| 905 |
} |
| 906 |
|
| 907 |
//only for adding |
| 908 |
if ( $action === 'add' ) { |
| 909 |
if ( !in_array($metadata_assoc_array['status'], array(-4, -3, -2, -1, 0, 1, 2, 99)) ) { |
| 910 |
$validation_error = 'metadata validate failed on "status" value'; |
| 911 |
} |
| 912 |
} |
| 913 |
|
| 914 |
if ( !empty($validation_error) ) { |
| 915 |
throw new InvalidArgumentException($error . $validation_error); |
| 916 |
} |
| 917 |
|
| 918 |
/** |
| 919 |
* Ip version logic |
| 920 |
*/ |
| 921 |
if ( is_string($metadata_assoc_array['network']) ) { |
| 922 |
$metadata_assoc_array['network'] = IP::convertIPv6ToFourIPv4(IP::extendIPv6(IP::normalizeIPv6($metadata_assoc_array['network']))); |
| 923 |
|
| 924 |
|
| 925 |
if ($metadata_assoc_array['mask'] > 128) { |
| 926 |
$validation_error = 'metadata validate failed on "mask" value'; |
| 927 |
break; |
| 928 |
} |
| 929 |
|
| 930 |
/** |
| 931 |
* Versatility for mask for v6 and v4 |
| 932 |
* @psalm-suppress LoopInvalidation |
| 933 |
*/ |
| 934 |
for ( $masks = array(), $mask = $metadata_assoc_array['mask'], $k = 4; $k >= 1; $k-- ) { |
| 935 |
$masks[$k] = (2 ** 32) - (2 ** (32 - ($mask > 32 ? 32 : $mask))); |
| 936 |
$mask -= 32; |
| 937 |
$mask = $mask > 0 ? $mask : 0; |
| 938 |
} |
| 939 |
$metadata_assoc_array['mask'] = $masks; |
| 940 |
} |
| 941 |
|
| 942 |
//all checks done, change on link |
| 943 |
$row = $metadata_assoc_array; |
| 944 |
} |
| 945 |
unset($row); |
| 946 |
|
| 947 |
if ( !empty($validation_error) ) { |
| 948 |
throw new InvalidArgumentException($error . $validation_error); |
| 949 |
} |
| 950 |
|
| 951 |
//method selection |
| 952 |
if ( $action === 'add' ) { |
| 953 |
$handler_output = FW::privateRecordsAdd( |
| 954 |
DB::getInstance(), |
| 955 |
$metadata |
| 956 |
); |
| 957 |
} elseif ( $action === 'delete' ) { |
| 958 |
$handler_output = FW::privateRecordsDelete( |
| 959 |
DB::getInstance(), |
| 960 |
$metadata |
| 961 |
); |
| 962 |
} else { |
| 963 |
$error .= 'unknown action name: ' . $action; |
| 964 |
throw new InvalidArgumentException($error); |
| 965 |
} |
| 966 |
} else { |
| 967 |
throw new InvalidArgumentException($error . 'empty action name'); |
| 968 |
} |
| 969 |
|
| 970 |
return json_encode(array('OK' => $handler_output)); |
| 971 |
} |
| 972 |
|
| 973 |
function spbc_update_postmeta_links($post_ID) |
| 974 |
{ |
| 975 |
delete_post_meta($post_ID, '_spbc_links_checked'); |
| 976 |
delete_post_meta($post_ID, 'spbc_links_checked'); |
| 977 |
} |
| 978 |
|
| 979 |
function spbc_update_postmeta_links__by_comment($id) |
| 980 |
{ |
| 981 |
$comment = get_comment($id); |
| 982 |
spbc_update_postmeta_links($comment->comment_post_ID); |
| 983 |
} |
| 984 |
|
| 985 |
// Install MU-plugin |
| 986 |
function spbc_mu_plugin__install() |
| 987 |
{ |
| 988 |
|
| 989 |
// If WPMU_PLUGIN_DIR is not exists -> create it |
| 990 |
if ( ! is_dir(WPMU_PLUGIN_DIR) && ! mkdir(WPMU_PLUGIN_DIR) && ! is_dir(WPMU_PLUGIN_DIR) ) { |
| 991 |
throw new \RuntimeException(sprintf('Directory "%s" was not created', WPMU_PLUGIN_DIR)); |
| 992 |
} |
| 993 |
|
| 994 |
// Get data from info file and write it to new plugin file |
| 995 |
$file = '<?php' . PHP_EOL . file_get_contents(SPBC_PLUGIN_DIR . '/install/security-malware-firewall-mu.php'); |
| 996 |
|
| 997 |
return @file_put_contents(WPMU_PLUGIN_DIR . '/0security-malware-firewall-mu.php', $file) ? true : false; |
| 998 |
} |
| 999 |
|
| 1000 |
/** |
| 1001 |
* Uninstall MU-plugin |
| 1002 |
* @deprecated 2.125 Use Deactivator::muPluginUninstall |
| 1003 |
* @return bool |
| 1004 |
*/ |
| 1005 |
function spbc_mu_plugin__uninstall() |
| 1006 |
{ |
| 1007 |
return \CleantalkSP\SpbctWP\Deactivator::muPluginUninstall(); |
| 1008 |
} |
| 1009 |
|
| 1010 |
function spbc_user_is_admin() |
| 1011 |
{ |
| 1012 |
global $spbc; |
| 1013 |
|
| 1014 |
if (!empty($spbc->settings['data__set_cookies'])) { |
| 1015 |
return |
| 1016 |
Cookie::getString('spbc_is_logged_in') === md5($spbc->data['salt'] . get_option('home')) && |
| 1017 |
Cookie::getString('spbc_admin_logged_in') === md5($spbc->data['salt'] . 'admin' . get_option('home')); |
| 1018 |
} |
| 1019 |
|
| 1020 |
return is_admin(); |
| 1021 |
} |
| 1022 |
|
| 1023 |
/** |
| 1024 |
* Ajax handler for sending logs. |
| 1025 |
*/ |
| 1026 |
function spbc_send_logs_ajax_handler() |
| 1027 |
{ |
| 1028 |
spbc_check_ajax_referer('spbc_secret_nonce', 'security'); |
| 1029 |
$result = spbc_send_logs(); |
| 1030 |
wp_send_json($result); |
| 1031 |
} |
| 1032 |
|
| 1033 |
//Function to send logs |
| 1034 |
function spbc_send_logs($api_key = null) |
| 1035 |
{ |
| 1036 |
global $spbc, $wpdb; |
| 1037 |
|
| 1038 |
if ( $api_key == null ) { |
| 1039 |
if ( ! $spbc->is_mainsite && $spbc->ms__work_mode == 2 ) { |
| 1040 |
$api_key = $spbc->network_settings['spbc_key']; |
| 1041 |
} else { |
| 1042 |
$api_key = $spbc->settings['spbc_key']; |
| 1043 |
} |
| 1044 |
} |
| 1045 |
|
| 1046 |
if ( ! $api_key ) { |
| 1047 |
return array( |
| 1048 |
'error' => 'KEY_EMPTY' |
| 1049 |
); |
| 1050 |
} |
| 1051 |
|
| 1052 |
$wpms_snippet = SPBC_WPMS |
| 1053 |
? $wpdb->prepare(" WHERE blog_id = %d AND ", get_current_blog_id()) |
| 1054 |
: " WHERE "; |
| 1055 |
|
| 1056 |
// @psalm-suppress WpdbUnsafeMethodsIssue |
| 1057 |
$rows = $wpdb->get_results( |
| 1058 |
"SELECT id, datetime, timestamp_gmt, user_login, page, page_time, event, auth_ip, role, user_agent, browser_sign |
| 1059 |
FROM " . SPBC_TBL_SECURITY_LOG |
| 1060 |
. $wpms_snippet |
| 1061 |
. " sent <> 1" |
| 1062 |
. " ORDER BY datetime DESC" |
| 1063 |
. " LIMIT " . SPBC_SELECT_LIMIT . ";" |
| 1064 |
); |
| 1065 |
|
| 1066 |
$rows_count = count($rows); |
| 1067 |
|
| 1068 |
if ( $rows_count ) { |
| 1069 |
$data = array(); |
| 1070 |
|
| 1071 |
foreach ( $rows as $record ) { |
| 1072 |
$page_time = (string) $record->page_time; |
| 1073 |
if ((int)$page_time <= 0) { |
| 1074 |
$page_time = '1'; |
| 1075 |
} |
| 1076 |
|
| 1077 |
$user_agent = null; |
| 1078 |
$browser_signature = null; |
| 1079 |
if ( in_array(strval($record->event), array( 'login', 'login_2fa', 'login_new_device', 'login_token', 'logout', )) ) { |
| 1080 |
$user_agent = $record->user_agent; |
| 1081 |
$browser_signature = $record->browser_sign; |
| 1082 |
} |
| 1083 |
|
| 1084 |
$security_logs_row_dto = new SecurityLogsDataRowDTO(array( |
| 1085 |
'log_id' => (string) $record->id, |
| 1086 |
'datetime' => (string) $record->datetime, |
| 1087 |
'datetime_gmt' => $record->timestamp_gmt, |
| 1088 |
'user_log' => (string) $record->user_login, |
| 1089 |
'event' => (string) $record->event, |
| 1090 |
'auth_ip' => strpos($record->auth_ip, ':') === false |
| 1091 |
? (int) sprintf('%u', ip2long($record->auth_ip)) |
| 1092 |
: (string) $record->auth_ip, |
| 1093 |
'page_url' => (string) $record->page, |
| 1094 |
'event_runtime' => $page_time, |
| 1095 |
'role' => (string) $record->role, |
| 1096 |
'user_agent' => (string) $user_agent, |
| 1097 |
'browser_signature' => (string) $browser_signature, |
| 1098 |
)); |
| 1099 |
$data[] = $security_logs_row_dto->getArray(); |
| 1100 |
} |
| 1101 |
|
| 1102 |
$security_logs_method_dto = new SecurityLogsDTO( |
| 1103 |
array( |
| 1104 |
'auth_key' => $api_key, |
| 1105 |
'method_name' => 'security_logs', |
| 1106 |
'timestamp' => current_time('timestamp'), |
| 1107 |
'data' => json_encode($data), |
| 1108 |
'rows' => count($data), |
| 1109 |
) |
| 1110 |
); |
| 1111 |
|
| 1112 |
$result = SpbcAPI::method__security_logs($security_logs_method_dto); |
| 1113 |
|
| 1114 |
if ( empty($result['error']) ) { |
| 1115 |
// Clear local table if it's ok. |
| 1116 |
if ( $result['rows'] == $rows_count ) { |
| 1117 |
$updated_ids = array(); |
| 1118 |
foreach ($data as $item) { |
| 1119 |
$updated_ids[] = $item['log_id']; |
| 1120 |
} |
| 1121 |
|
| 1122 |
$placeholders = rtrim(str_repeat('%s,', count($updated_ids)), ','); |
| 1123 |
if ( SPBC_WPMS ) { |
| 1124 |
$sql = "UPDATE " . SPBC_TBL_SECURITY_LOG . " SET sent = 1 WHERE id IN ($placeholders)" |
| 1125 |
. ( $spbc->ms__work_mode == 2 ? '' : ' AND blog_id = ' . get_current_blog_id() ) |
| 1126 |
. ";"; |
| 1127 |
} else { |
| 1128 |
$sql = "UPDATE " . SPBC_TBL_SECURITY_LOG . " SET sent = 1 WHERE id IN ($placeholders);"; |
| 1129 |
} |
| 1130 |
$wpdb->query($wpdb->prepare($sql, $updated_ids)); |
| 1131 |
|
| 1132 |
$result = $rows_count; |
| 1133 |
} else { |
| 1134 |
$result = array( |
| 1135 |
'error' => sprintf(__('Sent: %d. Confirmed receiving of %d rows.', 'security-malware-firewall'), $rows_count, intval($result['rows'])) |
| 1136 |
); |
| 1137 |
} |
| 1138 |
} |
| 1139 |
} else { |
| 1140 |
$result = array( |
| 1141 |
'error' => 'NO_LOGS_TO_SEND' |
| 1142 |
); |
| 1143 |
} |
| 1144 |
|
| 1145 |
global $spbc_cron; |
| 1146 |
if ( ! empty($spbc_cron) && empty($result['error']) ) { |
| 1147 |
$spbc->data['logs_last_sent'] = current_time('timestamp'); |
| 1148 |
$spbc->data['last_sent_events_count'] = $result; |
| 1149 |
} |
| 1150 |
|
| 1151 |
$has_unsent_logs = spbc_has_unsent_security_logs(); |
| 1152 |
|
| 1153 |
if ($has_unsent_logs) { |
| 1154 |
SpbcCron::updateTask('send_logs', 'spbc_send_logs', 60, time() + 60); |
| 1155 |
} else { |
| 1156 |
SpbcCron::updateTask('send_logs', 'spbc_send_logs', 3600, time() + 3600); |
| 1157 |
} |
| 1158 |
|
| 1159 |
return $result; |
| 1160 |
} |
| 1161 |
|
| 1162 |
/** |
| 1163 |
* Checks if there are unsent logs persists in the SPBC_TBL_SECURITY_LOG and the count is larger than SPBC_SELECT_LIMIT. |
| 1164 |
* Used in cases if logs were generated faster than sending logs cron handled them. |
| 1165 |
* @return bool |
| 1166 |
*/ |
| 1167 |
function spbc_has_unsent_security_logs() |
| 1168 |
{ |
| 1169 |
global $wpdb; |
| 1170 |
|
| 1171 |
$wpms_snippet = SPBC_WPMS |
| 1172 |
? $wpdb->prepare(" WHERE blog_id = %d AND ", get_current_blog_id()) |
| 1173 |
: " WHERE "; |
| 1174 |
|
| 1175 |
$rows = $wpdb->get_var( |
| 1176 |
"SELECT count(id) |
| 1177 |
FROM " . SPBC_TBL_SECURITY_LOG |
| 1178 |
. $wpms_snippet |
| 1179 |
. " sent <> 1" |
| 1180 |
. " ORDER BY datetime DESC;" |
| 1181 |
); |
| 1182 |
|
| 1183 |
return (int)$rows > SPBC_SELECT_LIMIT; |
| 1184 |
} |
| 1185 |
|
| 1186 |
/** |
| 1187 |
* @return bool |
| 1188 |
* @psalm-suppress RedundantCondition |
| 1189 |
*/ |
| 1190 |
function spbc_set_api_key() |
| 1191 |
{ |
| 1192 |
global $spbc; |
| 1193 |
|
| 1194 |
$website = parse_url(get_option('home'), PHP_URL_HOST) . parse_url(get_option('home'), PHP_URL_PATH); |
| 1195 |
$platform = 'wordpress'; |
| 1196 |
$user_ip = IP::get(); |
| 1197 |
$timezone = (string)get_option('gmt_offset'); |
| 1198 |
$language = Server::getString('HTTP_ACCEPT_LANGUAGE'); |
| 1199 |
$is_wpms = is_multisite() && defined('SUBDOMAIN_INSTALL') && ! SUBDOMAIN_INSTALL; |
| 1200 |
$white_label = false; |
| 1201 |
$hoster_api_key = $spbc->ms__hoster_api_key; |
| 1202 |
|
| 1203 |
$result = SpbcAPI::method__get_api_key( |
| 1204 |
'security', |
| 1205 |
get_network_option(0, 'admin_email'), |
| 1206 |
$website, |
| 1207 |
$platform, |
| 1208 |
$timezone, |
| 1209 |
$language, |
| 1210 |
$user_ip, |
| 1211 |
$is_wpms, |
| 1212 |
$white_label, |
| 1213 |
$hoster_api_key |
| 1214 |
); |
| 1215 |
|
| 1216 |
if ( ! empty($result['error']) ) { |
| 1217 |
$spbc->data['key_is_ok'] = false; |
| 1218 |
$spbc->error_add('get_key', $result); |
| 1219 |
|
| 1220 |
return false; |
| 1221 |
} else { |
| 1222 |
$api_key = trim($result['auth_key']); |
| 1223 |
$api_key = preg_match('/^[a-z\d]*$/', $api_key) ? $api_key : $spbc->settings['spbc_key']; // Check key format a-z\d |
| 1224 |
$api_key = is_main_site() || $spbc->ms__work_mode != 2 ? $api_key : $spbc->network_settings['spbc_key']; |
| 1225 |
$spbc->settings['spbc_key'] = $api_key; |
| 1226 |
$spbc->save('settings'); |
| 1227 |
|
| 1228 |
$spbc->data['user_token'] = ( ! empty($result['user_token']) ? $result['user_token'] : '' ); |
| 1229 |
$spbc->data['key_is_ok'] = spbc_api_key__is_correct($api_key); |
| 1230 |
$spbc->data['key_changed'] = true; |
| 1231 |
$spbc->save('data'); |
| 1232 |
|
| 1233 |
$spbc->error_delete('get_key api_key'); |
| 1234 |
|
| 1235 |
return true; |
| 1236 |
} |
| 1237 |
} |
| 1238 |
|
| 1239 |
/** |
| 1240 |
* The functions check to check an account |
| 1241 |
* Executes only via cron (on the main blog) |
| 1242 |
* |
| 1243 |
* @param null $spbc_key |
| 1244 |
* |
| 1245 |
* @return array|bool|bool[]|string[] |
| 1246 |
*/ |
| 1247 |
function spbc_access_key_notices($spbc_key = null) |
| 1248 |
{ |
| 1249 |
global $spbc; |
| 1250 |
|
| 1251 |
$spbc_key = $spbc_key ?: $spbc->settings['spbc_key']; |
| 1252 |
|
| 1253 |
if ( empty($spbc_key) ) { |
| 1254 |
if ( ! $spbc->is_mainsite && $spbc->ms__work_mode != 2 ) { |
| 1255 |
$spbc_key = ! empty($spbc->network_settings['spbc_key']) ? $spbc->network_settings['spbc_key'] : false; |
| 1256 |
if ( ! $spbc_key ) { |
| 1257 |
return array( 'error' => 'KEY_IS_NOT_OK_ON_MAIN_WPMS_SITE' ); |
| 1258 |
} |
| 1259 |
} else { |
| 1260 |
$spbc_key = ! empty($spbc->settings['spbc_key']) ? $spbc->settings['spbc_key'] : false; |
| 1261 |
if ( ! $spbc_key ) { |
| 1262 |
return array( 'error' => 'KEY_IS_NOT_OK' ); |
| 1263 |
} |
| 1264 |
} |
| 1265 |
} |
| 1266 |
|
| 1267 |
$account_status = spbc_check_account_status($spbc_key); |
| 1268 |
if (is_string($account_status)) { |
| 1269 |
return array( 'error' => $account_status); |
| 1270 |
} |
| 1271 |
|
| 1272 |
return true; |
| 1273 |
} |
| 1274 |
|
| 1275 |
function spbc_PHP_logs__collect($last_log_sent) |
| 1276 |
{ |
| 1277 |
$logs = array(); |
| 1278 |
$start_timestamp = time(); |
| 1279 |
|
| 1280 |
|
| 1281 |
// Try to get log from wp-content/debug/log if default file is not accessible |
| 1282 |
$file = ini_get('error_log'); |
| 1283 |
$file = file_exists($file) && is_readable($file) |
| 1284 |
? $file |
| 1285 |
: WP_CONTENT_DIR . '/debug.log'; |
| 1286 |
|
| 1287 |
if ( file_exists($file) ) { |
| 1288 |
if ( is_readable($file) ) { |
| 1289 |
// Return if file is empty |
| 1290 |
if ( ! filesize($file) ) { |
| 1291 |
return array(); |
| 1292 |
} |
| 1293 |
|
| 1294 |
$fd = @fopen($file, 'rb'); |
| 1295 |
|
| 1296 |
if ( $fd ) { |
| 1297 |
$eol = spbc_PHP_logs__detect_EOL_type($file, false); |
| 1298 |
if (is_null($eol) || is_int($eol)) { |
| 1299 |
$eol = "\n"; |
| 1300 |
} |
| 1301 |
|
| 1302 |
for ( |
| 1303 |
// Initialization |
| 1304 |
$fsize = filesize($file), $offset = 1024 * 5, $position = $fsize - $offset, |
| 1305 |
$max_log_size = 1024 * 1024 * 1, $max_read_size = 1024 * 1024 * 4, |
| 1306 |
$log_size = 0, $read = 0, |
| 1307 |
$log_count = 0; |
| 1308 |
// Conditions |
| 1309 |
$log_size < $max_log_size && // Max usefull data |
| 1310 |
$offset === 1024 * 5 && // End of file |
| 1311 |
$read < $max_read_size && // Max read depth |
| 1312 |
$log_count < 3500 && |
| 1313 |
time() < $start_timestamp + 25; |
| 1314 |
// Iteartion adjustments |
| 1315 |
$position -= $offset |
| 1316 |
) { |
| 1317 |
$offset = $position < 0 ? $offset + $position : $offset; |
| 1318 |
$position = $position < 0 ? 0 : $position; |
| 1319 |
|
| 1320 |
// Set pointer to $it * $offset from the EOF. Or 0 if it's negative. |
| 1321 |
fseek($fd, $position); |
| 1322 |
|
| 1323 |
// Read $offset bytes |
| 1324 |
$it_logs = fread($fd, $offset); |
| 1325 |
|
| 1326 |
// Clean to first EOL, splitting to array by PHP_EOL. |
| 1327 |
if ( $position != 0 ) { |
| 1328 |
$position_adjustment = strpos($it_logs, $eol); |
| 1329 |
$position += $position_adjustment + 1; |
| 1330 |
$it_logs = substr($it_logs, $position_adjustment); |
| 1331 |
} |
| 1332 |
|
| 1333 |
$read += strlen($it_logs); |
| 1334 |
$it_logs = explode($eol, $it_logs); |
| 1335 |
|
| 1336 |
// Filtering and parsing |
| 1337 |
foreach ( $it_logs as $log_line ) { |
| 1338 |
if ( spbc_PHP_logs__filter($log_line, $last_log_sent) ) { |
| 1339 |
$log_size += strlen($log_line); |
| 1340 |
$log_count++; |
| 1341 |
$parsed_log_line = spbc_PHP_logs__parse_line($log_line); |
| 1342 |
if ( $parsed_log_line ) { |
| 1343 |
$logs[] = $parsed_log_line; |
| 1344 |
} |
| 1345 |
} |
| 1346 |
} |
| 1347 |
} |
| 1348 |
|
| 1349 |
return $logs; |
| 1350 |
} else { |
| 1351 |
return array( 'error' => 'COULDNT_OPEN_LOG_FILE' ); |
| 1352 |
} |
| 1353 |
} else { |
| 1354 |
return array( 'error' => 'LOG_FILE_IS_UNACCESSIBLE' ); |
| 1355 |
} |
| 1356 |
} else { |
| 1357 |
return array( 'error' => 'LOG_FILE_NOT_EXISTS' ); |
| 1358 |
} |
| 1359 |
} |
| 1360 |
|
| 1361 |
function spbc_PHP_logs__filter($line, $php_logs_last_sent) |
| 1362 |
{ |
| 1363 |
$line = trim($line); |
| 1364 |
|
| 1365 |
if ( ! empty($line) ) { |
| 1366 |
preg_match('/^\[(.*?\s\d\d:\d\d:\d\d.*?)]/', $line, $matches); |
| 1367 |
if ( isset($matches[1]) && strtotime($matches[1]) >= $php_logs_last_sent ) { |
| 1368 |
if ( preg_match('/^\[(.*?)\]\s+PHP\s(Warning|Fatal|Notice|Parse)/', $line) ) { |
| 1369 |
} else { |
| 1370 |
$line = false; |
| 1371 |
} |
| 1372 |
} else { |
| 1373 |
$line = false; |
| 1374 |
} |
| 1375 |
} else { |
| 1376 |
$line = false; |
| 1377 |
} |
| 1378 |
|
| 1379 |
return $line; |
| 1380 |
} |
| 1381 |
|
| 1382 |
function spbc_PHP_logs__parse_line($line) |
| 1383 |
{ |
| 1384 |
if ( preg_match('/^\[(.*?)\]\s((.*?):\s+(.+))$/', $line, $matches) ) { |
| 1385 |
return array( |
| 1386 |
date('Y-m-d H:i:s', strtotime($matches[1])), |
| 1387 |
$matches[2], |
| 1388 |
); |
| 1389 |
} |
| 1390 |
} |
| 1391 |
|
| 1392 |
function spbc_PHP_logs__send() |
| 1393 |
{ |
| 1394 |
global $spbc; |
| 1395 |
|
| 1396 |
if ( empty($spbc->settings['misc__backend_logs_enable']) || empty($spbc->settings['spbc_key']) ) { |
| 1397 |
return true; |
| 1398 |
} |
| 1399 |
|
| 1400 |
$logs = spbc_PHP_logs__collect($spbc->data['last_php_log_sent']); |
| 1401 |
|
| 1402 |
if ( empty($logs['error']) ) { |
| 1403 |
if ( ! empty($logs) ) { |
| 1404 |
$result = SpbcAPI::method__security_backend_logs($spbc->settings['spbc_key'], $logs); |
| 1405 |
|
| 1406 |
if ( empty($result['error']) ) { |
| 1407 |
if ( isset($result['total_logs_found']) ) { |
| 1408 |
if ( $result['total_logs_found'] == count($logs) ) { |
| 1409 |
$spbc->data['last_php_log_sent'] = time(); |
| 1410 |
$spbc->data['last_php_log_amount'] = $result['total_logs_found']; |
| 1411 |
$spbc->save('data'); |
| 1412 |
|
| 1413 |
return true; |
| 1414 |
} else { |
| 1415 |
return array( 'error' => 'LOGS_COUNT_DOES_NOT_MATCH' ); |
| 1416 |
} |
| 1417 |
} else { |
| 1418 |
return array( 'error' => 'LOGS_COUNT_IS_EMPTY' ); |
| 1419 |
} |
| 1420 |
} else { |
| 1421 |
return $result; |
| 1422 |
} |
| 1423 |
} else { |
| 1424 |
return true; |
| 1425 |
} |
| 1426 |
} else { |
| 1427 |
return $logs; |
| 1428 |
} |
| 1429 |
} |
| 1430 |
|
| 1431 |
/** |
| 1432 |
* Extended check_ajax_referer function. Includes admin role check. |
| 1433 |
* @param string|int $action ajax action |
| 1434 |
* @param string|false $query_arg query arg where to search a nonce |
| 1435 |
* @param bool $die is need to stop the flow with 403 wp_die() |
| 1436 |
* @param bool $strict_admins is need to check current user capabilities comply to admin's |
| 1437 |
* @return bool |
| 1438 |
*/ |
| 1439 |
function spbc_check_ajax_referer($action = -1, $query_arg = false, $die = true, $strict_admins = true) |
| 1440 |
{ |
| 1441 |
$result = true; |
| 1442 |
if (function_exists('check_ajax_referer')) { |
| 1443 |
/** @psalm-suppress ForbiddenCode */ |
| 1444 |
$result = check_ajax_referer($action, $query_arg, $die); |
| 1445 |
if (!$result && $die) { |
| 1446 |
wp_die('-1', 403); |
| 1447 |
} |
| 1448 |
} |
| 1449 |
// if native check passed, check if user is admin - only if param provided |
| 1450 |
if ($result && $strict_admins) { |
| 1451 |
$result = current_user_can('manage_options'); |
| 1452 |
} |
| 1453 |
if (!$result && $die) { |
| 1454 |
wp_die('-1', 403); |
| 1455 |
} |
| 1456 |
return (bool)$result; |
| 1457 |
} |
| 1458 |
|
| 1459 |
/** |
| 1460 |
* Check connection to the API servers |
| 1461 |
* |
| 1462 |
* @param array $urls_to_test |
| 1463 |
* |
| 1464 |
* @return array |
| 1465 |
*/ |
| 1466 |
function spbc_test_connection($urls_to_test = array()) |
| 1467 |
{ |
| 1468 |
|
| 1469 |
$out = array(); |
| 1470 |
$urls_to_test = $urls_to_test ?: array_keys(HTTP::getCleantalksAPIServersFromDNS()); |
| 1471 |
|
| 1472 |
foreach ( $urls_to_test as $url ) { |
| 1473 |
$start = microtime(true); |
| 1474 |
$result = HTTP::getContentFromURL($url, false); |
| 1475 |
|
| 1476 |
$out[ $url ] = array( |
| 1477 |
'result' => ! empty($result['error']) ? $result['error'] : 'OK', |
| 1478 |
'exec_time' => microtime(true) - $start, |
| 1479 |
); |
| 1480 |
} |
| 1481 |
|
| 1482 |
return $out; |
| 1483 |
} |
| 1484 |
|
| 1485 |
/** |
| 1486 |
* Run full sync with CleanTalk cloud. |
| 1487 |
* |
| 1488 |
* @return array{success: bool, reload: bool} |
| 1489 |
*/ |
| 1490 |
function spbc_sync() |
| 1491 |
{ |
| 1492 |
return SpbcSync::run(); |
| 1493 |
} |
| 1494 |
|
| 1495 |
function spbct_perform_service_get() |
| 1496 |
{ |
| 1497 |
global $spbc; |
| 1498 |
|
| 1499 |
$result_service_get = SpbcAPI::method__service_get( |
| 1500 |
$spbc->api_key, |
| 1501 |
$spbc->data['user_token'] |
| 1502 |
); |
| 1503 |
|
| 1504 |
if ( empty($result_service_get['error']) ) { |
| 1505 |
$spbc->settings['fw__custom_message'] = isset($result_service_get['server_response']) |
| 1506 |
? $result_service_get['server_response'] |
| 1507 |
: ''; |
| 1508 |
$spbc->save('settings'); |
| 1509 |
} |
| 1510 |
|
| 1511 |
return $result_service_get; |
| 1512 |
} |
| 1513 |
|
| 1514 |
// The functions sends daily reports about attempts to login. |
| 1515 |
function spbc_send_daily_report($skip_data_rotation = false) |
| 1516 |
{ |
| 1517 |
|
| 1518 |
if ( ! function_exists('wp_mail') ) { |
| 1519 |
add_action('plugins_loaded', 'spbc_send_daily_report'); |
| 1520 |
|
| 1521 |
return; |
| 1522 |
} |
| 1523 |
|
| 1524 |
global $spbc, $wpdb, $spbc_tpl; |
| 1525 |
|
| 1526 |
//If key is not ok, send daily report! |
| 1527 |
if ( ! $spbc->key_is_ok ) { |
| 1528 |
include_once SPBC_PLUGIN_DIR . 'templates/spbc_send_daily_report.php'; |
| 1529 |
|
| 1530 |
// Hours |
| 1531 |
$report_interval = 24 * 7; |
| 1532 |
|
| 1533 |
$admin_email = spbc_get_admin_email(); |
| 1534 |
if ( ! $admin_email ) { |
| 1535 |
SpbcDevLogger::write( |
| 1536 |
sprintf( |
| 1537 |
'%s: can\'t send the Daily report because of empty Admin email. File: %s, line %d.', |
| 1538 |
$spbc->data["wl_brandname"], |
| 1539 |
__FILE__, |
| 1540 |
__LINE__ |
| 1541 |
) |
| 1542 |
); |
| 1543 |
|
| 1544 |
return false; |
| 1545 |
} |
| 1546 |
|
| 1547 |
$sql = $wpdb->prepare( |
| 1548 |
'SELECT id,datetime,user_login,event,auth_ip,page,page_time |
| 1549 |
FROM ' . SPBC_TBL_SECURITY_LOG . ' WHERE datetime between now() - interval %d hour and now();', |
| 1550 |
$report_interval |
| 1551 |
); |
| 1552 |
$rows = $wpdb->get_results($sql); |
| 1553 |
foreach ( $rows as $k => $v ) { |
| 1554 |
if ( isset($v->datetime) ) { |
| 1555 |
$v->datetime_ts = strtotime($v->datetime); |
| 1556 |
} |
| 1557 |
$rows[$k] = $v; |
| 1558 |
} |
| 1559 |
usort($rows, "spbc_usort_desc"); |
| 1560 |
|
| 1561 |
$record_datetime = time(); |
| 1562 |
$events = array(); |
| 1563 |
$auth_failed_events = array(); |
| 1564 |
$invalid_username_events = array(); |
| 1565 |
$auth_failed_count = 0; |
| 1566 |
$invalid_username_count = 0; |
| 1567 |
$ips_data = ''; |
| 1568 |
foreach ( $rows as $record ) { |
| 1569 |
if ( strtotime($record->datetime) > $record_datetime ) { |
| 1570 |
$record_datetime = strtotime($record->datetime); |
| 1571 |
} |
| 1572 |
$events[ $record->event ][ $record->user_login ][] = array( |
| 1573 |
'datetime' => $record->datetime, |
| 1574 |
'auth_ip' => $record->auth_ip, |
| 1575 |
'user_login' => $record->user_login, |
| 1576 |
'page' => $record->page ?: '-', |
| 1577 |
'page_time' => $record->page_time ?: 'Unknown' |
| 1578 |
); |
| 1579 |
|
| 1580 |
switch ( $record->event ) { |
| 1581 |
case 'auth_failed': |
| 1582 |
$auth_failed_events[ $record->user_login ][ $record->auth_ip ] = array( |
| 1583 |
'attempts' => isset($auth_failed_events[ $record->user_login ][ $record->auth_ip ]['attempts']) |
| 1584 |
? $auth_failed_events[ $record->user_login ][ $record->auth_ip ]['attempts'] + 1 |
| 1585 |
: 1, |
| 1586 |
'auth_ip' => $record->auth_ip, |
| 1587 |
'user_login' => $record->user_login |
| 1588 |
); |
| 1589 |
$auth_failed_count++; |
| 1590 |
break; |
| 1591 |
case 'invalid_username': |
| 1592 |
$invalid_username_events[ $record->user_login ][ $record->auth_ip ] = array( |
| 1593 |
'attempts' => isset($invalid_username_events[ $record->user_login ][ $record->auth_ip ]['attempts']) |
| 1594 |
? $invalid_username_events[ $record->user_login ][ $record->auth_ip ]['attempts'] + 1 |
| 1595 |
: 1, |
| 1596 |
'auth_ip' => $record->auth_ip, |
| 1597 |
'user_login' => $record->user_login |
| 1598 |
); |
| 1599 |
$invalid_username_count++; |
| 1600 |
break; |
| 1601 |
} |
| 1602 |
if ( $ips_data != '' ) { |
| 1603 |
$ips_data .= ','; |
| 1604 |
} |
| 1605 |
$ips_data .= $record->auth_ip; |
| 1606 |
} |
| 1607 |
|
| 1608 |
$ips_c = spbc_get_countries_by_ips($ips_data); |
| 1609 |
|
| 1610 |
$event_part = ''; |
| 1611 |
$auth_failed_part = sprintf( |
| 1612 |
"<p style=\"color: #666;\">%s</p>", |
| 1613 |
_("0 brute force attacks have been made for past day.") |
| 1614 |
); |
| 1615 |
if ( $auth_failed_count ) { |
| 1616 |
foreach ( $auth_failed_events as $e ) { |
| 1617 |
$ip_part = ''; |
| 1618 |
foreach ( $e as $ip ) { |
| 1619 |
$country_part = spbc_report_country_part($ips_c, $ip['auth_ip']); |
| 1620 |
$ip_part .= sprintf( |
| 1621 |
"<a href=\"https://cleantalk.org/blacklists/%s\">%s</a>, #%d, %s<br />", |
| 1622 |
$ip['auth_ip'], |
| 1623 |
$ip['auth_ip'], |
| 1624 |
$ip['attempts'], |
| 1625 |
$country_part |
| 1626 |
); |
| 1627 |
} |
| 1628 |
$event_part .= sprintf($spbc_tpl['event_part_tpl'], $ip['user_login'], $ip_part); |
| 1629 |
} |
| 1630 |
$auth_failed_part = sprintf($spbc_tpl['auth_failed_part'], $event_part); |
| 1631 |
} |
| 1632 |
|
| 1633 |
$invalid_username_part = sprintf( |
| 1634 |
"<p style=\"color: #666;\">%s</p>", |
| 1635 |
_('0 brute force attacks have been made for past day.') |
| 1636 |
); |
| 1637 |
|
| 1638 |
if ( $invalid_username_count ) { |
| 1639 |
foreach ( $invalid_username_events as $e ) { |
| 1640 |
$ip_part = ''; |
| 1641 |
foreach ( $e as $ip ) { |
| 1642 |
$country_part = spbc_report_country_part($ips_c, $ip['auth_ip']); |
| 1643 |
$ip_part .= sprintf( |
| 1644 |
"<a href=\"https://cleantalk.org/blacklists/%s\">%s</a>, #%d, %s<br />", |
| 1645 |
$ip['auth_ip'], |
| 1646 |
$ip['auth_ip'], |
| 1647 |
$ip['attempts'], |
| 1648 |
$country_part |
| 1649 |
); |
| 1650 |
} |
| 1651 |
$event_part .= sprintf( |
| 1652 |
$spbc_tpl['event_part_tpl'], |
| 1653 |
$ip['user_login'], |
| 1654 |
$ip_part |
| 1655 |
); |
| 1656 |
} |
| 1657 |
$invalid_username_part = sprintf($spbc_tpl['auth_failed_part'], $event_part); |
| 1658 |
} |
| 1659 |
|
| 1660 |
$logins_part = sprintf( |
| 1661 |
"<p style=\"color: #666;\">%s</p>", |
| 1662 |
_('0 users have been logged in for past day.') |
| 1663 |
); |
| 1664 |
if ( isset($events['login']) && count($events['login']) ) { |
| 1665 |
$event_part = ''; |
| 1666 |
foreach ( $events['login'] as $user_login => $e ) { |
| 1667 |
$l_part = ''; |
| 1668 |
foreach ( $e as $e2 ) { |
| 1669 |
$country_part = spbc_report_country_part($ips_c, $e2['auth_ip']); |
| 1670 |
$l_part .= sprintf( |
| 1671 |
"%s, <a href=\"https://cleantalk.org/blacklists/%s\">%s</a>, %s<br />", |
| 1672 |
date("M d Y H:i:s", strtotime($e2['datetime'])), |
| 1673 |
$e2['auth_ip'], |
| 1674 |
$e2['auth_ip'], |
| 1675 |
$country_part |
| 1676 |
); |
| 1677 |
} |
| 1678 |
$event_part .= sprintf( |
| 1679 |
$spbc_tpl['event_part_tpl'], |
| 1680 |
$user_login, |
| 1681 |
$l_part |
| 1682 |
); |
| 1683 |
} |
| 1684 |
$logins_part = sprintf( |
| 1685 |
$spbc_tpl['logins_part_tpl'], |
| 1686 |
$event_part |
| 1687 |
); |
| 1688 |
} |
| 1689 |
|
| 1690 |
$title_main_part = _('Daily security report'); |
| 1691 |
$subject = sprintf( |
| 1692 |
'%s %s', |
| 1693 |
parse_url(get_option('home'), PHP_URL_HOST), |
| 1694 |
$title_main_part |
| 1695 |
); |
| 1696 |
|
| 1697 |
$message_anounce = sprintf( |
| 1698 |
_('%s brute force attacks or failed logins, %d successful logins.'), |
| 1699 |
number_format($auth_failed_count + $invalid_username_count, 0, ',', ' '), |
| 1700 |
isset($events['login']) ? count($events['login']) : 0 |
| 1701 |
); |
| 1702 |
|
| 1703 |
|
| 1704 |
$message = sprintf( |
| 1705 |
$spbc_tpl['message_tpl'], |
| 1706 |
$spbc_tpl['message_style'], |
| 1707 |
$title_main_part, |
| 1708 |
$message_anounce, |
| 1709 |
$auth_failed_part, |
| 1710 |
$invalid_username_part, |
| 1711 |
$logins_part, |
| 1712 |
$spbc->data["wl_brandname"] |
| 1713 |
); |
| 1714 |
|
| 1715 |
|
| 1716 |
$headers = array('Content-Type: text/html; charset=UTF-8'); |
| 1717 |
wp_mail( |
| 1718 |
$admin_email, |
| 1719 |
$subject, |
| 1720 |
$message, |
| 1721 |
$headers |
| 1722 |
); |
| 1723 |
|
| 1724 |
if ( ! $skip_data_rotation ) { |
| 1725 |
$sql = $wpdb->prepare( |
| 1726 |
"DELETE FROM " . SPBC_TBL_SECURITY_LOG . " WHERE datetime <= %s;", |
| 1727 |
date("Y-m-d H:i:s", $record_datetime) |
| 1728 |
); |
| 1729 |
$wpdb->query($sql); |
| 1730 |
}; |
| 1731 |
} |
| 1732 |
|
| 1733 |
return null; |
| 1734 |
} |
| 1735 |
|
| 1736 |
function spbc_private_list_add() |
| 1737 |
{ |
| 1738 |
global $spbc, $current_user; |
| 1739 |
|
| 1740 |
spbc_check_ajax_referer('spbc_secret_nonce', 'security'); |
| 1741 |
|
| 1742 |
$ip = IP::get(); |
| 1743 |
|
| 1744 |
if ( Cookie::getString('spbc_secfw_ip_wl') === md5($ip . $spbc->spbc_key) ) { |
| 1745 |
return; |
| 1746 |
} |
| 1747 |
|
| 1748 |
if ( in_array('administrator', $current_user->roles) ) { |
| 1749 |
$res = spbc_private_list_add_api_call($ip); |
| 1750 |
if ( $res ) { |
| 1751 |
if ( ! headers_sent() ) { |
| 1752 |
$cookie_val = md5($ip . $spbc->spbc_key); |
| 1753 |
Cookie::set('spbc_secfw_ip_wl', $cookie_val, time() + 86400 * 25, '/', '', false, true); |
| 1754 |
} |
| 1755 |
|
| 1756 |
// Add to the local database |
| 1757 |
$status_for_db = 1; |
| 1758 |
$version = IP::validate($ip); |
| 1759 |
if ( $version === 'v4' ) { |
| 1760 |
$data[] = ip2long($ip) . ',' . ip2long('255.255.255.255') . ',' . $status_for_db; |
| 1761 |
} elseif ( $version === 'v6' ) { |
| 1762 |
$data[] = $ip . ',' . '128' . ',' . $status_for_db; |
| 1763 |
} else { |
| 1764 |
wp_send_json_error('Local database: adding IP ' . $ip . ' failed: ip does not look like a valid IP address'); |
| 1765 |
} |
| 1766 |
try { |
| 1767 |
$res_local = spbct_sfw_private_records_handler('add', json_encode($data, JSON_FORCE_OBJECT)); |
| 1768 |
wp_send_json_success($res_local); |
| 1769 |
} catch (\Exception $e) { |
| 1770 |
wp_send_json_error('Local database: adding IP ' . $ip . ' failed: ' . $e->getMessage()); |
| 1771 |
} |
| 1772 |
} |
| 1773 |
wp_send_json_error('API wrong answer.'); |
| 1774 |
} |
| 1775 |
} |
| 1776 |
|
| 1777 |
function spbc_private_list_add_api_call($ip) |
| 1778 |
{ |
| 1779 |
global $spbc; |
| 1780 |
if ( IP::validate($ip) !== false ) { |
| 1781 |
$res = SpbcAPI::method__private_list_add__secfw_wl($spbc->user_token, $ip, $spbc->data['service_id']); |
| 1782 |
|
| 1783 |
return isset($res['records'][0]['operation_status']) && $res['records'][0]['operation_status'] === 'SUCCESS'; |
| 1784 |
} |
| 1785 |
|
| 1786 |
return false; |
| 1787 |
} |
| 1788 |
|
| 1789 |
/** |
| 1790 |
* Cron. Update statuses of files sent to the cloud sandbox. |
| 1791 |
*/ |
| 1792 |
function spbc_scanner_update_pscan_files_status() |
| 1793 |
{ |
| 1794 |
global $wpdb; |
| 1795 |
// Reading DB for NEW files |
| 1796 |
$undone_files_list = $wpdb->get_results( |
| 1797 |
'SELECT fast_hash' |
| 1798 |
. ' FROM ' . SPBC_TBL_SCAN_FILES |
| 1799 |
. ' WHERE pscan_processing_status <> "DONE" AND pscan_processing_status IS NOT NULL', |
| 1800 |
ARRAY_A |
| 1801 |
); |
| 1802 |
|
| 1803 |
if ( ! empty($undone_files_list) ) { |
| 1804 |
$files_fast_hashes_to_update = array(); |
| 1805 |
foreach ( $undone_files_list as $file ) { |
| 1806 |
$files_fast_hashes_to_update[] = $file['fast_hash']; |
| 1807 |
} |
| 1808 |
ScannerAjaxEndpoints::checkFilesAnalysisStatus(true, $files_fast_hashes_to_update); |
| 1809 |
} else { |
| 1810 |
\CleantalkSP\SpbctWP\Cron::removeTask('scanner_update_pscan_files_status'); |
| 1811 |
} |
| 1812 |
} |
| 1813 |
|
| 1814 |
/** |
| 1815 |
* Cron. Resend files that were not added to the cloud sandbox queue. |
| 1816 |
*/ |
| 1817 |
function spbc_scanner_resend_pscan_files() |
| 1818 |
{ |
| 1819 |
$pending_queue_files = ScanRepository::getPendingQueueFiles(); |
| 1820 |
|
| 1821 |
if (!empty($pending_queue_files)) { |
| 1822 |
foreach ($pending_queue_files as $file) { |
| 1823 |
// fix for files sent to manual analysis |
| 1824 |
if (!empty($file['status']) && $file['status'] === 'APPROVED_BY_CT') { |
| 1825 |
ScanStorage::setFileAsNotPendingQueue($file['fast_hash']); |
| 1826 |
continue; |
| 1827 |
} |
| 1828 |
|
| 1829 |
SendFileToCloudService::sendFile($file['fast_hash']); |
| 1830 |
} |
| 1831 |
} else { |
| 1832 |
\CleantalkSP\SpbctWP\Cron::removeTask('scanner_resend_pscan_files'); |
| 1833 |
} |
| 1834 |
} |
| 1835 |
|
| 1836 |
/** |
| 1837 |
* Checking account status. |
| 1838 |
* @param $api_key |
| 1839 |
* @return true|string - true if account is ok, string with first error message otherwise |
| 1840 |
*/ |
| 1841 |
function spbc_check_account_status($api_key) |
| 1842 |
{ |
| 1843 |
global $spbc, $plugin_info; |
| 1844 |
|
| 1845 |
$validation_result = spbc_validate_access_key($api_key); |
| 1846 |
$result = $validation_result['api_response']; |
| 1847 |
$validation_errors = $validation_result['errors']; |
| 1848 |
if ( !empty($validation_errors) ) { |
| 1849 |
foreach ($validation_errors as $error) { |
| 1850 |
$error = is_string($error) ? $error : json_encode($error); |
| 1851 |
$error = __spbc(SpbcET::__KEY_VALIDATION__FAILED_COMMON) . ' ' . $error; |
| 1852 |
$spbc->error_add('apikey', $error); |
| 1853 |
} |
| 1854 |
$spbc->data['key_is_ok'] = false; |
| 1855 |
$spbc->save('data'); |
| 1856 |
$first_error = reset($validation_errors); |
| 1857 |
return is_string($first_error) ? $first_error : 'UNKNOWN_ACCOUNT_STATUS_ERROR'; |
| 1858 |
} |
| 1859 |
if (!empty($result)) { |
| 1860 |
$spbc->data['key_is_ok'] = true; |
| 1861 |
$spbc->error_delete('apikey', true); |
| 1862 |
} |
| 1863 |
|
| 1864 |
if ( isset($result['user_token']) ) { |
| 1865 |
$spbc->data['user_token'] = $result['user_token']; |
| 1866 |
} |
| 1867 |
$spbc->data['notice_show'] = isset($result['show_notice']) ? $result['show_notice'] : 0; |
| 1868 |
$spbc->data['notice_renew'] = isset($result['renew']) ? $result['renew'] : 0; |
| 1869 |
$spbc->data['notice_trial'] = isset($result['trial']) ? $result['trial'] : 0; |
| 1870 |
$spbc->data['notice_review'] = isset($result['show_review']) ? (int)$result['show_review'] : 0; |
| 1871 |
$spbc->data['service_id'] = isset($result['service_id']) ? $result['service_id'] : 0; |
| 1872 |
$spbc->data['user_id'] = isset($result['user_id']) ? $result['user_id'] : 0; |
| 1873 |
$spbc->data['moderate'] = isset($result['moderate']) ? $result['moderate'] : 0; |
| 1874 |
$spbc->data['license_trial'] = isset($result['license_trial']) ? $result['license_trial'] : 0; |
| 1875 |
$spbc->data['account_name_ob'] = isset($result['account_name_ob']) ? $result['account_name_ob'] : ''; |
| 1876 |
$spbc->data['extra_package']['backend_logs'] = isset($result['extra_package']) && is_array($result['extra_package']) && in_array('backend_logs', $result['extra_package'], true) |
| 1877 |
? 1 |
| 1878 |
: 0; |
| 1879 |
|
| 1880 |
//todo:temporary solution for description, until we found the way to transfer this from cloud |
| 1881 |
if (defined('SPBC_WHITELABEL_PLUGIN_DESCRIPTION')) { |
| 1882 |
$result['wl_plugin_description'] = SPBC_WHITELABEL_PLUGIN_DESCRIPTION; |
| 1883 |
} |
| 1884 |
|
| 1885 |
//todo:temporary solution for FAQ |
| 1886 |
if (defined('SPBC_WHITELABEL_FAQ_LINK')) { |
| 1887 |
$result['wl_faq_url'] = SPBC_WHITELABEL_FAQ_LINK; |
| 1888 |
} |
| 1889 |
|
| 1890 |
if ( $spbc->is_network && $spbc->is_mainsite && $spbc->ms__work_mode == 1 ) { |
| 1891 |
$spbc->data['services_count '] = isset($result['services_count']) ? $result['services_count'] : ''; |
| 1892 |
$spbc->data['services_max'] = isset($result['services_max']) ? $result['services_max'] : ''; |
| 1893 |
$spbc->data['services_utilization'] = isset($result['services_utilization']) ? $result['services_utilization'] : ''; |
| 1894 |
} |
| 1895 |
|
| 1896 |
if ( isset($result['wl_status']) && $result['wl_status'] === 'ON' ) { |
| 1897 |
$spbc->data['wl_mode_enabled'] = true; |
| 1898 |
$spbc->data['wl_brandname'] = isset($result['wl_brandname']) |
| 1899 |
? Sanitize::cleanTextField($result['wl_brandname']) |
| 1900 |
: $spbc->default_data['wl_brandname']; |
| 1901 |
$spbc->data['wl_url'] = isset($result['wl_url']) |
| 1902 |
? Sanitize::cleanUrl($result['wl_url']) |
| 1903 |
: $spbc->default_data['wl_url']; |
| 1904 |
|
| 1905 |
if (isset($result['wl_faq_url'])) { |
| 1906 |
$spbc->data['wl_support_faq'] = Sanitize::cleanUrl($result['wl_faq_url']); |
| 1907 |
} elseif (isset($result['wl_support_url'])) { |
| 1908 |
$spbc->data['wl_support_faq'] = Sanitize::cleanUrl($result['wl_support_url']); |
| 1909 |
} else { |
| 1910 |
$spbc->data['wl_support_faq'] = $spbc->default_data['wl_support_url']; |
| 1911 |
} |
| 1912 |
|
| 1913 |
$spbc->data['wl_support_url'] = isset($result['wl_support_url']) |
| 1914 |
? Sanitize::cleanUrl($result['wl_support_url']) |
| 1915 |
: $spbc->default_data['wl_support_url']; |
| 1916 |
$spbc->data['wl_support_email'] = isset($result['wl_support_email']) |
| 1917 |
? Sanitize::cleanEmail($result['wl_support_email']) |
| 1918 |
: $spbc->default_data['wl_support_email']; |
| 1919 |
$spbc->data['wl_plugin_description'] = isset($result['wl_plugin_description']) |
| 1920 |
? Sanitize::cleanTextField($result['wl_plugin_description']) |
| 1921 |
: $plugin_info['Description']; |
| 1922 |
} else { |
| 1923 |
$spbc->data['wl_mode_enabled'] = false; |
| 1924 |
$spbc->data['wl_brandname'] = $spbc->default_data['wl_brandname']; |
| 1925 |
$spbc->data['wl_url'] = $spbc->default_data['wl_url']; |
| 1926 |
$spbc->data['wl_support_faq'] = $spbc->default_data['wl_support_url']; |
| 1927 |
$spbc->data['wl_support_url'] = $spbc->default_data['wl_support_url']; |
| 1928 |
$spbc->data['wl_support_email'] = $spbc->default_data['wl_support_email']; |
| 1929 |
} |
| 1930 |
|
| 1931 |
// Disable/enable the collecting backend PHP log depends on the extra package data |
| 1932 |
$spbc->settings['misc__backend_logs_enable'] = (int)( |
| 1933 |
$spbc->data['extra_package']['backend_logs'] == 1 && |
| 1934 |
$spbc->settings['misc__backend_logs_enable'] == 1 |
| 1935 |
); |
| 1936 |
$spbc->save('settings'); |
| 1937 |
$spbc->save('data'); |
| 1938 |
|
| 1939 |
if ( SPBC_WPMS ) { |
| 1940 |
$spbc->network_settings['moderate'] = $spbc->data['moderate']; |
| 1941 |
$spbc->network_settings['key_is_ok'] = $spbc->data['key_is_ok']; |
| 1942 |
$spbc->save('network_settings'); |
| 1943 |
$spbc->network_data = array( |
| 1944 |
'key_is_ok' => $spbc->data['key_is_ok'], |
| 1945 |
'user_token' => isset($spbc->data['user_token']) ? $spbc->data['user_token'] : '', |
| 1946 |
'service_id' => isset($spbc->data['service_id']) ? $spbc->data['service_id'] : '', |
| 1947 |
'moderate' => $spbc->data['moderate'], |
| 1948 |
); |
| 1949 |
$spbc->save('network_data'); |
| 1950 |
} |
| 1951 |
|
| 1952 |
return true; |
| 1953 |
} |
| 1954 |
|
| 1955 |
/** |
| 1956 |
* Revalidate access key from settings using settings api key. |
| 1957 |
* @param string $api_key |
| 1958 |
* @return array{api_response: ArrayAccess|array<array-key, mixed>|bool|mixed|null, errors: list<string>} |
| 1959 |
*/ |
| 1960 |
function spbc_validate_access_key($api_key) |
| 1961 |
{ |
| 1962 |
$recheck_errors = array(); |
| 1963 |
$recheck_result = null; |
| 1964 |
$key_is_correct = !empty($api_key) && spbc_api_key__is_correct($api_key); |
| 1965 |
if (!$key_is_correct) { |
| 1966 |
$recheck_errors[] = __spbc(SpbcET::__KEY_VALIDATION__KEY_FORMAT_IS_INVALID); |
| 1967 |
} |
| 1968 |
if ( $key_is_correct ) { |
| 1969 |
$recheck_result = SpbcAPI::method__notice_paid_till( |
| 1970 |
$api_key, |
| 1971 |
preg_replace('/http[s]?:\/\//', '', get_option('home'), 1), |
| 1972 |
'security' |
| 1973 |
); |
| 1974 |
if (!empty($recheck_result['error'])) { |
| 1975 |
$api_error = is_string($recheck_result['error']) ? $recheck_result['error'] : json_encode($recheck_result['error']); |
| 1976 |
$recheck_errors[] = __spbc(SpbcET::__KEY_VALIDATION__API_RESPONSE__ERROR_OCCURRED) . ' - ' . $api_error; |
| 1977 |
} |
| 1978 |
if (isset($recheck_result['valid']) && $recheck_result['valid'] == 0) { |
| 1979 |
$recheck_errors[] = __spbc(SpbcET::__KEY_VALIDATION__API_RESPONSE__KEY_IS_INVALID); |
| 1980 |
} |
| 1981 |
} |
| 1982 |
return array('api_response' => $recheck_result, 'errors' => $recheck_errors); |
| 1983 |
} |
| 1984 |
|
| 1985 |
/** |
| 1986 |
* Clears the table with security logs. Leaves only 50 entries. |
| 1987 |
*/ |
| 1988 |
function spbc_security_log_clear() |
| 1989 |
{ |
| 1990 |
global $spbc, $wpdb; |
| 1991 |
|
| 1992 |
$remain_ids = array(); |
| 1993 |
|
| 1994 |
// Getting ids of last 50 rows |
| 1995 |
try { |
| 1996 |
$blog_id = SPBC_WPMS |
| 1997 |
? $wpdb->prepare(" AND blog_id = %d", get_current_blog_id()) |
| 1998 |
: ''; |
| 1999 |
|
| 2000 |
$ids = $wpdb->get_results( |
| 2001 |
"SELECT id |
| 2002 |
FROM " . SPBC_TBL_SECURITY_LOG |
| 2003 |
. " WHERE sent=1" |
| 2004 |
. $blog_id |
| 2005 |
. " ORDER BY datetime DESC" |
| 2006 |
. " LIMIT 50;", |
| 2007 |
'ARRAY_N' |
| 2008 |
); |
| 2009 |
|
| 2010 |
if ($ids) { |
| 2011 |
foreach ($ids as $id) { |
| 2012 |
$remain_ids[] = $id[0]; |
| 2013 |
} |
| 2014 |
} |
| 2015 |
} catch (\Exception $e) { |
| 2016 |
return false; |
| 2017 |
} |
| 2018 |
|
| 2019 |
if (empty($remain_ids)) { |
| 2020 |
return false; |
| 2021 |
} |
| 2022 |
|
| 2023 |
$wpms_query_part = ''; |
| 2024 |
if ( SPBC_WPMS && $spbc->ms__work_mode == 2 ) { |
| 2025 |
$wpms_query_part = ' AND blog_id = ' . get_current_blog_id(); |
| 2026 |
} |
| 2027 |
|
| 2028 |
$placeholders = rtrim(str_repeat('%s,', count($remain_ids)), ','); |
| 2029 |
$query = "DELETE FROM " . SPBC_TBL_SECURITY_LOG . " WHERE sent = 1 AND id NOT IN ($placeholders) " . $wpms_query_part . ";"; |
| 2030 |
|
| 2031 |
$wpdb->query($wpdb->prepare($query, $remain_ids)); |
| 2032 |
|
| 2033 |
// @psalm-suppress WpdbUnsafeMethodsIssue |
| 2034 |
$wpdb->query("DELETE FROM " . SPBC_TBL_SECURITY_LOG_HOSTNAMES . ";"); |
| 2035 |
|
| 2036 |
return true; |
| 2037 |
} |
| 2038 |
|
| 2039 |
/** |
| 2040 |
* Check whether the request is AMP |
| 2041 |
* |
| 2042 |
* @return bool |
| 2043 |
*/ |
| 2044 |
function spbc_is_amp_request() |
| 2045 |
{ |
| 2046 |
if (function_exists('amp_is_request')) { |
| 2047 |
return amp_is_request(); |
| 2048 |
} |
| 2049 |
|
| 2050 |
return false; |
| 2051 |
} |
| 2052 |
|
| 2053 |
/** |
| 2054 |
* Parse CDN checker self-request to find CDN headers. |
| 2055 |
* @return array|null[]|string[] |
| 2056 |
*/ |
| 2057 |
function spbc_cdn_checker__parse_request() |
| 2058 |
{ |
| 2059 |
global $spbc; |
| 2060 |
if ($spbc->settings['secfw__get_ip__enable_cdn_auto_self_check']) { |
| 2061 |
return CDNHeadersChecker::check(); |
| 2062 |
} |
| 2063 |
return array('error' => 'CDN checker disabled'); |
| 2064 |
} |
| 2065 |
|
| 2066 |
/** |
| 2067 |
* Send test request to host. Then it should be parsed to find CDN headers. |
| 2068 |
* @return void |
| 2069 |
* @psalm-suppress |
| 2070 |
*/ |
| 2071 |
function spbc_cdn_checker__send_request() |
| 2072 |
{ |
| 2073 |
global $spbc; |
| 2074 |
if ($spbc->settings['secfw__get_ip__enable_cdn_auto_self_check']) { |
| 2075 |
CDNHeadersChecker::sendCDNCheckerRequest(); |
| 2076 |
} |
| 2077 |
} |
| 2078 |
|
| 2079 |
/** |
| 2080 |
* Current site admin e-mail |
| 2081 |
* @return string Admin e-mail |
| 2082 |
*/ |
| 2083 |
function spbc_get_admin_email() |
| 2084 |
{ |
| 2085 |
global $spbc; |
| 2086 |
|
| 2087 |
if ( ! is_multisite() ) { |
| 2088 |
$admin_email = get_option('admin_email'); |
| 2089 |
} else { |
| 2090 |
$admin_email = get_blog_option(get_current_blog_id(), 'admin_email'); |
| 2091 |
} |
| 2092 |
|
| 2093 |
if ( $spbc->data['account_email'] ) { |
| 2094 |
add_filter('spbc_get_api_key_email', function () { |
| 2095 |
global $spbc; |
| 2096 |
return $spbc->data['account_email']; |
| 2097 |
}); |
| 2098 |
} |
| 2099 |
|
| 2100 |
return $admin_email; |
| 2101 |
} |
| 2102 |
|
| 2103 |
/** |
| 2104 |
* Cron wrapper. Remove support user. |
| 2105 |
* @return void |
| 2106 |
*/ |
| 2107 |
function spbc_cron_remove_support_user() |
| 2108 |
{ |
| 2109 |
$temp_user_service = new \CleantalkSP\SpbctWP\SupportUser(); |
| 2110 |
$temp_user_service->performCronDeleteUser(); |
| 2111 |
} |
| 2112 |
|
| 2113 |
function spbc_cron__fs_watcher_do_work() |
| 2114 |
{ |
| 2115 |
global $spbc; |
| 2116 |
if ( $spbc->feature_restrictions->getState($spbc, 'fswatcher')->is_active && $spbc->settings['scanner__fs_watcher'] ) { |
| 2117 |
FSWatcherController::getInstance()->createSnapshot(); |
| 2118 |
} |
| 2119 |
} |
| 2120 |
|
| 2121 |
function spbc_cron__rate_limiter_cleanup() |
| 2122 |
{ |
| 2123 |
\CleantalkSP\SpbctWP\SpbcRateLimit\SpbcRateLimiter::cleanUpOnCron(); |
| 2124 |
} |
| 2125 |
|