feat(cloudron): add tirreno package artifacts
- Add CloudronStack/output/CloudronPackages-Artifacts/tirreno/ directory and its contents - Includes package manifest, Dockerfile, source code, documentation, and build artifacts - Add tirreno-1761840148.tar.gz as a build artifact - Add tirreno-cloudron-package-1761841304.tar.gz as the Cloudron package - Include all necessary files for the tirreno Cloudron package This adds the complete tirreno Cloudron package artifacts to the repository.
This commit is contained in:
@@ -0,0 +1,353 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Tirreno ~ Open source user analytics
|
||||
* Copyright (c) Tirreno Technologies Sàrl (https://www.tirreno.com)
|
||||
*
|
||||
* Licensed under GNU Affero General Public License version 3 of the or any later version.
|
||||
* For full copyright and license information, please see the LICENSE
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Tirreno Technologies Sàrl (https://www.tirreno.com)
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPL License
|
||||
* @link https://www.tirreno.com Tirreno(tm)
|
||||
*/
|
||||
|
||||
namespace Models\Context;
|
||||
|
||||
class Data {
|
||||
private \Models\Context\User $userModel;
|
||||
private \Models\Context\Ip $ipModel;
|
||||
private \Models\Context\Device $deviceModel;
|
||||
private \Models\Context\Email $emailModel;
|
||||
private \Models\Context\Phone $phoneModel;
|
||||
private \Models\Context\Event $eventModel;
|
||||
private \Models\Context\Session $sessionModel;
|
||||
private \Models\ApiKeys $keyModel;
|
||||
|
||||
private array $suspiciousWordsUrl;
|
||||
private array $suspiciousWordsUserAgent;
|
||||
private array $suspiciousWordsEmail;
|
||||
|
||||
public function __construct() {
|
||||
$this->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);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user