Skip to content

Commit

Permalink
Merge pull request #1824 from spryker/feature/cc-11768/dev-security-bugs
Browse files Browse the repository at this point in the history
CC-11768 Security bugs in November
  • Loading branch information
Spryker Release Bot authored Nov 24, 2020
2 parents 0d5e053 + b0c5d76 commit 8048fed
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 8 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"php": ">=7.3",
"spryker-shop/quick-order-page-extension": "^1.1.0",
"spryker-shop/shop-application": "^1.0.0",
"spryker-shop/shop-ui": "^1.28.1",
"spryker-shop/shop-ui": "^1.41.0",
"spryker/application": "^3.0.0",
"spryker/cart": "^4.0.0 || ^5.0.0 || ^7.0.0",
"spryker/kernel": "^3.52.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\Security\Csrf\CsrfToken;

/**
* @method \SprykerShop\Yves\QuickOrderPage\QuickOrderPageFactory getFactory()
Expand All @@ -33,10 +34,12 @@ class QuickOrderController extends AbstractController
public const PARAM_ROW_INDEX = 'row-index';
public const PARAM_QUICK_ORDER_FORM = 'quick_order_form';
protected const PARAM_QUICK_ORDER_FILE_TYPE = 'file-type';
protected const PARAM_FORM_TOKEN = '_token';
protected const MESSAGE_CLEAR_ALL_ROWS_SUCCESS = 'quick-order.message.success.the-form-items-have-been-successfully-cleared';
protected const ERROR_MESSAGE_QUANTITY_INVALID = 'quick-order.errors.quantity-invalid';
protected const MESSAGE_TYPE_WARNING = 'warning';
protected const MESSAGE_PERMISSION_FAILED = 'global.permission.failed';
protected const MESSAGE_FORM_INVALID_CSRF = 'form.csrf.error.text';

/**
* @uses \SprykerShop\Yves\CartPage\Plugin\Router\CartPageRouteProviderPlugin::ROUTE_NAME_CART
Expand All @@ -48,6 +51,11 @@ class QuickOrderController extends AbstractController
*/
protected const ROUTE_NAME_CHECKOUT_INDEX = 'checkout-index';

protected const FLASH_MESSAGE_LIST_TEMPLATE_PATH = '@ShopUi/components/organisms/flash-message-list/flash-message-list.twig';

protected const KEY_CODE = 'code';
protected const KEY_MESSAGES = 'messages';

/**
* @param \Symfony\Component\HttpFoundation\Request $request
*
Expand Down Expand Up @@ -86,15 +94,15 @@ protected function executeQuickOrderFormSubmitAction(Request $request)
->getQuickOrderForm($quickOrderTransfer)
->handleRequest($request);

if ($quickOrderForm->isSubmitted() && $quickOrderForm->isValid()) {
$response = $this->processQuickOrderForm($quickOrderForm, $request);

if ($response !== null) {
return $response;
if (!$quickOrderForm->isSubmitted() || !$quickOrderForm->isValid()) {
foreach ($quickOrderForm->getErrors(true) as $formError) {
$this->addErrorMessage($formError->getMessage());
}

return [];
}

return [];
return $this->processQuickOrderForm($quickOrderForm, $request) ?? [];
}

/**
Expand Down Expand Up @@ -333,6 +341,10 @@ public function deleteRowAction(Request $request)

$viewData = $this->executeDeleteRowAction($request);

if (isset($viewData[static::KEY_CODE])) {
return $this->jsonResponse($viewData);
}

return $this->view(
$viewData,
$this->getFactory()->getQuickOrderPageWidgetPlugins(),
Expand All @@ -351,8 +363,15 @@ protected function executeDeleteRowAction(Request $request): array
{
$rowIndex = $request->get(static::PARAM_ROW_INDEX);
$formData = $request->get(static::PARAM_QUICK_ORDER_FORM);
$formDataItems = $formData['items'] ?? [];

if (!$this->isQuickOrderFormCsrfTokenValid($formData)) {
return $this->createAjaxErrorResponse(
Response::HTTP_BAD_REQUEST,
[static::MESSAGE_FORM_INVALID_CSRF]
);
}

$formDataItems = $formData['items'] ?? [];
if (!isset($formDataItems[$rowIndex])) {
throw new HttpException(Response::HTTP_BAD_REQUEST, '"row-index" is out of the bound.');
}
Expand Down Expand Up @@ -672,4 +691,51 @@ protected function transformProductsViewData(array $productConcreteTransfers): a
->createViewDataTransformer()
->transformProductData($productConcreteTransfers, $this->getFactory()->getQuickOrderFormColumnPlugins());
}

/**
* @param array|null $quickOrderFormData
*
* @return bool
*/
protected function isQuickOrderFormCsrfTokenValid(?array $quickOrderFormData): bool
{
if (!$quickOrderFormData || !isset($quickOrderFormData[static::PARAM_FORM_TOKEN])) {
return false;
}

$csrfToken = $this->createCsrfToken(static::PARAM_QUICK_ORDER_FORM, $quickOrderFormData[static::PARAM_FORM_TOKEN]);

return $this->getFactory()->getCsrfTokenManager()->isTokenValid($csrfToken);
}

/**
* @param string $tokenId
* @param string $value
*
* @return \Symfony\Component\Security\Csrf\CsrfToken
*/
protected function createCsrfToken(string $tokenId, string $value): CsrfToken
{
return new CsrfToken($tokenId, $value);
}

/**
* @param int $code
* @param string[] $messages
*
* @return array
*/
protected function createAjaxErrorResponse(int $code, array $messages): array
{
foreach ($messages as $message) {
$this->addErrorMessage($message);
}

$flashMessageListHtml = $this->renderView(static::FLASH_MESSAGE_LIST_TEMPLATE_PATH)->getContent();

return [
static::KEY_CODE => $code,
static::KEY_MESSAGES => $flashMessageListHtml,
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,14 @@ class QuickOrderPageDependencyProvider extends AbstractBundleDependencyProvider
public const PLUGINS_QUICK_ORDER_UPLOADED_FILE_PARSER = 'PLUGINS_QUICK_ORDER_UPLOADED_FILE_PARSER';
public const PLUGINS_QUICK_ORDER_UPLOADED_FILE_VALIDATOR = 'PLUGINS_QUICK_ORDER_UPLOADED_FILE_VALIDATOR';
public const PLUGINS_QUICK_ORDER_FILE_TEMPLATE = 'PLUGINS_QUICK_ORDER_FILE_TEMPLATE';

public const SERVICE_UTIL_CSV = 'SERVICE_UTIL_CSV';

/**
* @uses \Spryker\Yves\Form\Plugin\Application\FormApplicationPlugin::SERVICE_FORM_CSRF_PROVIDER
*/
public const SERVICE_FORM_CSRF_PROVIDER = 'form.csrf_provider';

/**
* @uses \Spryker\Yves\Http\Plugin\Application\HttpApplicationPlugin::SERVICE_REQUEST_STACK
*/
Expand All @@ -66,6 +72,7 @@ public function provideDependencies(Container $container): Container
$container = $this->addQuickOrderPageWidgetPlugins($container);
$container = $this->addZedRequestClient($container);
$container = $this->addQuickOrderUtilCsvService($container);
$container = $this->addCsrfProviderService($container);
$container = $this->addQuickOrderItemTransferExpanderPlugins($container);
$container = $this->addQuickOrderFormHandlerStrategyPlugins($container);
$container = $this->addQuickOrderFormAdditionalDataColumnProviderPlugins($container);
Expand Down Expand Up @@ -128,6 +135,20 @@ protected function addQuickOrderUtilCsvService(Container $container): Container
return $container;
}

/**
* @param \Spryker\Yves\Kernel\Container $container
*
* @return \Spryker\Yves\Kernel\Container
*/
protected function addCsrfProviderService(Container $container): Container
{
$container->set(static::SERVICE_FORM_CSRF_PROVIDER, function (Container $container) {
return $container->getApplicationService(static::SERVICE_FORM_CSRF_PROVIDER);
});

return $container;
}

/**
* @param \Spryker\Yves\Kernel\Container $container
*
Expand Down
9 changes: 9 additions & 0 deletions src/SprykerShop/Yves/QuickOrderPage/QuickOrderPageFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
use SprykerShop\Yves\QuickOrderPage\ViewDataTransformer\ViewDataTransformerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Validator\Constraints\NotBlank;

/**
Expand Down Expand Up @@ -424,4 +425,12 @@ public function getModuleConfig(): QuickOrderPageConfig
{
return $this->getConfig();
}

/**
* @return \Symfony\Component\Security\Csrf\CsrfTokenManagerInterface
*/
public function getCsrfTokenManager(): CsrfTokenManagerInterface
{
return $this->getProvidedDependency(QuickOrderPageDependencyProvider::SERVICE_FORM_CSRF_PROVIDER);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Component from 'ShopUi/models/component';
import AjaxProvider from 'ShopUi/components/molecules/ajax-provider/ajax-provider';
import { mount } from 'ShopUi/app';
import { EVENT_UPDATE_DYNAMIC_MESSAGES } from 'ShopUi/components/organisms/dynamic-notification-area/dynamic-notification-area';

export default class QuickOrderForm extends Component {
/**
Expand Down Expand Up @@ -97,7 +98,40 @@ export default class QuickOrderForm extends Component {
'row-index': rowIndex
});
const response = await this.removeRowAjaxProvider.fetch(data);
const parsedResponse = this.parseResponse(response);

if (typeof parsedResponse !== 'string') {
this.showFlashMessage(parsedResponse);

return;
}

this.updateTableHtml(response);
}

protected parseResponse(response: string): string|object {
try {
return JSON.parse(response);
} catch {
return response;
}
}

protected hasMessages(response: object): response is { messages: string } {
return 'messages' in response;
}

protected async showFlashMessage(response: object): Promise<void> {
if (!this.hasMessages(response)) {
return;
}
const dynamicNotificationCustomEvent = new CustomEvent(EVENT_UPDATE_DYNAMIC_MESSAGES, {
detail: response.messages,
});
document.dispatchEvent(dynamicNotificationCustomEvent);
}

protected async updateTableHtml(response: string): Promise<void> {
this.rows.innerHTML = response;
await mount();
this.registerRemoveRowTriggers();
Expand Down

0 comments on commit 8048fed

Please sign in to comment.