: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
public function checkCoreVulnerabilities($initial = false) {
$vulnerabilities = array();
include(ABSPATH . WPINC . '/version.php'); /** @var $wp_version */
'current' => $wp_version,
if ($this->needs_core_update) {
$core['edge'] = $this->core_update_version;
if ($this->core_update_patch_available) {
$core['patch'] = $this->core_update_patch_version;
$result = $this->api->call('core_vulnerability_check', array(), array(
'core' => json_encode($core),
wfConfig::set_ser('vulnerabilities_core', $result['vulnerable'], false, wfConfig::DONT_AUTOLOAD); //Will have the index `current` with possibly `edge` and `patch` depending on what was provided above
private function initializePluginVulnerabilityData($plugin, &$installedPlugins, &$records, $values = null, $update = false) {
$file = $this->checkPluginFile($plugin, $installedPlugins);
$data = $installedPlugins[$plugin];
'slug' => $this->extractSlug($plugin, $values),
'fromVersion' => isset($data['Version']) ? $data['Version'] : 'Unknown',
if ($update && is_array($values))
$record['toVersion'] = isset($values['new_version']) ? $values['new_version'] : 'Unknown';
unset($installedPlugins[$plugin]);
* @param bool $initial if true, treat as the initial scan run
public function checkPluginVulnerabilities($initial=false) {
self::requirePluginsApi();
$vulnerabilities = array();
//Get the full plugin list
if (!function_exists('get_plugins')) {
require_once(ABSPATH . '/wp-admin/includes/plugin.php');
$installedPlugins = get_plugins();
//Get the info for plugins on wordpress.org
$update_plugins = $this->fetchPluginUpdates();
if (!empty($update_plugins->response)) {
foreach ($update_plugins->response as $plugin => $vals) {
$this->initializePluginVulnerabilityData($plugin, $installedPlugins, $vulnerabilities, (array) $vals, true);
if (!empty($update_plugins->no_update)) {
foreach ($update_plugins->no_update as $plugin => $vals) {
$this->initializePluginVulnerabilityData($plugin, $installedPlugins, $vulnerabilities, (array) $vals);
//Get the remaining plugins (not in the wordpress.org repo for whatever reason)
foreach ($installedPlugins as $plugin => $data) {
$this->initializePluginVulnerabilityData($plugin, $installedPlugins, $vulnerabilities, $data);
if (count($vulnerabilities) > 0) {
$result = $this->api->call('plugin_vulnerability_check', array(), array(
'plugins' => json_encode($vulnerabilities),
foreach ($vulnerabilities as &$v) {
$vulnerableList = $result['vulnerable'];
foreach ($vulnerableList as $r) {
if ($r['slug'] == $v['slug']) {
$v['vulnerable'] = !!$r['vulnerable'];
if (isset($r['score'])) {
$v['score'] = $r['score'];
if (isset($r['vector'])) {
$v['vector'] = $r['vector'];
wfConfig::set_ser('vulnerabilities_plugin', $vulnerabilities, false, wfConfig::DONT_AUTOLOAD);
* @param bool $initial whether or not this is the initial run
public function checkThemeVulnerabilities($initial = false) {
if (!function_exists('wp_update_themes')) {
require_once(ABSPATH . WPINC . '/update.php');
self::requirePluginsApi();
$this->checkThemeUpdates(!$initial, false);
$update_themes = get_site_transient('update_themes');
$vulnerabilities = array();
if ($update_themes && !empty($update_themes->response)) {
if (!function_exists('get_plugin_data'))
require_once(ABSPATH . '/wp-admin/includes/plugin.php');
foreach ($update_themes->response as $themeSlug => $vals) {
$valsArray = (array) $vals;
$theme = wp_get_theme($themeSlug);
$record['slug'] = $themeSlug;
$record['toVersion'] = (isset($valsArray['new_version']) ? $valsArray['new_version'] : 'Unknown');
$record['fromVersion'] = $theme->version;
$record['vulnerable'] = false;
$vulnerabilities[] = $record;
$result = $this->api->call('theme_vulnerability_check', array(), array(
'themes' => json_encode($vulnerabilities),
foreach ($vulnerabilities as &$v) {
$vulnerableList = $result['vulnerable'];
foreach ($vulnerableList as $r) {
if ($r['slug'] == $v['slug']) {
$v['vulnerable'] = !!$r['vulnerable'];
if (isset($r['score'])) {
$v['score'] = $r['score'];
if (isset($r['vector'])) {
$v['vector'] = $r['vector'];
wfConfig::set_ser('vulnerabilities_theme', $vulnerabilities, false, wfConfig::DONT_AUTOLOAD);
* Returns whether the core version is vulnerable. Available $which values are `current` for the version running now,
* `patch` for the patch update (if available), and `edge` for the most recent update available. `patch` and `edge`
* are accurate only if an update is actually available and will return false otherwise.
public function isCoreVulnerable($which = 'current') {
static $_vulnerabilitiesRefreshed = false;
$vulnerabilities = wfConfig::get_ser('vulnerabilities_core', null);
if ($vulnerabilities === null) {
if (!$_vulnerabilitiesRefreshed) {
$this->checkCoreVulnerabilities(true);
$_vulnerabilitiesRefreshed = true;
//Verify that we got a valid response, if not, avoid infinite recursion
$vulnerabilities = wfConfig::get_ser('vulnerabilities_core', null);
if ($vulnerabilities === null) {
wordfence::status(4, 'error', __("Failed obtaining core vulnerability data, skipping check.", 'wordfence'));
return $this->isCoreVulnerable($which);
if (!isset($vulnerabilities[$which])) {
return !!$vulnerabilities[$which]['vulnerable'];
public function isPluginVulnerable($slug, $version) {
return $this->_isSlugVulnerable('vulnerabilities_plugin', $slug, $version, function(){ $this->checkPluginVulnerabilities(true); });
public function isThemeVulnerable($slug, $version) {
return $this->_isSlugVulnerable('vulnerabilities_theme', $slug, $version, function(){ $this->checkThemeVulnerabilities(true); });
private function _isSlugVulnerable($vulnerabilitiesKey, $slug, $version, $populateVulnerabilities=null) {
static $_vulnerabilitiesRefreshed = array();
$vulnerabilities = wfConfig::get_ser($vulnerabilitiesKey, null);
if ( $vulnerabilities === null) {
if (is_callable($populateVulnerabilities)) {
if (!isset($_vulnerabilitiesRefreshed[$vulnerabilitiesKey])) {
$populateVulnerabilities();
$_vulnerabilitiesRefreshed[$vulnerabilitiesKey] = true;
$vulnerabilities = wfConfig::get_ser($vulnerabilitiesKey, null);
if ($vulnerabilities === null) {
wordfence::status(4, 'error', __("Failed obtaining vulnerability data, skipping check.", 'wordfence'));
return $this->_isSlugVulnerable($vulnerabilitiesKey, $slug, $version);
foreach ($vulnerabilities as $v) {
if ($v['slug'] == $slug) {
($v['fromVersion'] == 'Unknown' && $v['toVersion'] == 'Unknown') ||
((!isset($v['toVersion']) || $v['toVersion'] == 'Unknown') && version_compare($version, $v['fromVersion']) >= 0) ||
($v['fromVersion'] == 'Unknown' && isset($v['toVersion']) && version_compare($version, $v['toVersion']) < 0) ||
(version_compare($version, $v['fromVersion']) >= 0 && isset($v['toVersion']) && version_compare($version, $v['toVersion']) < 0)
if ($v['vulnerable']) { return $v; }
public function needsCoreUpdate() {
return $this->needs_core_update;
public function getCoreUpdateVersion() {
return $this->core_update_version;
* Returns true if there is a patch version available for the site's current minor branch and the site is not on
* the most recent minor branch (e.g., a backported security update).
* Example: suppose the site is currently on 4.1.37. This will return true and `getCoreUpdatePatchVersion` will
* return 4.1.39. `getCoreUpdateVersion` will return 6.4.2 (as of writing this comment).
public function coreUpdatePatchAvailable() {
return $this->core_update_patch_available;
* The version number for the patch update if available.
public function getCoreUpdatePatchVersion() {
return $this->core_update_patch_version;
* Returns whether or not the current core version is on a major or minor release earlier than the current available
public function getCoreEarlierBranch() {
return $this->core_earlier_branch;
public function getPluginUpdates() {
return $this->plugin_updates;
public function getAllPlugins() {
return $this->all_plugins;
public function getPluginSlugs() {
return $this->plugin_slugs;
public function getThemeUpdates() {
return $this->theme_updates;