: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$whitelistedData['timestamp'],
$whitelistedData['description'],
isset($whitelistedData['userID']) ? $whitelistedData['userID'] : 0,
if ($whitelistedData['timestamp'] > $mostRecentWhitelisting) {
$mostRecentWhitelisting = $whitelistedData['timestamp'];
$falsePositives[] = array(
base64_decode($paramKey),
if (!empty($wafFailures))
$data['waf_failures'] = $wafFailures;
if (!empty($falsePositives))
$data['false_positives'] = $falsePositives;
$homeurl = wfUtils::wpHomeURL();
$siteurl = wfUtils::wpSiteURL();
$installType = wfUtils::wafInstallationType();
$response = wp_remote_post(WFWAF_API_URL_SEC . "?" . http_build_query(array(
'action' => 'send_waf_false_positives',
'k' => $waf->getStorageEngine()->getConfig('apiKey', null, 'synced'),
'lang' => get_site_option('WPLANG'),
'body' => json_encode($data),
'Content-Type' => 'application/json',
if (!is_wp_error($response) && ($body = wp_remote_retrieve_body($response))) {
$jsonData = json_decode($body, true);
if (is_array($jsonData) && array_key_exists('success', $jsonData)) {
wfConfig::set('lastFalsePositiveSendTime', $mostRecentWhitelisting);
else if (is_string($okToSendBody) && preg_match('/next check in: ([0-9]+)/', $okToSendBody, $matches)) {
self::delaySendAttackData(time() + $matches[1]);
else { // Could be that the server is down, so hold off on sending data for a little while
self::delaySendAttackData(time() + 7200);
else if (!wfConfig::get('other_WFNet', true)) {
wfConfig::set('lastAttackDataSendTime', time());
wfConfig::set('lastFalsePositiveSendTime', time());
self::truncateWafFailures();
public static function syncAttackData($exit = true) {
if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
$waf = wfWAF::getInstance();
$table_wfHits = wfDB::networkTable('wfHits');
if ($waf->getStorageEngine() instanceof wfWAFStorageMySQL) {
$lastAttackMicroseconds = floatval($waf->getStorageEngine()->getConfig('lastAttackDataTruncateTime'));
$lastAttackMicroseconds = $wpdb->get_var("SELECT MAX(attackLogTime) FROM {$table_wfHits}");
if ($waf->getStorageEngine()->hasNewerAttackData($lastAttackMicroseconds)) {
$attackData = $waf->getStorageEngine()->getNewestAttackDataArray($lastAttackMicroseconds);
foreach ($attackData as $request) {
if (count($request) !== 9 && count($request) !== 10 /* with metadata */ && count($request) !== 11) {
list($logTimeMicroseconds, $requestTime, $ip, $learningMode, $paramKey, $paramValue, $failedRules, $ssl, $requestString) = $request;
if (array_key_exists(9, $request)) {
if (array_key_exists(10, $request)) {
$recordID = $request[10];
// Skip old entries and hits in learning mode, since they'll get picked up anyways.
if ($logTimeMicroseconds <= $lastAttackMicroseconds || $learningMode) {
$hit = new wfRequestModel();
if (is_numeric($recordID)) {
$hit->attackLogTime = $logTimeMicroseconds;
$hit->ctime = $requestTime;
$hit->IP = wfUtils::inet_pton($ip);
if (preg_match('/user\-agent:(.*?)\n/i', $requestString, $matches)) {
$hit->UA = trim($matches[1]);
$hit->isGoogle = wfCrawl::isGoogleCrawler($hit->UA);
if (preg_match('/Referer:(.*?)\n/i', $requestString, $matches)) {
$hit->referer = trim($matches[1]);
if (preg_match('/^[a-z]+\s+(.*?)\s+/i', $requestString, $uriMatches) && preg_match('/Host:(.*?)\n/i', $requestString, $hostMatches)) {
$hit->URL = 'http' . ($ssl ? 's' : '') . '://' . trim($hostMatches[1]) . trim($uriMatches[1]);
$hit->jsRun = (int) wfLog::isHumanRequest($ip, $hit->UA);
$isHuman = !!$hit->jsRun;
if (preg_match('/cookie:(.*?)\n/i', $requestString, $matches)) {
$authCookieName = $waf->getAuthCookieName();
$hasLoginCookie = strpos($matches[1], $authCookieName) !== false;
if ($hasLoginCookie && preg_match('/' . preg_quote($authCookieName) . '=(.*?);/', $matches[1], $cookieMatches)) {
$authCookie = rawurldecode($cookieMatches[1]);
$decodedAuthCookie = $waf->parseAuthCookie($authCookie);
if ($decodedAuthCookie !== false) {
$hit->userID = $decodedAuthCookie['userID'];
if (preg_match('/^[A-Z]+ (.*?) HTTP\\/1\\.1/', $requestString, $matches)) {
if (($pos = strpos($matches[1], '?')) !== false) {
$path = substr($matches[1], 0, $pos);
$metadata = ($metadata != null ? (array) $metadata : array());
if (isset($metadata['finalAction']) && $metadata['finalAction']) { // The request was blocked/redirected because of its IP based on the plugin's blocking settings. WAF blocks should be reported but not shown in live traffic with that as a reason.
$action = $metadata['finalAction']['action'];
$actionDescription = $action;
if (class_exists('wfWAFIPBlocksController')) {
if ($action == wfWAFIPBlocksController::WFWAF_BLOCK_UAREFIPRANGE) {
wfActivityReport::logBlockedIP($ip, null, 'advanced');
else if ($action == wfWAFIPBlocksController::WFWAF_BLOCK_COUNTRY_BYPASS_REDIR) {
else if ($action == wfWAFIPBlocksController::WFWAF_BLOCK_COUNTRY_REDIR) {
$actionDescription .= ' (' . wfConfig::get('cbl_redirURL') . ')';
wfConfig::inc('totalCountryBlocked');
wfActivityReport::logBlockedIP($ip, null, 'country');
else if ($action == wfWAFIPBlocksController::WFWAF_BLOCK_COUNTRY) {
wfConfig::inc('totalCountryBlocked');
wfActivityReport::logBlockedIP($ip, null, 'country');
else if ($action == wfWAFIPBlocksController::WFWAF_BLOCK_WFSN) {
wordfence::wfsnReportBlockedAttempt($ip, 'login');
wfActivityReport::logBlockedIP($ip, null, 'brute');
else if (defined('wfWAFIPBlocksController::WFWAF_BLOCK_BADPOST') && $action == wfWAFIPBlocksController::WFWAF_BLOCK_BADPOST) {
wfActivityReport::logBlockedIP($ip, null, 'badpost');
else if (defined('wfWAFIPBlocksController::WFWAF_BLOCK_BANNEDURL') && $action == wfWAFIPBlocksController::WFWAF_BLOCK_BANNEDURL) {
wfActivityReport::logBlockedIP($ip, null, 'bannedurl');
else if (defined('wfWAFIPBlocksController::WFWAF_BLOCK_FAKEGOOGLE') && $action == wfWAFIPBlocksController::WFWAF_BLOCK_FAKEGOOGLE) {
wfActivityReport::logBlockedIP($ip, null, 'fakegoogle');
else if ((defined('wfWAFIPBlocksController::WFWAF_BLOCK_LOGINSEC') && $action == wfWAFIPBlocksController::WFWAF_BLOCK_LOGINSEC) ||
(defined('wfWAFIPBlocksController::WFWAF_BLOCK_LOGINSEC_FORGOTPASSWD') && strpos($action, wfWAFIPBlocksController::WFWAF_BLOCK_LOGINSEC_FORGOTPASSWD) === 0) ||
(defined('wfWAFIPBlocksController::WFWAF_BLOCK_LOGINSEC_FAILURES') && strpos($action, wfWAFIPBlocksController::WFWAF_BLOCK_LOGINSEC_FAILURES) === 0)) {
wfActivityReport::logBlockedIP($ip, null, 'brute');
else if ((defined('wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLEGLOBAL') && $action == wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLEGLOBAL) ||
(defined('wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLESCAN') && $action == wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLESCAN) ||
(defined('wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLECRAWLER') && $action == wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLECRAWLER) ||
(defined('wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLECRAWLERNOTFOUND') && $action == wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLECRAWLERNOTFOUND) ||
(defined('wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLEHUMAN') && $action == wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLEHUMAN) ||
(defined('wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLEHUMANNOTFOUND') && $action == wfWAFIPBlocksController::WFWAF_BLOCK_THROTTLEHUMANNOTFOUND)
wfConfig::inc('totalIPsThrottled');
wfActivityReport::logBlockedIP($ip, null, 'throttle');
wfActivityReport::logBlockedIP($ip, null, 'manual');
if (isset($metadata['finalAction']['id']) && $action != wfWAFIPBlocksController::WFWAF_BLOCK_COUNTRY_BYPASS_REDIR) {
$id = $metadata['finalAction']['id'];
$block = new wfBlock($id);
$block->recordBlock(1, (int) $requestTime);
if (strlen($actionDescription) == 0) {
$actionDescription = 'Blocked by Wordfence';
if (empty($failedRules)) { // Just a plugin block
$hit->action = 'blocked:wordfence';
if (class_exists('wfWAFIPBlocksController')) {
if ($action == wfWAFIPBlocksController::WFWAF_BLOCK_COUNTRY_BYPASS_REDIR) {
$hit->action = 'cbl:redirect';
else if ($action == wfWAFIPBlocksController::WFWAF_BLOCK_WFSN) {
$hit->action = 'blocked:wfsnrepeat';
wordfence::wfsnReportBlockedAttempt($ip, 'waf');
else if (isset($metadata['finalAction']['lockout'])) {
$hit->action = 'lockedOut';
else if (isset($metadata['finalAction']['block'])) {
$hit->actionDescription = $actionDescription;
else if (preg_match('/\blogged\b/i', $failedRules)) {
$hit->action = 'logged:waf';
else { // Blocked by the WAF but would've been blocked anyway by the plugin settings so that message takes priority
$hit->action = 'blocked:waf-always';
$hit->actionDescription = $actionDescription;
if (preg_match('/\blogged\b/i', $failedRules)) {
$hit->action = 'logged:waf';
$hit->action = 'blocked:waf';
if ($failedRules == 'blocked') {
else if (is_numeric($failedRules)) {
wfActivityReport::logBlockedIP($hit->IP, null, $type);
/** @var wfWAFRule $rule */
$ruleIDs = explode('|', $failedRules);
'learningMode' => $learningMode,
'failedRules' => $failedRules,
'paramValue' => $paramValue,
if ($ruleIDs && $ruleIDs[0]) {
$rule = $waf->getRule($ruleIDs[0]);
if ($hit->action == 'logged:waf' || $hit->action == 'blocked:waf') { $hit->actionDescription = $rule->getDescription(); }
$actionData['category'] = $rule->getCategory();
$actionData['ssl'] = $ssl;
$actionData['fullRequest'] = base64_encode($requestString);
else if ($ruleIDs[0] == 'logged' && isset($ruleIDs[1]) && ($rule = $waf->getRule($ruleIDs[1]))) {
if ($hit->action == 'logged:waf' || $hit->action == 'blocked:waf') { $hit->actionDescription = $rule->getDescription(); }
$actionData['category'] = $rule->getCategory();
$actionData['ssl'] = $ssl;
$actionData['fullRequest'] = base64_encode($requestString);
else if ($ruleIDs[0] == 'logged') {
if ($hit->action == 'logged:waf' || $hit->action == 'blocked:waf') { $hit->actionDescription = 'Watched IP Traffic: ' . $ip; }
$actionData['category'] = 'logged';
$actionData['ssl'] = $ssl;
$actionData['fullRequest'] = base64_encode($requestString);
else if ($ruleIDs[0] == 'blocked') {
$actionData['category'] = 'blocked';
$actionData['ssl'] = $ssl;
$actionData['fullRequest'] = base64_encode($requestString);
$hit->actionData = wfRequestModel::serializeActionData($actionData, array('fullRequest', 'ssl', 'category', 'learningMode', 'paramValue'));
$hit->statusCode = $statusCode;
self::scheduleSendAttackData();
$waf->getStorageEngine()->truncateAttackData();
update_site_option('wordfence_syncingAttackData', 0);
update_site_option('wordfence_syncAttackDataAttempts', 0);
update_site_option('wordfence_lastSyncAttackData', time());
public static function addSyncAttackDataAjax() {
$URL = home_url('/?wordfence_syncAttackData=' . microtime(true));
$URL = esc_url(preg_replace('/^https?:/i', '', $URL));
// Load as external script async so we don't slow page down.
echo "<script type=\"text/javascript\" src=\"$URL\" async></script>";
* This is the only hook I see to tie into WP's core update process.
* Since we hide the readme.html to prevent the WordPress version from being discovered, it breaks the upgrade
* process because it cannot copy the previous readme.html.
public static function restoreReadmeForUpgrade($string) {
register_shutdown_function('wfUtils::hideReadme');
public static function showOnboardingBanner() {
wfOnboardingController::enqueue_assets();
if (self::isWordfencePage(false) && !self::isWordfenceInstallPage() && !self::isWordfenceSupportPage() && !self::isWordfenceSubpage('tools', 'diagnostics')) {
echo wfView::create('onboarding/disabled-overlay')->render();
echo wfView::create('onboarding/banner', array('dismissable' => !self::isWordfencePage(false)))->render();
public static function showCentralConfigurationIssueNotice() {
<p><?php echo wp_kses(sprintf(__('An error was detected with this site\'s configuration that is preventing a successful connection to Wordfence Central. Disconnecting from Central <a href="%s">on the Wordfence Dashboard</a> and reconnecting may resolve it. If the issue persists, please contact Wordfence support.', 'wordfence'), network_admin_url('admin.php?page=Wordfence#wf-central-status')), array('a' => array('href' => array()))) ?></p>
public static function wafAutoPrependNotice() {
$url = network_admin_url('admin.php?page=WordfenceWAF&subpage=waf_options#configureAutoPrepend');
echo '<div class="update-nag" id="wf-extended-protection-notice">' . __('To make your site as secure as possible, take a moment to optimize the Wordfence Web Application Firewall:', 'wordfence') . ' <a class="wf-btn wf-btn-default wf-btn-sm" href="' . esc_url($url) . '">' . __('Click here to configure', 'wordfence') . '</a>
<a class="wf-btn wf-btn-default wf-btn-sm wf-dismiss-link" href="#" onclick="wordfenceExt.setOption(\'dismissAutoPrependNotice\', 1); jQuery(\'#wf-extended-protection-notice\').fadeOut(); return false;" role="button">' . __('Dismiss', 'wordfence') . '</a>
<em style="font-size: 85%;">' . wp_kses(sprintf(/* translators: Support URL. */ __('If you cannot complete the setup process, <a target="_blank" rel="noopener noreferrer" href="%s">click here for help<span class="screen-reader-text"> (opens in new tab)</span></a>.', 'wordfence'), wfSupportController::esc_supportURL(wfSupportController::ITEM_FIREWALL_WAF_INSTALL_MANUALLY)), array('a' => array('href' => array(), 'target' => array(), 'rel' => array()), 'span' => array('class' => array()))) . '</em>
public static function wafAutoPrependVerify() {
if (WFWAF_AUTO_PREPEND && !WFWAF_SUBDIRECTORY_INSTALL) {
echo '<div class="updated is-dismissible"><p>' . __('Nice work! The firewall is now optimized.', 'wordfence') . '</p></div>';
echo '<div class="notice notice-error"><p>' . __('The changes have not yet taken effect. If you are using LiteSpeed or IIS as your web server or CGI/FastCGI interface, you may need to wait a few minutes for the changes to take effect since the configuration files are sometimes cached. You also may need to select a different server configuration in order to complete this step, but wait for a few minutes before trying. You can try refreshing this page.', 'wordfence') . '</p></div>';
public static function wafAutoPrependRemoved() {
if (!WFWAF_AUTO_PREPEND) {
echo '<div class="updated is-dismissible"><p>' . __('Uninstallation was successful!', 'wordfence') . '</p></div>';
else if (WFWAF_SUBDIRECTORY_INSTALL) {
echo '<div class="notice notice-warning"><p>' . __('Uninstallation from this site was successful! The Wordfence Firewall is still active because it is installed in another WordPress installation.', 'wordfence') . '</p></div>';
echo '<div class="notice notice-error"><p>' . __('The changes have not yet taken effect. If you are using LiteSpeed or IIS as your web server or CGI/FastCGI interface, you may need to wait a few minutes for the changes to take effect since the configuration files are sometimes cached. You also may need to select a different server configuration in order to complete this step, but wait for a few minutes before trying. You can try refreshing this page.', 'wordfence') . '</p></div>';
public static function wafUpdateSuccessful() {
echo '<div class="updated is-dismissible"><p>' . __('The update was successful!', 'wordfence') . '</p></div>';
public static function getWAFBootstrapPath() {
if (WF_IS_PRESSABLE || WF_IS_FLYWHEEL) {
return WP_CONTENT_DIR . '/wordfence-waf.php';
return ABSPATH . 'wordfence-waf.php';
public static function getWAFBootstrapContent($currentAutoPrependedFile = null) {
$bootstrapPath = dirname(self::getWAFBootstrapPath());
$currentAutoPrepend = '';
if ($currentAutoPrependedFile && is_file($currentAutoPrependedFile) && !WFWAF_SUBDIRECTORY_INSTALL) {
$currentAutoPrepend = sprintf('
// This file was the current value of auto_prepend_file during the Wordfence WAF installation (%2$s)
}', var_export($currentAutoPrependedFile, true), date('r'));
// Before removing this file, please verify the PHP ini setting `auto_prepend_file` does not point to this.
if (file_exists(__DIR__.%1$s)) {
define("WFWAF_LOG_PATH", __DIR__.%2$s);
include_once __DIR__.%1$s;
var_export(wfUtils::relativePath(WORDFENCE_PATH . 'waf/bootstrap.php', $bootstrapPath, true), true),
var_export(wfUtils::relativePath((WFWAF_SUBDIRECTORY_INSTALL ? WP_CONTENT_DIR . '/wflogs/' : WFWAF_LOG_PATH), $bootstrapPath, true), true),
private static function isCurrentUserAdmin() {
return self::getCurrentUserRole() === 'administrator';
private static function getCurrentUserRole() {
if (current_user_can('administrator') || is_super_admin()) {
$roles = array('editor', 'author', 'contributor', 'subscriber');
foreach ($roles as $role) {
if (current_user_can($role)) {
private static function getCurrentUserCapabilities() {
foreach ($capabilities as $index=>$capability) {
if (!current_user_can($capability)) {
unset($capabilities[$index]);
return array_values($capabilities);
public static function licenseStatusChanged() {
$event = new wfWAFCronFetchRulesEvent(time() - 2);
$event->setWaf(wfWAF::getInstance());
$cron = (array) wfWAF::getInstance()->getStorageEngine()->getConfig('cron', null, 'livewaf');
/** @var wfWAFCronEvent $event */
foreach ($cron as $index => $event) {
$event->setWaf(wfWAF::getInstance());
if (!$event->isInPast()) {
$newEvent = $event->reschedule();
if ($newEvent instanceof wfWAFCronEvent && $newEvent !== $event) {
$cron[$index] = $newEvent;
wfWAF::getInstance()->getStorageEngine()->setConfig('cron', $cron, 'livewaf');
* @param string $adminURL