diff --git a/CHANGELOG.md b/CHANGELOG.md index b4af81b3..412f5394 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 2026-06-17 v6.8.1 +- Исправлены замечания по безопасности модуля. Устранены уязвимости после проверки приложения на Маркетплейсе. + ## 2026-06-16 v6.8.0 - Исправлены замечания по безопасности модуля. Устранены уязвимости Authentication Bypass, IDOR и SSRF. Снижены риски CSRF, XSS и Information Disclosure. Усилены проверки доступа и обработки пользовательских данных. diff --git a/intaro.retailcrm/classes/general/Logger.php b/intaro.retailcrm/classes/general/Logger.php index 75c5f687..b837fe86 100644 --- a/intaro.retailcrm/classes/general/Logger.php +++ b/intaro.retailcrm/classes/general/Logger.php @@ -62,18 +62,19 @@ public function write($dump, $file = 'info') { $rsSites = CSite::GetList($by, $sort, array('DEFAULT' => 'Y')); $ar = $rsSites->Fetch(); + $logDir = $ar['ABS_DOC_ROOT'] . $this->logPath . '/'; - if (!is_dir($ar['ABS_DOC_ROOT'] . $this->logPath . '/')) { - mkdir($ar['ABS_DOC_ROOT'] . $this->logPath . '/'); + if (!is_dir($logDir)) { + mkdir($logDir, 0755, true); } - - $file = $ar['ABS_DOC_ROOT'] . $this->logPath . '/' . $file . '.log'; + + $file = $logDir . $file . '.log'; $data['TIME'] = date('Y-m-d H:i:s'); $data['DATA'] = $dump; $f = fopen($file, "a+"); - fwrite($f, print_r($data, true)); + fwrite($f, $this->maskSensitiveData(print_r($data, true))); fclose($f); // if filesize more than 5 Mb rotate it @@ -119,4 +120,21 @@ private function clean($file) { file_put_contents($file, ''); } + + private function maskSensitiveData($data) + { + $sensitiveKeys = 'api[_-]?key|token|access[_-]?token|password|passwd|secret|authorization'; + + $data = preg_replace( + '/(\[(?:' . $sensitiveKeys . ')\]\s*=>\s*)[^\r\n]*/i', + '$1[masked]', + $data + ); + + return preg_replace( + '/([?&](?:' . $sensitiveKeys . ')=)[^&\s"\']+/i', + '$1[masked]', + $data + ); + } } diff --git a/intaro.retailcrm/classes/general/RCrmActions.php b/intaro.retailcrm/classes/general/RCrmActions.php index 65516420..1e431834 100644 --- a/intaro.retailcrm/classes/general/RCrmActions.php +++ b/intaro.retailcrm/classes/general/RCrmActions.php @@ -261,7 +261,10 @@ public static function eventLog($auditType, $itemId, $description) public static function uploadOrdersAgent() { RetailCrmOrder::uploadOrders(); - $failedIds = unserialize(COption::GetOptionString(self::$MODULE_ID, self::$CRM_ORDER_FAILED_IDS, 0)); + $failedIds = unserialize( + COption::GetOptionString(self::$MODULE_ID, self::$CRM_ORDER_FAILED_IDS, 0), + ['allowed_classes' => false] + ); if (is_array($failedIds) && !empty($failedIds)) { RetailCrmOrder::uploadOrders(50, true); @@ -426,7 +429,7 @@ public static function unserializeArrayRecursive($string) } if (is_string($string)) { - $string = unserialize($string); + $string = unserialize($string, ['allowed_classes' => false]); } if (!is_array($string)) { diff --git a/intaro.retailcrm/classes/general/collector/RetailCrmCollector.php b/intaro.retailcrm/classes/general/collector/RetailCrmCollector.php index 61b1aeed..719811b2 100644 --- a/intaro.retailcrm/classes/general/collector/RetailCrmCollector.php +++ b/intaro.retailcrm/classes/general/collector/RetailCrmCollector.php @@ -28,7 +28,10 @@ class RetailCrmCollector */ public static function add() { - $keys = unserialize(COption::GetOptionString(self::$MODULE_ID, self::$CRM_COLL_KEY, 0)); + $keys = unserialize( + COption::GetOptionString(self::$MODULE_ID, self::$CRM_COLL_KEY, 0), + ['allowed_classes' => false] + ); $collector = COption::GetOptionString(self::$MODULE_ID, self::$CRM_COLL, 0); $request = \Bitrix\Main\Context::getCurrent()->getRequest(); diff --git a/intaro.retailcrm/classes/general/consultant/RetailCrmOnlineConsultant.php b/intaro.retailcrm/classes/general/consultant/RetailCrmOnlineConsultant.php index ee123abc..658245c8 100644 --- a/intaro.retailcrm/classes/general/consultant/RetailCrmOnlineConsultant.php +++ b/intaro.retailcrm/classes/general/consultant/RetailCrmOnlineConsultant.php @@ -25,10 +25,15 @@ class RetailCrmOnlineConsultant public static function add() { $request = \Bitrix\Main\Context::getCurrent()->getRequest(); + $script = RetailcrmConfigProvider::getOnlineConsultantScript(); - if (RetailcrmConfigProvider::isOnlineConsultantEnabled() && $request->isAdminSection() !== true) { + if ( + RetailcrmConfigProvider::isOnlineConsultantEnabled() + && $request->isAdminSection() !== true + && $script !== '' + ) { \Bitrix\Main\Page\Asset::getInstance()->addString( - RetailcrmConfigProvider::getOnlineConsultantScript(), + $script, true ); diff --git a/intaro.retailcrm/classes/general/ua/RetailCrmUa.php b/intaro.retailcrm/classes/general/ua/RetailCrmUa.php index bf7245d2..f61413c3 100644 --- a/intaro.retailcrm/classes/general/ua/RetailCrmUa.php +++ b/intaro.retailcrm/classes/general/ua/RetailCrmUa.php @@ -24,7 +24,10 @@ class RetailCrmUa public static function add() { $ua = COption::GetOptionString(self::$MODULE_ID, self::$CRM_UA, 0); - $uaKeys = unserialize(COption::GetOptionString(self::$MODULE_ID, self::$CRM_UA_KEYS, 0)); + $uaKeys = unserialize( + COption::GetOptionString(self::$MODULE_ID, self::$CRM_UA_KEYS, 0), + ['allowed_classes' => false] + ); $request = \Bitrix\Main\Context::getCurrent()->getRequest(); if ($ua === 'Y' && !empty($uaKeys[SITE_ID]['ID']) && !empty($uaKeys[SITE_ID]['INDEX']) && $request->isAdminSection() !== true) { diff --git a/intaro.retailcrm/description.ru b/intaro.retailcrm/description.ru index 3761b2f5..ebb585df 100644 --- a/intaro.retailcrm/description.ru +++ b/intaro.retailcrm/description.ru @@ -1 +1 @@ -- Исправлены замечания по безопасности модуля. Устранены уязвимости Authentication Bypass, IDOR и SSRF. Снижены риски CSRF, XSS и Information Disclosure. Усилены проверки доступа и обработки пользовательских данных. +- Исправлены замечания по безопасности модуля. Устранены уязвимости после проверки приложения на Маркетплейсе. diff --git a/intaro.retailcrm/install/export/local/components/intaro/main.register/templates/.default/template.php b/intaro.retailcrm/install/export/local/components/intaro/main.register/templates/.default/template.php index 6657ce09..03d45b27 100644 --- a/intaro.retailcrm/install/export/local/components/intaro/main.register/templates/.default/template.php +++ b/intaro.retailcrm/install/export/local/components/intaro/main.register/templates/.default/template.php @@ -20,6 +20,12 @@ die(); } +$sanitizer = new CBXSanitizer(); +$sanitizer->SetLevel(CBXSanitizer::SECURE_LEVEL_MIDDLE); + +$agreementLoyaltyProgram = $sanitizer->SanitizeHtml((string) ($arResult['AGREEMENT_LOYALTY_PROGRAM'] ?? '')); +$agreementPersonalData = $sanitizer->SanitizeHtml((string) ($arResult['AGREEMENT_PERSONAL_DATA'] ?? '')); + if ($arResult["SHOW_SMS_FIELD"] == true) { CJSCore::Init('phone_auth'); } @@ -34,10 +40,10 @@ ?> ', htmlspecialcharsbx($rcct)); + } + + return $script . sprintf('', htmlspecialcharsbx($url)); + } + public static function setEventTrackerCart(string $value) { static::setOption(Constants::CRM_EVENT_TRACKER_CART, $value); diff --git a/intaro.retailcrm/lib/component/constants.php b/intaro.retailcrm/lib/component/constants.php index 855cc6a4..f3b3851b 100644 --- a/intaro.retailcrm/lib/component/constants.php +++ b/intaro.retailcrm/lib/component/constants.php @@ -18,7 +18,7 @@ */ class Constants { - public const MODULE_VERSION = '6.8.0'; + public const MODULE_VERSION = '6.8.1'; public const CRM_PURCHASE_PRICE_NULL = 'purchasePrice_null'; public const BITRIX_USER_ID_PREFIX = 'bitrixUserId-'; public const CRM_USERS_MAP = 'crm_users_map'; diff --git a/intaro.retailcrm/lib/controller/loyalty/basket.php b/intaro.retailcrm/lib/controller/loyalty/basket.php index 8784954b..d966550a 100644 --- a/intaro.retailcrm/lib/controller/loyalty/basket.php +++ b/intaro.retailcrm/lib/controller/loyalty/basket.php @@ -11,6 +11,9 @@ namespace Intaro\RetailCrm\Controller\Loyalty; +use Bitrix\Main\Engine\ActionFilter\Authentication; +use Bitrix\Main\Engine\ActionFilter\Csrf; +use Bitrix\Main\Engine\ActionFilter\HttpMethod; use Bitrix\Main\Engine\Controller; use Bitrix\Main\Request; use Exception; @@ -38,6 +41,19 @@ public function __construct(Request $request = null) parent::__construct($request); } + public function configureActions(): array + { + return [ + 'addLoyaltyToBasket' => [ + 'prefilters' => [ + new Authentication(), + new HttpMethod([HttpMethod::METHOD_POST]), + new Csrf(), + ], + ], + ]; + } + /** * Добавляет данные, полученные при расчете привилегии, в массив корзины * diff --git a/intaro.retailcrm/lib/controller/loyalty/order.php b/intaro.retailcrm/lib/controller/loyalty/order.php index 732551ea..2e17a080 100644 --- a/intaro.retailcrm/lib/controller/loyalty/order.php +++ b/intaro.retailcrm/lib/controller/loyalty/order.php @@ -195,6 +195,11 @@ private function isOrderCheckIdValid(int $orderId, string $checkId): bool public function configureActions(): array { return [ + 'loyaltyCalculate' => [ + 'prefilters' => [ + new HttpMethod([HttpMethod::METHOD_POST]), + ], + ], 'sendVerificationCode' => [ 'prefilters' => [ new Authentication(), diff --git a/intaro.retailcrm/lib/controller/loyalty/register.php b/intaro.retailcrm/lib/controller/loyalty/register.php index 5001961b..def3ed5c 100644 --- a/intaro.retailcrm/lib/controller/loyalty/register.php +++ b/intaro.retailcrm/lib/controller/loyalty/register.php @@ -55,6 +55,34 @@ public function __construct(Request $request = null) public function configureActions(): array { return [ + 'accountCreate' => [ + 'prefilters' => [ + new Authentication(), + new HttpMethod([HttpMethod::METHOD_POST]), + new Csrf(), + ], + ], + 'resendRegisterSms' => [ + 'prefilters' => [ + new Authentication(), + new HttpMethod([HttpMethod::METHOD_POST]), + new Csrf(), + ], + ], + 'activateLpBySms' => [ + 'prefilters' => [ + new Authentication(), + new HttpMethod([HttpMethod::METHOD_POST]), + new Csrf(), + ], + ], + 'activateAccount' => [ + 'prefilters' => [ + new Authentication(), + new HttpMethod([HttpMethod::METHOD_POST]), + new Csrf(), + ], + ], 'saveUserLpFields' => [ 'prefilters' => [ new Authentication(), diff --git a/intaro.retailcrm/lib/icml/settingsservice.php b/intaro.retailcrm/lib/icml/settingsservice.php index 130b20db..9181cb47 100644 --- a/intaro.retailcrm/lib/icml/settingsservice.php +++ b/intaro.retailcrm/lib/icml/settingsservice.php @@ -805,7 +805,10 @@ public function setCatalogCustomPropsOptionName(string $catalogId): self private function getCustomProps(): ?array { - $props = unserialize(COption::GetOptionString(self::MODULE_ID, $this->catalogCustomPropsOptionName)); + $props = unserialize( + COption::GetOptionString(self::MODULE_ID, $this->catalogCustomPropsOptionName), + ['allowed_classes' => false] + ); if (!$props) { return null; @@ -857,7 +860,10 @@ public function saveCustomProps(array $newProps): void private function getProfileCatalogs(): ?array { - $catalogs = unserialize(COption::GetOptionString(self::MODULE_ID, $this->profileCatalogsOptionName)); + $catalogs = unserialize( + COption::GetOptionString(self::MODULE_ID, $this->profileCatalogsOptionName), + ['allowed_classes' => false] + ); if (!$catalogs) { return null; @@ -912,11 +918,17 @@ private function deleteOptionEntry(string $name) private function linkNewProfile(): void { - $currentProfileCatalogs = unserialize(COption::GetOptionString(self::MODULE_ID, $this->profileCatalogsOptionName)); + $currentProfileCatalogs = unserialize( + COption::GetOptionString(self::MODULE_ID, $this->profileCatalogsOptionName), + ['allowed_classes' => false] + ); if (!$currentProfileCatalogs) { $tmpProfileName = 'exportProfileId_0_catalogs'; - $currentProfileCatalogs = unserialize(COption::GetOptionString(self::MODULE_ID, $tmpProfileName)); + $currentProfileCatalogs = unserialize( + COption::GetOptionString(self::MODULE_ID, $tmpProfileName), + ['allowed_classes' => false] + ); if ($currentProfileCatalogs) { $this->setOptionEntry($this->profileCatalogsOptionName, serialize($currentProfileCatalogs)); @@ -926,11 +938,17 @@ private function linkNewProfile(): void foreach ($currentProfileCatalogs as $catalogId) { $optionName = sprintf('exportCustomProps_ProfileId_%s_catalogId_%s', $this->exportProfileId, $catalogId); - $propsCatalog = unserialize(COption::GetOptionString(self::MODULE_ID, $optionName)); + $propsCatalog = unserialize( + COption::GetOptionString(self::MODULE_ID, $optionName), + ['allowed_classes' => false] + ); if (!$propsCatalog) { $tmpOptionName = sprintf('exportCustomProps_ProfileId_%s_catalogId_%s', '0', $catalogId); - $propsCatalog = unserialize(COption::GetOptionString(self::MODULE_ID, $tmpOptionName)); + $propsCatalog = unserialize( + COption::GetOptionString(self::MODULE_ID, $tmpOptionName), + ['allowed_classes' => false] + ); if ($propsCatalog) { $this->setOptionEntry($optionName, serialize($propsCatalog)); diff --git a/intaro.retailcrm/lib/service/loyaltyservice.php b/intaro.retailcrm/lib/service/loyaltyservice.php index b861b49a..ab68f0d5 100644 --- a/intaro.retailcrm/lib/service/loyaltyservice.php +++ b/intaro.retailcrm/lib/service/loyaltyservice.php @@ -143,7 +143,7 @@ public function getLoyaltyCalculate(array $basketItems, float $bonuses = 0): ?Lo $prices = ConfigProvider::getCrmPrices(); $product->priceType = new PriceType(); - $serializePrice = unserialize($prices); + $serializePrice = unserialize($prices, ['allowed_classes' => false]); if (isset($serializePrice[$item['PRICE_TYPE_ID']])) { $product->priceType->code = $serializePrice[$item['PRICE_TYPE_ID']]; diff --git a/intaro.retailcrm/options.php b/intaro.retailcrm/options.php index a697caee..09c1b306 100644 --- a/intaro.retailcrm/options.php +++ b/intaro.retailcrm/options.php @@ -118,7 +118,10 @@ die(json_encode(['success' => false, 'errMsg' => $e->getCode()])); } - $optionsDelivTypes = unserialize(COption::GetOptionString($mid, Constants::CRM_DELIVERY_TYPES_ARR, 0)); + $optionsDelivTypes = unserialize( + COption::GetOptionString($mid, Constants::CRM_DELIVERY_TYPES_ARR, 0), + ['allowed_classes' => false] + ); $arDeliveryServiceAll = Manager::getActiveList(); foreach ($optionsDelivTypes as $key => $deliveryType) { @@ -607,7 +610,7 @@ function maskPrice($var) { //online_consultant if (htmlspecialchars(trim($_POST['online_consultant'] === 'Y'))) { $onlineConsultant = 'Y'; - $onlineConsultantScript = trim($_POST['online_consultant_script']); + $onlineConsultantScript = trim((string) ($_POST['online_consultant_script'] ?? '')); RegisterModuleDependences("main", "OnBeforeProlog", $mid, "RetailCrmOnlineConsultant", "add"); } else { $onlineConsultant = 'N'; @@ -792,8 +795,14 @@ function maskCorp($var) { RCrmActions::sendConfiguration($api, false); } else { - $deactivateAgents = unserialize(COption::GetOptionString($mid, Constants::AGENTS_DEACTIVATE, '')); - $deactivateEvents = unserialize(COption::GetOptionString($mid, Constants::EVENTS_DEACTIVATE, '')); + $deactivateAgents = unserialize( + COption::GetOptionString($mid, Constants::AGENTS_DEACTIVATE, ''), + ['allowed_classes' => false] + ); + $deactivateEvents = unserialize( + COption::GetOptionString($mid, Constants::EVENTS_DEACTIVATE, ''), + ['allowed_classes' => false] + ); if (!empty($deactivateAgents)) { $dateAgent = new DateTime(); @@ -1320,39 +1329,39 @@ function ($payment) { //saved params $useCrmOrderMethods = ConfigProvider::useCrmOrderMethods(); - $crmOrderMethods = unserialize(COption::GetOptionString($mid, Constants::CRM_ORDER_METHODS, 0)); - $moduleDeactivate = unserialize(COption::GetOptionString($mid, Constants::MODULE_DEACTIVATE, 'N')); - $optionsOrderTypes = unserialize(COption::GetOptionString($mid, Constants::CRM_ORDER_TYPES_ARR, 0)); - $optionsDelivTypes = unserialize(COption::GetOptionString($mid, Constants::CRM_DELIVERY_TYPES_ARR, 0)); - $optionsPayTypes = unserialize(COption::GetOptionString($mid, Constants::CRM_PAYMENT_TYPES, 0)); - $optionsPayStatuses = unserialize(COption::GetOptionString($mid, Constants::CRM_PAYMENT_STATUSES, 0)); - $optionsPayment = unserialize(COption::GetOptionString($mid, Constants::CRM_PAYMENT, 0)); - $optionsSitesList = unserialize(COption::GetOptionString($mid, Constants::CRM_SITES_LIST, 0)); + $crmOrderMethods = unserialize(COption::GetOptionString($mid, Constants::CRM_ORDER_METHODS, 0), ['allowed_classes' => false]); + $moduleDeactivate = unserialize(COption::GetOptionString($mid, Constants::MODULE_DEACTIVATE, 'N'), ['allowed_classes' => false]); + $optionsOrderTypes = unserialize(COption::GetOptionString($mid, Constants::CRM_ORDER_TYPES_ARR, 0), ['allowed_classes' => false]); + $optionsDelivTypes = unserialize(COption::GetOptionString($mid, Constants::CRM_DELIVERY_TYPES_ARR, 0), ['allowed_classes' => false]); + $optionsPayTypes = unserialize(COption::GetOptionString($mid, Constants::CRM_PAYMENT_TYPES, 0), ['allowed_classes' => false]); + $optionsPayStatuses = unserialize(COption::GetOptionString($mid, Constants::CRM_PAYMENT_STATUSES, 0), ['allowed_classes' => false]); + $optionsPayment = unserialize(COption::GetOptionString($mid, Constants::CRM_PAYMENT, 0), ['allowed_classes' => false]); + $optionsSitesList = unserialize(COption::GetOptionString($mid, Constants::CRM_SITES_LIST, 0), ['allowed_classes' => false]); $optionsDischarge = (int) COption::GetOptionString($mid, Constants::CRM_ORDER_DISCHARGE, 0); - $optionsOrderProps = unserialize(COption::GetOptionString($mid, Constants::CRM_ORDER_PROPS, 0)); - $optionsContragentType = unserialize(COption::GetOptionString($mid, Constants::CRM_CONTRAGENT_TYPE, 0)); - $optionsLegalDetails = unserialize(COption::GetOptionString($mid, Constants::CRM_LEGAL_DETAILS, 0)); - $optionsCustomFields = unserialize(COption::GetOptionString($mid, Constants::CRM_CUSTOM_FIELDS, 0)); + $optionsOrderProps = unserialize(COption::GetOptionString($mid, Constants::CRM_ORDER_PROPS, 0), ['allowed_classes' => false]); + $optionsContragentType = unserialize(COption::GetOptionString($mid, Constants::CRM_CONTRAGENT_TYPE, 0), ['allowed_classes' => false]); + $optionsLegalDetails = unserialize(COption::GetOptionString($mid, Constants::CRM_LEGAL_DETAILS, 0), ['allowed_classes' => false]); + $optionsCustomFields = unserialize(COption::GetOptionString($mid, Constants::CRM_CUSTOM_FIELDS, 0), ['allowed_classes' => false]); $optionsOrderNumbers = COption::GetOptionString($mid, Constants::CRM_ORDER_NUMBERS, 0); $optionsOrderVat = COption::GetOptionString($mid, Constants::CRM_ORDER_VAT, 0); $optionsOrderTrackNumber = ConfigProvider::getTrackNumberStatus(); $optionsSyncIntegrationPayment = ConfigProvider::getSyncIntegrationPayment(); - $canselOrderArr = unserialize(COption::GetOptionString($mid, Constants::CRM_CANCEL_ORDER, 0)); + $canselOrderArr = unserialize(COption::GetOptionString($mid, Constants::CRM_CANCEL_ORDER, 0), ['allowed_classes' => false]); $sendPickupPointAddress = COption::GetOptionString($mid, Constants::CRM_SEND_PICKUP_POINT_ADDRESS, 'N'); $optionInventotiesUpload = COption::GetOptionString($mid, Constants::CRM_INVENTORIES_UPLOAD, 0); - $optionStores = unserialize(COption::GetOptionString($mid, Constants::CRM_STORES, 0)); - $optionShops = unserialize(COption::GetOptionString($mid, Constants::CRM_SHOPS, 0)); - $optionIblocksInventories = unserialize(COption::GetOptionString($mid, Constants::CRM_IBLOCKS_INVENTORIES, 0)); - $optionShopsCorporate = unserialize(COption::GetOptionString($mid, Constants::CRM_SHOPS, 0)); + $optionStores = unserialize(COption::GetOptionString($mid, Constants::CRM_STORES, 0), ['allowed_classes' => false]); + $optionShops = unserialize(COption::GetOptionString($mid, Constants::CRM_SHOPS, 0), ['allowed_classes' => false]); + $optionIblocksInventories = unserialize(COption::GetOptionString($mid, Constants::CRM_IBLOCKS_INVENTORIES, 0), ['allowed_classes' => false]); + $optionShopsCorporate = unserialize(COption::GetOptionString($mid, Constants::CRM_SHOPS, 0), ['allowed_classes' => false]); $optionPricesUpload = COption::GetOptionString($mid, Constants::CRM_PRICES_UPLOAD, 0); - $optionPrices = unserialize(COption::GetOptionString($mid, Constants::CRM_PRICES, 0)); - $optionPriceShops = unserialize(COption::GetOptionString($mid, Constants::CRM_PRICE_SHOPS, 0)); - $optionIblocksPrices = unserialize(COption::GetOptionString($mid, Constants::CRM_IBLOCKS_PRICES, 0)); + $optionPrices = unserialize(COption::GetOptionString($mid, Constants::CRM_PRICES, 0), ['allowed_classes' => false]); + $optionPriceShops = unserialize(COption::GetOptionString($mid, Constants::CRM_PRICE_SHOPS, 0), ['allowed_classes' => false]); + $optionIblocksPrices = unserialize(COption::GetOptionString($mid, Constants::CRM_IBLOCKS_PRICES, 0), ['allowed_classes' => false]); $optionCollector = COption::GetOptionString($mid, Constants::CRM_COLLECTOR, 0); - $optionCollectorKeys = unserialize(COption::GetOptionString($mid, Constants::CRM_COLL_KEY)); + $optionCollectorKeys = unserialize(COption::GetOptionString($mid, Constants::CRM_COLL_KEY), ['allowed_classes' => false]); $optionOnlineConsultant = RetailcrmConfigProvider::isOnlineConsultantEnabled(); $optionOnlineConsultantScript = RetailcrmConfigProvider::getOnlineConsultantScript(); @@ -1361,7 +1370,7 @@ function ($payment) { $optionEventTrackerOpenCart= RetailcrmConfigProvider::isEventTrackerOpenCartEnabled(); $optionUa = COption::GetOptionString($mid, Constants::CRM_UA, 0); - $optionUaKeys = unserialize(COption::GetOptionString($mid, Constants::CRM_UA_KEYS)); + $optionUaKeys = unserialize(COption::GetOptionString($mid, Constants::CRM_UA_KEYS), ['allowed_classes' => false]); $optionDiscRound = COption::GetOptionString($mid, Constants::CRM_DISCOUNT_ROUND, 0); $optionPricePrchaseNull = COption::GetOptionString($mid, Constants::CRM_PURCHASE_PRICE_NULL, 0); @@ -1369,7 +1378,7 @@ function ($payment) { //corporate-cliente $optionCorpClient = COption::GetOptionString($mid, Constants::CRM_CC, 0); - $optionCorpShops = unserialize(COption::GetOptionString($mid, Constants::CRM_CORP_SHOPS, 0)); + $optionCorpShops = unserialize(COption::GetOptionString($mid, Constants::CRM_CORP_SHOPS, 0), ['allowed_classes' => false]); $optionsCorpComName = COption::GetOptionString($mid, Constants::CRM_CORP_NAME, 0); $optionsCorpAdres = COption::GetOptionString($mid, Constants::CRM_CORP_ADDRESS, 0); @@ -1446,7 +1455,7 @@ function ($payment) { } while($getCustomFields['pagination']['currentPage'] < $getCustomFields['pagination']['totalPageCount']); $optionsOrderDimensions = COption::GetOptionString($mid, Constants::CRM_DIMENSIONS, 'N'); - $addressOptions = unserialize(COption::GetOptionString($mid, Constants::CRM_ADDRESS_OPTIONS, 0)); + $addressOptions = unserialize(COption::GetOptionString($mid, Constants::CRM_ADDRESS_OPTIONS, 0), ['allowed_classes' => false]); $optionCart = COption::GetOptionString($mid, Constants::CART, 'N'); @@ -3486,11 +3495,28 @@ function customerFixDate() { warning.style = 'color:red; margin-top:5px; display:none;'; warning.textContent = ''; textarea.insertAdjacentElement('afterend', warning); + const allowedDomains = ['retailcrm.ru', 'retailcrm.pro', 'retailcrm.es', 'retailcrm.tech']; const renderEventCheckboxes = () => { const textareaValue = textarea.value.trim(); const hasCode = textareaValue !== ''; - const hasWidget = textareaValue.includes('c.retailcrm.tech/widget/loader.js'); + const urlMatches = textareaValue.match(/(?:https:)?\/\/[^\s'"<>]+/g) || []; + const hasWidget = urlMatches.some((url) => { + try { + const normalizedUrl = url.startsWith('//') ? 'https:' + url : url; + const parsedUrl = new URL(normalizedUrl); + const host = parsedUrl.hostname.toLowerCase(); + const hasAllowedDomain = allowedDomains.some((domain) => + host === domain || host.endsWith('.' + domain) + ); + + return parsedUrl.protocol === 'https:' + && hasAllowedDomain + && parsedUrl.pathname === '/widget/loader.js'; + } catch (e) { + return false; + } + }); const canRenderElements = eventTrackerCheckbox.checked && hasCode && hasWidget; warning.style.display = (!hasWidget && hasCode) ? 'block' : 'none'; @@ -3529,7 +3555,7 @@ function customerFixDate() { - +