: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$group_regex = '([a-f0-9]{1,4}|\[[a-f0-9]{1,4}\-[a-f0-9]{1,4}\])';
return preg_match('/^' . str_repeat("$group_regex:", 7) . $group_regex . '$/i', $ip_string) > 0;
public function isValidLinearRange() { //e.g., 192.0.2.1-192.0.2.100
$ip_string = $this->getIPString();
if (preg_match('/[^0-9a-f:\.\-]/i', $ip_string)) { return false; }
list($ip1, $ip2) = explode("-", $ip_string);
$ip1N = @wfUtils::inet_pton($ip1);
$ip2N = @wfUtils::inet_pton($ip2);
if ($ip1N === false || !wfUtils::isValidIP($ip1) || $ip2N === false || !wfUtils::isValidIP($ip2)) {
return strcmp($ip1N, $ip2N) <= 0;
public function isMixedRange() { //e.g., 192.0.2.1-2001:db8::ffff
$ip_string = $this->getIPString();
if (preg_match('/[^0-9a-f:\.\-]/i', $ip_string)) { return false; }
list($ip1, $ip2) = explode("-", $ip_string);
$ipv4Count += filter_var($ip1, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false ? 1 : 0;
$ipv4Count += filter_var($ip2, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false ? 1 : 0;
$ipv6Count += filter_var($ip1, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false ? 1 : 0;
$ipv6Count += filter_var($ip2, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false ? 1 : 0;
if ($ipv4Count != 2 && $ipv6Count != 2) {
protected function _sanitizeIPRange($ip_string) {
if (!is_string($ip_string))
$ip_string = preg_replace('/\s/', '', $ip_string); //Strip whitespace
$ip_string = preg_replace('/[\\x{2013}-\\x{2015}]/u', '-', $ip_string); //Non-hyphen dashes to hyphen
$ip_string = strtolower($ip_string);
if (preg_match('/^\d+-\d+$/', $ip_string)) { //v5 32 bit int style format
list($start, $end) = explode('-', $ip_string);
$start = long2ip($start);
$ip_string = "{$start}-{$end}";
public function getIPString() {
* @param string|null $ip_string
public function setIPString($ip_string) {
$this->ip_string = $this->_sanitizeIPRange($ip_string);
* The function of this class is to detect admin users created via direct access to the database (in other words, not
class wfAdminUserMonitor {
protected $currentAdminList = array();
public function isEnabled() {
$options = wfScanner::shared()->scanOptions();
$enabled = $options['scansEnabled_suspiciousAdminUsers'];
if ($enabled && is_multisite()) {
if (!function_exists('wp_is_large_network')) {
require_once(ABSPATH . WPINC . '/ms-functions.php');
$enabled = !wp_is_large_network('sites') && !wp_is_large_network('users');
public function createInitialList() {
$admins = $this->getCurrentAdmins();
$adminUserList = array();
foreach ($admins as $id => $user) {
wfConfig::set_ser('adminUserList', $adminUserList);
public function grantSuperAdmin($userID = null) {
$this->addAdmin($userID);
public function revokeSuperAdmin($userID = null) {
$this->removeAdmin($userID);
* @param mixed $old_roles
public function updateToUserRole($ID = null, $role = null, $old_roles = null) {
$admins = $this->getLoggedAdmins();
if ($role !== 'administrator' && array_key_exists($ID, $admins)) {
} else if ($role === 'administrator') {
public function checkNewAdmins() {
$loggedAdmins = $this->getLoggedAdmins();
$admins = $this->getCurrentAdmins();
$suspiciousAdmins = array();
foreach ($admins as $adminID => $v) {
if (!array_key_exists($adminID, $loggedAdmins)) {
$suspiciousAdmins[] = $adminID;
return $suspiciousAdmins ? $suspiciousAdmins : false;
* Checks if the supplied user ID is suspicious.
public function isAdminUserLogged($userID) {
$loggedAdmins = $this->getLoggedAdmins();
return array_key_exists($userID, $loggedAdmins);
* @param bool $forceReload
public function getCurrentAdmins($forceReload = false) {
if (empty($this->currentAdminList) || $forceReload) {
require_once(ABSPATH . WPINC . '/user.php');
if (function_exists("get_sites")) {
$sites = get_sites(array(
$sites = wp_get_sites(array(
'blog_id' => get_current_blog_id(),
// not very efficient, but the WordPress API doesn't provide a good way to do this.
$this->currentAdminList = array();
foreach ($sites as $siteRow) {
$siteRowArray = (array) $siteRow;
$user_query = new WP_User_Query(array(
'blog_id' => $siteRowArray['blog_id'],
'role' => 'administrator',
$users = $user_query->get_results();
/** @var WP_User $user */
foreach ($users as $user) {
$this->currentAdminList[$user->ID] = $user;
// Add any super admins that aren't also admins on a network
$superAdmins = get_super_admins();
foreach ($superAdmins as $userLogin) {
$user = get_user_by('login', $userLogin);
$this->currentAdminList[$user->ID] = $user;
return $this->currentAdminList;
public function getLoggedAdmins() {
$loggedAdmins = wfConfig::get_ser('adminUserList', false);
if (!is_array($loggedAdmins)) {
$this->createInitialList();
$loggedAdmins = wfConfig::get_ser('adminUserList', false);
if (!is_array($loggedAdmins)) {
public function addAdmin($userID) {
$loggedAdmins = $this->getLoggedAdmins();
if (!array_key_exists($userID, $loggedAdmins)) {
$loggedAdmins[$userID] = 1;
wfConfig::set_ser('adminUserList', $loggedAdmins);
public function removeAdmin($userID) {
$loggedAdmins = $this->getLoggedAdmins();
if (array_key_exists($userID, $loggedAdmins) && !array_key_exists($userID, $this->getCurrentAdmins())) {
unset($loggedAdmins[$userID]);
wfConfig::set_ser('adminUserList', $loggedAdmins);
* Represents a request record
* @property float $attackLogTime
* @property int $statusCode
* @property bool $isGoogle
* @property string $referer
* @property string $action
* @property string $actionDescription
* @property string $actionData
class wfRequestModel extends wfModel {
private static $actionDataEncodedParams = array(
* @return mixed|string|void
public static function serializeActionData($actionData, $optionalKeys = array(), $maxLength = 65535) {
if (is_array($actionData)) {
foreach (self::$actionDataEncodedParams as $key) {
if (array_key_exists($key, $actionData)) {
$actionData[$key] = base64_encode($actionData[$key]);
$serialized = json_encode($actionData, JSON_UNESCAPED_SLASHES);
$length = strlen($serialized);
if ($length <= $maxLength)
$excess = $length - $maxLength;
foreach ($optionalKeys as $key) {
if (array_key_exists($key, $actionData)) {
$fieldValue = $actionData[$key];
$fieldLength = strlen($fieldValue);
$truncatedLength = min($fieldLength, $excess);
if ($truncatedLength > 0) {
$actionData[$key] = substr($fieldValue, 0, -$truncatedLength);
$excess -= $truncatedLength;
unset($actionData[$key]);
* @return mixed|string|void
public static function unserializeActionData($actionDataJSON) {
$actionData = json_decode($actionDataJSON, true);
if (is_array($actionData)) {
foreach (self::$actionDataEncodedParams as $key) {
if (array_key_exists($key, $actionData)) {
$actionData[$key] = base64_decode($actionData[$key]);
private $columns = array(
public function getIDColumn() {
public function getTable() {
return wfDB::networkTable('wfHits');
public function hasColumn($column) {
return in_array($column, $this->columns);
$sapi = @php_sapi_name();
class wfLiveTrafficQuery {
protected $validParams = array(
'statuscode' => 'h.statuscode',
'isgoogle' => 'h.isgoogle',
'referer' => 'h.referer',
'actiondescription' => 'h.actiondescription',
'actiondata' => 'h.actiondata',
'user_login' => 'u.user_login',
'username' => 'l.username',
/** @var wfLiveTrafficQueryFilterCollection */
private $filters = array();
/** @var wfLiveTrafficQueryGroupBy */
* wfLiveTrafficQuery constructor.
* @param wfLiveTrafficQueryFilterCollection $filters
* @param wfLiveTrafficQueryGroupBy $groupBy
* @param float $startDate
public function __construct($wfLog, $filters = null, $groupBy = null, $startDate = null, $endDate = null, $limit = 20, $offset = 0) {
$this->filters = $filters;
$this->groupBy = $groupBy;
$this->startDate = $startDate;
$this->endDate = $endDate;
* @return array|null|object
public function execute() {
$delayedHumanBotFiltering = false;
$sql = $this->buildQuery($delayedHumanBotFiltering, $humanOnly);
$results = $wpdb->get_results($sql, ARRAY_A);
if ($delayedHumanBotFiltering) {
$browscap = wfBrowscap::shared();
foreach ($results as $index => $res) {
$b = $browscap->getBrowser($res['UA']);
$jsRun = wfUtils::truthyToBoolean($res['jsRun']);
if ($b && $b['Parent'] != 'DefaultProperties') {
$jsRun = wfUtils::truthyToBoolean($res['jsRun']);
if (!wfConfig::liveTrafficEnabled() && !$jsRun) {
$jsRun = !(isset($b['Crawler']) && $b['Crawler']);
if (!$humanOnly && $jsRun || $humanOnly && !$jsRun) {
$this->getWFLog()->processGetHitsResults('', $results);
if ($this->filters !== null && count($this->filters->getFilters()) > 0) {
$filters = $this->filters->getFilters();
foreach ($filters as $f) {
if (strtolower($f->getParam()) == "isgoogle") {
foreach ($results as $key => &$row) {
if ($row['isGoogle'] && $verifyCrawlers) {
if (!wfCrawl::isVerifiedGoogleCrawler($row['IP'], $row['UA'])) {
unset($results[$key]); //foreach copies $results and iterates on the copy, so it is safe to mutate $results within the loop
$row['actionData'] = $row['actionData'] === null ? array() : (array) json_decode($row['actionData'], true);
return array_values($results);