diff --git a/app/code/Magento/Contact/Model/Mail.php b/app/code/Magento/Contact/Model/Mail.php
index 43c1974252b5a..7735f5d7dd818 100644
--- a/app/code/Magento/Contact/Model/Mail.php
+++ b/app/code/Magento/Contact/Model/Mail.php
@@ -10,6 +10,9 @@
use Magento\Store\Model\StoreManagerInterface;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\Area;
+use Magento\Framework\Validator\GlobalForbiddenPatterns;
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\Exception\LocalizedException;
class Mail implements MailInterface
{
@@ -33,6 +36,11 @@ class Mail implements MailInterface
*/
private $storeManager;
+ /**
+ * @var GlobalForbiddenPatterns
+ */
+ private $forbiddenPatternsValidator;
+
/**
* Initialize dependencies.
*
@@ -40,17 +48,20 @@ class Mail implements MailInterface
* @param TransportBuilder $transportBuilder
* @param StateInterface $inlineTranslation
* @param StoreManagerInterface|null $storeManager
+ * @param GlobalForbiddenPatterns $forbiddenPatternsValidator
*/
public function __construct(
ConfigInterface $contactsConfig,
TransportBuilder $transportBuilder,
StateInterface $inlineTranslation,
- StoreManagerInterface $storeManager = null
+ StoreManagerInterface $storeManager = null,
+ GlobalForbiddenPatterns $forbiddenPatternsValidator
) {
$this->contactsConfig = $contactsConfig;
$this->transportBuilder = $transportBuilder;
$this->inlineTranslation = $inlineTranslation;
$this->storeManager = $storeManager ?: ObjectManager::getInstance()->get(StoreManagerInterface::class);
+ $this->forbiddenPatternsValidator = $forbiddenPatternsValidator;
}
/**
@@ -59,9 +70,24 @@ public function __construct(
* @param string $replyTo
* @param array $variables
* @return void
+ * @throws LocalizedException
*/
public function send($replyTo, array $variables)
{
+ $validationErrors = [];
+ $fieldsToValidate = [
+ 'name' => $variables['data']['name'] ?? '',
+ 'comment' => $variables['data']['comment'] ?? '',
+ 'email' => $variables['data']['email'] ?? '',
+ ];
+ $this->forbiddenPatternsValidator->validateData($fieldsToValidate, $validationErrors);
+
+ if (!empty($validationErrors)) {
+ throw new \Magento\Framework\Exception\LocalizedException(
+ __(implode("\n", $validationErrors))
+ );
+ }
+
/** @see \Magento\Contact\Controller\Index\Post::validatedParams() */
$replyToName = !empty($variables['data']['name']) ? $variables['data']['name'] : null;
diff --git a/app/code/Magento/Customer/Model/Validator/City.php b/app/code/Magento/Customer/Model/Validator/City.php
index 0b53551dfd88f..6ff8665f32a83 100644
--- a/app/code/Magento/Customer/Model/Validator/City.php
+++ b/app/code/Magento/Customer/Model/Validator/City.php
@@ -9,6 +9,7 @@
use Magento\Customer\Model\Customer;
use Magento\Framework\Validator\AbstractValidator;
+use Magento\Framework\Validator\GlobalCityValidator;
/**
* Customer city fields validator.
@@ -16,46 +17,34 @@
class City extends AbstractValidator
{
/**
- * Allowed characters:
- *
- * \p{L}: Unicode letters.
- * \p{M}: Unicode marks (diacritic marks, accents, etc.).
- * ': Apostrophe mark.
- * \s: Whitespace characters (spaces, tabs, newlines, etc.).
+ * @var GlobalCityValidator
*/
- private const PATTERN_CITY = '/(?:[\p{L}\p{M}\s\-\']{1,100})/u';
+ private $cityValidator;
/**
- * Validate city fields.
+ * City constructor.
*
- * @param Customer $customer
- * @return bool
+ * @param GlobalCityValidator $cityValidator
*/
- public function isValid($customer)
+ public function __construct(GlobalCityValidator $cityValidator)
{
- if (!$this->isValidCity($customer->getCity())) {
- parent::_addMessages([[
- 'city' => "Invalid City. Please use A-Z, a-z, 0-9, -, ', spaces"
- ]]);
- }
-
- return count($this->_messages) == 0;
+ $this->cityValidator = $cityValidator;
}
/**
- * Check if city field is valid.
+ * Validate city fields.
*
- * @param string|null $cityValue
+ * @param Customer $customer
* @return bool
*/
- private function isValidCity($cityValue)
+ public function isValid($customer): bool
{
- if ($cityValue != null) {
- if (preg_match(self::PATTERN_CITY, $cityValue, $matches)) {
- return $matches[0] == $cityValue;
- }
+ if (!$this->cityValidator->isValidCity($customer->getCity())) {
+ parent::_addMessages([[
+ 'city' => __("Invalid City. Please use only A-Z, a-z, 0-9, spaces, commas, -, ., ', &, [], ().")
+ ]]);
}
- return true;
+ return count($this->_messages) == 0;
}
}
diff --git a/app/code/Magento/Customer/Model/Validator/Name.php b/app/code/Magento/Customer/Model/Validator/Name.php
index 75d460358970c..3a2f05bad4989 100644
--- a/app/code/Magento/Customer/Model/Validator/Name.php
+++ b/app/code/Magento/Customer/Model/Validator/Name.php
@@ -9,13 +9,27 @@
use Magento\Customer\Model\Customer;
use Magento\Framework\Validator\AbstractValidator;
+use Magento\Framework\Validator\GlobalNameValidator;
/**
* Customer name fields validator.
*/
class Name extends AbstractValidator
{
- private const PATTERN_NAME = '/(?:[\p{L}\p{M}\,\-\_\.\'’`&\s\d]){1,255}+/u';
+ /**
+ * @var GlobalNameValidator
+ */
+ private $nameValidator;
+
+ /**
+ * Name constructor.
+ *
+ * @param GlobalNameValidator $nameValidator
+ */
+ public function __construct(GlobalNameValidator $nameValidator)
+ {
+ $this->nameValidator = $nameValidator;
+ }
/**
* Validate name fields.
@@ -25,35 +39,18 @@ class Name extends AbstractValidator
*/
public function isValid($customer)
{
- if (!$this->isValidName($customer->getFirstname())) {
- parent::_addMessages([['firstname' => 'First Name is not valid!']]);
+ if (!$this->nameValidator->isValidName($customer->getFirstname())) {
+ parent::_addMessages([['firstname' => __('First Name is not valid!')]]);
}
- if (!$this->isValidName($customer->getLastname())) {
- parent::_addMessages([['lastname' => 'Last Name is not valid!']]);
+ if (!$this->nameValidator->isValidName($customer->getLastname())) {
+ parent::_addMessages([['lastname' => __('Last Name is not valid!')]]);
}
- if (!$this->isValidName($customer->getMiddlename())) {
- parent::_addMessages([['middlename' => 'Middle Name is not valid!']]);
+ if (!$this->nameValidator->isValidName($customer->getMiddlename())) {
+ parent::_addMessages([['middlename' => __('Middle Name is not valid!')]]);
}
return count($this->_messages) == 0;
}
-
- /**
- * Check if name field is valid.
- *
- * @param string|null $nameValue
- * @return bool
- */
- private function isValidName($nameValue)
- {
- if ($nameValue != null) {
- if (preg_match(self::PATTERN_NAME, $nameValue, $matches)) {
- return $matches[0] == $nameValue;
- }
- }
-
- return true;
- }
}
diff --git a/app/code/Magento/Customer/Model/Validator/Street.php b/app/code/Magento/Customer/Model/Validator/Street.php
index 7de57d0ed32ef..96e5d92e667f7 100644
--- a/app/code/Magento/Customer/Model/Validator/Street.php
+++ b/app/code/Magento/Customer/Model/Validator/Street.php
@@ -9,6 +9,7 @@
use Magento\Customer\Model\Customer;
use Magento\Framework\Validator\AbstractValidator;
+use Magento\Framework\Validator\GlobalStreetValidator;
/**
* Customer street fields validator.
@@ -16,19 +17,19 @@
class Street extends AbstractValidator
{
/**
- * Allowed characters:
+ * @var GlobalStreetValidator
+ */
+ private $streetValidator;
+
+ /**
+ * Street constructor.
*
- * \p{L}: Unicode letters.
- * \p{M}: Unicode marks (diacritic marks, accents, etc.).
- * ,: Comma.
- * -: Hyphen.
- * .: Period.
- * `'’: Single quotes, both regular and right single quotation marks.
- * &: Ampersand.
- * \s: Whitespace characters (spaces, tabs, newlines, etc.).
- * \d: Digits (0-9).
+ * @param GlobalStreetValidator $streetValidator
*/
- private const PATTERN_STREET = "/(?:[\p{L}\p{M}\"[],-.'’`&\s\d]){1,255}+/u";
+ public function __construct(GlobalStreetValidator $streetValidator)
+ {
+ $this->streetValidator = $streetValidator;
+ }
/**
* Validate street fields.
@@ -36,33 +37,19 @@ class Street extends AbstractValidator
* @param Customer $customer
* @return bool
*/
- public function isValid($customer)
+ public function isValid($customer): bool
{
foreach ($customer->getStreet() as $street) {
- if (!$this->isValidStreet($street)) {
+ if (!$this->streetValidator->isValidStreet($street)) {
parent::_addMessages([[
- 'street' => "Invalid Street Address. Please use A-Z, a-z, 0-9, , - . ' ’ ` & spaces"
+ 'street' => __(
+ "Invalid Street Address. Please use only A-Z, a-z, 0-9, spaces, commas, -, ., ', " .
+ "&, [], ()"
+ )
]]);
}
}
return count($this->_messages) == 0;
}
-
- /**
- * Check if street field is valid.
- *
- * @param string|null $streetValue
- * @return bool
- */
- private function isValidStreet($streetValue)
- {
- if ($streetValue != null) {
- if (preg_match(self::PATTERN_STREET, $streetValue, $matches)) {
- return $matches[0] == $streetValue;
- }
- }
-
- return true;
- }
}
diff --git a/app/code/Magento/Customer/Model/Validator/Telephone.php b/app/code/Magento/Customer/Model/Validator/Telephone.php
index 0c85cb51f7e3d..668d89fc26445 100644
--- a/app/code/Magento/Customer/Model/Validator/Telephone.php
+++ b/app/code/Magento/Customer/Model/Validator/Telephone.php
@@ -9,6 +9,7 @@
use Magento\Customer\Model\Customer;
use Magento\Framework\Validator\AbstractValidator;
+use Magento\Framework\Validator\GlobalPhoneValidation;
/**
* Customer telephone fields validator.
@@ -16,15 +17,20 @@
class Telephone extends AbstractValidator
{
/**
- * Allowed char:
+ * @var GlobalPhoneValidation
+ */
+ private $phoneValidator;
+
+ /**
+ * Telephone constructor.
*
- * \() :Matches open and close parentheses
- * \+: Matches the plus sign.
- * \-: Matches the hyphen.
- * \d: Digits (0-9).
+ * @param GlobalPhoneValidation $phoneValidator
*/
- private const PATTERN_TELEPHONE = '/(?:[\d\s\+\-\()]{1,20})/u';
-
+ public function __construct(GlobalPhoneValidation $phoneValidator)
+ {
+ $this->phoneValidator = $phoneValidator;
+ }
+
/**
* Validate telephone fields.
*
@@ -33,29 +39,12 @@ class Telephone extends AbstractValidator
*/
public function isValid($customer)
{
- if (!$this->isValidTelephone($customer->getTelephone())) {
+ if (!$this->phoneValidator->isValidPhone($customer->getTelephone())) {
parent::_addMessages([[
- 'telephone' => "Invalid Phone Number. Please use 0-9, +, -, (, ) and space."
+ 'telephone' => __('Invalid Phone Number. Please use 0-9, +, -, (), /, and space.')
]]);
}
return count($this->_messages) == 0;
}
-
- /**
- * Check if telephone field is valid.
- *
- * @param string|null $telephoneValue
- * @return bool
- */
- private function isValidTelephone($telephoneValue)
- {
- if ($telephoneValue != null) {
- if (preg_match(self::PATTERN_TELEPHONE, (string) $telephoneValue, $matches)) {
- return $matches[0] == $telephoneValue;
- }
- }
-
- return true;
- }
}
diff --git a/app/code/Magento/Customer/Test/Unit/Model/Validator/CityTest.php b/app/code/Magento/Customer/Test/Unit/Model/Validator/CityTest.php
index 9c15427154fea..01cda4b49fab3 100644
--- a/app/code/Magento/Customer/Test/Unit/Model/Validator/CityTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Model/Validator/CityTest.php
@@ -9,6 +9,7 @@
use Magento\Customer\Model\Validator\City;
use Magento\Customer\Model\Customer;
+use Magento\Framework\Validator\GlobalCityValidator;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
@@ -20,19 +21,25 @@ class CityTest extends TestCase
/**
* @var City
*/
- private City $nameValidator;
+ private City $cityValidator;
/**
* @var Customer|MockObject
*/
private MockObject $customerMock;
+ /**
+ * @var GlobalCityValidator|MockObject
+ */
+ private MockObject $globalCityValidatorMock;
+
/**
* @return void
*/
protected function setUp(): void
{
- $this->nameValidator = new City;
+ $this->globalCityValidatorMock = $this->createMock(GlobalCityValidator::class);
+ $this->cityValidator = new City($this->globalCityValidatorMock);
$this->customerMock = $this
->getMockBuilder(Customer::class)
->disableOriginalConstructor()
@@ -41,45 +48,66 @@ protected function setUp(): void
}
/**
- * Test for allowed apostrophe and other punctuation characters in customer names
+ * Test for allowed punctuation characters in city names
*
* @param string $city
* @param string $message
* @return void
- * @dataProvider expectedPunctuationInNamesDataProvider
+ * @dataProvider expectedPunctuationInCityDataProvider
*/
- public function testValidateCorrectPunctuationInNames(
+ public function testValidateCorrectPunctuationInCity(
string $city,
string $message
) {
$this->customerMock->expects($this->once())->method('getCity')->willReturn($city);
- $isValid = $this->nameValidator->isValid($this->customerMock);
+ $this->globalCityValidatorMock->expects($this->once())
+ ->method('isValidCity')
+ ->with($city)
+ ->willReturn(true);
+
+ $isValid = $this->cityValidator->isValid($this->customerMock);
$this->assertTrue($isValid, $message);
}
/**
* @return array
*/
- public function expectedPunctuationInNamesDataProvider(): array
+ public function expectedPunctuationInCityDataProvider(): array
{
return [
+ [
+ 'city' => 'New York',
+ 'message' => 'Spaces must be allowed in city names'
+ ],
+ [
+ 'city' => 'São Paulo',
+ 'message' => 'Accented characters and spaces must be allowed in city names'
+ ],
+ [
+ 'city' => 'St. Louis',
+ 'message' => 'Periods and spaces must be allowed in city names'
+ ],
[
'city' => 'Москва',
- 'message' => 'Unicode letters must be allowed in city'
+ 'message' => 'Unicode letters must be allowed in city names'
],
[
- 'city' => 'Мо́сква',
- 'message' => 'Unicode marks must be allowed in city'
+ 'city' => 'Moscow \'',
+ 'message' => 'Apostrophe characters must be allowed in city names'
],
[
- 'city' => ' Moscow \'',
- 'message' => 'Apostrophe characters must be allowed in city'
+ 'city' => 'St.-Pierre',
+ 'message' => 'Hyphens must be allowed in city names'
],
[
- 'city' => ' Moscow Moscow',
- 'message' => 'Whitespace characters must be allowed in city'
- ]
+ 'city' => 'Offenbach (Main)',
+ 'message' => 'Parentheses must be allowed in city names'
+ ],
+ [
+ 'city' => 'Rome: The Eternal City',
+ 'message' => 'Colons must be allowed in city names'
+ ],
];
}
}
diff --git a/app/code/Magento/Customer/Test/Unit/Model/Validator/NameTest.php b/app/code/Magento/Customer/Test/Unit/Model/Validator/NameTest.php
index 5033774d54494..c5f953ff44fa7 100644
--- a/app/code/Magento/Customer/Test/Unit/Model/Validator/NameTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Model/Validator/NameTest.php
@@ -8,6 +8,7 @@
namespace Magento\Customer\Test\Unit\Model\Validator;
use Magento\Customer\Model\Validator\Name;
+use Magento\Framework\Validator\GlobalNameValidator;
use Magento\Customer\Model\Customer;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
@@ -22,6 +23,11 @@ class NameTest extends TestCase
*/
private Name $nameValidator;
+ /**
+ * @var GlobalNameValidator|MockObject
+ */
+ private MockObject $globalNameValidatorMock;
+
/**
* @var Customer|MockObject
*/
@@ -32,7 +38,8 @@ class NameTest extends TestCase
*/
protected function setUp(): void
{
- $this->nameValidator = new Name;
+ $this->globalNameValidatorMock = $this->createMock(GlobalNameValidator::class);
+ $this->nameValidator = new Name($this->globalNameValidatorMock);
$this->customerMock = $this
->getMockBuilder(Customer::class)
->disableOriginalConstructor()
@@ -60,6 +67,15 @@ public function testValidateCorrectPunctuationInNames(
$this->customerMock->expects($this->once())->method('getMiddlename')->willReturn($middleName);
$this->customerMock->expects($this->once())->method('getLastname')->willReturn($lastName);
+ // Mock the GlobalNameValidator behavior
+ $this->globalNameValidatorMock->expects($this->exactly(3))
+ ->method('isValidName')
+ ->willReturnMap([
+ [$firstName, true],
+ [$middleName, true],
+ [$lastName, true],
+ ]);
+
$isValid = $this->nameValidator->isValid($this->customerMock);
$this->assertTrue($isValid, $message);
}
@@ -73,25 +89,25 @@ public function expectedPunctuationInNamesDataProvider(): array
[
'firstName' => 'John',
'middleName' => '',
- 'lastNameName' => 'O’Doe',
+ 'lastName' => 'O’Doe',
'message' => 'Inclined apostrophe must be allowed in names (iOS Smart Punctuation compatibility)'
],
[
'firstName' => 'John',
'middleName' => '',
- 'lastNameName' => 'O\'Doe',
+ 'lastName' => 'O\'Doe',
'message' => 'Legacy straight apostrophe must be allowed in names'
],
[
'firstName' => 'John',
'middleName' => '',
- 'lastNameName' => 'O`Doe',
+ 'lastName' => 'O`Doe',
'message' => 'Grave accent back quote character must be allowed in names'
],
[
'firstName' => 'John & Smith',
'middleName' => '',
- 'lastNameName' => 'O`Doe',
+ 'lastName' => 'O`Doe',
'message' => 'Special character ampersand(&) must be allowed in names'
]
];
diff --git a/app/code/Magento/Customer/Test/Unit/Model/Validator/StreetTest.php b/app/code/Magento/Customer/Test/Unit/Model/Validator/StreetTest.php
index 6d40bec460b3e..9a7a91bd750ca 100644
--- a/app/code/Magento/Customer/Test/Unit/Model/Validator/StreetTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Model/Validator/StreetTest.php
@@ -8,6 +8,7 @@
namespace Magento\Customer\Test\Unit\Model\Validator;
use Magento\Customer\Model\Validator\Street;
+use Magento\Framework\Validator\GlobalStreetValidator;
use Magento\Customer\Model\Customer;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
@@ -20,7 +21,12 @@ class StreetTest extends TestCase
/**
* @var Street
*/
- private Street $nameValidator;
+ private Street $streetValidator;
+
+ /**
+ * @var GlobalStreetValidator|MockObject
+ */
+ private MockObject $globalStreetValidatorMock;
/**
* @var Customer|MockObject
@@ -32,7 +38,8 @@ class StreetTest extends TestCase
*/
protected function setUp(): void
{
- $this->nameValidator = new Street;
+ $this->globalStreetValidatorMock = $this->createMock(GlobalStreetValidator::class);
+ $this->streetValidator = new Street($this->globalStreetValidatorMock);
$this->customerMock = $this
->getMockBuilder(Customer::class)
->disableOriginalConstructor()
@@ -41,27 +48,34 @@ protected function setUp(): void
}
/**
- * Test for allowed apostrophe and other punctuation characters in customer names
+ * Test for allowed characters in street addresses
*
* @param array $street
* @param string $message
* @return void
- * @dataProvider expectedPunctuationInNamesDataProvider
+ * @dataProvider expectedPunctuationInStreetDataProvider
*/
- public function testValidateCorrectPunctuationInNames(
+ public function testValidateCorrectPunctuationInStreet(
array $street,
string $message
- ) {
+ ): void {
$this->customerMock->expects($this->once())->method('getStreet')->willReturn($street);
- $isValid = $this->nameValidator->isValid($this->customerMock);
+ // Mock the GlobalStreetValidator behavior
+ $this->globalStreetValidatorMock->expects($this->exactly(count($street)))
+ ->method('isValidStreet')
+ ->willReturn(true);
+
+ $isValid = $this->streetValidator->isValid($this->customerMock);
$this->assertTrue($isValid, $message);
}
/**
+ * Data provider for valid street names
+ *
* @return array
*/
- public function expectedPunctuationInNamesDataProvider(): array
+ public function expectedPunctuationInStreetDataProvider(): array
{
return [
[
@@ -102,7 +116,7 @@ public function expectedPunctuationInNamesDataProvider(): array
'O`Connell Street',
'321 Birch Boulevard ’Willow Retreat’'
],
- 'message' => 'quotes must be allowed in street'
+ 'message' => 'Quotes must be allowed in street'
],
[
'street' => [
@@ -127,6 +141,14 @@ public function expectedPunctuationInNamesDataProvider(): array
'876 Elm Way'
],
'message' => 'Digits must be allowed in street'
+ ],
+ [
+ 'street' => [
+ '1234 Elm St. [Apartment 5]',
+ 'Main St. (Suite 200)',
+ '456 Pine St. [Unit 10]'
+ ],
+ 'message' => 'Square brackets and parentheses must be allowed in street'
]
];
}
diff --git a/app/code/Magento/Customer/Test/Unit/Model/Validator/TelephoneTest.php b/app/code/Magento/Customer/Test/Unit/Model/Validator/TelephoneTest.php
index 47a9d6da18831..75388d3ac5484 100644
--- a/app/code/Magento/Customer/Test/Unit/Model/Validator/TelephoneTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Model/Validator/TelephoneTest.php
@@ -8,6 +8,7 @@
namespace Magento\Customer\Test\Unit\Model\Validator;
use Magento\Customer\Model\Validator\Telephone;
+use Magento\Framework\Validator\GlobalPhoneValidation;
use Magento\Customer\Model\Customer;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
@@ -20,7 +21,12 @@ class TelephoneTest extends TestCase
/**
* @var Telephone
*/
- private Telephone $nameValidator;
+ private Telephone $telephoneValidator;
+
+ /**
+ * @var GlobalPhoneValidation|MockObject
+ */
+ private MockObject $globalPhoneValidationMock;
/**
* @var Customer|MockObject
@@ -28,58 +34,76 @@ class TelephoneTest extends TestCase
private MockObject $customerMock;
/**
+ * Set up the test environment.
+ *
* @return void
*/
protected function setUp(): void
{
- $this->nameValidator = new Telephone;
- $this->customerMock = $this
- ->getMockBuilder(Customer::class)
+ $this->globalPhoneValidationMock = $this->createMock(GlobalPhoneValidation::class);
+ $this->telephoneValidator = new Telephone($this->globalPhoneValidationMock);
+ $this->customerMock = $this->getMockBuilder(Customer::class)
->disableOriginalConstructor()
->addMethods(['getTelephone'])
->getMock();
}
/**
- * Test for allowed apostrophe and other punctuation characters in customer names
+ * Test for allowed characters in customer telephone numbers.
*
* @param string $telephone
* @param string $message
* @return void
- * @dataProvider expectedPunctuationInNamesDataProvider
+ * @dataProvider expectedPunctuationInTelephoneDataProvider
*/
- public function testValidateCorrectPunctuationInNames(
+ public function testValidateCorrectPunctuationInTelephone(
string $telephone,
string $message
) {
$this->customerMock->expects($this->once())->method('getTelephone')->willReturn($telephone);
- $isValid = $this->nameValidator->isValid($this->customerMock);
+ // Mock the GlobalPhoneValidation behavior
+ $this->globalPhoneValidationMock->expects($this->once())
+ ->method('isValidPhone')
+ ->with($telephone)
+ ->willReturn(true);
+
+ $isValid = $this->telephoneValidator->isValid($this->customerMock);
$this->assertTrue($isValid, $message);
}
/**
+ * Data provider for testValidateCorrectPunctuationInTelephone.
+ *
* @return array
*/
- public function expectedPunctuationInNamesDataProvider(): array
+ public function expectedPunctuationInTelephoneDataProvider(): array
{
return [
[
'telephone' => '(1)99887766',
- 'message' => 'parentheses must be allowed in telephone'
+ 'message' => 'Parentheses must be allowed in telephone numbers.'
],
[
'telephone' => '+6255554444',
- 'message' => 'plus sign be allowed in telephone'
+ 'message' => 'Plus sign must be allowed in telephone numbers.'
],
[
'telephone' => '555-555-555',
- 'message' => 'hyphen must be allowed in telephone'
+ 'message' => 'Hyphen must be allowed in telephone numbers.'
],
[
'telephone' => '123456789',
- 'message' => 'Digits (numbers) must be allowed in telephone'
- ]
+ 'message' => 'Digits (numbers) must be allowed in telephone numbers.'
+ ],
+ [
+ 'telephone' => '123 456 789',
+ 'message' => 'Spaces must be allowed in telephone numbers.'
+ ],
+ [
+ 'telephone' => '123/456/789',
+ 'message' => 'Forward slashes must be allowed in telephone numbers.'
+ ],
];
}
}
diff --git a/app/code/Magento/Quote/Model/ValidationRules/AddressValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/AddressValidationRule.php
new file mode 100644
index 0000000000000..453ee82e354a5
--- /dev/null
+++ b/app/code/Magento/Quote/Model/ValidationRules/AddressValidationRule.php
@@ -0,0 +1,109 @@
+forbiddenPatternsValidator = $forbiddenPatternsValidator;
+ $this->nameValidator = $nameValidator;
+ $this->cityValidator = $cityValidator;
+ $this->phoneValidator = $phoneValidator;
+ $this->streetValidator = $streetValidator;
+ }
+
+ /**
+ * Validates the address fields and applies forbidden pattern checks
+ *
+ * @param mixed $address The address object to validate.
+ * @param array &$validationErrors An array to store validation errors.
+ * @return void
+ */
+ public function validateAddress($address, array &$validationErrors): void
+ {
+ // Define the fields to validate with their respective validators
+ $fieldsToValidate = [
+ 'First Name' => [$address->getFirstname(), 'isValidName', $this->nameValidator],
+ 'Middle Name' => [$address->getMiddlename(), 'isValidName', $this->nameValidator],
+ 'Last Name' => [$address->getLastname(), 'isValidName', $this->nameValidator],
+ 'Prefix' => [$address->getPrefix(), 'isValidName', $this->nameValidator],
+ 'Suffix' => [$address->getSuffix(), 'isValidName', $this->nameValidator],
+ 'City' => [$address->getCity(), 'isValidCity', $this->cityValidator],
+ 'Telephone' => [$address->getTelephone(), 'isValidPhone', $this->phoneValidator],
+ 'Fax' => [$address->getFax(), 'isValidPhone', $this->phoneValidator],
+ 'Street' => [$address->getStreet(), 'isValidStreet', $this->streetValidator],
+ ];
+
+ // Validate each field
+ foreach ($fieldsToValidate as $fieldName => [$fieldValue, $validationMethod, $validatorInstance]) {
+ if (is_array($fieldValue)) {
+ foreach ($fieldValue as $value) {
+ if (!$validatorInstance->$validationMethod($value)) {
+ error_log("Invalid value: " . $fieldValue);
+ $validationErrors[] = __("$fieldName is not valid");
+ }
+ }
+ } else {
+ if (!$validatorInstance->$validationMethod($fieldValue)) {
+ error_log("Invalid value: " . $fieldValue);
+ $validationErrors[] = __("$fieldName is not valid");
+ }
+ }
+ }
+
+ if (empty($validationErrors)) {
+ $this->forbiddenPatternsValidator->validateData($address->getData(), $validationErrors);
+ }
+ }
+}
diff --git a/app/code/Magento/Quote/Model/ValidationRules/BillingAddressValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/BillingAddressValidationRule.php
index 465aebdc418ed..4adc857469481 100644
--- a/app/code/Magento/Quote/Model/ValidationRules/BillingAddressValidationRule.php
+++ b/app/code/Magento/Quote/Model/ValidationRules/BillingAddressValidationRule.php
@@ -9,6 +9,7 @@
use Magento\Framework\Validation\ValidationResultFactory;
use Magento\Quote\Model\Quote;
+use Magento\Quote\Model\ValidationRules\AddressValidationRule;
/**
* @inheritdoc
@@ -26,14 +27,24 @@ class BillingAddressValidationRule implements QuoteValidationRuleInterface
private $validationResultFactory;
/**
+ * @var AddressValidationRule
+ */
+ private $addressValidationRule;
+
+ /**
+ * Constructor.
+ *
* @param ValidationResultFactory $validationResultFactory
+ * @param AddressValidationRule $addressValidationRule
* @param string $generalMessage
*/
public function __construct(
ValidationResultFactory $validationResultFactory,
+ AddressValidationRule $addressValidationRule,
string $generalMessage = ''
) {
$this->validationResultFactory = $validationResultFactory;
+ $this->addressValidationRule = $addressValidationRule;
$this->generalMessage = $generalMessage;
}
@@ -43,8 +54,10 @@ public function __construct(
public function validate(Quote $quote): array
{
$validationErrors = [];
+
$billingAddress = $quote->getBillingAddress();
$billingAddress->setStoreId($quote->getStoreId());
+
$validationResult = $billingAddress->validate();
if ($validationResult !== true) {
$validationErrors = [__($this->generalMessage)];
@@ -53,6 +66,10 @@ public function validate(Quote $quote): array
$validationErrors = array_merge($validationErrors, $validationResult);
}
+ if (empty($validationErrors)) {
+ $this->addressValidationRule->validateAddress($billingAddress, $validationErrors);
+ }
+
return [$this->validationResultFactory->create(['errors' => $validationErrors])];
}
}
diff --git a/app/code/Magento/Quote/Model/ValidationRules/NameValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/NameValidationRule.php
new file mode 100644
index 0000000000000..792d556346d03
--- /dev/null
+++ b/app/code/Magento/Quote/Model/ValidationRules/NameValidationRule.php
@@ -0,0 +1,86 @@
+validationResultFactory = $validationResultFactory;
+ $this->nameValidator = $nameValidator;
+ $this->forbiddenPatternsValidator = $forbiddenPatternsValidator;
+ }
+
+ /**
+ * Validate the first name, middle name, last name, prefix, and suffix in the quote.
+ *
+ * @param Quote $quote
+ * @return array
+ */
+ public function validate(Quote $quote): array
+ {
+ $validationErrors = [];
+
+ // Define the fields to validate with their respective validators
+ $fieldsToValidate = [
+ 'First Name' => [$quote->getCustomerFirstname(), 'isValidName', $this->nameValidator],
+ 'Middle Name' => [$quote->getCustomerMiddlename(), 'isValidName', $this->nameValidator],
+ 'Last Name' => [$quote->getCustomerLastname(), 'isValidName', $this->nameValidator],
+ 'Prefix' => [$quote->getCustomerPrefix(), 'isValidName', $this->nameValidator],
+ 'Suffix' => [$quote->getCustomerSuffix(), 'isValidName', $this->nameValidator],
+ ];
+
+ // Validate each field
+ foreach ($fieldsToValidate as $fieldName => [$fieldValue, $validationMethod, $validatorInstance]) {
+ if (!$validatorInstance->$validationMethod($fieldValue)) {
+ $validationErrors[] = __("$fieldName is not valid");
+ }
+ }
+
+ // Perform regex validation only if no other errors exist
+ if (empty($validationErrors)) {
+ $this->forbiddenPatternsValidator->validateData($quote->getData(), $validationErrors);
+ }
+
+ return [$this->validationResultFactory->create(['errors' => $validationErrors])];
+ }
+}
diff --git a/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php b/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php
index 2f215c17e6d73..0ee0f987b3790 100644
--- a/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php
+++ b/app/code/Magento/Quote/Model/ValidationRules/ShippingAddressValidationRule.php
@@ -9,6 +9,7 @@
use Magento\Framework\Validation\ValidationResultFactory;
use Magento\Quote\Model\Quote;
+use Magento\Quote\Model\ValidationRules\AddressValidationRule;
/**
* @inheritdoc
@@ -25,15 +26,23 @@ class ShippingAddressValidationRule implements QuoteValidationRuleInterface
*/
private $validationResultFactory;
+ /**
+ * @var AddressValidationRule
+ */
+ private $addressValidationRule;
+
/**
* @param ValidationResultFactory $validationResultFactory
+ * @param AddressValidationRule $addressValidationRule
* @param string $generalMessage
*/
public function __construct(
ValidationResultFactory $validationResultFactory,
+ AddressValidationRule $addressValidationRule,
string $generalMessage = ''
) {
$this->validationResultFactory = $validationResultFactory;
+ $this->addressValidationRule = $addressValidationRule;
$this->generalMessage = $generalMessage;
}
@@ -47,6 +56,7 @@ public function validate(Quote $quote): array
if (!$quote->isVirtual()) {
$shippingAddress = $quote->getShippingAddress();
$shippingAddress->setStoreId($quote->getStoreId());
+
$validationResult = $shippingAddress->validate();
if ($validationResult !== true) {
$validationErrors = [__($this->generalMessage)];
@@ -54,6 +64,10 @@ public function validate(Quote $quote): array
if (is_array($validationResult)) {
$validationErrors = array_merge($validationErrors, $validationResult);
}
+
+ if (empty($validationErrors)) {
+ $this->addressValidationRule->validateAddress($shippingAddress, $validationErrors);
+ }
}
return [$this->validationResultFactory->create(['errors' => $validationErrors])];
diff --git a/app/code/Magento/Quote/etc/di.xml b/app/code/Magento/Quote/etc/di.xml
index 04be517537b02..a0694ec752395 100644
--- a/app/code/Magento/Quote/etc/di.xml
+++ b/app/code/Magento/Quote/etc/di.xml
@@ -117,6 +117,7 @@
- Magento\Quote\Model\ValidationRules\BillingAddressValidationRule
- Magento\Quote\Model\ValidationRules\PaymentMethodValidationRule
- Magento\Quote\Model\ValidationRules\MinimumAmountValidationRule
+ - Magento\Quote\Model\ValidationRules\NameValidationRule
@@ -145,6 +146,11 @@
Enter a valid payment method and try again.
+
+
+ Please check the name fields.
+
+
diff --git a/app/code/Magento/Review/Model/Review.php b/app/code/Magento/Review/Model/Review.php
index ef2474637f384..28f8ceada7109 100644
--- a/app/code/Magento/Review/Model/Review.php
+++ b/app/code/Magento/Review/Model/Review.php
@@ -14,6 +14,7 @@
use Magento\Framework\Validator\ValidatorChain;
use Magento\Review\Model\ResourceModel\Review\Product\Collection as ProductCollection;
use Magento\Review\Model\ResourceModel\Review\Status\Collection as StatusCollection;
+use Magento\Framework\Validator\GlobalForbiddenPatterns;
/**
* Review model
@@ -125,6 +126,13 @@ class Review extends \Magento\Framework\Model\AbstractModel implements IdentityI
protected $_urlModel;
/**
+ * @var GlobalForbiddenPatterns
+ */
+ private $forbiddenPatternsValidator;
+
+ /**
+ * Constructor.
+ *
* @param \Magento\Framework\Model\Context $context
* @param \Magento\Framework\Registry $registry
* @param \Magento\Review\Model\ResourceModel\Review\Product\CollectionFactory $productFactory
@@ -134,6 +142,7 @@ class Review extends \Magento\Framework\Model\AbstractModel implements IdentityI
* @param \Magento\Review\Model\Review\Summary $reviewSummary
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
* @param \Magento\Framework\UrlInterface $urlModel
+ * @param GlobalForbiddenPatterns $forbiddenPatternsValidator
* @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
* @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
* @param array $data
@@ -149,6 +158,7 @@ public function __construct(
\Magento\Review\Model\Review\Summary $reviewSummary,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Framework\UrlInterface $urlModel,
+ GlobalForbiddenPatterns $forbiddenPatternsValidator,
\Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
\Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
array $data = []
@@ -160,6 +170,7 @@ public function __construct(
$this->_reviewSummary = $reviewSummary;
$this->_storeManager = $storeManager;
$this->_urlModel = $urlModel;
+ $this->forbiddenPatternsValidator = $forbiddenPatternsValidator;
parent::__construct($context, $registry, $resource, $resourceCollection, $data);
}
@@ -292,9 +303,21 @@ public function validate()
$errors[] = __('Please enter a review.');
}
+ // Validate fields with forbidden patterns
+ if (empty($errors)) {
+ $dataToValidate = [
+ 'Title' => $this->getTitle(),
+ 'Nickname' => $this->getNickname(),
+ 'Detail' => $this->getDetail(),
+ ];
+
+ $this->forbiddenPatternsValidator->validateData($dataToValidate, $errors);
+ }
+
if (empty($errors)) {
return true;
}
+
return $errors;
}
diff --git a/app/code/Magento/Security/etc/adminhtml/system.xml b/app/code/Magento/Security/etc/adminhtml/system.xml
index a31e1b1949b1a..afb51f0efeb94 100644
--- a/app/code/Magento/Security/etc/adminhtml/system.xml
+++ b/app/code/Magento/Security/etc/adminhtml/system.xml
@@ -57,6 +57,11 @@
Magento\Security\Model\Config\Backend\Session\SessionSize
Limit the maximum session size in bytes. Use 0 to disable.
+
+
+ Magento\Config\Model\Config\Source\Yesno
+ Activate the extended field regex function.
+
diff --git a/lib/internal/Magento/Framework/Validator/GlobalCityValidator.php b/lib/internal/Magento/Framework/Validator/GlobalCityValidator.php
new file mode 100644
index 0000000000000..524dfd9937def
--- /dev/null
+++ b/lib/internal/Magento/Framework/Validator/GlobalCityValidator.php
@@ -0,0 +1,47 @@
+scopeConfig = $scopeConfig;
+ }
+
+ /**
+ * Returns an array of forbidden patterns.
+ *
+ * @return string[]
+ */
+ public function getPatterns(): array
+ {
+ return [
+ '/{{.*}}/',
+ '/<\?=/',
+ '/<\?php/',
+ '/shell_exec/',
+ '/eval\(/',
+ '/\${IFS%/',
+ '/\bcurl\b/',
+ ];
+ }
+
+ /**
+ * Checks if the given field value is valid according to the forbidden patterns.
+ *
+ * @param mixed $fieldValue
+ * @return bool
+ */
+ public function isValid(mixed $fieldValue): bool
+ {
+ if ($fieldValue === null || $fieldValue === '' || !is_string($fieldValue)) {
+ return true;
+ }
+
+ $fieldValue = trim($fieldValue);
+
+ foreach (self::getPatterns() as $pattern) {
+ if (preg_match($pattern, $fieldValue)) {
+ return false;
+ }
+ }
+
+ // Check if the field contains a base64 encoded string and decode it for further validation
+ if (preg_match('/base64_decode\(/', $fieldValue)) {
+ $decodedValue = base64_decode($fieldValue);
+ // Recursively check the decoded value
+ return self::isValid($decodedValue);
+ }
+
+ return true;
+ }
+
+ /**
+ * Validate all fields in the provided data array based on forbidden patterns.
+ *
+ * @param array $data The data array to be validated.
+ * @param array &$validationErrors An array to collect validation errors.
+ * @return void
+ */
+ public function validateData(
+ array $data,
+ array &$validationErrors
+ ): void {
+ $isRegexEnabled = $this->scopeConfig->isSetFlag(
+ self::XML_PATH_SECURITY_REGEX_ENABLED,
+ ScopeInterface::SCOPE_STORE
+ );
+
+ if ($isRegexEnabled) {
+ foreach ($data as $key => $value) {
+ if (is_string($value) && !$this->isValid($value)) {
+ $validationErrors[] = __("Field %1 contains invalid characters.", $key);
+ }
+ }
+ }
+ }
+}
diff --git a/lib/internal/Magento/Framework/Validator/GlobalNameValidator.php b/lib/internal/Magento/Framework/Validator/GlobalNameValidator.php
new file mode 100644
index 0000000000000..0e5c3ae42f74c
--- /dev/null
+++ b/lib/internal/Magento/Framework/Validator/GlobalNameValidator.php
@@ -0,0 +1,51 @@
+