diff --git a/.github/workflows/releaseNotice.yml b/.github/workflows/releaseNotice.yml new file mode 100644 index 0000000..46abd80 --- /dev/null +++ b/.github/workflows/releaseNotice.yml @@ -0,0 +1,23 @@ +name: Notice about releases via Telegram + +on: + release: + types: [published] + +jobs: + + build: + runs-on: ubuntu-latest + steps: + - name: send telegram message on release + uses: appleboy/telegram-action@master + with: + to: ${{ secrets.TELEGRAM_TO }} + token: ${{ secrets.TELEGRAM_TOKEN }} + message: | + ${{ github.event.repository.description }} v${{github.event.release.name}} released + ${{github.event.release.html_url}} + + ${{ github.event.release.body }} + format: html + disable_web_page_preview: true \ No newline at end of file diff --git a/.github/workflows/reviewNotice.yml b/.github/workflows/reviewNotice.yml new file mode 100644 index 0000000..d29c9ad --- /dev/null +++ b/.github/workflows/reviewNotice.yml @@ -0,0 +1,27 @@ +name: Notice about review approved via Telegram + +on: + pull_request_review: + types: [ submitted ] + +jobs: + + build: + if: github.event.review.state == 'approved' && toJSON(github.event.pull_request.requested_reviewers) == '[]' + runs-on: ubuntu-latest + steps: + - name: Convert date format + id: date + run: echo "::set-output name=date::$(date -d "${{ github.event.pull_request.created_at }}" +"%Y-%m-%d")" + - name: Send telegram message on review approved + uses: appleboy/telegram-action@master + with: + to: ${{ secrets.TELEGRAM_PLUGINS_TO }} + token: ${{ secrets.TELEGRAM_REVIEWER_TOKEN }} + message: | + 💥🎉🎉🎉💥 Pull-request ${{ github.event.pull_request.title }} + submitted by ${{ github.event.pull_request.user.login }} at ${{ steps.date.outputs.date }} + + was approved and is ready to merge ➡️ !!! + format: html + disable_web_page_preview: true \ No newline at end of file diff --git a/.gitignore b/.gitignore index f131eca..7681e02 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /* !/cleantalk.antispam -!/cleantalk.antispam/* \ No newline at end of file +!/cleantalk.antispam/* +!/.github +!/.github/* \ No newline at end of file diff --git a/README.md b/README.md index 075f4fd..f39149f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ bitrix-antispam =============== -1C Bitrix anti-spam mod. 3.12.0 +1C Bitrix anti-spam mod. Information page, http://cleantalk.org/bitrix-antispam-module-bez-captcha diff --git a/cleantalk.antispam/default_option.php b/cleantalk.antispam/default_option.php index efaa679..c296e6c 100644 --- a/cleantalk.antispam/default_option.php +++ b/cleantalk.antispam/default_option.php @@ -11,7 +11,9 @@ 'web_form' => 1, 'form_global_check' => 0, 'form_global_check_without_email' => 0, + 'bot_detector' => 1, 'form_sfw' => 1, + 'form_sfw_uniq_get_option' => 1, 'site_exclusions' => '', 'form_exclusions_url' => '', 'form_exclusions_url__regexp' => 0, diff --git a/cleantalk.antispam/description.en b/cleantalk.antispam/description.en index 25b4005..10afc20 100644 --- a/cleantalk.antispam/description.en +++ b/cleantalk.antispam/description.en @@ -1,23 +1,15 @@ diff --git a/cleantalk.antispam/description.ru b/cleantalk.antispam/description.ru index 25b4005..10afc20 100644 --- a/cleantalk.antispam/description.ru +++ b/cleantalk.antispam/description.ru @@ -1,23 +1,15 @@ diff --git a/cleantalk.antispam/include.php b/cleantalk.antispam/include.php index ab92100..81f5208 100644 --- a/cleantalk.antispam/include.php +++ b/cleantalk.antispam/include.php @@ -7,6 +7,7 @@ require_once(dirname(__FILE__) . '/lib/autoload.php'); //Antispam classes +use Bitrix\Main\Page\Asset; use Cleantalk\Antispam\Cleantalk; use Cleantalk\Antispam\CleantalkRequest; use Cleantalk\Antispam\CleantalkResponse; @@ -19,7 +20,7 @@ use Cleantalk\ApbctBitrix\Cron; use Cleantalk\ApbctBitrix\DB; use Cleantalk\Common\Variables\Server; -use Cleantalk\Common\Firewall\Modules\SFW; +use Cleantalk\ApbctBitrix\SFW; if ( ! defined( 'CLEANTALK_USER_AGENT' ) ) define( 'CLEANTALK_USER_AGENT', 'bitrix-3.12.0' ); @@ -162,6 +163,7 @@ public static function OnPageStartHandler() $ct_key = COption::GetOptionString( 'cleantalk.antispam', 'key', '' ); $last_checked = COption::GetOptionInt( 'cleantalk.antispam', 'last_checked', 0 ); $show_review = COption::GetOptionInt( 'cleantalk.antispam', 'show_review', 0 ); + $bot_detector = COption::GetOptionInt( 'cleantalk.antispam', 'bot_detector', 0 ); $is_sfw = COption::GetOptionInt( 'cleantalk.antispam', 'form_sfw', 0 ); $sfw_last_update = COption::GetOptionInt( 'cleantalk.antispam', 'sfw_last_update', 0); $sfw_last_send_log = COption::GetOptionInt( 'cleantalk.antispam', 'sfw_last_send_log', 0); @@ -179,6 +181,10 @@ public static function OnPageStartHandler() if( ! $USER->IsAdmin() ){ + if ( $bot_detector ) { + Asset::getInstance()->addJs('https://moderate.cleantalk.org/ct-bot-detector-wrapper.js'); + } + // Set cookies if( ! headers_sent() ) self::ct_cookie(); @@ -1032,6 +1038,8 @@ static function OnEventLogGetAuditTypesHandler(){ public static function OnEndBufferContentHandler( &$content ) { global $USER, $APPLICATION; + if (!is_object($USER)) $USER = new CUser; + if( ! $USER->IsAdmin() && ! defined( "ADMIN_SECTION" ) && @@ -1061,11 +1069,7 @@ static function FormAddon() { $ct_check_values = self::SetCheckJSValues(); $js_template = ""; + $replaces['{SFWGETOPTION}'] = $sfwgetoption; + + // Debug + if($this->debug){ + $debug = '

Headers

' + . var_export( apache_request_headers(), true ) + . '

REMOTE_ADDR

' + . Server::get( 'REMOTE_ADDR' ) + . '

SERVER_ADDR

' + . Server::get( 'REMOTE_ADDR' ) + . '

IP_ARRAY

' + . var_export( $this->ip_array, true ) + . '

ADDITIONAL

' + . var_export( $this->debug_data, true ); + } + $replaces['{DEBUG}'] = isset( $debug ) ? $debug : ''; + + foreach( $replaces as $place_holder => $replace ){ + $sfw_die_page = str_replace( $place_holder, $replace, $sfw_die_page ); + } + + die( $sfw_die_page ); + + } + + die( "IP BLACKLISTED. Blocked by SFW " . $result['ip'] ); + + } + +} diff --git a/cleantalk.antispam/lib/Cleantalk/ApbctBitrix/die_page_sfw.html b/cleantalk.antispam/lib/Cleantalk/ApbctBitrix/die_page_sfw.html new file mode 100644 index 0000000..0d700c1 --- /dev/null +++ b/cleantalk.antispam/lib/Cleantalk/ApbctBitrix/die_page_sfw.html @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + {SFWGETOPTION} + + + + +
+

{SFW_DIE_NOTICE_IP}{REMOTE_ADDRESS}

+ +

{REAL_IP__HEADER} {REAL_IP}

+

{TEST_IP__HEADER} {TEST_IP}

+ +

{TEST_TITLE}

+ +

{SFW_DIE_MAKE_SURE_JS_ENABLED}
+ +
+

{SFW_DIE_CLICK_TO_PASS}

+ +
+

{SFW_DIE_YOU_WILL_BE_REDIRECTED}

+
+
+
+
+
+
+
+ {GENERATED} +

Browser time

+
+ +
+
+

{SERVICE_ID},

+

{HOST}

+
+ + + {DEBUG} + + diff --git a/cleantalk.antispam/lib/Cleantalk/Common/Firewall/FirewallUpdater.php b/cleantalk.antispam/lib/Cleantalk/Common/Firewall/FirewallUpdater.php index 26d6f94..4050d94 100644 --- a/cleantalk.antispam/lib/Cleantalk/Common/Firewall/FirewallUpdater.php +++ b/cleantalk.antispam/lib/Cleantalk/Common/Firewall/FirewallUpdater.php @@ -97,7 +97,7 @@ public function update() if( Get::get('spbc_remote_call_action') == 'sfw_update__write_base' && Get::get('firewall_updating_id') && - Get::get('firewall_updating_id') !== $fw_stats['firewall_updating_id'] && + (int)Get::get('firewall_updating_id') !== (int)$fw_stats['firewall_updating_id'] && time() - $fw_stats['firewall_updating_last_start'] < 8600 ) { return array( 'error' => 'FIREWALL_IS_UPDATING' ); @@ -355,21 +355,27 @@ private function writeDbExclusions() */ private function createTempTables() { + //drop current temp table to prevent any keys mismatches with the main table + $this->db->execute('DROP TABLE IF EXISTS `' . $this->db->prefix . APBCT_TBL_FIREWALL_DATA . '_temp`'); + + //check if main table exists $sql = 'SHOW TABLES LIKE "%scleantalk_sfw";'; $sql = sprintf( $sql, $this->db->prefix ); // Adding current blog prefix $result = $this->db->fetch( $sql ); + // create the main table if not if( ! $result ){ $sql = sprintf( Schema::getSchema('sfw'), $this->db->prefix ); $this->db->execute( $sql ); } - $is_source_column_exist = $this->db->fetch( 'SHOW COLUMNS FROM `' . APBCT_TBL_FIREWALL_DATA . '` LIKE "source";' ); + //hardcode for new "source" column + $is_source_column_exist = $this->db->fetch( 'SHOW COLUMNS FROM `' . $this->db->prefix . APBCT_TBL_FIREWALL_DATA . '` LIKE "source";' ); if( ! $is_source_column_exist ) { - $sql = 'ALTER TABLE `' . APBCT_TBL_FIREWALL_DATA . '` ADD `source` TINYINT NULL DEFAULT NULL AFTER `status`;'; + $sql = 'ALTER TABLE `' . $this->db->prefix . APBCT_TBL_FIREWALL_DATA . '` ADD `source` TINYINT NULL DEFAULT NULL AFTER `status`;'; $this->db->execute( $sql ); } - $this->db->execute( 'CREATE TABLE IF NOT EXISTS `' . APBCT_TBL_FIREWALL_DATA . '_temp` LIKE `' . APBCT_TBL_FIREWALL_DATA . '`;' ); - $this->db->execute( 'TRUNCATE TABLE `' . APBCT_TBL_FIREWALL_DATA . '_temp`;' ); + $this->db->execute( 'CREATE TABLE IF NOT EXISTS `' . $this->db->prefix . APBCT_TBL_FIREWALL_DATA . '_temp` LIKE `' . $this->db->prefix . APBCT_TBL_FIREWALL_DATA . '`;' ); + $this->db->execute( 'TRUNCATE TABLE `' . $this->db->prefix . APBCT_TBL_FIREWALL_DATA . '_temp`;' ); } /** @@ -392,4 +398,4 @@ private function renameDataTables() $this->db->execute( 'ALTER TABLE `'. APBCT_TBL_FIREWALL_DATA .'_temp` RENAME `'. APBCT_TBL_FIREWALL_DATA .'`;' ); } -} \ No newline at end of file +} diff --git a/cleantalk.antispam/lib/Cleantalk/Common/Firewall/Modules/SFW.php b/cleantalk.antispam/lib/Cleantalk/Common/Firewall/Modules/SFW.php index 7d59252..9914887 100644 --- a/cleantalk.antispam/lib/Cleantalk/Common/Firewall/Modules/SFW.php +++ b/cleantalk.antispam/lib/Cleantalk/Common/Firewall/Modules/SFW.php @@ -16,7 +16,7 @@ class SFW extends FirewallModule { // Additional params private $sfw_counter = false; private $set_cookies = false; - private $cookie_domain = false; + protected $cookie_domain = false; /** * FireWall_module constructor. @@ -28,12 +28,12 @@ class SFW extends FirewallModule { public function __construct( $data_table, $params = array() ) { $this->db_data_table_name = $data_table ?: null; - + foreach( $params as $param_name => $param ){ $this->$param_name = isset( $this->$param_name ) ? $param : false; } } - + /** * Use this method to execute main logic of the module. * @@ -51,7 +51,7 @@ public function check() } else { $ct_sfw_pass_key = Cookie::get( 'ct_sfw_pass_key'); } - + // Skip by cookie foreach( $this->ip_array as $current_ip ){ @@ -66,7 +66,7 @@ public function check() } // Do logging an one passed request - $this->update_log( $current_ip, 'PASS_SFW' ); + $this->update_log( $current_ip, 'PASS_SFW', false ); if( $this->sfw_counter ){ // @ToDo have to implement the logic of incrementing and saving count of all handled requests. @@ -81,11 +81,11 @@ public function check() if( $status ) { $results[] = array('ip' => $current_ip, 'is_personal' => false, 'status' => 'PASS_SFW__BY_WHITELIST',); } - + return $results; } } - + // Common check foreach( $this->ip_array as $origin => $current_ip ) { @@ -96,7 +96,7 @@ public function check() $needles[] = sprintf( "%u", bindec( $mask & base_convert( $current_ip_v4, 10, 2 ) ) ); } $needles = array_unique( $needles ); - + $db_results = $this->db->fetch_all("SELECT * FROM " . $this->db_data_table_name . " WHERE network IN (". implode( ',', $needles ) .") AND network = " . $current_ip_v4 . " & mask @@ -104,7 +104,7 @@ public function check() ORDER BY status DESC"); if ( ! empty( $db_results ) ) { - + foreach( $db_results as $db_result ){ $is_personal = isset($db_result['source']) && $db_result['source'] == 1 ? true : false; if ( $db_result['status'] == 1 ) { @@ -126,7 +126,7 @@ public function check() } return $results; } - + /** * Add entry to SFW log. * Writes to database. @@ -136,11 +136,10 @@ public function check() */ public function update_log( $ip, $status, $is_personal ) { + $id = md5( $ip . $this->module_name ); + $time = time(); - $id = md5( $ip . $this->module_name ); - $time = time(); - - $query = "INSERT INTO " . $this->db_log_table_name . " + $query = "INSERT INTO " . $this->db_log_table_name . " SET id = '$id', ip = '$ip', @@ -148,24 +147,17 @@ public function update_log( $ip, $status, $is_personal ) all_entries = 1, blocked_entries = " . ( strpos( $status, 'DENY' ) !== false ? 1 : 0 ) . ", entries_timestamp = '" . $time . "', - ua_name = '" . addslashes(Server::get('HTTP_USER_AGENT')) . "'%s + ua_name = '" . addslashes(Server::get('HTTP_USER_AGENT')) . "' ON DUPLICATE KEY UPDATE status = '$status', all_entries = all_entries + 1, blocked_entries = blocked_entries" . ( strpos( $status, 'DENY' ) !== false ? ' + 1' : '' ) . ", entries_timestamp = '" . intval( $time ) . "', - ua_name = '" . addslashes(Server::get('HTTP_USER_AGENT')) . "'%s"; - - if ( $is_personal ) { - $is_personal_sql = ", source = '" . $is_personal . "'"; - $query = sprintf( $query, $is_personal_sql, $is_personal_sql); - } else { - $query = sprintf( $query, '', ''); - } + ua_name = '" . addslashes(Server::get('HTTP_USER_AGENT')) . "'"; - $this->db->execute( $query ); - } + $this->db->execute( $query ); + } /** * @inheritdoc @@ -195,9 +187,9 @@ public function actionsForPassed( $result ) */ public function _die( $result ) { - + parent::_die( $result ); - + // Statistics if( ! empty( $this->blocked_ips ) ){ reset($this->blocked_ips); @@ -208,10 +200,10 @@ public function _die( $result ) $this->apbct->save('stats'); */ } - + // File exists? if( file_exists( __DIR__ . "/die_page_sfw.html" ) ){ - + $sfw_die_page = file_get_contents( __DIR__ . "/die_page_sfw.html" ); $net_count = $this->db->fetch( 'SELECT COUNT(*) as net_count FROM ' . $this->db_data_table_name ); @@ -231,12 +223,12 @@ public function _die( $result ) '{HOST}' => '', '{GENERATED}' => '

The page was generated at ' . date( 'D, d M Y H:i:s' ) . "

", '{REQUEST_URI}' => Server::get( 'REQUEST_URI' ), - + // Cookie '{COOKIE_PREFIX}' => '', '{COOKIE_DOMAIN}' => $this->cookie_domain, '{COOKIE_SFW}' => $this->test ? $this->test_ip : $cookie_val, - + // Test '{TEST_TITLE}' => '', '{REAL_IP__HEADER}' => '', @@ -244,7 +236,7 @@ public function _die( $result ) '{TEST_IP}' => '', '{REAL_IP}' => '', ); - + // Test if($this->test){ $replaces['{TEST_TITLE}'] = $this->__( 'This is the testing page for SpamFireWall', 'cleantalk-spam-protect' ); @@ -253,7 +245,7 @@ public function _die( $result ) $replaces['{TEST_IP}'] = $this->test_ip; $replaces['{REAL_IP}'] = $this->real_ip; } - + // Debug if($this->debug){ $debug = '

Headers

' @@ -268,17 +260,17 @@ public function _die( $result ) . var_export( $this->debug_data, true ); } $replaces['{DEBUG}'] = isset( $debug ) ? $debug : ''; - + foreach( $replaces as $place_holder => $replace ){ $sfw_die_page = str_replace( $place_holder, $replace, $sfw_die_page ); } - + die( $sfw_die_page ); - + } die( "IP BLACKLISTED. Blocked by SFW " . $result['ip'] ); } -} \ No newline at end of file +} diff --git a/cleantalk.antispam/options.php b/cleantalk.antispam/options.php index 3ad7937..2aa2a11 100644 --- a/cleantalk.antispam/options.php +++ b/cleantalk.antispam/options.php @@ -19,6 +19,21 @@ $cleantalk_is_wrong_regexp = false; $cleantalk_is_wrong_url_regexp = false; +$is_account_exists = false; + +$sites_from_bd = CSite::GetList("", "", Array("ACTIVE" => "Y")); +$sites = array(); +$sub_tabs = array(); +while( $site = $sites_from_bd->Fetch() ) { + $site["ID"] = htmlspecialcharsbx($site["ID"]); + $site["NAME"] = htmlspecialcharsbx($site["NAME"]); + $sites[] = $site; + $sub_tabs[] = array("DIV" => "opt_site_".$site["ID"], "TAB" => "(".$site["ID"].") ".$site["NAME"], 'TITLE' => ''); +} + +$subTabControl = new CAdminViewTabControl("subTabControl", $sub_tabs); + +$current_options = ct_get_options($sModuleId); if ( ! empty($REQUEST_METHOD) && $REQUEST_METHOD == 'POST' && $_POST['Update'] == 'Y' ) { //try to get default options @@ -44,6 +59,10 @@ $result = CleantalkAPI::method__get_api_key('antispam', COption::GetOptionString("main", "email_from"), $_SERVER["HTTP_HOST"], 'bitrix'); + if ( isset($result['account_exists']) && $result['account_exists'] == 1 ) { + $is_account_exists = true; + } + if (empty($result['error'])){ if(isset($result['user_token'])){ @@ -117,6 +136,8 @@ Option::set( $sModuleId, 'form_global_check', $_POST['form_global_check'] == '1' ? 1 : 0 ); Option::set( $sModuleId, 'form_global_check_without_email', $_POST['form_global_check_without_email'] == '1' ? 1 : 0 ); Option::set( $sModuleId, 'form_sfw', $_POST['form_sfw'] == '1' ? 1 : 0 ); + Option::set( $sModuleId, 'bot_detector', $_POST['bot_detector'] == '1' ? 1 : 0 ); + Option::set( $sModuleId, 'form_sfw_uniq_get_option', $_POST['form_sfw_uniq_get_option'] == '1' ? 1 : 0 ); Option::set( $sModuleId, 'complete_deactivation', $_POST['complete_deactivation'] == '1' ? 1 : 0 ); if (isset($_POST['form_exclusions_sites']) && is_array($_POST['form_exclusions_sites'])) { @@ -176,6 +197,18 @@ CAgent::RemoveModuleAgents("cleantalk.antispam"); } } + + foreach( $sites as $site ) { + $key = "key_" . $site["SITE_ID"]; + if ( isset($_POST[$key]) ) { + if ( empty($_POST[$key]) ) { + COption::RemoveOption($sModuleId, "_key", $site["SITE_ID"]); + } else { + // @ToDo add key_is_ok checking here and output error message + COption::SetOptionString($sModuleId, "_key", $_POST[$key], false, $site["SITE_ID"]); + } + } + } } function ct_is_valid_regexp($exclusion_string) @@ -289,10 +322,10 @@ function ct_get_options($sModuleId){ BeginNextTab();?>