: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
if ($this->i->totalIgnoredIssues > 0) {
$ignoredText = ' ' . sprintf(
/* translators: Number of scan results. */
'%d ignored issue was also detected.',
'%d ignored issues were also detected.',
$this->i->totalIgnoredIssues,
), $this->i->totalIgnoredIssues);
if ($this->i->totalIssues > 0) {
$this->status(10, 'info', "SUM_FINAL:" . sprintf(
/* translators: Number of scan results. */
"Scan complete. You have %d new issue to fix.",
"Scan complete. You have %d new issues to fix.",
__('See below.', 'wordfence')
$this->status(10, 'info', "SUM_FINAL:" . __('Scan complete. Congratulations, no new problems found.', 'wordfence') . $ignoredText);
public function getCurrentJob() {
return $this->jobList[0];
private function scan_checkSpamIP() {
if ($this->scanController->isPremiumScan()) {
$this->statusIDX['checkSpamIP'] = wfIssues::statusStart(__("Checking if your site IP is generating spam", 'wordfence'));
$this->scanController->startStage(wfScanner::STAGE_SPAM_CHECK);
$result = $this->api->call('check_spam_ip', array(), array(
$haveIssues = wfIssues::STATUS_SECURE;
if (!empty($result['haveIssues']) && is_array($result['issues'])) {
foreach ($result['issues'] as $issue) {
$added = $this->addIssue($issue['type'], wfIssues::SEVERITY_HIGH, $issue['ignoreP'], $issue['ignoreC'], $issue['shortMsg'], $issue['longMsg'], $issue['data']);
if ($added == wfIssues::ISSUE_ADDED || $added == wfIssues::ISSUE_UPDATED) {
$haveIssues = wfIssues::STATUS_PROBLEM;
} else if ($added == wfIssues::ISSUE_IGNOREP || $added == wfIssues::ISSUE_IGNOREC) {
$haveIssues = wfIssues::STATUS_IGNORED;
wfIssues::statusEnd($this->statusIDX['checkSpamIP'], $haveIssues);
$this->scanController->completeStage(wfScanner::STAGE_SPAM_CHECK, $haveIssues);
wfIssues::statusPaidOnly(__("Checking if your IP is generating spam is for paid members only", 'wordfence'));
private function scan_checkGSB_init() {
if ($this->scanController->isPremiumScan()) {
$this->statusIDX['checkGSB'] = wfIssues::statusStart(__("Checking if your site is on a domain blocklist", 'wordfence'));
$this->scanController->startStage(wfScanner::STAGE_BLACKLIST_CHECK);
$h = new wordfenceURLHoover($this->apiKey, $this->wp_version);
wfIssues::statusPaidOnly(__("Checking if your site is on a domain blocklist is for paid members only", 'wordfence'));
private function scan_checkGSB_main() {
if ($this->scanController->isPremiumScan()) {
$h = new wordfenceURLHoover($this->apiKey, $this->wp_version, false, true);
$blogIDs = $wpdb->get_col($wpdb->prepare("SELECT blog_id FROM {$wpdb->blogs} WHERE blog_id > %d ORDER BY blog_id ASC", $this->gsbMultisiteBlogOffset)); //Can't use wp_get_sites or get_sites because they return empty at 10k sites
foreach ($blogIDs as $id) {
$homeURL = get_home_url($id);
$h->hoover($id, $homeURL);
$this->scanController->incrementSummaryItem(wfScanner::SUMMARY_SCANNED_URLS);
$siteURL = get_site_url($id);
if ($homeURL != $siteURL) {
$h->hoover($id, $siteURL);
$this->scanController->incrementSummaryItem(wfScanner::SUMMARY_SCANNED_URLS);
if ($this->shouldFork()) {
$this->gsbMultisiteBlogOffset = $id;
private function scan_checkGSB_finish() {
if ($this->scanController->isPremiumScan()) {
$h = new wordfenceURLHoover($this->apiKey, $this->wp_version, false, true);
$badURLs = $h->getBaddies();
$this->status(4, 'info', sprintf(/* translators: Error message. */ __("Error checking domain blocklists: %s", 'wordfence'), $h->errorMsg));
wfIssues::statusEnd($this->statusIDX['checkGSB'], wfIssues::STATUS_FAILED);
$this->scanController->completeStage(wfScanner::STAGE_BLACKLIST_CHECK, wfIssues::STATUS_FAILED);
$urlsToCheck = array(array(wfUtils::wpHomeURL(), wfUtils::wpSiteURL()));
$badURLs = $this->api->call('check_bad_urls', array(), array('toCheck' => json_encode($urlsToCheck))); //Skipping the separate prefix check since there are just two URLs
foreach ($badURLs as $file => $badSiteList) {
if (!isset($finalResults[$file])) {
$finalResults[$file] = array();
foreach ($badSiteList as $badSite) {
$finalResults[$file][] = array(
$badURLs = $finalResults;
$haveIssues = wfIssues::STATUS_SECURE;
if (is_array($badURLs) && count($badURLs) > 0) {
foreach ($badURLs as $id => $badSiteList) {
foreach ($badSiteList as $badSite) {
$badList = $badSite['badList'];
$data = array('badURL' => $url);
if ($badList == 'goog-malware-shavar') {
$shortMsg = sprintf(/* translators: WordPress site ID. */ __('The multisite blog with ID %d is listed on Google\'s Safe Browsing malware list.', 'wordfence'), intval($id));
$data['multisite'] = intval($id);
$shortMsg = __('Your site is listed on Google\'s Safe Browsing malware list.', 'wordfence');
/* translators: 1. URL. 2. URL. */
__('The URL %1$s is on the malware list. More info available at <a href="http://safebrowsing.clients.google.com/safebrowsing/diagnostic?site=%2$s&client=googlechrome&hl=en-US" target="_blank" rel="noopener noreferrer">Google Safe Browsing diagnostic page<span class="screen-reader-text"> (' . esc_html__('opens in new tab', 'wordfence') . ')</span></a>.', 'wordfence'), esc_html($url), urlencode($url));
} else if ($badList == 'googpub-phish-shavar') {
/* translators: WordPress site ID. */
__('The multisite blog with ID %d is listed on Google\'s Safe Browsing phishing list.', 'wordfence'), intval($id));
$data['multisite'] = intval($id);
$shortMsg = __('Your site is listed on Google\'s Safe Browsing phishing list.', 'wordfence');
/* translators: 1. URL. 2. URL. */
__('The URL %1$s is on the phishing list. More info available at <a href="http://safebrowsing.clients.google.com/safebrowsing/diagnostic?site=%2$s&client=googlechrome&hl=en-US" target="_blank" rel="noopener noreferrer">Google Safe Browsing diagnostic page<span class="screen-reader-text"> (' . esc_html__('opens in new tab', 'wordfence') . ')</span></a>.', 'wordfence'), esc_html($url), urlencode($url));
} else if ($badList == 'wordfence-dbl') {
/* translators: WordPress site ID. */
__('The multisite blog with ID %d is listed on the Wordfence domain blocklist.', 'wordfence'), intval($id));
$data['multisite'] = intval($id);
$shortMsg = __('Your site is listed on the Wordfence domain blocklist.', 'wordfence');
__("The URL %s is on the blocklist.", 'wordfence'), esc_html($url));
/* translators: WordPress site ID. */
__('The multisite blog with ID %d is listed on a domain blocklist.', 'wordfence'), intval($id));
$data['multisite'] = intval($id);
$shortMsg = __('Your site is listed on a domain blocklist.', 'wordfence');
$longMsg = sprintf(/* translators: URL. */ __("The URL is: %s", 'wordfence'), esc_html($url));
$data['gsb'] = 'unknown';
$added = $this->addIssue('checkGSB', wfIssues::SEVERITY_CRITICAL, 'checkGSB', 'checkGSB' . $url, $shortMsg, $longMsg, $data);
if ($added == wfIssues::ISSUE_ADDED || $added == wfIssues::ISSUE_UPDATED) {
$haveIssues = wfIssues::STATUS_PROBLEM;
} else if ($haveIssues != wfIssues::STATUS_PROBLEM && ($added == wfIssues::ISSUE_IGNOREP || $added == wfIssues::ISSUE_IGNOREC)) {
$haveIssues = wfIssues::STATUS_IGNORED;
wfIssues::statusEnd($this->statusIDX['checkGSB'], $haveIssues);
$this->scanController->completeStage(wfScanner::STAGE_BLACKLIST_CHECK, $haveIssues);
private function scan_checkHowGetIPs_init() {
$this->statusIDX['checkHowGetIPs'] = wfIssues::statusStart(__("Checking for the most secure way to get IPs", 'wordfence'));
$this->scanController->startStage(wfScanner::STAGE_SERVER_STATE);
$this->checkHowGetIPsRequestTime = time();
wfUtils::requestDetectProxyCallback();
private function scan_checkHowGetIPs_main() {
if (!defined('WORDFENCE_CHECKHOWGETIPS_TIMEOUT')) {
define('WORDFENCE_CHECKHOWGETIPS_TIMEOUT', 30);
$haveIssues = wfIssues::STATUS_SECURE;
$existing = wfConfig::get('howGetIPs', '');
$recommendation = wfConfig::get('detectProxyRecommendation', '');
while (empty($recommendation) && (time() - $this->checkHowGetIPsRequestTime) < WORDFENCE_CHECKHOWGETIPS_TIMEOUT) {
$recommendation = wfConfig::get('detectProxyRecommendation', '');
if ($recommendation == 'DEFERRED') {
$haveIssues = wfIssues::STATUS_SKIPPED;
} else if (empty($recommendation)) {
$haveIssues = wfIssues::STATUS_FAILED;
} else if ($recommendation == 'UNKNOWN') {
$added = $this->addIssue('checkHowGetIPs', wfIssues::SEVERITY_HIGH, 'checkHowGetIPs', 'checkHowGetIPs' . $recommendation . WORDFENCE_VERSION,
__("Unable to accurately detect IPs", 'wordfence'),
sprintf(/* translators: Support URL. */ __('Wordfence was unable to validate a test request to your website. This can happen if your website is behind a proxy that does not use one of the standard ways to convey the IP of the request or it is unreachable publicly. IP blocking and live traffic information may not be accurate. <a href="%s" target="_blank" rel="noopener noreferrer">Get More Information<span class="screen-reader-text"> (' . esc_html__('opens in new tab', 'wordfence') . ')</span></a>', 'wordfence'), wfSupportController::esc_supportURL(wfSupportController::ITEM_NOTICE_MISCONFIGURED_HOW_GET_IPS))
if ($added == wfIssues::ISSUE_ADDED || $added == wfIssues::ISSUE_UPDATED) {
$haveIssues = wfIssues::STATUS_PROBLEM;
} else if ($added == wfIssues::ISSUE_IGNOREP || $added == wfIssues::ISSUE_IGNOREC) {
$haveIssues = wfIssues::STATUS_IGNORED;
} else if (!empty($existing) && $existing != $recommendation) {
if ($recommendation == 'REMOTE_ADDR') {
$extraMsg = ' ' . __('For maximum security use PHP\'s built in REMOTE_ADDR.', 'wordfence');
} else if ($recommendation == 'HTTP_X_FORWARDED_FOR') {
$extraMsg = ' ' . __('This site appears to be behind a front-end proxy, so using the X-Forwarded-For HTTP header will resolve to the correct IPs.', 'wordfence');
} else if ($recommendation == 'HTTP_X_REAL_IP') {
$extraMsg = ' ' . __('This site appears to be behind a front-end proxy, so using the X-Real-IP HTTP header will resolve to the correct IPs.', 'wordfence');
} else if ($recommendation == 'HTTP_CF_CONNECTING_IP') {
$extraMsg = ' ' . __('This site appears to be behind Cloudflare, so using the Cloudflare "CF-Connecting-IP" HTTP header will resolve to the correct IPs.', 'wordfence');
$added = $this->addIssue('checkHowGetIPs', wfIssues::SEVERITY_HIGH, 'checkHowGetIPs', 'checkHowGetIPs' . $recommendation . WORDFENCE_VERSION,
__("'How does Wordfence get IPs' is misconfigured", 'wordfence'),
/* translators: Support URL. */
__('A test request to this website was detected on a different value for this setting. IP blocking and live traffic information may not be accurate. <a href="%s" target="_blank" rel="noopener noreferrer">Get More Information<span class="screen-reader-text"> (' . esc_html__('opens in new tab', 'wordfence') . ')</span></a>', 'wordfence'),
wfSupportController::esc_supportURL(wfSupportController::ITEM_NOTICE_MISCONFIGURED_HOW_GET_IPS)
array('recommendation' => $recommendation));
if ($added == wfIssues::ISSUE_ADDED || $added == wfIssues::ISSUE_UPDATED) {
$haveIssues = wfIssues::STATUS_PROBLEM;
} else if ($added == wfIssues::ISSUE_IGNOREP || $added == wfIssues::ISSUE_IGNOREC) {
$haveIssues = wfIssues::STATUS_IGNORED;
wfIssues::statusEnd($this->statusIDX['checkHowGetIPs'], $haveIssues);
$this->scanController->completeStage(wfScanner::STAGE_SERVER_STATE, $haveIssues);
private function scan_checkHowGetIPs_finish() {
private function scan_checkReadableConfig() {
$haveIssues = wfIssues::STATUS_SECURE;
$status = wfIssues::statusStart(__("Check for publicly accessible configuration files, backup files and logs", 'wordfence'));
$this->scanController->startStage(wfScanner::STAGE_PUBLIC_FILES);
$backupFileTests = array(
wfCommonBackupFileTest::createFromRootPath('.env'),
wfCommonBackupFileTest::createFromRootPath('.user.ini'),
// wfCommonBackupFileTest::createFromRootPath('.htaccess'),
wfCommonBackupFileTest::createFromRootPath('wp-config.php.bak'),
wfCommonBackupFileTest::createFromRootPath('wp-config.php.bak.a2'),
wfCommonBackupFileTest::createFromRootPath('wp-config.php.swo'),
wfCommonBackupFileTest::createFromRootPath('wp-config.php.save'),
new wfCommonBackupFileTest(home_url('%23wp-config.php%23'), ABSPATH . '#wp-config.php#'),
wfCommonBackupFileTest::createFromRootPath('wp-config.php~'),
wfCommonBackupFileTest::createFromRootPath('wp-config.old'),
wfCommonBackupFileTest::createFromRootPath('.wp-config.php.swp'),
wfCommonBackupFileTest::createFromRootPath('wp-config.bak'),
wfCommonBackupFileTest::createFromRootPath('wp-config.save'),
wfCommonBackupFileTest::createFromRootPath('wp-config.php_bak'),
wfCommonBackupFileTest::createFromRootPath('wp-config.php.swp'),
wfCommonBackupFileTest::createFromRootPath('wp-config.php.old'),
wfCommonBackupFileTest::createFromRootPath('wp-config.php.original'),
wfCommonBackupFileTest::createFromRootPath('wp-config.php.orig'),
wfCommonBackupFileTest::createFromRootPath('wp-config.txt'),
wfCommonBackupFileTest::createFromRootPath('wp-config.original'),
wfCommonBackupFileTest::createFromRootPath('wp-config.orig'),
new wfCommonBackupFileTest(content_url('/debug.log'), WP_CONTENT_DIR . '/debug.log', array(
'Range' => 'bytes=0-700',
$backupFileTests = array_merge($backupFileTests, wfCommonBackupFileTest::createAllForFile('searchreplacedb2.php', wfCommonBackupFileTest::MATCH_REGEX, '/<title>Search and replace DB/i'));
$userIniFilename = ini_get('user_ini.filename');
if ($userIniFilename && $userIniFilename !== '.user.ini') {
$backupFileTests[] = wfCommonBackupFileTest::createFromRootPath($userIniFilename);
/** @var wfCommonBackupFileTest $test */
foreach ($backupFileTests as $test) {
$pathFromRoot = (strpos($test->getPath(), ABSPATH) === 0) ? substr($test->getPath(), strlen(ABSPATH)) : $test->getPath();
wordfence::status(4, 'info', "Testing {$pathFromRoot}");
if ($test->fileExists() && $test->isPubliclyAccessible()) {
$key = "configReadable" . bin2hex($test->getUrl());
$added = $this->addIssue(
wfIssues::SEVERITY_CRITICAL,
/* translators: File path. */
__('Publicly accessible config, backup, or log file found: %s', 'wordfence'), esc_html($pathFromRoot)),
/* translators: 1. URL to publicly accessible file. 2. Support URL. */
__('<a href="%1$s" target="_blank" rel="noopener noreferrer">%1$s</a> is publicly accessible and may expose source code or sensitive information about your site. Files such as this one are commonly checked for by scanners and should be made inaccessible. Alternately, some can be removed if you are certain your site does not need them. Sites using the nginx web server may need manual configuration changes to protect such files. <a href="%2$s" target="_blank" rel="noopener noreferrer">Learn more<span class="screen-reader-text"> (' . esc_html__('opens in new tab', 'wordfence') . ')</span></a>', 'wordfence'),
wfSupportController::esc_supportURL(wfSupportController::ITEM_SCAN_RESULT_PUBLIC_CONFIG)
'url' => $test->getUrl(),
'realFile' => $test->getPath(),
if ($added == wfIssues::ISSUE_ADDED || $added == wfIssues::ISSUE_UPDATED) {
$haveIssues = wfIssues::STATUS_PROBLEM;
} else if ($haveIssues != wfIssues::STATUS_PROBLEM && ($added == wfIssues::ISSUE_IGNOREP || $added == wfIssues::ISSUE_IGNOREC)) {
$haveIssues = wfIssues::STATUS_IGNORED;
wfIssues::statusEnd($status, $haveIssues);
$this->scanController->completeStage(wfScanner::STAGE_PUBLIC_FILES, $haveIssues);
private function scan_wpscan_fullPathDisclosure() {
$file = realpath(ABSPATH . WPINC . "/rss-functions.php");
$haveIssues = wfIssues::STATUS_SECURE;
$status = wfIssues::statusStart(__("Checking if your server discloses the path to the document root", 'wordfence'));
$testPage = includes_url() . basename($file);
if (self::testForFullPathDisclosure($testPage, $file)) {
$key = 'wpscan_fullPathDisclosure' . $testPage;
$added = $this->addIssue(
'wpscan_fullPathDisclosure',
__('Web server exposes the document root', 'wordfence'),
__('Full Path Disclosure (FPD) vulnerabilities enable the attacker to see the path to the webroot/file. e.g.: /home/user/htdocs/file/. Certain vulnerabilities, such as using the load_file() (within a SQL Injection) query to view the page source, require the attacker to have the full path to the file they wish to view.', 'wordfence'),
array('url' => $testPage)
if ($added == wfIssues::ISSUE_ADDED || $added == wfIssues::ISSUE_UPDATED) {
$haveIssues = wfIssues::STATUS_PROBLEM;
} else if ($haveIssues != wfIssues::STATUS_PROBLEM && ($added == wfIssues::ISSUE_IGNOREP || $added == wfIssues::ISSUE_IGNOREC)) {
$haveIssues = wfIssues::STATUS_IGNORED;
wfIssues::statusEnd($status, $haveIssues);
private function scan_wpscan_directoryListingEnabled() {
$this->statusIDX['wpscan_directoryListingEnabled'] = wfIssues::statusStart("Checking to see if directory listing is enabled");
$uploadPaths = wp_upload_dir();
$enabled = self::isDirectoryListingEnabled($uploadPaths['baseurl']);
$haveIssues = wfIssues::STATUS_SECURE;
$added = $this->addIssue(
'wpscan_directoryListingEnabled',
'wpscan_directoryListingEnabled',
'wpscan_directoryListingEnabled',
__("Directory listing is enabled", 'wordfence'),
__("Directory listing provides an attacker with the complete index of all the resources located inside of the directory. The specific risks and consequences vary depending on which files are listed and accessible, but it is recommended that you disable it unless it is needed.", 'wordfence'),
'url' => $uploadPaths['baseurl'],
if ($added == wfIssues::ISSUE_ADDED || $added == wfIssues::ISSUE_UPDATED) {
$haveIssues = wfIssues::STATUS_PROBLEM;
} else if ($haveIssues != wfIssues::STATUS_PROBLEM && ($added == wfIssues::ISSUE_IGNOREP || $added == wfIssues::ISSUE_IGNOREC)) {
$haveIssues = wfIssues::STATUS_IGNORED;
wfIssues::statusEnd($this->statusIDX['wpscan_directoryListingEnabled'], $haveIssues);
private function scan_checkSpamvertized() {
if ($this->scanController->isPremiumScan()) {
$this->statusIDX['spamvertizeCheck'] = wfIssues::statusStart(__("Checking if your site is being Spamvertised", 'wordfence'));
$this->scanController->startStage(wfScanner::STAGE_SPAMVERTISING_CHECKS);
$result = $this->api->call('spamvertize_check', array(), array(
$haveIssues = wfIssues::STATUS_SECURE;
if ($result['haveIssues'] && is_array($result['issues'])) {
foreach ($result['issues'] as $issue) {
$added = $this->addIssue($issue['type'], wfIssues::SEVERITY_CRITICAL, $issue['ignoreP'], $issue['ignoreC'], $issue['shortMsg'], $issue['longMsg'], $issue['data']);
if ($added == wfIssues::ISSUE_ADDED || $added == wfIssues::ISSUE_UPDATED) {
$haveIssues = wfIssues::STATUS_PROBLEM;
} else if ($haveIssues != wfIssues::STATUS_PROBLEM && ($added == wfIssues::ISSUE_IGNOREP || $added == wfIssues::ISSUE_IGNOREC)) {
$haveIssues = wfIssues::STATUS_IGNORED;
wfIssues::statusEnd($this->statusIDX['spamvertizeCheck'], $haveIssues);
$this->scanController->completeStage(wfScanner::STAGE_SPAMVERTISING_CHECKS, $haveIssues);
wfIssues::statusPaidOnly(__("Check if your site is being Spamvertized is for paid members only", 'wordfence'));
private function _scannedSkippedPaths() {
$directoryConstants = array(
'WP_PLUGIN_DIR' => '/wp-content/plugins',
'UPLOADS' => '/wp-content/uploads',
'WP_CONTENT_DIR' => '/wp-content',
foreach ($directoryConstants as $constant => $wordpressPath) {
$path = constant($constant);
if ($constant === 'UPLOADS')
$scanPaths[] = new wfScanPath(
catch (wfInvalidPathException $e) {
//Ignore invalid scan paths
wordfence::status(4, 'info', sprintf(__("Ignoring invalid scan path: %s", 'wordfence'), $e->getPath()));
$scanPaths[] = new wfScanPath(
array('.htaccess', 'index.php', 'license.txt', 'readme.html', 'wp-activate.php', 'wp-admin', 'wp-app.php', 'wp-blog-header.php', 'wp-comments-post.php', 'wp-config-sample.php', 'wp-content', 'wp-cron.php', 'wp-includes', 'wp-links-opml.php', 'wp-load.php', 'wp-login.php', 'wp-mail.php', 'wp-pass.php', 'wp-register.php', 'wp-settings.php', 'wp-signup.php', 'wp-trackback.php', 'xmlrpc.php', '.well-known', 'cgi-bin')
if (WF_IS_FLYWHEEL && !empty($_SERVER['DOCUMENT_ROOT'])) {
$scanPaths[] = new wfScanPath(
$_SERVER['DOCUMENT_ROOT'],
$scanOutside = $this->scanController->scanOutsideWordPress();
foreach ($scanPaths as $scanPath) {
if (!$scanOutside && $scanPath->hasExpectedFiles()) {
foreach ($scanPath->getContents() as $fileName) {
$file = $scanPath->createScanFile($fileName);
if (wfUtils::fileTooBig($file->getRealPath()))
$entrypoint = new wfScanEntrypoint($file);
if ($scanPath->expectsFile($fileName) || wfFileUtils::isReadableFile($file->getRealPath())) {
$entrypoint->setIncluded();
$entrypoint->addTo($entrypoints);
catch (wfInvalidPathException $e) {
wordfence::status(4, 'info', sprintf(__("Ignoring invalid expected scan file: %s", 'wordfence'), $e->getPath()));