: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$day = $oneWeekSchedule[$checkingDayNumber];
$dayHour = array_search(1, $day);
if ($dayHour !== false) {
$preferredHourUTC = (round(($dayHour * 3600 - $tzOffset) / 3600, 2) + 12) % 24;
if ($preferredHourUTC !== false) { //The preferred hour of a manual scan schedule has been determined, run the quick scan at the desired offset if we're in that hour
return ($currentHourUTC >= $preferredHourUTC);
$noc1ScanSchedule = wfConfig::get_ser('noc1ScanSchedule', array());
if (count($noc1ScanSchedule)) {
$preferredHourUTC = (((int) (($noc1ScanSchedule[0] % 86400) / 3600)) + 12) % 24;
return ($currentHourUTC >= $preferredHourUTC);
return false; //If we've reached this point, the scan config is in a weird state so just skip the quick scan
* Returns an associative array containing the current state each scan stage and its corresponding status.
public function stageStatus() {
$status = $this->_defaultStageStatuses();
$runningStatus = wfConfig::get_ser('scanStageStatuses', array(), false);
$status = array_merge($status, $runningStatus);
foreach ($status as $stage => &$value) { //Convert value array into status only
$value = $value['status'];
if (!$this->isRunning() && $value == self::STATUS_RUNNING) {
$value = self::STATUS_PENDING;
* Returns an array of all scan options for the given stage that are enabled.
* @param string $stage One of the STAGE_ constants
private function _scanJobsForStage($stage) {
case self::STAGE_SPAMVERTISING_CHECKS:
case self::STAGE_SPAM_CHECK:
case self::STAGE_BLACKLIST_CHECK:
case self::STAGE_SERVER_STATE:
if ($this->scanType() != self::SCAN_TYPE_QUICK) {
'scansEnabled_checkHowGetIPs',
'scansEnabled_diskSpace',
'scansEnabled_wafStatus',
'scansEnabled_geoipSupport',
case self::STAGE_FILE_CHANGES:
'scansEnabled_coreUnknown',
case self::STAGE_PUBLIC_FILES:
'scansEnabled_checkReadableConfig',
'scansEnabled_suspectedFiles',
case self::STAGE_MALWARE_SCAN:
'scansEnabled_fileContents',
case self::STAGE_CONTENT_SAFETY:
'scansEnabled_fileContentsGSB',
case self::STAGE_PASSWORD_STRENGTH:
case self::STAGE_VULNERABILITY_SCAN:
'scansEnabled_oldVersions',
case self::STAGE_OPTIONS_AUDIT:
'scansEnabled_suspiciousOptions',
'scansEnabled_suspiciousAdminUsers',
$enabledOptions = $this->scanOptions();
$filteredOptions = array();
foreach ($options as $o) {
if (isset($enabledOptions[$o]) && $enabledOptions[$o]) {
return array_merge($filteredOptions, $always);
* Returns an associative array containing each scan stage's default state. The keys are the stage identifiers and the value
* is an array in the format
* 'started' => the number of tasks for this stage that have started (initially 0),
* 'finished' => the number of tasks that have started and finished (initially 0),
* 'expected' => the expected number of tasks to run for this stage (based on the scan type and options enabled)
private function _defaultStageStatuses() {
self::STAGE_SPAMVERTISING_CHECKS => array('status' => ($this->isPremiumScan() ? self::STATUS_PENDING : self::STATUS_PREMIUM), 'started' => 0, 'finished' => 0, 'expected' => 0),
self::STAGE_SPAM_CHECK => array('status' => ($this->isPremiumScan() ? self::STATUS_PENDING : self::STATUS_PREMIUM), 'started' => 0, 'finished' => 0, 'expected' => 0),
self::STAGE_BLACKLIST_CHECK => array('status' => ($this->isPremiumScan() ? self::STATUS_PENDING : self::STATUS_PREMIUM), 'started' => 0, 'finished' => 0, 'expected' => 0),
self::STAGE_SERVER_STATE => array('status' => self::STATUS_PENDING, 'started' => 0, 'finished' => 0, 'expected' => 0),
self::STAGE_FILE_CHANGES => array('status' => self::STATUS_PENDING, 'started' => 0, 'finished' => 0, 'expected' => 0),
self::STAGE_PUBLIC_FILES => array('status' => self::STATUS_PENDING, 'started' => 0, 'finished' => 0, 'expected' => 0),
self::STAGE_MALWARE_SCAN => array('status' => self::STATUS_PENDING, 'started' => 0, 'finished' => 0, 'expected' => 0),
self::STAGE_CONTENT_SAFETY => array('status' => self::STATUS_PENDING, 'started' => 0, 'finished' => 0, 'expected' => 0),
self::STAGE_PASSWORD_STRENGTH => array('status' => self::STATUS_PENDING, 'started' => 0, 'finished' => 0, 'expected' => 0),
self::STAGE_VULNERABILITY_SCAN => array('status' => self::STATUS_PENDING, 'started' => 0, 'finished' => 0, 'expected' => 0),
self::STAGE_OPTIONS_AUDIT => array('status' => self::STATUS_PENDING, 'started' => 0, 'finished' => 0, 'expected' => 0),
foreach ($status as $stage => &$parameters) {
if ($parameters['status'] == self::STATUS_PREMIUM) {
$jobs = $this->_scanJobsForStage($stage);
$parameters['expected'] = count($jobs);
$parameters['status'] = self::STATUS_DISABLED;
* Resets the state of the scan stage status record.
public function resetStages() {
if ($this->scanType() == self::SCAN_TYPE_QUICK) { //Suppress for quick scans
wfConfig::set_ser('scanStageStatuses', $this->_defaultStageStatuses(), false, wfConfig::DONT_AUTOLOAD);
private function _shouldForceUpdate($stageID) {
if ($stageID == wfScanner::STAGE_MALWARE_SCAN) {
* Increments the stage started counter and marks it as running if not already in that state.
* @param string $stageID One of the STAGE_ constants
public function startStage($stageID) {
if ($this->scanType() == self::SCAN_TYPE_QUICK) { //Suppress for quick scans
$runningStatus = wfConfig::get_ser('scanStageStatuses', array(), false);
if ($runningStatus[$stageID]['status'] != self::STATUS_RUNNING_WARNING) {
$runningStatus[$stageID]['status'] = self::STATUS_RUNNING;
$runningStatus[$stageID]['started'] += 1;
wfConfig::set_ser('scanStageStatuses', $runningStatus, false, wfConfig::DONT_AUTOLOAD);
if (wfCentral::isConnected() && ($this->_shouldForceUpdate($stageID) || (time() - wfConfig::getInt('lastScanStageStatusUpdate', 0)) > self::CENTRAL_STAGE_UPDATE_THRESHOLD)) {
wfCentral::updateScanStatus($runningStatus);
* Increments the stage finished counter and updates the stage status according to whether it's fully finished or encountered a negative status.
* @param string $stageID One of the STAGE_ constants.
* @param string $status One of the wfIssues::STATUS_ constants
public function completeStage($stageID, $status) {
if ($this->scanType() == self::SCAN_TYPE_QUICK) { //Suppress for quick scans
$runningStatus = wfConfig::get_ser('scanStageStatuses', array(), false);
if ($runningStatus[$stageID]['status'] == self::STATUS_RUNNING && ($status == wfIssues::STATUS_PROBLEM)) {
$runningStatus[$stageID]['status'] = self::STATUS_RUNNING_WARNING;
$runningStatus[$stageID]['finished'] += 1;
if ($runningStatus[$stageID]['finished'] >= $runningStatus[$stageID]['expected']) {
if ($runningStatus[$stageID]['status'] == self::STATUS_RUNNING) {
$runningStatus[$stageID]['status'] = self::STATUS_COMPLETE_SUCCESS;
$runningStatus[$stageID]['status'] = self::STATUS_COMPLETE_WARNING;
wfConfig::set_ser('scanStageStatuses', $runningStatus, false, wfConfig::DONT_AUTOLOAD);
if (wfCentral::isConnected()) {
$forceSend = true; //Force sending the last stage completion update even if the timing would otherwise prevent it
foreach ($runningStatus as $stageID => $stage) {
if ($runningStatus[$stageID]['finished'] < $runningStatus[$stageID]['expected']) {
if ($forceSend || (time() - wfConfig::getInt('lastScanStageStatusUpdate', 0)) > self::CENTRAL_STAGE_UPDATE_THRESHOLD) {
wfCentral::updateScanStatus($runningStatus);
* Returns the selected type of the scan.
public function scanType() {
switch ($this->_scanType) {
case self::SCAN_TYPE_QUICK://SCAN_TYPE_QUICK is not user-selectable
case self::SCAN_TYPE_LIMITED:
case self::SCAN_TYPE_STANDARD:
case self::SCAN_TYPE_HIGH_SENSITIVITY:
case self::SCAN_TYPE_CUSTOM:
return self::SCAN_TYPE_STANDARD;
* Returns the display name for the selected type of the scan.
public function scanTypeName() {
switch ($this->_scanType) {
case self::SCAN_TYPE_QUICK:
return __('Quick Scan', 'wordfence');
case self::SCAN_TYPE_LIMITED:
return __('Limited Scan', 'wordfence');
case self::SCAN_TYPE_HIGH_SENSITIVITY:
return __('High Sensitivity', 'wordfence');
case self::SCAN_TYPE_CUSTOM:
return __('Custom Scan', 'wordfence');
case self::SCAN_TYPE_STANDARD:
return __('Standard Scan', 'wordfence');
* Returns a normalized percentage (i.e., in the range [0, 1]) to the corresponding display percentage
* @param float $percentage
protected function _normalizedPercentageToDisplay($percentage) {
if ($this->isPremiumScan()) {
return round($percentage, 2);
return round($percentage * 0.70, 2);
* Returns a normalized percentage (i.e., in the range [0, 1]) for the scan type status indicator.
public function scanTypeStatus() {
$isFree = !wfConfig::get('isPaid');
$weights = self::_scanOptionWeights();
$options = $this->scanOptions();
$premiumOptions = self::_premiumScanOptions();
foreach ($options as $key => $value) {
if ($isFree && array_search($key, $premiumOptions) !== false) {
$score += $weights[$key];
return $this->_normalizedPercentageToDisplay($score);
public function scanTypeStatusList() {
$isFree = !wfConfig::get('isPaid');
$weights = self::_scanOptionWeights();
$options = $this->scanOptions();
$disabledOptionCount = 0;
$premiumDisabledOptionCount = 0;
$premiumPercentage = 0.0;
$premiumOptions = self::_premiumScanOptions();
foreach ($options as $key => $value) {
if ($isFree && array_search($key, $premiumOptions) !== false) {
$premiumPercentage += $weights[$key];
$premiumDisabledOptionCount++;
if (!$value && $weights[$key] > 0) {
$percentage += $weights[$key];
$remainingPercentage = 1 - $this->scanTypeStatus();
$remainingPercentage -= 0.30;
'title' => __('Enable Premium Scan Signatures.', 'wordfence'),
if ($premiumPercentage > 0) {
$subtraction = min($this->_normalizedPercentageToDisplay($premiumPercentage), $remainingPercentage);
$remainingPercentage -= $subtraction;
'percentage' => $subtraction,
'title' => __('Enable Premium Reputation Checks.', 'wordfence'),
$subtraction = min($this->_normalizedPercentageToDisplay($percentage), $remainingPercentage);
'percentage' => $subtraction,
'title' => sprintf(_n('Enable %d scan option.', 'Enable %d scan options.', $disabledOptionCount,'wordfence'), number_format_i18n($disabledOptionCount)),
* Returns the malware signature feed that is in use.
public function signatureMode() {
if ($this->isPremiumScan()) {
return self::SIGNATURE_MODE_PREMIUM;
return self::SIGNATURE_MODE_COMMUNITY;
* Returns a normalized percentage (i.e., in the range [0, 1]) for the reputation status indicator.
public function reputationStatus() {
if ($this->isPremiumScan()) {
$options = $this->scanOptions();
if ($options['spamvertizeCheck']) { $score += 0.333; }
if ($options['checkSpamIP']) { $score += 0.333; }
if ($options['scansEnabled_checkGSB']) { $score += 0.333; }
public function reputationStatusList() {
$options = $this->scanOptions();
$reputationChecks = array(
'spamvertizeCheck' => __('Enable scan option to check if this website is being "Spamvertised".', 'wordfence'),
'checkSpamIP' => __('Enable scan option to check if your website IP is generating spam.', 'wordfence'),
'scansEnabled_checkGSB' => __('Enable scan option to check if your website is on a domain blocklist.', 'wordfence'),
foreach ($reputationChecks as $option => $optionLabel) {
if (!$this->isPremiumScan() || !$options[$option]) {
'percentage' => round(1 / count($reputationChecks), 2),
* Returns the options for the configured scan type.
public function scanOptions() {
switch ($this->scanType()) {
case self::SCAN_TYPE_QUICK:
return self::quickScanTypeOptions();
case self::SCAN_TYPE_LIMITED:
return self::limitedScanTypeOptions();
case self::SCAN_TYPE_STANDARD:
return self::standardScanTypeOptions();
case self::SCAN_TYPE_HIGH_SENSITIVITY:
return self::highSensitivityScanTypeOptions();
case self::SCAN_TYPE_CUSTOM:
return self::customScanTypeOptions();
* Returns the array of jobs for the scan type.
$options = $this->scanOptions();
'checkSpamvertized' => array('spamvertizeCheck'),
'checkSpamIP' => array('checkSpamIP'),
'checkGSB' => array('scansEnabled_checkGSB'),
'checkHowGetIPs' => array('scansEnabled_checkHowGetIPs'),
'diskSpace' => array('scansEnabled_diskSpace'),
'wafStatus' => array('scansEnabled_wafStatus'),
'geoipSupport' => array('scansEnabled_geoipSupport'),
'checkSkippedFiles' => ($this->scanType() != self::SCAN_TYPE_QUICK), //Always runs except for quick
'knownFiles' => ($this->scanType() != self::SCAN_TYPE_QUICK), //Always runs except for quick, options are scansEnabled_core, scansEnabled_themes, scansEnabled_plugins, scansEnabled_coreUnknown, scansEnabled_malware
'checkReadableConfig' => array('scansEnabled_checkReadableConfig'),
'fileContents' => ($this->scanType() != self::SCAN_TYPE_QUICK), //Always runs except for quick, options are scansEnabled_fileContents and scansEnabled_fileContentsGSB
'suspectedFiles' => array('scansEnabled_suspectedFiles'),
'posts' => array('scansEnabled_posts'),
'comments' => array('scansEnabled_comments'),
'passwds' => array('scansEnabled_passwds'),
'oldVersions' => array('scansEnabled_oldVersions'),
'suspiciousAdminUsers' => array('scansEnabled_suspiciousAdminUsers'),
'suspiciousOptions' => array('scansEnabled_suspiciousOptions'),
foreach ($preferredOrder as $job => $enabler) {
else if (is_array($enabler)) {
foreach ($enabler as $o) {