Skip to content

Commit

Permalink
Merge pull request #12 from BulkGate/security-ajax-authorization
Browse files Browse the repository at this point in the history
Security - Broken Access Control
  • Loading branch information
lukaspijak committed Nov 21, 2023
2 parents 7149b5f + e63bcda commit 0371d4f
Show file tree
Hide file tree
Showing 13 changed files with 103 additions and 22 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
- Tags: sms, viber, rcs, rbm, whatsapp, send sms, sms notifications, order notification, order alert, bulk sms, viber campaign, viber for business, viber business, viber notifications, woocommerce, woocommerce sms, sms woocommerce, sms order, customer notification, notify admin, sms campaign, sms plugin, sms module, eshop, ecommerce, sms marketing, target marketing, woocommerce store, custom hooks, sms api, sms gateway, sms alerts, alerts, notifications, order status, stav objednávky, notifikace, upozornění, sms brána, sms kampaň, viber notifikace, oznámení objednávky, oznámení o objednávce, notifikace objednávky, viber oznámení, viber kampaň, sms objednávka, informační sms, sms notifikace, hromadná sms, sms upozornění, poslat sms, woocommerce sms notifications, woocommerce sms notifikace, affiliate program, woocommerce order sms notifications, woocommerce sms notification plugin, marketing campaigns, 2-way SMS, two way SMS, two way communication, viber notification woocommerce, viber notifications, two-way
- Requires at least: 5.7
- Tested up to: 6.4
- Stable tag: 3.0.0
- Stable tag: 3.0.3
- Requires PHP: 7.4
- License: GPLv3
- License URI: http://www.gnu.org/licenses/gpl-3.0.html
Expand Down Expand Up @@ -162,6 +162,10 @@ Yes. The SMS plugin for WooCommerce communicates with our BulkGate <a href="http


## Changelog
**3.0.3**
* Broken Access Control vulnerability fix
* OptIn Checkbox set default to off
* Order message mutation fix values

**3.0.2**
* Fix asset cron init bug
Expand Down
7 changes: 6 additions & 1 deletion readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Tags: sms, viber, rcs, rbm, whatsapp, send sms, sms notifications, order notification, order alert, bulk sms, viber campaign, viber for business, viber business, viber notifications, woocommerce, woocommerce sms, sms woocommerce, sms order, customer notification, notify admin, sms campaign, sms plugin, sms module, eshop, ecommerce, sms marketing, target marketing, woocommerce store, custom hooks, sms api, sms gateway, sms alerts, alerts, notifications, order status, stav objednávky, notifikace, upozornění, sms brána, sms kampaň, viber notifikace, oznámení objednávky, oznámení o objednávce, notifikace objednávky, viber oznámení, viber kampaň, sms objednávka, informační sms, sms notifikace, hromadná sms, sms upozornění, poslat sms, woocommerce sms notifications, woocommerce sms notifikace, affiliate program, woocommerce order sms notifications, woocommerce sms notification plugin, marketing campaigns, 2-way SMS, two way SMS, two way communication, viber notification woocommerce, viber notifications, two-way
Requires at least: 5.7
Tested up to: 6.4
Stable tag: 3.0.0
Stable tag: 3.0.3
Requires PHP: 7.4
License: GPLv3
License URI: http://www.gnu.org/licenses/gpl-3.0.html
Expand Down Expand Up @@ -160,6 +160,11 @@ Yes. The SMS plugin for WooCommerce communicates with our BulkGate <a href="http

== Changelog ==

= 3.0.3 =
* Broken Access Control vulnerability fix
* OptIn Checkbox set default to off
* Order message mutation fix values

= 3.0.2 =
* Fix asset cron init bug

Expand Down
15 changes: 14 additions & 1 deletion src/Event/Helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
*/

use BulkGate\{Plugin\DI\MissingServiceException, Plugin\Event\Dispatcher, Plugin\Strict, WooSms\DI\Factory};
use function apply_filters, has_filter, str_replace;
use function apply_filters, has_filter, str_replace, current_user_can, wp_die, wp_verify_nonce;

class Helpers
{
use Strict;

public const CrossSiteRequestForgerySecurityParameter = 'security';

public static function dispatch(string $name, callable $callback): callable
{
return function (...$parameters) use ($name, $callback): void
Expand Down Expand Up @@ -48,4 +50,15 @@ public static function resolveOrderStatus(string &$status): string

return $statuses["wc-$status"] ?? $status;
}


public static function checkAccess(?string $nonce): bool
{
if (!current_user_can('manage_options') || wp_verify_nonce($nonce ?? '') === false)
{
wp_die('', 403);
}

return true;
}
}
2 changes: 1 addition & 1 deletion src/Event/OrderForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class OrderForm
{
use Strict;

public const DefaultEnabled = true;
public const DefaultEnabled = false;

private const Consent = [
'en' => 'I consent to receiving marketing communications via SMS, Viber, RCS, WhatsApp, and other similar channels.',
Expand Down
11 changes: 6 additions & 5 deletions src/Template/Basic.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/

use function time, date, admin_url, is_ssl, wp_print_inline_script_tag, wp_print_script_tag;
use BulkGate\{Plugin\Event\Dispatcher, Plugin\IO\Url, Plugin\Settings\Settings, Plugin\Settings\Synchronizer, Plugin\Strict, Plugin\DI\Container, Plugin\User\Sign, WooSms\Utils\Escape, WooSms\Event\OrderForm, WooSms\Utils\Logo};
use BulkGate\{Plugin\Event\Dispatcher, Plugin\IO\Url, Plugin\Settings\Settings, Plugin\Settings\Synchronizer, Plugin\Strict, Plugin\DI\Container, Plugin\User\Sign, WooSms\Event\Helpers, WooSms\Utils\Escape, WooSms\Event\OrderForm, WooSms\Utils\Logo};

class Basic
{
Expand All @@ -21,19 +21,20 @@ public static function print(Container $di): void
$escape_js = [Escape::class, 'js'];

$ajax_url = admin_url('/admin-ajax.php', is_ssl() ? 'https' : 'http');
$csfr_token = wp_create_nonce();

$proxy = [
'PROXY_LOG_IN' => [
'url' => $ajax_url,
'params' => ['action' => 'login']
'params' => ['action' => 'login', Helpers::CrossSiteRequestForgerySecurityParameter => $csfr_token]
],
'PROXY_LOG_OUT' => [
'url' => $ajax_url,
'params' => ['action' => 'logout_module']
'params' => ['action' => 'logout_module', Helpers::CrossSiteRequestForgerySecurityParameter => $csfr_token]
],
'PROXY_SAVE_MODULE_SETTINGS' => [
'url' => $ajax_url,
'params' => ['action' => 'save_module_settings']
'params' => ['action' => 'save_module_settings', Helpers::CrossSiteRequestForgerySecurityParameter => $csfr_token]
]
];

Expand Down Expand Up @@ -77,7 +78,7 @@ function getHeaders(token) {
headers: {
'Content-Type': "application/x-www-form-urlencoded"
},
body: "action=authenticate",
body: {$escape_js('action=authenticate&' . Helpers::CrossSiteRequestForgerySecurityParameter . "=$csfr_token")},
});
let {token, redirect} = await response.json();
Expand Down
31 changes: 22 additions & 9 deletions src/Template/Init.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
* @link https://www.bulkgate.com/
*/

use BulkGate\{Plugin\Debug\Logger, Plugin\Debug\Requirements, Plugin\Eshop, Plugin\Settings\Settings, Plugin\Strict, Plugin\User\Sign, Plugin\Utils\JsonResponse, WooSms\Ajax\Authenticate, WooSms\Ajax\PluginSettingsChange, WooSms\Debug\Page, WooSms\DI\Factory, WooSms\Utils\Logo, WooSms\Utils\Meta};
use BulkGate\Plugin\{Debug\Logger, Debug\Requirements, Eshop, Settings\Settings, Strict, User\Sign, Utils\JsonResponse};
use BulkGate\WooSms\{Ajax\Authenticate, Ajax\PluginSettingsChange, Debug\Page, DI\Factory, Event\Helpers, Utils\Logo, Utils\Meta};
use function method_exists, in_array;

class Init
Expand Down Expand Up @@ -46,15 +47,27 @@ public static function init(): void
}
});

add_action('wp_ajax_authenticate', fn () => Factory::get()->getByClass(Authenticate::class)->run(admin_url('admin.php?page=bulkgate#/sign/in')));
add_action('wp_ajax_authenticate', function (): void
{
Helpers::checkAccess($_POST[Helpers::CrossSiteRequestForgerySecurityParameter] ?? null) && Factory::get()->getByClass(Authenticate::class)->run(admin_url('admin.php?page=bulkgate#/sign/in'));
});

add_action('wp_ajax_login', fn () => JsonResponse::send(Factory::get()->getByClass(Sign::class)->in(
sanitize_text_field((string) ($_POST['__bulkgate']['email'] ?? '')),
sanitize_text_field((string) ($_POST['__bulkgate']['password'] ?? '')),
admin_url('admin.php?page=bulkgate#/dashboard')
)));
add_action('wp_ajax_login', function (): void
{
Helpers::checkAccess($_POST[Helpers::CrossSiteRequestForgerySecurityParameter] ?? null) && JsonResponse::send(Factory::get()->getByClass(Sign::class)->in(
sanitize_text_field((string)($_POST['__bulkgate']['email'] ?? '')),
sanitize_text_field((string)($_POST['__bulkgate']['password'] ?? '')),
admin_url('admin.php?page=bulkgate#/dashboard')
));
});

add_action('wp_ajax_logout_module', fn () => JsonResponse::send(Factory::get()->getByClass(Sign::class)->out(admin_url('admin.php?page=bulkgate#/sign/in'))));
add_action('wp_ajax_save_module_settings', fn () => JsonResponse::send(Factory::get()->getByClass(PluginSettingsChange::class)->run($_POST['__bulkgate'] ?? [])));
add_action('wp_ajax_logout_module', function (): void
{
Helpers::checkAccess($_POST[Helpers::CrossSiteRequestForgerySecurityParameter] ?? null) && JsonResponse::send(Factory::get()->getByClass(Sign::class)->out(admin_url('admin.php?page=bulkgate#/sign/in')));
});
add_action('wp_ajax_save_module_settings', function (): void
{
Helpers::checkAccess($_POST[Helpers::CrossSiteRequestForgerySecurityParameter] ?? null) && JsonResponse::send(Factory::get()->getByClass(PluginSettingsChange::class)->run($_POST['__bulkgate'] ?? []));
});
}
}
17 changes: 17 additions & 0 deletions tests/Event/.mock.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,20 @@ function wc_get_order_statuses(): array
'wc-completed' => 'Completed',
];
}

function current_user_can($capability, ...$args): bool
{
return $capability === 'manage_options';
}


function wp_verify_nonce($capability, ...$args): bool
{
return $capability === 'nonce_token';
}


function wp_die(): void
{
throw new Exception('wp_die() called');
}
10 changes: 9 additions & 1 deletion tests/Event/HelpersTest.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace BulkGate\WooSms\Event\Test;
* @link https://www.bulkgate.com/
*/

use Mockery;
use Mockery, Exception;
use Tester\{Assert, TestCase};
use BulkGate\{Plugin\DI\Container, Plugin\DI\MissingServiceException, Plugin\Event\Dispatcher, WooSms\DI\Factory, WooSms\Event\Helpers, Plugin\Event\Variables};

Expand Down Expand Up @@ -76,6 +76,14 @@ class HelpersTest extends TestCase
Assert::same('Completed', Helpers::resolveOrderStatus($status));
Assert::same('completed', $status);
}


public function testCheckAccess(): void
{
Assert::exception(fn () => Helpers::checkAccess(null), Exception::class);
Assert::exception(fn () => Helpers::checkAccess('xxx'), Exception::class);
Assert::true(Helpers::checkAccess('nonce_token'));
}
}

(new HelpersTest())->run();
4 changes: 3 additions & 1 deletion tests/Event/OrderFormTest.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,13 @@ class OrderFormTest extends TestCase

// ADD ASSET
$container->shouldReceive('getByClass')->with(Settings::class)->once()->andReturn($settings = Mockery::mock(Settings::class));
$settings->shouldReceive('load')->with('main:marketing_message_opt_in_enabled')->once()->andReturnNull();
$settings->shouldReceive('load')->with('main:marketing_message_opt_in_enabled')->once()->andReturnTrue();
$settings->shouldReceive('load')->with('main:marketing_message_opt_in_label')->once()->andReturnNull();
$settings->shouldReceive('load')->with('main:marketing_message_opt_in_default')->once()->andReturnNull();
$settings->shouldReceive('load')->with('main:marketing_message_opt_in_url')->once()->andReturnNull();

Assert::false(OrderForm::DefaultEnabled);

$callbacks = $GLOBALS['order_form_callback'];
$callbacks['action_woocommerce_review_order_before_submit']();

Expand Down
5 changes: 5 additions & 0 deletions tests/Template/.mock-basic.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,8 @@ function esc_attr(string $attr): string
{
return "?_{$attr}_?";
}

function wp_create_nonce(): string
{
return 'nonce_token';
}
13 changes: 13 additions & 0 deletions tests/Template/.mock-init.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,20 @@ function sanitize_text_field(string $text): string
return "$$text$";
}


function wc_get_order(int $id): WC_Order
{
return new WC_Order();
}


function current_user_can($capability, ...$args): bool
{
return $capability === 'manage_options';
}


function wp_verify_nonce($capability, ...$args): bool
{
return true;
}
Loading

0 comments on commit 0371d4f

Please sign in to comment.