userModel = new User(); $this->ipModel = new Ip(); $this->deviceModel = new Device(); $this->emailModel = new Email(); $this->phoneModel = new Phone(); $this->eventModel = new Event(); $this->sessionModel = new Session(); $this->keyModel = new \Models\ApiKeys(); $this->suspiciousWordsUrl = \Utils\WordsLists\Url::getWords(); $this->suspiciousWordsUserAgent = \Utils\WordsLists\UserAgent::getWords(); $this->suspiciousWordsEmail = \Utils\WordsLists\Email::getWords(); } public function getContext(array $accountIds, int $apiKey): array { $userDetails = $this->userModel->getContext($accountIds, $apiKey); $ipDetails = $this->ipModel->getContext($accountIds, $apiKey); $deviceDetails = $this->deviceModel->getContext($accountIds, $apiKey); $emailDetails = $this->emailModel->getContext($accountIds, $apiKey); $phoneDetails = $this->phoneModel->getContext($accountIds, $apiKey); $eventDetails = $this->eventModel->getContext($accountIds, $apiKey); //$domainDetails = $this->domainModel->getContext($accountIds, $apiKey); $timezoneName = $this->keyModel->getTimezoneByKeyId($apiKey); $utcTime = new \DateTime('now', new \DateTimeZone('UTC')); $timezone = new \DateTimeZone($timezoneName); $offsetInSeconds = $timezone->getOffset($utcTime); // get only suspicious sessions $sessionDetails = $this->sessionModel->getContext($accountIds, $apiKey, $offsetInSeconds); //Extend user details context foreach ($userDetails as $userId => $user) { $user['le_exists'] = ($user['le_email'] ?? null) !== null; $user['le_email'] = $user['le_email'] ?? ''; $user['le_local_part'] = explode('@', $user['le_email'])[0] ?? ''; $user['le_domain_part'] = explode('@', $user['le_email'])[1] ?? ''; $userId = $user['ea_id']; $ip = $ipDetails[$userId] ?? []; $device = $deviceDetails[$userId] ?? []; $email = $emailDetails[$userId] ?? []; $phone = $phoneDetails[$userId] ?? []; $events = $eventDetails[$userId] ?? []; $session = $sessionDetails[$userId] ?? []; $user['eip_ip_id'] = $ip['eip_ip_id'] ?? []; $user['eip_ip'] = $ip['eip_ip'] ?? []; $user['eip_cidr'] = $ip['eip_cidr'] ?? []; $user['eip_country_id'] = $ip['eip_country_id'] ?? []; $user['eip_data_center'] = $ip['eip_data_center'] ?? []; $user['eip_tor'] = $ip['eip_tor'] ?? []; $user['eip_vpn'] = $ip['eip_vpn'] ?? []; $user['eip_relay'] = $ip['eip_relay'] ?? []; $user['eip_starlink'] = $ip['eip_starlink'] ?? []; $user['eip_total_visit'] = $ip['eip_total_visit'] ?? []; $user['eip_blocklist'] = $ip['eip_blocklist'] ?? []; $user['eip_shared'] = $ip['eip_shared'] ?? []; //$user['eip_domains'] = $ip['eip_domains'] ?? []; $user['eip_country_id'] = $ip['eip_country_id'] ?? []; $user['eip_fraud_detected'] = $ip['eip_fraud_detected'] ?? []; $user['eip_alert_list'] = $ip['eip_alert_list'] ?? []; $user['eip_domains_count_len'] = $ip['eip_domains_count_len'] ?? []; $user['eup_device'] = $device['eup_device'] ?? []; $user['eup_device_id'] = $device['eup_device_id'] ?? []; $user['eup_browser_name'] = $device['eup_browser_name'] ?? []; $user['eup_browser_version'] = $device['eup_browser_version'] ?? []; $user['eup_os_name'] = $device['eup_os_name'] ?? []; $user['eup_lang'] = $device['eup_lang'] ?? []; $user['eup_ua'] = $device['eup_ua'] ?? []; // $user['eup_lastseen'] = $device['eup_lastseen'] ?? []; // $user['eup_created'] = $device['eup_created'] ?? []; $user['ee_email'] = $email['ee_email'] ?? []; $user['ee_earliest_breach'] = $email['ee_earliest_breach'] ?? []; $user['ep_phone_number'] = $phone['ep_phone_number'] ?? []; $user['ep_shared'] = $phone['ep_shared'] ?? []; $user['ep_type'] = $phone['ep_type'] ?? []; $user['event_ip'] = $events['event_ip'] ?? []; $user['event_url_string'] = $events['event_url_string'] ?? []; $user['event_empty_referer'] = $events['event_empty_referer'] ?? []; $user['event_device'] = $events['event_device'] ?? []; $user['event_type'] = $events['event_type'] ?? []; $user['event_http_method'] = $events['event_http_method'] ?? []; $user['event_http_code'] = $events['event_http_code'] ?? []; $user['event_device_created'] = $events['event_device_created'] ?? []; $user['event_device_lastseen'] = $events['event_device_lastseen'] ?? []; $user['event_session_multiple_country'] = $session[0]['event_session_multiple_country'] ?? false; $user['event_session_multiple_ip'] = $session[0]['event_session_multiple_ip'] ?? false; $user['event_session_multiple_device'] = $session[0]['event_session_multiple_device'] ?? false; $user['event_session_night_time'] = $session[0]['event_session_night_time'] ?? false; //Extra params for rules $user = $this->extendParams($user); $userDetails[$userId] = $this->extendEventParams($user); } return $userDetails; } private function extendParams(array $record): array { //$record['timezone'] $localPartLen = strlen($record['le_local_part']); $domainPartLen = strlen($record['le_domain_part']); $fullName = $this->getUserFullName($record); $record['le_local_part_len'] = $localPartLen; $record['ea_fullname_has_numbers'] = preg_match('~[0-9]+~', $fullName) > 0; $record['ea_fullname_has_spaces_hyphens'] = preg_match('~[\-\s]~', $fullName) > 0; $record['ea_days_since_account_creation'] = $this->getDaysSinceAccountCreation($record); $record['ea_days_since_last_visit'] = $this->getDaysSinceLastVisit($record); //$record['le_has_no_profiles'] = $record['le_profiles'] === 0; $record['le_has_no_data_breaches'] = $record['le_data_breach'] === false; $record['le_has_suspicious_str'] = $this->checkEmailForSuspiciousString($record); $record['le_has_numeric_only_local_part'] = preg_match('/^[0-9]+$/', $record['le_local_part']) > 0; $record['le_email_has_consec_s_chars'] = preg_match('/[^a-zA-Z0-9]{2,}/', $record['le_local_part']) > 0; $record['le_email_has_consec_nums'] = preg_match('/\d{2}/', $record['le_local_part']) > 0; $record['le_email_has_no_digits'] = !preg_match('/\d/', $record['le_local_part']); $record['le_email_has_vowels'] = preg_match('/[aeoui]/i', $record['le_local_part']) > 0; $record['le_email_has_consonants'] = preg_match('/[bcdfghjklmnpqrstvwxyz]/i', $record['le_local_part']) > 0; $record['le_with_long_local_part_length'] = $localPartLen > \Utils\Constants::get('RULE_EMAIL_MAXIMUM_LOCAL_PART_LENGTH'); $record['le_with_long_domain_length'] = $domainPartLen > \Utils\Constants::get('RULE_EMAIL_MAXIMUM_DOMAIN_LENGTH'); $record['le_email_in_blockemails'] = $record['le_blockemails'] ?? false; $record['le_is_invalid'] = $record['le_exists'] && filter_var($record['le_email'], FILTER_VALIDATE_EMAIL) === false; $record['le_appears_on_alert_list'] = $record['le_alert_list'] ?? false; $record['ld_is_disposable'] = $record['ld_disposable_domains'] ?? false; $record['ld_days_since_domain_creation'] = $this->getDaysSinceDomainCreation($record); $record['ld_domain_free_email_provider'] = $record['ld_free_email_provider'] ?? false; $record['ld_from_blockdomains'] = $record['ld_blockdomains'] ?? false; $record['ld_domain_without_mx_record'] = $record['ld_mx_record'] === false; $record['ld_website_is_disabled'] = $record['ld_disabled'] ?? false; $record['ld_tranco_rank'] = $record['ld_tranco_rank'] ?? -1; $record['lp_invalid_phone'] = $record['lp_invalid'] === true; $record['ep_shared_phone'] = (bool) count(array_filter($record['ep_shared'], static function ($item) { return $item !== null && $item > 1; })); $daysSinceBreaches = array_map(function ($item) { return $this->getDaysTillToday($item); }, $record['ee_earliest_breach']); $record['ee_days_since_first_breach'] = count($daysSinceBreaches) ? max($daysSinceBreaches) : -1; $onlyNonResidentialParams = !(bool) count(array_filter(array_merge( $record['eip_fraud_detected'], $record['eip_blocklist'], $record['eip_tor'], $record['eip_starlink'], $record['eip_relay'], $record['eip_vpn'], $record['eip_data_center'], ), static function ($value): bool { return $value === true; })); $record['eip_only_residential'] = $onlyNonResidentialParams && !in_array(0, $record['eip_country_id']); $record['eip_has_fraud'] = in_array(true, $record['eip_fraud_detected']); $record['eip_unique_cidrs'] = count(array_unique($record['eip_cidr'])); $record['lp_fraud_detected'] = $record['lp_fraud_detected'] ?? false; $record['le_fraud_detected'] = $record['le_fraud_detected'] ?? false; $record['eup_has_rare_browser'] = (bool) count(array_diff($record['eup_browser_name'], array_keys(\Utils\Constants::get('RULE_REGULAR_BROWSER_NAMES')))); $record['eup_has_rare_os'] = (bool) count(array_diff($record['eup_os_name'], \Utils\Constants::get('RULE_REGULAR_OS_NAMES'))); $record['eup_device_count'] = count($record['eup_device']); $record['eup_vulnerable_ua'] = false; if (count($this->suspiciousWordsUserAgent)) { foreach ($record['eup_ua'] as $url) { foreach ($this->suspiciousWordsUserAgent as $sub) { if (stripos($url, $sub) !== false) { $record['eup_vulnerable_ua'] = true; break 2; } } } } return $record; } private function extendEventParams(array $record): array { // Remove null values specifically $eventTypeFiltered = $this->filterStringNum($record['event_type']); $eventHttpCodeFiltered = $this->filterStringNum($record['event_http_code']); $eventTypeCount = array_count_values($eventTypeFiltered); //$accountLoginFailId = \Utils\Constants::get('ACCOUNT_LOGIN_FAIL_EVENT_TYPE_ID'); $accountEmailChangeId = \Utils\Constants::get('ACCOUNT_EMAIL_CHANGE_EVENT_TYPE_ID'); $accountPwdChangeId = \Utils\Constants::get('ACCOUNT_PASSWORD_CHANGE_EVENT_TYPE_ID'); //$record['event_failed_login_attempts'] = $eventTypeCount[$accountLoginFailId] ?? 0; $record['event_email_changed'] = array_key_exists($accountEmailChangeId, $eventTypeCount); $record['event_password_changed'] = array_key_exists($accountPwdChangeId, $eventTypeCount); $record['event_http_method_head'] = in_array(\Utils\Constants::get('EVENT_REQUEST_TYPE_HEAD'), $record['event_http_method']); $record['event_empty_referer'] = in_array(true, $record['event_empty_referer'], true); $clientErrors = 0; $serverErrors = 0; $successEvents = 0; foreach ($eventHttpCodeFiltered as $code) { if (is_int($code) && $code >= 400 && $code < 500) { ++$clientErrors; } elseif (is_int($code) && $code >= 500 && $code < 600) { ++$serverErrors; } elseif (is_int($code) && $code >= 200 && $code < 300) { ++$successEvents; } } $record['event_multiple_5xx_http'] = $serverErrors; $record['event_multiple_4xx_http'] = $clientErrors; $record['event_2xx_http'] = (bool) $successEvents; $record['event_vulnerable_url'] = false; if (count($this->suspiciousWordsUrl)) { foreach ($record['event_url_string'] as $url) { foreach ($this->suspiciousWordsUrl as $sub) { if (stripos($url, $sub) !== false) { $record['event_vulnerable_url'] = true; break 2; } } } } return $record; } private function getDaysSinceDomainCreation(array $params): int { $dt1 = date('Y-m-d'); $dt2 = $params['ld_creation_date']; return $this->getDaysDiff($dt1, $dt2); } private function getDaysSinceAccountCreation(array $params): int { $dt1 = date('Y-m-d'); $dt2 = $params['ea_created'] ?? null; return $this->getDaysDiff($dt1, $dt2); } private function getDaysSinceLastVisit(array $params): int { $dt1 = date('Y-m-d'); $dt2 = $params['ea_lastseen'] ?? null; return $this->getDaysDiff($dt1, $dt2); } private function getDaysTillToday(?string $dt2): int { $diff = -1; if ($dt2 !== null) { $dt1 = date('Y-m-d'); $dt1 = new \DateTime($dt1); $dt2 = new \DateTime($dt2); $diff = $dt1->diff($dt2)->format('%a'); } return $diff; } private function getDaysDiff(?string $dt1, ?string $dt2): int { $diff = -1; if ($dt2) { $dt1 = new \DateTime($dt1); $dt2 = new \DateTime($dt2); $diff = $dt1->diff($dt2)->format('%a'); } return $diff; } private function getUserFullName(array $record): string { $name = []; $fName = $record['ea_firstname'] ?? ''; if ($fName) { $name[] = $fName; } $lName = $record['ea_lastname'] ?? ''; if ($lName) { $name[] = $lName; } return trim(join(' ', $name)); } private function checkEmailForSuspiciousString(array $record): bool { foreach ($this->suspiciousWordsEmail as $sub) { if (stripos($record['le_email'], $sub) !== false) { return true; } } return false; } private function filterStringNum(array $record): array { return array_filter($record, static function ($value): bool { return is_string($value) || is_int($value); }); } }