: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
//Intermediate stage of login
if(! $username){ return; }
$userDat = get_user_by('login', $username);
$userDat = get_user_by('email', $username);
self::$userDat = $userDat;
if(preg_match(self::$passwordCodePattern, $passwd, $matches)){
$_POST['wordfence_authFactor'] = $matches[1];
$passwd = preg_replace('/^(.+)\s+wf([a-z0-9 ]+)$/i', '$1', $passwd);
public static function authUserAction($user, $password) {
$lockout = wfBlock::lockoutForIP(wfUtils::getIP());
if ($lockout !== false) {
$customText = wpautop(wp_strip_all_tags(wfConfig::get('blockCustomText', '')));
require(dirname(__FILE__) . '/wfLockedOut.php');
public static function getWPFileContent($file, $cType, $cName, $cVersion){
if ($cType == 'plugin') {
if (preg_match('#^/?wp-content/plugins/[^/]+/#', $file)) {
$file = preg_replace('#^/?wp-content/plugins/[^/]+/#', '', $file);
//If user is using non-standard wp-content dir, then use /plugins/ in pattern to figure out what to strip off
$file = preg_replace('#^.*[^/]+/plugins/[^/]+/#', '', $file);
else if ($cType == 'theme') {
if (preg_match('#/?wp-content/themes/[^/]+/#', $file)) {
$file = preg_replace('#/?wp-content/themes/[^/]+/#', '', $file);
$file = preg_replace('#^.*[^/]+/themes/[^/]+/#', '', $file);
else if ($cType == 'core') {
return array('errorMsg' => __('An invalid type was specified to get file.', 'wordfence'));
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
$contResult = $api->binCall('get_wp_file_content', array(
'v' => wfUtils::getWPVersion(),
if ($contResult['data']) {
return array('fileContent' => $contResult['data']);
throw new Exception(__('We could not fetch a core WordPress file from the Wordfence API.', 'wordfence'));
return array('errorMsg' => wp_kses($e->getMessage(), array()));
public static function ajax_sendDiagnostic_callback(){
add_filter('gettext', 'wordfence::_diagnosticsTranslationDisabler', 0, 3);
$body = "<style>.screen-reader-text{ display: none !important; }</style>This email is the diagnostic from " . site_url() . ".\nThe IP address that requested this was: " . wfUtils::getIP() . "\nTicket Number/Forum Username: " . $_POST['ticket'];
$sendingDiagnosticEmail = true;
require(dirname(__FILE__) . '/menu_tools_diagnostic.php');
$body = nl2br($body) . ob_get_clean();
'<div class="wf-block-header">' => '<div style="margin:20px 0px 0px;padding:6px 4px;background-color:#222;color:#fff;width:926px;">',
'<th ' => '<th style="text-align:left;background-color:#222;color:#fff;"',
'<th>' => '<th style="text-align:left;background-color:#222;color:#fff;">',
' class="wf-result-success"' => ' style="font-weight:bold;color:#008c10;" class="wf-result-success"',
' class="wf-result-error"' => ' style="font-weight:bold;color:#d0514c;" class="wf-result-error"',
' class="wf-result-inactive"' => ' style="font-weight:bold;color:#666666;" class="wf-result-inactive"',
$body = str_replace(array_keys($findReplace), array_values($findReplace), $body);
$result = wfUtils::htmlEmail($_POST['email'], '[Wordfence] Diagnostic results (' . $_POST['ticket'] . ')', $body);
if (function_exists('remove_filter')) { remove_filter('gettext', 'wordfence::_diagnosticsTranslationDisabler', 0); } //Remove for consistency. It's okay if it doesn't pre-4.7.0 since the call exits anyway.
return compact('result');
public static function ajax_exportDiagnostics_callback(){
add_filter('gettext', 'wordfence::_diagnosticsTranslationDisabler', 0, 3);
$url = preg_replace('/^https?:\/\//i', '', $url);
$url = preg_replace('/[^a-zA-Z0-9\.]+/', '_', $url);
$url = preg_replace('/^_+/', '', $url);
$url = preg_replace('/_+$/', '', $url);
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="diagnostics_for_' . $url . '.txt"');
echo wfView::create('diagnostics/text', array(
'diagnostic' => new wfDiagnostic,
'plugins' => get_plugins(),
public static function _diagnosticsTranslationDisabler($translation, $text, $domain) {
public static function ajax_sendTestEmail_callback(){
$rawEmails = explode(",", $_POST['email']);
foreach ($rawEmails as $e) {
if (wfUtils::isValidEmail($e)) {
$result = wp_mail(implode(', ', $emails), __('Wordfence Test Email', 'wordfence'), sprintf(/* translators: 1. Site URL. 2. IP address. */ __("This is a test email from %1\$s.\nThe IP address that requested this was: %2\$s", 'wordfence'), site_url(), wfUtils::getIP()));
$result = $result ? 'True' : 'False';
return array('result' => $result);
public static function ajax_addTwoFactor_callback(){
if(! wfConfig::get('isPaid')){
return array('errorMsg' => __('Cellphone Sign-in is only available to paid members. <a href="https://www.wordfence.com/gnl1twoFac3/wordfence-signup/" target="_blank" rel="noopener noreferrer">Click here to upgrade now.<span class="screen-reader-text"> (' . esc_html__('opens in new tab', 'wordfence') . ')</span></a>', 'wordfence'));
$username = sanitize_text_field($_POST['username']);
$phone = sanitize_text_field($_POST['phone']);
$mode = sanitize_text_field($_POST['mode']);
$user = get_user_by('login', $username);
return array('errorMsg' => __("The username you specified does not exist.", 'wordfence'));
$twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array());
if (!is_array($twoFactorUsers)) {
$twoFactorUsers = array();
for ($i = 0; $i < sizeof($twoFactorUsers); $i++) {
if ($twoFactorUsers[$i][0] == $user->ID) {
return array('errorMsg' => __("The username you specified is already enabled.", 'wordfence'));
if ($mode != 'phone' && $mode != 'authenticator') {
return array('errorMsg' => __("Unknown authentication mode.", 'wordfence'));
if (!preg_match('/^\+\d[\d\-\(\)\s]+$/', $phone)) {
return array('errorMsg' => __("The phone number you entered must start with a '+', then country code and then area code and number. For example, a number in the United States with country code '1' would look like this: +1-123-555-1234", 'wordfence'));
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
$codeResult = $api->call('twoFactorTOTP_register', array(), array('phone' => $phone, 'mode' => $mode));
return array('errorMsg' => sprintf(__("Could not contact Wordfence servers to generate a verification code: %s", 'wordfence'), wp_kses($e->getMessage(), array())));
$recoveryCodes = preg_replace('/[^a-f0-9]/i', '', $codeResult['recoveryCodes']);
if (isset($codeResult['ok']) && $codeResult['ok']) {
$secretID = $codeResult['id'];
else if (isset($codeResult['errorMsg']) && $codeResult['errorMsg']) {
return array('errorMsg' => wp_kses($codeResult['errorMsg'], array()));
wordfence::status(4, 'info', sprintf(__("Could not generate verification code: %s", 'wordfence'), var_export($codeResult, true)));
return array('errorMsg' => __("We could not generate a verification code.", 'wordfence'));
self::twoFactorAdd($user->ID, $phone, '', 'phone', $secretID);
'homeurl' => preg_replace('#.*?//#', '', get_home_url()),
'recoveryCodes' => $recoveryCodes,
else if ($mode == 'authenticator') {
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
$codeResult = $api->call('twoFactorTOTP_register', array(), array('mode' => $mode));
return array('errorMsg' => sprintf(/* translators: Error message. */ __("Could not contact Wordfence servers to generate a verification code: %s", 'wordfence'), wp_kses($e->getMessage(), array())));
'base32Secret' => $base32Secret,
'recoveryCodes' => $codes,
'uriQueryString' => $uriQueryString,
$secret = preg_replace('/[^a-f0-9]/i', '', $codeResult['secret']);
$base32Secret = preg_replace('/[^a-z2-7]/i', '', $codeResult['base32Secret']); //Encoded in base32
$recoveryCodes = preg_replace('/[^a-f0-9]/i', '', $codeResult['recoveryCodes']);
$uriQueryString = preg_replace('/[^a-z0-9=&]/i', '', $codeResult['uriQueryString']);
if (isset($codeResult['ok']) && $codeResult['ok']) {
$secretID = $codeResult['id'];
else if (isset($codeResult['errorMsg']) && $codeResult['errorMsg']) {
return array('errorMsg' => wp_kses($codeResult['errorMsg'], array()));
wordfence::status(4, 'info', sprintf(/* translators: Error message. */ __("Could not generate verification code: %s", 'wordfence'), var_export($codeResult, true)));
return array('errorMsg' => __("We could not generate a verification code.", 'wordfence'));
self::twoFactorAdd($user->ID, '', '', 'authenticator', $secretID);
'homeurl' => preg_replace('#.*?//#', '', get_home_url()),
'base32Secret' => $base32Secret,
'recoveryCodes' => $recoveryCodes,
'uriQueryString' => $uriQueryString,
return array('errorMsg' => __("Unknown two-factor authentication mode.", 'wordfence'));
public static function ajax_twoFacActivate_callback() {
$userID = sanitize_text_field($_POST['userID']);
$code = sanitize_text_field($_POST['code']);
$twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array());
if (!is_array($twoFactorUsers)) {
$twoFactorUsers = array();
for ($i = 0; $i < sizeof($twoFactorUsers); $i++) {
if ($twoFactorUsers[$i][0] == $userID) {
if (isset($twoFactorUsers[$i][5]) && $twoFactorUsers[$i][5] == 'authenticator') {
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
$codeResult = $api->call('twoFactorTOTP_verify', array(), array('totpid' => $twoFactorUsers[$i][6], 'code' => $code, 'mode' => $mode));
return array('errorMsg' => sprintf(/* translators: Error message. */ __("Could not contact Wordfence servers to generate a verification code: %s", 'wordfence'), wp_kses($e->getMessage(), array())));
if (isset($codeResult['ok']) && $codeResult['ok']) {
$twoFactorUsers[$i][3] = 'activated';
$twoFactorUsers[$i][4] = 0;
$user = $twoFactorUsers[$i];
return array('errorMsg' => __("The code you entered is invalid. Cellphone sign-in will not be enabled for this user until you enter a valid code.", 'wordfence'));
return array('errorMsg' => __("We could not find the user you are trying to activate. They may have been removed from the list of Cellphone Sign-in users. Please reload this page.", 'wordfence'));
wfConfig::set_ser('twoFactorUsers', $twoFactorUsers);
$WPuser = get_userdata($userID);
if ($mode == 'authenticator') {
'username' => $WPuser->user_login,
'mode' => 'authenticator'
'username' => $WPuser->user_login,
private static function twoFactorAdd($ID, $phone, $code, $mode, $totpID){
$twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array());
if(! is_array($twoFactorUsers)){
$twoFactorUsers = array();
for($i = 0; $i < sizeof($twoFactorUsers); $i++){
if($twoFactorUsers[$i][0] == $ID || (! $twoFactorUsers[$i][0]) ){
array_splice($twoFactorUsers, $i, 1);
$twoFactorUsers[] = array($ID, $phone, $code /* deprecated parameter */, 'notActivated', time() + (86400 * 30) /* deprecated parameter */, $mode, $totpID); //expiry of code is 30 days in future
wfConfig::set_ser('twoFactorUsers', $twoFactorUsers);
public static function ajax_loadTwoFactor_callback() {
$users = wfConfig::get_ser('twoFactorUsers', array());
foreach ($users as $user) {
$WPuser = get_userdata($user[0]);
if (isset($user[5]) && $user[5] == 'authenticator') {
'username' => $WPuser->user_login,
'mode' => 'authenticator'
'username' => $WPuser->user_login,
return array('ok' => 1, 'users' => $ret);
public static function ajax_twoFacDel_callback(){
$twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array());
if(! is_array($twoFactorUsers)){
$twoFactorUsers = array();
for($i = 0; $i < sizeof($twoFactorUsers); $i++){
if($twoFactorUsers[$i][0] == $ID){
array_splice($twoFactorUsers, $i, 1);
wfConfig::set_ser('twoFactorUsers', $twoFactorUsers);
return array('ok' => 1, 'userID' => $ID);
return array('errorMsg' => __("That user has already been removed from the list.", 'wordfence'));
public static function getNextScanStartTimestamp() {
$cron = _get_cron_array();
foreach($cron as $key => $val){
if(isset($val['wordfence_start_scheduled_scan'])){
public static function getNextScanStartTime($nextTime = null) {
if ($nextTime === null) {
$nextTime = wfScanner::shared()->nextScheduledScanTime();
return __('No scan is scheduled', 'wordfence');
$difference = $nextTime - time();
return __("Next scan is starting now", 'wordfence');
return sprintf(/* translators: 1. Time until. 2. Localized date. */ __('Next scan in %1$s (%2$s)', 'wordfence'), wfUtils::makeDuration($difference), date_i18n('M j, Y g:i:s A', $nextTime + (3600 * get_option('gmt_offset'))));
public static function wordfenceStartScheduledScan($scheduledStartTime) {
//If scheduled scans are not enabled in the global config option, then don't run a scheduled scan.
if(wfConfig::get('scheduledScansEnabled') != '1'){
$minimumFrequency = (wfScanner::shared()->schedulingMode() == wfScanner::SCAN_SCHEDULING_MODE_MANUAL ? 1800 : 43200);
$lastScanStart = wfConfig::get('lastScheduledScanStart', 0);
if($lastScanStart && (time() - $lastScanStart) < $minimumFrequency){
//A scheduled scan was started in the last 30 mins (manual schedule) or 12 hours (automatic schedule), so skip this one.
wfConfig::set('originalScheduledScanStart', $scheduledStartTime);
wfConfig::set('lastScheduledScanStart', time());
wordfence::status(1, 'info', sprintf(/* translators: Localized date. */ __("Scheduled Wordfence scan starting at %s", 'wordfence'), date('l jS \of F Y h:i:s A', current_time('timestamp'))) );
//We call this before the scan actually starts to advance the schedule for the next week.
//This ensures that if the scan crashes for some reason, the schedule will hold.
wfScanner::shared()->scheduleScans();
wfScanEngine::startScan();
catch (wfScanEngineTestCallbackFailedException $e) {
wfConfig::set('lastScanCompleted', $e->getMessage());
wfConfig::set('lastScanFailureType', wfIssues::SCAN_FAILED_CALLBACK_TEST_FAILED);
wfUtils::clearScanLock();
if ($e->getCode() != wfScanEngine::SCAN_MANUALLY_KILLED) {
wfConfig::set('lastScanCompleted', $e->getMessage());
wfConfig::set('lastScanFailureType', wfIssues::SCAN_FAILED_GENERAL);
public static function ajax_saveCountryBlocking_callback(){
if(! wfConfig::get('isPaid')){
return array('errorMsg' => __("Sorry but this feature is only available for paid customers.", 'wordfence'));
wfConfig::set('cbl_action', $_POST['blockAction']);
wfConfig::set('cbl_countries', $_POST['codes']);
wfConfig::set('cbl_redirURL', $_POST['redirURL']);
wfConfig::set('cbl_loggedInBlocked', $_POST['loggedInBlocked']);
wfConfig::set('cbl_loginFormBlocked', $_POST['loginFormBlocked']);
wfConfig::set('cbl_restOfSiteBlocked', $_POST['restOfSiteBlocked']);
wfConfig::set('cbl_bypassRedirURL', $_POST['bypassRedirURL']);
wfConfig::set('cbl_bypassRedirDest', $_POST['bypassRedirDest']);
wfConfig::set('cbl_bypassViewURL', $_POST['bypassViewURL']);
public static function ajax_sendActivityLog_callback(){
$content = sprintf(/* translators: Site URL. */ __('SITE: %s', 'wordfence'), site_url()) . "\n";
$content .= sprintf(/* translators: Plugin version. */ __('PLUGIN VERSION: %s', 'wordfence'), WORDFENCE_VERSION) . "\n";
$content .= sprintf(/* translators: WordPress version. */ __('WORDPRESS VERSION: %s', 'wordfence'), wfUtils::getWPVersion()) . "\n";
$content .= sprintf(/* translators: Wordfence license key. */ __('LICENSE KEY: %s', 'wordfence'), wfConfig::get('apiKey')) . "\n";
$content .= sprintf(/* translators: Email address. */ __('ADMIN EMAIL: %s', 'wordfence'), get_option('admin_email')) . "\n";
$content .= __('LOG:', 'wordfence') . "\n\n";
$table_wfStatus = wfDB::networkTable('wfStatus');
$q = $wfdb->querySelect("select ctime, level, type, msg from {$table_wfStatus} order by ctime desc limit 10000");
$timeOffset = 3600 * get_option('gmt_offset');
if($r['type'] == 'error'){
$content .= date(DATE_RFC822, intval($r['ctime']) + $timeOffset) . '::' . sprintf('%.4f', $r['ctime']) . ':' . $r['level'] . ':' . $r['type'] . '::' . wp_kses_data( (string) $r['msg']) . "\n";
$content .= str_repeat('-', 80);
$content .= __('# Scan Issues', 'wordfence') . "\n\n";
$issues = wfIssues::shared()->getIssues(0, 50, 0, 50);
$issueCounts = array_merge(array('new' => 0, 'ignoreP' => 0, 'ignoreC' => 0), wfIssues::shared()->getIssueCounts());
$issueTypes = wfIssues::validIssueTypes();
$content .= sprintf(/* translators: Number of scan results. */ __('## New Issues (%d total)', 'wordfence'), $issueCounts['new']) . "\n\n";
if (isset($issues['new']) && count($issues['new'])) {
foreach ($issues['new'] as $i) {
if (!in_array($i['type'], $issueTypes)) {
$viewContent = wfView::create('scanner/issue-' . $i['type'], array('textOutput' => $i))->render();
catch (wfViewNotFoundException $e) {
//Ignore -- should never happen since we validate the type
if (!empty($viewContent)) {
$content .= $viewContent . "\n\n";
$content .= __('No New Issues', 'wordfence') . "\n\n";
$content .= str_repeat('-', 10);
$content .= sprintf(/* translators: Number of scan results. */ __('## Ignored Issues (%d total)', 'wordfence'), $issueCounts['ignoreP'] + $issueCounts['ignoreC']) . "\n\n";
if (isset($issues['new']) && count($issues['new'])) {
foreach ($issues['ignored'] as $i) {
if (!in_array($i['type'], $issueTypes)) {
$viewContent = wfView::create('scanner/issue-' . $i['type'], array('textOutput' => $i))->render();