`_
-* `Docker `_
diff --git a/features/admin/product/managing_products/preventing_xss_attack_while_adding_new_product.feature b/features/admin/product/managing_products/preventing_xss_attack_while_adding_new_product.feature
new file mode 100644
index 00000000000..8ab868842a6
--- /dev/null
+++ b/features/admin/product/managing_products/preventing_xss_attack_while_adding_new_product.feature
@@ -0,0 +1,21 @@
+@managing_products
+Feature: Preventing a potential XSS attack while adding a new product
+ In order to keep my information safe
+ As an Administrator
+ I want to be protected against the potential XSS attacks
+
+ Background:
+ Given the store operates on a single channel in "United States"
+ And the store has "" taxonomy
+ And the store has "No XSS" taxonomy
+ And I am logged in as an administrator
+
+ @ui @javascript @no-api
+ Scenario: Preventing a potential XSS attack while adding new product
+ When I want to create a new simple product
+ Then I should be able to name it "No XSS" in "English (United States)"
+
+ @ui @javascript @no-api
+ Scenario: Preventing a potential XSS attack while choosing main taxon for a new product
+ When I want to create a new simple product
+ Then I should be able to choose main taxon "No XSS"
diff --git a/features/admin/product/managing_products/preventing_xss_attack_while_editing_product.feature b/features/admin/product/managing_products/preventing_xss_attack_while_editing_product.feature
new file mode 100644
index 00000000000..2b058d1544e
--- /dev/null
+++ b/features/admin/product/managing_products/preventing_xss_attack_while_editing_product.feature
@@ -0,0 +1,16 @@
+@managing_products
+Feature: Preventing a potential XSS attack while selecting similar product
+ In order to keep my information safe
+ As an Administrator
+ I want to be protected against the potential XSS attacks
+
+ Background:
+ Given the store operates on a single channel in "United States"
+ And the store has a product association type "Accessories"
+ And the store has "" and "LG headphones" products
+ And I am logged in as an administrator
+
+ @ui @javascript @no-api
+ Scenario: Preventing a potential XSS attack while editing product
+ When I want to create a new simple product
+ Then I should be able to associate as "Accessories" the "LG headphones" product
diff --git a/features/admin/taxonomy/managing_taxons/preventing_xss_attack_while_adding_new_taxon.feature b/features/admin/taxonomy/managing_taxons/preventing_xss_attack_while_adding_new_taxon.feature
new file mode 100644
index 00000000000..72f507aad82
--- /dev/null
+++ b/features/admin/taxonomy/managing_taxons/preventing_xss_attack_while_adding_new_taxon.feature
@@ -0,0 +1,16 @@
+@managing_taxons
+Feature: Preventing a potential XSS attack while adding a new taxon
+ In order to keep my information safe
+ As an Administrator
+ I want to be protected against the potential XSS attacks
+
+ Background:
+ Given the store operates on a single channel in "United States"
+ And the store has "Category" taxonomy
+ And the store has "" taxonomy
+ And I am logged in as an administrator
+
+ @ui @javascript @no-api
+ Scenario: Preventing a potential XSS attack while adding new taxon
+ When I want to create a new taxon
+ Then I should be able to change its parent taxon to "Category"
diff --git a/features/shop/account/customer_account/address_book/preventing_xss_attack_during_updating_address.feature b/features/shop/account/customer_account/address_book/preventing_xss_attack_during_updating_address.feature
new file mode 100644
index 00000000000..db2cc2adc7f
--- /dev/null
+++ b/features/shop/account/customer_account/address_book/preventing_xss_attack_during_updating_address.feature
@@ -0,0 +1,16 @@
+@address_book
+Feature: Preventing a potential XSS attack during updating the address
+ In order to keep my information safe
+ As a Customer
+ I want to be protected against the potential XSS attacks
+
+ Background:
+ Given the store operates on a single channel in "United States"
+ And I am a logged in customer
+ And I have an address "Lucifer Morningstar", "Seaside Fwy", "90802", "Los Angeles", "United States", "Arkansas" in my address book
+ And this address has province '
">'
+
+ @ui @javascript @no-api
+ Scenario: Preventing a potential XSS attack during updating the address
+ When I want to edit the address of "Lucifer Morningstar"
+ Then I should be able to update it without unexpected alert
diff --git a/features/shop/checkout/addressing_order/preventing_xss_attack_during_checkout.feature b/features/shop/checkout/addressing_order/preventing_xss_attack_during_checkout.feature
new file mode 100644
index 00000000000..f8c11316fd4
--- /dev/null
+++ b/features/shop/checkout/addressing_order/preventing_xss_attack_during_checkout.feature
@@ -0,0 +1,21 @@
+@checkout
+Feature: Preventing a potential XSS attack during updating the address in the checkout
+ In order to keep my information safe
+ As a Visitor
+ I want to be protected against the potential XSS attacks
+
+ Background:
+ Given the store operates on a single channel in "United States"
+ And the store has a product "PHP T-Shirt" priced at "$19.99"
+ And the store ships everywhere for Free
+ And I have product "PHP T-Shirt" in the cart
+ And I am at the checkout addressing step
+
+ @ui @javascript @no-api
+ Scenario: Preventing a potential XSS attack during updating the address in the checkout
+ When I specify the email as "john.doe@example.com"
+ And I specify the billing address as "Ankh Morpork", "Frost Alley", "90210", "United States" for "Jon Doe"
+ And I specify the province name manually as '
">' for billing address
+ And I complete the addressing step
+ And I decide to change my address
+ Then I should be able to update the address without unexpected alert
diff --git a/src/Sylius/Behat/Context/Setup/AddressContext.php b/src/Sylius/Behat/Context/Setup/AddressContext.php
index f810565fff7..82c762964d7 100644
--- a/src/Sylius/Behat/Context/Setup/AddressContext.php
+++ b/src/Sylius/Behat/Context/Setup/AddressContext.php
@@ -66,6 +66,19 @@ public function iHaveAnAddressInAddressBook(ShopUserInterface $user, AddressInte
$customer = $user->getCustomer();
$this->addAddressToCustomer($customer, $address);
+
+ $this->sharedStorage->set('address', $address);
+ }
+
+ /**
+ * @Given this address has province :province
+ */
+ public function thisAddressHasProvince(string $provinceName): void
+ {
+ $address = $this->sharedStorage->get('address');
+ $address->setProvinceName($provinceName);
+
+ $this->customerManager->flush();
}
/**
diff --git a/src/Sylius/Behat/Context/Ui/Admin/ManagingProductsContext.php b/src/Sylius/Behat/Context/Ui/Admin/ManagingProductsContext.php
index 3cdac986314..7a0b6121369 100644
--- a/src/Sylius/Behat/Context/Ui/Admin/ManagingProductsContext.php
+++ b/src/Sylius/Behat/Context/Ui/Admin/ManagingProductsContext.php
@@ -92,6 +92,7 @@ public function iSpecifyItsCodeAs(?string $code = null): void
* @When I do not name it
* @When I name it :name in :language
* @When I rename it to :name in :language
+ * @When I should be able to name it :name in :language
*/
public function iRenameItToIn(?string $name = null, ?string $language = null): void
{
@@ -777,6 +778,7 @@ public function iShouldNotBeAbleToEditItsOptions(): void
/**
* @When /^I choose main (taxon "[^"]+")$/
+ * @Then /^I should be able to choose main (taxon "[^"]+")$/
*/
public function iChooseMainTaxon(TaxonInterface $taxon)
{
@@ -881,6 +883,7 @@ public function iSelectVariantForTheFirstImage(ProductVariantInterface $productV
/**
* @When I associate as :productAssociationType the :productName product
* @When I associate as :productAssociationType the :firstProductName and :secondProductName products
+ * @Then I should be able to associate as :productAssociationType the :productName product
*/
public function iAssociateProductsAsProductAssociation(
ProductAssociationTypeInterface $productAssociationType,
diff --git a/src/Sylius/Behat/Context/Ui/Admin/ManagingTaxonsContext.php b/src/Sylius/Behat/Context/Ui/Admin/ManagingTaxonsContext.php
index 8ba45ca87b5..b5f53a2fa87 100644
--- a/src/Sylius/Behat/Context/Ui/Admin/ManagingTaxonsContext.php
+++ b/src/Sylius/Behat/Context/Ui/Admin/ManagingTaxonsContext.php
@@ -143,6 +143,7 @@ public function iDescribeItAs($description, $language)
/**
* @Given /^I set its (parent taxon to "[^"]+")$/
* @Given /^I change its (parent taxon to "[^"]+")$/
+ * @Then /^I should be able to change its (parent taxon to "[^"]+")$/
*/
public function iChangeItsParentTaxonTo(TaxonInterface $taxon)
{
diff --git a/src/Sylius/Behat/Context/Ui/Shop/AddressBookContext.php b/src/Sylius/Behat/Context/Ui/Shop/AddressBookContext.php
index dd223762044..2b538eead96 100644
--- a/src/Sylius/Behat/Context/Ui/Shop/AddressBookContext.php
+++ b/src/Sylius/Behat/Context/Ui/Shop/AddressBookContext.php
@@ -41,8 +41,9 @@ public function __construct(
/**
* @Given I am editing the address of :fullName
+ * @When I want to edit the address of :fullName
*/
- public function iEditAddressOf($fullName)
+ public function iEditAddressOf(string $fullName): void
{
$this->sharedStorage->set('full_name', $fullName);
@@ -350,6 +351,14 @@ public function addressShouldBeMarkedAsMyDefaultAddress(AddressInterface $addres
Assert::same($actualFullName, $expectedFullName);
}
+ /**
+ * @Then I should be able to update it without unexpected alert
+ */
+ public function iShouldBeAbleToUpdateItWithoutUnexpectedAlert(): void
+ {
+ $this->addressBookUpdatePage->waitForFormToStopLoading();
+ }
+
/**
* @param string $fullName
*
diff --git a/src/Sylius/Behat/Context/Ui/Shop/Checkout/CheckoutAddressingContext.php b/src/Sylius/Behat/Context/Ui/Shop/Checkout/CheckoutAddressingContext.php
index c2ed635eb6c..10577201501 100644
--- a/src/Sylius/Behat/Context/Ui/Shop/Checkout/CheckoutAddressingContext.php
+++ b/src/Sylius/Behat/Context/Ui/Shop/Checkout/CheckoutAddressingContext.php
@@ -484,6 +484,14 @@ public function shouldHaveCountriesToChooseFrom(string ...$countries): void
Assert::same($availableBillingCountries, $countries);
}
+ /**
+ * @Then I should be able to update the address without unexpected alert
+ */
+ public function iShouldBeAbleToUpdateTheAddressWithoutUnexpectedAlert(): void
+ {
+ $this->addressPage->waitForFormToStopLoading();
+ }
+
/**
* @return AddressInterface
*/
diff --git a/src/Sylius/Behat/Page/Shop/Account/AddressBook/UpdatePage.php b/src/Sylius/Behat/Page/Shop/Account/AddressBook/UpdatePage.php
index 5cda3d3606f..5308a0f4278 100644
--- a/src/Sylius/Behat/Page/Shop/Account/AddressBook/UpdatePage.php
+++ b/src/Sylius/Behat/Page/Shop/Account/AddressBook/UpdatePage.php
@@ -71,6 +71,11 @@ public function selectCountry(string $name): void
JQueryHelper::waitForFormToStopLoading($this->getDocument());
}
+ public function waitForFormToStopLoading(): void
+ {
+ JQueryHelper::waitForFormToStopLoading($this->getDocument());
+ }
+
public function saveChanges(): void
{
JQueryHelper::waitForFormToStopLoading($this->getDocument());
diff --git a/src/Sylius/Behat/Page/Shop/Account/AddressBook/UpdatePageInterface.php b/src/Sylius/Behat/Page/Shop/Account/AddressBook/UpdatePageInterface.php
index 336354eca79..5988a1da23f 100644
--- a/src/Sylius/Behat/Page/Shop/Account/AddressBook/UpdatePageInterface.php
+++ b/src/Sylius/Behat/Page/Shop/Account/AddressBook/UpdatePageInterface.php
@@ -29,5 +29,7 @@ public function selectProvince(string $name): void;
public function selectCountry(string $name): void;
+ public function waitForFormToStopLoading(): void;
+
public function saveChanges(): void;
}
diff --git a/src/Sylius/Behat/Page/Shop/Checkout/AddressPage.php b/src/Sylius/Behat/Page/Shop/Checkout/AddressPage.php
index 89fea14f9f5..26aea7708ed 100644
--- a/src/Sylius/Behat/Page/Shop/Checkout/AddressPage.php
+++ b/src/Sylius/Behat/Page/Shop/Checkout/AddressPage.php
@@ -281,6 +281,11 @@ public function getAvailableBillingCountries(): array
return $this->getOptionsFromSelect($this->getElement('billing_country'));
}
+ public function waitForFormToStopLoading(): void
+ {
+ JQueryHelper::waitForFormToStopLoading($this->getDocument());
+ }
+
protected function getDefinedElements(): array
{
return array_merge(parent::getDefinedElements(), [
diff --git a/src/Sylius/Behat/Page/Shop/Checkout/AddressPageInterface.php b/src/Sylius/Behat/Page/Shop/Checkout/AddressPageInterface.php
index de0fa8ad7a6..2a787df6f16 100644
--- a/src/Sylius/Behat/Page/Shop/Checkout/AddressPageInterface.php
+++ b/src/Sylius/Behat/Page/Shop/Checkout/AddressPageInterface.php
@@ -81,4 +81,6 @@ public function getAvailableBillingCountries(): array;
public function isDifferentShippingAddressChecked(): bool;
public function isShippingAddressVisible(): bool;
+
+ public function waitForFormToStopLoading(): void;
}
diff --git a/src/Sylius/Bundle/AdminBundle/Resources/private/js/sylius-lazy-choice-tree.js b/src/Sylius/Bundle/AdminBundle/Resources/private/js/sylius-lazy-choice-tree.js
index b1f4dd0d27d..18bb2d3a27e 100644
--- a/src/Sylius/Bundle/AdminBundle/Resources/private/js/sylius-lazy-choice-tree.js
+++ b/src/Sylius/Bundle/AdminBundle/Resources/private/js/sylius-lazy-choice-tree.js
@@ -10,6 +10,7 @@
import 'semantic-ui-css/components/api';
import 'semantic-ui-css/components/checkbox';
import $ from 'jquery';
+import { sanitizeInput } from "sylius/ui/sylius-sanitizer";
const createRootContainer = function createRootContainer() {
return $('');
@@ -81,7 +82,7 @@ $.fn.extend({
onSuccess(response) {
response.forEach((leafNode) => {
leafContainerElement.append((
- createLeafFunc(leafNode.name, leafNode.code, leafNode.hasChildren, multiple, leafNode.level)
+ createLeafFunc(sanitizeInput(leafNode.name), sanitizeInput(leafNode.code), leafNode.hasChildren, multiple, leafNode.level)
));
});
content.append(leafContainerElement);
@@ -169,7 +170,7 @@ $.fn.extend({
const rootContainer = createRootContainer();
response.forEach((rootNode) => {
rootContainer.append((
- createLeaf(rootNode.name, rootNode.code, rootNode.hasChildren, multiple, rootNode.level)
+ createLeaf(sanitizeInput(rootNode.name), sanitizeInput(rootNode.code), rootNode.hasChildren, multiple, rootNode.level)
));
});
tree.append(rootContainer);
diff --git a/src/Sylius/Bundle/ShopBundle/Resources/private/js/sylius-province-field.js b/src/Sylius/Bundle/ShopBundle/Resources/private/js/sylius-province-field.js
index 17b3d350cf0..60983648ab1 100644
--- a/src/Sylius/Bundle/ShopBundle/Resources/private/js/sylius-province-field.js
+++ b/src/Sylius/Bundle/ShopBundle/Resources/private/js/sylius-province-field.js
@@ -8,9 +8,10 @@
*/
import $ from 'jquery';
+import { sanitizeInput } from 'sylius/ui/sylius-sanitizer';
const getProvinceInputValue = function getProvinceInputValue(valueSelector) {
- return valueSelector == undefined ? '' : `value="${valueSelector}"`;
+ return valueSelector == undefined ? '' : `value="${sanitizeInput(valueSelector)}"`;
};
$.fn.extend({
diff --git a/src/Sylius/Bundle/UiBundle/Resources/private/js/sylius-auto-complete.js b/src/Sylius/Bundle/UiBundle/Resources/private/js/sylius-auto-complete.js
index 737c47a55a2..e52a6f42dac 100644
--- a/src/Sylius/Bundle/UiBundle/Resources/private/js/sylius-auto-complete.js
+++ b/src/Sylius/Bundle/UiBundle/Resources/private/js/sylius-auto-complete.js
@@ -9,6 +9,7 @@
import 'semantic-ui-css/components/dropdown';
import $ from 'jquery';
+import { sanitizeInput } from "./sylius-sanitizer";
$.fn.extend({
autoComplete() {
@@ -37,8 +38,8 @@ $.fn.extend({
},
onResponse(response) {
let results = response.map(item => ({
- name: item[choiceName],
- value: item[choiceValue],
+ name: sanitizeInput(item[choiceName]),
+ value: sanitizeInput(item[choiceValue]),
}));
if (!element.hasClass('multiple')) {
@@ -72,7 +73,7 @@ $.fn.extend({
onSuccess(response) {
response.forEach((item) => {
menuElement.append((
- $(`${item[choiceName]}
`)
+ $(`${sanitizeInput(item[choiceName])}
`)
));
});
diff --git a/src/Sylius/Bundle/UiBundle/Resources/private/js/sylius-product-auto-complete.js b/src/Sylius/Bundle/UiBundle/Resources/private/js/sylius-product-auto-complete.js
index acbe655ba32..64e34d9bfb3 100644
--- a/src/Sylius/Bundle/UiBundle/Resources/private/js/sylius-product-auto-complete.js
+++ b/src/Sylius/Bundle/UiBundle/Resources/private/js/sylius-product-auto-complete.js
@@ -9,6 +9,7 @@
import 'semantic-ui-css/components/dropdown';
import $ from 'jquery';
+import { sanitizeInput } from "./sylius-sanitizer";
$.fn.extend({
productAutoComplete() {
@@ -38,8 +39,8 @@ $.fn.extend({
return {
success: true,
results: response._embedded.items.map(item => ({
- name: item.name,
- value: item.code,
+ name: sanitizeInput(item.name),
+ value: sanitizeInput(item.code),
})),
};
},
diff --git a/src/Sylius/Bundle/UiBundle/Resources/private/js/sylius-sanitizer.js b/src/Sylius/Bundle/UiBundle/Resources/private/js/sylius-sanitizer.js
new file mode 100644
index 00000000000..f3dd2cd4cfb
--- /dev/null
+++ b/src/Sylius/Bundle/UiBundle/Resources/private/js/sylius-sanitizer.js
@@ -0,0 +1,5 @@
+export function sanitizeInput(input) {
+ const div = document.createElement('div');
+ div.textContent = input;
+ return div.innerHTML; // Converts text content to plain HTML, stripping any scripts
+}