Skip to content

Commit 1246f05

Browse files
authored
Fix. Upload checker. Check ability to run the checker. (#665)
* Upd. Upload checker. Checking ability to run the checker updated. * Upd. Upload checker. Checking ability to run the checker updated #2. * Upd. Upload checker. Rate limit implemented. * Upd. Upload checker. Methods calling fixed. * Fix. Code. Psalm notice fixed. * Fix. Code. Copilot review notice fixed #1. * Fix. Code. Copilot review notice fixed #2. * Fix. Code. Copilot review notice fixed #3. * Fix. Code. Copilot review notice fixed #4. * Fix. Code. Copilot review notice fixed #5.
1 parent 3672f4e commit 1246f05

2 files changed

Lines changed: 80 additions & 3 deletions

File tree

inc/spbc-firewall.php

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
<?php
22

3+
use CleantalkSP\Variables\Get;
4+
use CleantalkSP\Variables\Post;
5+
use CleantalkSP\Variables\Request;
36
use CleantalkSP\Variables\Server;
47
use CleantalkSP\SpbctWP\DB;
58
use CleantalkSP\SpbctWP\Firewall;
@@ -8,6 +11,7 @@
811
use CleantalkSP\SpbctWP\Firewall\TC;
912
use CleantalkSP\SpbctWP\Firewall\WAF;
1013
use CleantalkSP\SpbctWP\Firewall\WafBlocker;
14+
use CleantalkSP\SpbctWP\Firewall\UploadChecker;
1115
use CleantalkSP\SpbctWP\Helpers\IP;
1216
use CleantalkSP\SpbctWP\Variables\Cookie;
1317
use CleantalkSP\SpbctWP\RenameLoginPage;
@@ -152,7 +156,60 @@ function spbc_upload_checker__check()
152156
{
153157
global $spbc;
154158
if ( $spbc->settings['upload_checker__file_check'] && !empty($_FILES) ) {
155-
$upload_checker = new Firewall\UploadChecker(array(
159+
/** @var WP_Error|null $run_checker_error */
160+
$run_checker_error = null;
161+
if (is_user_logged_in()) {
162+
if (is_admin()) {
163+
$action = Post::getString('action') ?: Get::getString('action') ?: '';
164+
if ($action === 'upload-plugin' || $action === 'upload-theme') {
165+
if ($action === 'upload-plugin') {
166+
if (!wp_verify_nonce(Request::getString('_wpnonce') ?: '', 'plugin-upload')) {
167+
// Install plugins interface - exit if nonce is wrong
168+
$run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files.', 'security-malware-firewall'));
169+
}
170+
if (!current_user_can('install_plugins')) {
171+
// Install plugins interface - exit if no permission to do that
172+
$run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files.', 'security-malware-firewall'));
173+
}
174+
}
175+
if ($action === 'upload-theme') {
176+
if (!wp_verify_nonce(Request::getString('_wpnonce') ?: '', 'theme-upload')) {
177+
// Install themes interface - exit if nonce is wrong
178+
$run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files.', 'security-malware-firewall'));
179+
}
180+
if (!current_user_can('install_themes')) {
181+
// Install themes interface - exit if no permission to do that
182+
$run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files.', 'security-malware-firewall'));
183+
}
184+
}
185+
if (!current_user_can('install_plugins')) {
186+
// Install plugins/themes interface - exit if no permission to do that
187+
$run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files.', 'security-malware-firewall'));
188+
}
189+
} elseif (!current_user_can('upload_files') || !wp_verify_nonce(Request::getString('_wpnonce') ?: '', 'media-form')) {
190+
// Media interface - exit if no permission to uploading
191+
$run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files.', 'security-malware-firewall'));
192+
}
193+
} elseif (!current_user_can('upload_files')) {
194+
// Not admin area - exit if no permission to uploading
195+
$run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files.', 'security-malware-firewall'));
196+
}
197+
}
198+
199+
// RateLimit for all uploads, but permission check only for logged-in users
200+
if ( ! $run_checker_error && UploadChecker::hasRateOverlimit() ) {
201+
$run_checker_error = new WP_Error(429, __('You have exceeded the upload limit. Please try again later.', 'security-malware-firewall'));
202+
}
203+
204+
if ( $run_checker_error ) {
205+
wp_die(
206+
$run_checker_error->get_error_message(),
207+
__('Upload Checker Exceeded', 'security-malware-firewall'),
208+
array('response' => $run_checker_error->get_error_code())
209+
);
210+
}
211+
212+
$upload_checker = new UploadChecker(array(
156213
'upload_checker__do_check_wordpress_modules' => $spbc->settings['upload_checker__do_check_wordpress_modules'],
157214
'api_key' => $spbc->api_key,
158215
));

lib/CleantalkSP/SpbctWP/Firewall/UploadChecker.php

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,25 @@
22

33
namespace CleantalkSP\SpbctWP\Firewall;
44

5+
use CleantalkSP\Common\RateLimit\RateLimiterConfig;
56
use CleantalkSP\Security\Firewall\Result;
67
use CleantalkSP\SpbctWP\Helpers\Data;
78
use CleantalkSP\SpbctWP\Scanner\FileInfoExtended;
9+
use CleantalkSP\SpbctWP\SpbcRateLimit\SpbcRateLimiter;
810
use CleantalkSP\Variables\Post;
911
use CleantalkSP\Variables\Server;
1012
use FilesystemIterator;
1113

1214
class UploadChecker extends FirewallModule
1315
{
1416
const BINARY_CHECK_THRESHOLD = 0.3; // Threshold for binary file detection, ratio of non-printable characters to total length
17+
18+
const RATE_LIMITER_LIMIT = 5;
19+
20+
const RATE_LIMITER_TIMEFRAME = 600;
21+
22+
const RATE_LIMITER_ACTION = 'spbct_upload_checker_limits';
23+
1524
public $module_name = 'UploadChecker';
1625

1726
/**
@@ -165,9 +174,9 @@ private function runCheckForFilesGlobalVariable($global_files_variable)
165174
Server::get('QUERY_STRING', null, 'url') !== 'action=upload-theme' &&
166175
in_array(Data::getMIMEType($file_path), $this->waf_file_mime_check_zip)
167176
) {
168-
$file_check_result = self::checkUploadedArchive($file_path);
177+
$file_check_result = $this->checkUploadedArchive($file_path);
169178
} else {
170-
$file_check_result = self::checkFileContent($file_path);
179+
$file_check_result = $this->checkFileContent($file_path);
171180
}
172181

173182
// if we have a result, return it immediately
@@ -336,4 +345,15 @@ private function isBinaryFile($file_path)
336345

337346
return $ratio > self::BINARY_CHECK_THRESHOLD;
338347
}
348+
349+
public static function hasRateOverlimit()
350+
{
351+
$rate_limit_config = new RateLimiterConfig(self::RATE_LIMITER_ACTION, self::RATE_LIMITER_LIMIT, self::RATE_LIMITER_TIMEFRAME);
352+
$rate_limiter = new SpbcRateLimiter($rate_limit_config);
353+
$check_passed = $rate_limiter->checkPassed();
354+
if ($rate_limiter->process_ok) {
355+
return !$check_passed;
356+
}
357+
return false; // No limit exceeded by default
358+
}
339359
}

0 commit comments

Comments
 (0)