: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
public function addPendingIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData) {
wfIssues::updateScanStillRunning();
return $this->i->addPendingIssue($type, $severity, $ignoreP, $ignoreC, $shortMsg, $longMsg, $templateData);
public function getPendingIssueCount() {
return $this->i->getPendingIssueCount();
public function getPendingIssues($offset = 0, $limit = 100) {
return $this->i->getPendingIssues($offset, $limit);
public static function requestKill() {
wfScanMonitor::endMonitoring();
wfConfig::set('wfKillRequested', time(), wfConfig::DONT_AUTOLOAD);
public static function checkForKill() {
$kill = wfConfig::get('wfKillRequested', 0);
if ($kill && time() - $kill < 600) { //Kill lasts for 10 minutes
wordfence::status(10, 'info', "SUM_KILLED:" . __('Previous scan was stopped successfully.', 'wordfence'));
throw new Exception(__("Scan was stopped on administrator request.", 'wordfence'), wfScanEngine::SCAN_MANUALLY_KILLED);
public static function startScan($isFork = false, $scanMode = false, $isResume = false) {
if (!defined('DONOTCACHEDB')) {
define('DONOTCACHEDB', true);
if ($scanMode === false) {
$scanMode = wfScanner::shared()->scanType();
if (!$isFork) { //beginning of scan
wfConfig::inc('totalScansRun');
wfConfig::set('wfKillRequested', 0, wfConfig::DONT_AUTOLOAD);
wordfence::status(4, 'info', __("Entering start scan routine", 'wordfence'));
if (wfScanner::shared()->isRunning()) {
return __("A scan is already running. Use the stop scan button if you would like to terminate the current scan.", 'wordfence');
wfConfig::set('currentCronKey', ''); //Ensure the cron key is cleared
wfScanMonitor::handleScanStart($scanMode);
wfScanMonitor::logLastAttempt($isFork);
$timeout = self::getMaxExecutionTime() - 2; //2 seconds shorter than max execution time which ensures that only 2 HTTP processes are ever occupied
$testURL = admin_url('admin-ajax.php?action=wordfence_testAjax');
$forceIpv4 = wfConfig::get('scan_force_ipv4_start');
$interceptor = new wfCurlInterceptor($forceIpv4);
$interceptor->setOption(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
if (!wfConfig::get('startScansRemotely', false)) {
$testSuccessful = (bool) wfConfig::get('scanAjaxTestSuccessful');
wordfence::status(4, 'info', sprintf(__("Cached result for scan start test: %s", 'wordfence'), var_export($testSuccessful, true)));
$testResult = $interceptor->intercept(function () use ($testURL, $timeout) {
return wp_remote_post($testURL, array(
//Fall through to the remote start test below
wordfence::status(4, 'info', sprintf(/* translators: Scan start test result data. */ __("Test result of scan start URL fetch: %s", 'wordfence'), var_export($testResult, true)));
$testSuccessful = !is_wp_error($testResult) && (is_array($testResult) || $testResult instanceof ArrayAccess) && strstr($testResult['body'], 'WFSCANTESTOK') !== false;
wfConfig::set('scanAjaxTestSuccessful', $testSuccessful);
$cronKey = wfUtils::bigRandomHex();
wfConfig::set('currentCronKey', time() . ',' . $cronKey);
if ((!wfConfig::get('startScansRemotely', false)) && $testSuccessful) {
//ajax requests can be sent by the server to itself
$cronURL = self::_localStartURL($isFork, $scanMode, $cronKey);
$headers = array('Referer' => false/*, 'Cookie' => 'XDEBUG_SESSION=1'*/);
wordfence::status(4, 'info', sprintf(/* translators: WordPress admin panel URL. */ __("Starting cron with normal ajax at URL %s", 'wordfence'), $cronURL));
wfConfig::set('scanStartAttempt', time());
$response = $interceptor->intercept(function () use ($cronURL, $headers) {
return wp_remote_get($cronURL, array(
if (wfCentral::isConnected()) {
wfCentral::updateScanStatus();
wfConfig::set('lastScanCompleted', $e->getMessage());
wfConfig::set('lastScanFailureType', wfIssues::SCAN_FAILED_CALLBACK_TEST_FAILED);
if (is_wp_error($response)) {
$error_message = $response->get_error_message();
$lastScanCompletedMessage = sprintf(/* translators: Error message. */ __("There was an error starting the scan: %s.", 'wordfence'), $error_message);
$lastScanCompletedMessage = __("There was an unknown error starting the scan.", 'wordfence');
wfConfig::set('lastScanCompleted', $lastScanCompletedMessage);
wfConfig::set('lastScanFailureType', wfIssues::SCAN_FAILED_CALLBACK_TEST_FAILED);
wordfence::status(4, 'info', __("Scan process ended after forking.", 'wordfence'));
$cronURL = self::_remoteStartURL($isFork, $scanMode, $cronKey);
wordfence::status(4, 'info', sprintf(/* translators: WordPress admin panel URL. */ __("Starting cron via proxy at URL %s", 'wordfence'), $cronURL));
wfConfig::set('scanStartAttempt', time());
$response = wp_remote_get($cronURL, array(
if (wfCentral::isConnected()) {
wfCentral::updateScanStatus();
wfConfig::set('lastScanCompleted', $e->getMessage());
wfConfig::set('lastScanFailureType', wfIssues::SCAN_FAILED_CALLBACK_TEST_FAILED);
if (is_wp_error($response)) {
$error_message = $response->get_error_message();
$lastScanCompletedMessage = sprintf(/* translators: WordPress admin panel URL. */ __("There was an error starting the scan: %s.", 'wordfence'), $error_message);
$lastScanCompletedMessage = __("There was an unknown error starting the scan.", 'wordfence');
wfConfig::set('lastScanCompleted', $lastScanCompletedMessage);
wfConfig::set('lastScanFailureType', wfIssues::SCAN_FAILED_CALLBACK_TEST_FAILED);
wordfence::status(4, 'info', __("Scan process ended after forking.", 'wordfence'));
public static function verifyStartSignature($signature, $isFork, $scanMode, $cronKey, $remote) {
$url = self::_baseStartURL($isFork, $scanMode, $cronKey);
$url = self::_remoteStartURL($isFork, $scanMode, $cronKey);
$url = remove_query_arg('signature', $url);
$test = self::_signStartURL($url);
return hash_equals($signature, $test);
protected static function _baseStartURL($isFork, $scanMode, $cronKey) {
$url = admin_url('admin-ajax.php');
$url .= '?action=wordfence_doScan&isFork=' . ($isFork ? '1' : '0') . '&scanMode=' . urlencode($scanMode) . '&cronKey=' . urlencode($cronKey);
protected static function _localStartURL($isFork, $scanMode, $cronKey) {
$url = self::_baseStartURL($isFork, $scanMode, $cronKey);
return add_query_arg('signature', self::_signStartURL($url), $url);
protected static function _remoteStartURL($isFork, $scanMode, $cronKey) {
$url = self::_baseStartURL($isFork, $scanMode, $cronKey);
$url = preg_replace('/^https?:\/\//i', (wfAPI::SSLEnabled() ? WORDFENCE_API_URL_SEC : WORDFENCE_API_URL_NONSEC) . 'scanp/', $url);
$url = add_query_arg('k', wfConfig::get('apiKey'), $url);
$url = add_query_arg('ssl', wfUtils::isFullSSL() ? '1' : '0', $url);
return add_query_arg('signature', self::_signStartURL($url), $url);
protected static function _signStartURL($url) {
$payload = preg_replace('~^https?://[^/]+~i', '', $url);
return wfCrypt::local_sign($payload);
public function processResponse($result) {
public static function getMaxExecutionTime($staySilent = false) {
$config = wfConfig::get('maxExecutionTime');
wordfence::status(4, 'info', sprintf(/* translators: Time in seconds. */ __("Got value from wf config maxExecutionTime: %s", 'wordfence'), $config));
if (is_numeric($config) && $config >= WORDFENCE_SCAN_MIN_EXECUTION_TIME) {
wordfence::status(4, 'info', sprintf(/* translators: Time in seconds. */ __("getMaxExecutionTime() returning config value: %s", 'wordfence'), $config));
$ini = @ini_get('max_execution_time');
wordfence::status(4, 'info', sprintf(/* translators: PHP ini value. */ __("Got max_execution_time value from ini: %s", 'wordfence'), $ini));
if (is_numeric($ini) && $ini >= WORDFENCE_SCAN_MIN_EXECUTION_TIME) {
if ($ini > WORDFENCE_SCAN_MAX_INI_EXECUTION_TIME) {
wordfence::status(4, 'info', sprintf(
/* translators: 1. PHP ini setting. 2. Time in seconds. */
__('ini value of %1$d is higher than value for WORDFENCE_SCAN_MAX_INI_EXECUTION_TIME (%2$d), reducing', 'wordfence'),
WORDFENCE_SCAN_MAX_INI_EXECUTION_TIME
$ini = WORDFENCE_SCAN_MAX_INI_EXECUTION_TIME;
wordfence::status(4, 'info', sprintf(/* translators: PHP ini setting. */ __("getMaxExecutionTime() returning half ini value: %d", 'wordfence'), $ini));
wordfence::status(4, 'info', __("getMaxExecutionTime() returning default of: 15", 'wordfence'));
* @return wfScanKnownFilesLoader
public function getKnownFilesLoader() {
if ($this->knownFilesLoader === null) {
$this->knownFilesLoader = new wfScanKnownFilesLoader($this->api, $this->getPlugins(), $this->getThemes());
return $this->knownFilesLoader;
public function getPlugins() {
if (!function_exists('get_plugins')) {
require_once(ABSPATH . '/wp-admin/includes/plugin.php');
$pluginData = get_plugins();
foreach ($pluginData as $key => $data) {
if (preg_match('/^([^\/]+)\//', $key, $matches)) {
$pluginDir = $matches[1];
$pluginFullDir = "wp-content/plugins/" . $pluginDir;
'Version' => $data['Version'],
'ShortDir' => $pluginDir,
'FullDir' => $pluginFullDir
if (!$this->pluginsCounted) {
$this->scanController->incrementSummaryItem(wfScanner::SUMMARY_SCANNED_PLUGINS);
$this->pluginsCounted = true;
public function getThemes() {
if (!function_exists('wp_get_themes')) {
require_once(ABSPATH . '/wp-includes/theme.php');
$themeData = wp_get_themes();
foreach ($themeData as $themeName => $themeVal) {
if (preg_match('/\/([^\/]+)$/', $themeVal['Stylesheet Dir'], $matches)) {
$shortDir = $matches[1]; //e.g. evo4cms
$fullDir = "wp-content/themes/{$shortDir}"; //e.g. wp-content/themes/evo4cms
$themes[$themeName] = array(
'Name' => $themeVal['Name'],
'Version' => $themeVal['Version'],
if (!$this->themesCounted) {
$this->scanController->incrementSummaryItem(wfScanner::SUMMARY_SCANNED_THEMES);
$this->themesCounted = true;
public function recordMetric($type, $key, $value, $singular = true) {
if (!isset($this->metrics[$type])) {
$this->metrics[$type] = array();
if (!isset($this->metrics[$type][$key])) {
$this->metrics[$type][$key] = array();
$this->metrics[$type][$key] = $value;
$this->metrics[$type][$key][] = $value;
* Queries the is_safe_file endpoint. If provided an array, it does a bulk check and returns an array containing the
* hashes that were marked as safe. If provided a string, it returns a boolean to indicate the safeness of the file.
* @param string|array $shac
public function isSafeFile($shac) {
$result = $this->api->call('is_safe_file', array(), array('multipleSHAC' => json_encode($shac)));
if (isset($result['isSafe'])) {
return $result['isSafe'];
$result = $this->api->call('is_safe_file', array(), array('shac' => strtoupper($shac)));
return isset($result['isSafe']) && $result['isSafe'] == 1;
class wfScanKnownFilesLoader {
private $knownFiles = array();
public function __construct($api, $plugins = null, $themes = null) {
$this->plugins = $plugins;
public function isLoaded() {
return is_array($this->knownFiles) && count($this->knownFiles) > 0;
* @throws wfScanKnownFilesException
public function isKnownFile($file) {
if (!$this->isLoaded()) {
$this->fetchKnownFiles();
return isset($this->knownFiles['core'][$file]) ||
isset($this->knownFiles['plugins'][$file]) ||
isset($this->knownFiles['themes'][$file]);
* @throws wfScanKnownFilesException
public function isKnownCoreFile($file) {
if (!$this->isLoaded()) {
$this->fetchKnownFiles();
return isset($this->knownFiles['core'][$file]);
* @throws wfScanKnownFilesException
public function isKnownPluginFile($file) {
if (!$this->isLoaded()) {
$this->fetchKnownFiles();
return isset($this->knownFiles['plugins'][$file]);
* @throws wfScanKnownFilesException
public function isKnownThemeFile($file) {
if (!$this->isLoaded()) {
$this->fetchKnownFiles();
return isset($this->knownFiles['themes'][$file]);
* @throws wfScanKnownFilesException
public function fetchKnownFiles() {
$dataArr = $this->api->binCall('get_known_files', json_encode(array(
'plugins' => $this->plugins,
'themes' => $this->themes
if ($dataArr['code'] != 200) {
throw new wfScanKnownFilesException(sprintf(/* translators: 1. HTTP status code. */ __("Got error response from Wordfence servers: %s", 'wordfence'), $dataArr['code']), $dataArr['code']);
$this->knownFiles = @json_decode($dataArr['data'], true);
if (!is_array($this->knownFiles)) {
throw new wfScanKnownFilesException(__("Invalid response from Wordfence servers.", 'wordfence'));
throw new wfScanKnownFilesException($e->getMessage(), $e->getCode(), $e);
public function getKnownPluginData($file) {
if ($this->isKnownPluginFile($file)) {
return $this->knownFiles['plugins'][$file];
public function getKnownThemeData($file) {
if ($this->isKnownThemeFile($file)) {
return $this->knownFiles['themes'][$file];
public function getPlugins() {
public function setPlugins($plugins) {
$this->plugins = $plugins;
public function getThemes() {