diff --git a/app/code/Magento/Backend/Block/Widget/Button/SplitButton.php b/app/code/Magento/Backend/Block/Widget/Button/SplitButton.php index e2e83457a571c33cb2cf73ec62cdaa1338a3940a..d2603eebad675005569bec73614a4bccc2e23263 100644 --- a/app/code/Magento/Backend/Block/Widget/Button/SplitButton.php +++ b/app/code/Magento/Backend/Block/Widget/Button/SplitButton.php @@ -229,7 +229,7 @@ class SplitButton extends \Magento\Backend\Block\Widget if ($attributeValue === null || $attributeValue == '') { continue; } - $html[] = $attributeKey . '="' . htmlspecialchars($attributeValue, ENT_COMPAT, 'UTF-8', false) . '"'; + $html[] = $attributeKey . '="' . $this->escapeHtmlAttr($attributeValue, false) . '"'; } return join(' ', $html); } diff --git a/app/code/Magento/Ui/Component/Control/Button.php b/app/code/Magento/Ui/Component/Control/Button.php index 5e407d93e3959b5ff5a0320f75e765e85a704097..fda9a104e037783a0a23a61d6be851caa6902a5e 100644 --- a/app/code/Magento/Ui/Component/Control/Button.php +++ b/app/code/Magento/Ui/Component/Control/Button.php @@ -134,7 +134,7 @@ class Button extends Template implements ControlInterface if ($attributeValue === null || $attributeValue == '') { continue; } - $html .= $attributeKey . '="' . htmlspecialchars($attributeValue, ENT_COMPAT, 'UTF-8', false) . '" '; + $html .= $attributeKey . '="' . $this->escapeHtmlAttr($attributeValue, false) . '" '; } return $html; diff --git a/app/code/Magento/Widget/view/adminhtml/templates/instance/edit/layout.phtml b/app/code/Magento/Widget/view/adminhtml/templates/instance/edit/layout.phtml index 418e3975d2aa25eb0a8232257b543d8eb621ad9b..6158b6adc3a68c0bdee43f03d31e0400ceddf0e9 100644 --- a/app/code/Magento/Widget/view/adminhtml/templates/instance/edit/layout.phtml +++ b/app/code/Magento/Widget/view/adminhtml/templates/instance/edit/layout.phtml @@ -77,10 +77,10 @@ var pageGroupTemplate = '<div class="fieldset-wrapper page_group_container" id=" '<p>' + '<input disabled="disabled" type="text" class="input-text entities" name="widget_instance[<%- data.id %>][<?php /* @escapeNotVerified */ echo $container['name'] ?>][entities]" value="<%- data.<?php /* @escapeNotVerified */ echo $container['name'] ?>_entities %>" readonly="readonly" /> ' + '<a class="widget-option-chooser" href="javascript:void(0)" onclick="WidgetInstance.displayEntityChooser(\'<?php /* @escapeNotVerified */ echo $container['code'] ?>\', \'<?php /* @escapeNotVerified */ echo $container['name'] ?>_ids_<%- data.id %>\')" title="<?php /* @escapeNotVerified */ echo $block->escapeJs(__('Open Chooser')) ?>">' + - '<img src="<?php /* @escapeNotVerified */ echo $block->getViewFileUrl('images/rule_chooser_trigger.gif') ?>" alt="<?php /* @escapeNotVerified */ echo $block->escapeJs(__('Open Chooser')); ?>" />' + + '<img src="<?php echo $block->getViewFileUrl('images/rule_chooser_trigger.gif') ?>" alt="<?php /* @escapeNotVerified */ echo $block->escapeJs(__('Open Chooser')); ?>" />' + '</a> ' + '<a href="javascript:void(0)" onclick="WidgetInstance.hideEntityChooser(\'<?php /* @escapeNotVerified */ echo $container['name'] ?>_ids_<%- data.id %>\')" title="<?php /* @escapeNotVerified */ echo $block->escapeJs(__('Apply')); ?>">' + - '<img src="<?php /* @escapeNotVerified */ echo $block->getViewFileUrl('images/rule_component_apply.gif') ?>" alt="<?php /* @escapeNotVerified */ echo $block->escapeJs(__('Apply')); ?>" />' + + '<img src="<?php echo $block->getViewFileUrl('images/rule_component_apply.gif') ?>" alt="<?php /* @escapeNotVerified */ echo $block->escapeJs(__('Apply')); ?>" />' + '</a>' + '</p>'+ '<div class="chooser"></div>'+ diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php index bc1c4466f626f8ba7b69579d6b2edb8710a787c6..cc7015e3f9902505e2115cfb7dab9e6d025a4d93 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php @@ -273,7 +273,7 @@ class AccountTest extends \Magento\TestFramework\TestCase\AbstractController $this->equalTo([ 'You must confirm your account. Please check your email for the confirmation link or ' . '<a href="http://localhost/index.php/customer/account/confirmation/email/' - . rawurlencode($email) . '/">click here</a> for a new link.' + . 'test2%40email.com/">click here</a> for a new link.' ]), MessageInterface::TYPE_SUCCESS ); diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/_files/xss_safe.phtml b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/_files/xss_safe.phtml index 56fb9a9e46bbd7024d20439cdd71cf46a13d1250..0a83ed0168f4320dd6df391ecd7be186ee146f7a 100644 --- a/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/_files/xss_safe.phtml +++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/_files/xss_safe.phtml @@ -12,7 +12,6 @@ <input name="form_key" type="hidden" value="<?php echo $block->escapeUrl($var) ?>" /> <input name="form_key" type="hidden" value="<?php echo $block->escapeHtmlAttr('value') ?>" /> <input name="form_key" type="hidden" value="<?php echo $block->escapeJs('value') ?>" /> -<input name="form_key" type="hidden" value="<?php echo $block->escapeXssInUrl("some value") ?>" /> echo $var; <?php echo $block->getHtmlId("some value") ?> <?php echo $block->getIdHtml("some value") ?> diff --git a/lib/internal/Magento/Framework/Escaper.php b/lib/internal/Magento/Framework/Escaper.php index 312e61bb6e4b3094ece8fe0d68f242613a7ac263..ecedd676a5601995f1a0a382d550a4a4541e78f5 100644 --- a/lib/internal/Magento/Framework/Escaper.php +++ b/lib/internal/Magento/Framework/Escaper.php @@ -48,11 +48,15 @@ class Escaper * Escape a string for the HTML attribute context * * @param string $string + * @param boolean $escapeSingleQuote * @return string */ - public function escapeHtmlAttr($string) + public function escapeHtmlAttr($string, $escapeSingleQuote = true) { - return $this->getEscaper()->escapeHtmlAttr($string); + if ($escapeSingleQuote) { + return $this->getEscaper()->escapeHtmlAttr($string); + } + return htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false); } /** @@ -66,6 +70,28 @@ class Escaper return $this->escapeHtml($this->escapeXssInUrl($string)); } + /** + * Encode URL + * + * @param string $string + * @return string + */ + public function encodeUrlParam($string) + { + return $this->getEscaper()->escapeUrl($string); + } + + /** + * Encode JSON + * + * @param array $data + * @return string + */ + public function encodeJSON($data) + { + return json_encode($data, JSON_HEX_QUOT | JSON_HEX_APOS); + } + /** * Escape string for the JavaScript context * diff --git a/lib/internal/Magento/Framework/Url.php b/lib/internal/Magento/Framework/Url.php index 1e5de34ad27bc8916f01f84c12e084eeaa099ef9..5ab006dc70275258ed0f5d54527bbd19eb6a5e8a 100644 --- a/lib/internal/Magento/Framework/Url.php +++ b/lib/internal/Magento/Framework/Url.php @@ -174,7 +174,7 @@ class Url extends \Magento\Framework\DataObject implements \Magento\Framework\Ur private $urlModifier; /** - * @var \Magento\Framework\ZendEscaper + * @var \Magento\Framework\Escaper */ private $escaper; @@ -933,7 +933,7 @@ class Url extends \Magento\Framework\DataObject implements \Magento\Framework\Ur } if (!is_null($fragment)) { - $url .= '#' . $this->getEscaper()->escapeUrl($fragment); + $url .= '#' . $this->getEscaper()->encodeUrlParam($fragment); } $this->getRouteParamsResolver()->unsetData('secure'); $this->getRouteParamsResolver()->unsetData('escape_params'); @@ -988,7 +988,7 @@ class Url extends \Magento\Framework\DataObject implements \Magento\Framework\Ur $fragment = $this->_getFragment(); if ($fragment) { - $url .= '#' . $this->getEscaper()->escapeUrl($fragment); + $url .= '#' . $this->getEscaper()->encodeUrlParam($fragment); } return $url; @@ -1182,7 +1182,7 @@ class Url extends \Magento\Framework\DataObject implements \Magento\Framework\Ur { if ($this->escaper == null) { $this->escaper = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\ZendEscaper::class); + ->get(\Magento\Framework\Escaper::class); } return $this->escaper; } diff --git a/lib/internal/Magento/Framework/Url/RouteParamsResolver.php b/lib/internal/Magento/Framework/Url/RouteParamsResolver.php index 3c37ef27a7a28afae6f58ae78f28bcb3842da284..d6d0bd383ffb44bb509aa06514e94d9eedb7107c 100644 --- a/lib/internal/Magento/Framework/Url/RouteParamsResolver.php +++ b/lib/internal/Magento/Framework/Url/RouteParamsResolver.php @@ -32,7 +32,7 @@ class RouteParamsResolver extends \Magento\Framework\DataObject implements Route protected $queryParamsResolver; /** - * @var \Magento\Framework\ZendEscaper + * @var \Magento\Framework\Escaper */ protected $escaper; @@ -112,15 +112,15 @@ class RouteParamsResolver extends \Magento\Framework\DataObject implements Route } else { if (is_object($value)) { if ($this->getData('escape_params')) { - $this->setRouteParam($this->getEscaper()->escapeUrl($key), $value); + $this->setRouteParam($this->getEscaper()->encodeUrlParam($key), $value); } else { $this->setRouteParam($key, $value); } } else { if ($this->getData('escape_params')) { $this->setRouteParam( - $this->getEscaper()->escapeUrl($key), - $this->getEscaper()->escapeUrl($value) + $this->getEscaper()->encodeUrlParam($key), + $this->getEscaper()->encodeUrlParam($value) ); } else { $this->setRouteParam($key, $value); @@ -166,14 +166,14 @@ class RouteParamsResolver extends \Magento\Framework\DataObject implements Route * Get escaper * * @param void - * @return \Magento\Framework\ZendEscaper + * @return \Magento\Framework\Escaper * @deprecated */ private function getEscaper() { if ($this->escaper == null) { $this->escaper = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\ZendEscaper::class); + ->get(\Magento\Framework\Escaper::class); } return $this->escaper; } diff --git a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php index 3e21ff1b762f84019bb8900e85aa087eabd3946f..3a1b69dc64097dc04f44f6b75a1ef670b1f8bf4d 100644 --- a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php +++ b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php @@ -891,11 +891,23 @@ abstract class AbstractBlock extends \Magento\Framework\DataObject implements Bl * Escape a string for the HTML attribute context * * @param string $string + * @param boolean $escapeSingleQuote * @return string */ - public function escapeHtmlAttr($string) + public function escapeHtmlAttr($string, $escapeSingleQuote = true) { - return $this->_escaper->escapeHtmlAttr($string); + return $this->_escaper->escapeHtmlAttr($string, $escapeSingleQuote); + } + + /** + * Encode JSON + * + * @param array $data + * @return string + */ + public function encodeJSON($data) + { + return $this->_escaper->encodeJSON($data); } /**