escaper = new Escaper();
$this->zendEscaper = new \Magento\Framework\ZendEscaper();
$this->loggerMock = $this->getMockForAbstractClass(\Psr\Log\LoggerInterface::class);
$objectManagerHelper = new ObjectManager($this);
$objectManagerHelper->setBackwardCompatibleProperty($this->escaper, 'escaper', $this->zendEscaper);
$objectManagerHelper->setBackwardCompatibleProperty($this->escaper, 'logger', $this->loggerMock);
}
/**
* Convert a unicode codepoint to a literal UTF-8 character
*
* @param int $codepoint Unicode codepoint in hex notation
* @return string UTF-8 literal string
*/
protected function codepointToUtf8($codepoint)
{
if ($codepoint < 0x80) {
return chr($codepoint);
}
if ($codepoint < 0x800) {
return chr($codepoint >> 6 & 0x3f | 0xc0)
. chr($codepoint & 0x3f | 0x80);
}
if ($codepoint < 0x10000) {
return chr($codepoint >> 12 & 0x0f | 0xe0)
. chr($codepoint >> 6 & 0x3f | 0x80)
. chr($codepoint & 0x3f | 0x80);
}
if ($codepoint < 0x110000) {
return chr($codepoint >> 18 & 0x07 | 0xf0)
. chr($codepoint >> 12 & 0x3f | 0x80)
. chr($codepoint >> 6 & 0x3f | 0x80)
. chr($codepoint & 0x3f | 0x80);
}
throw new \Exception('Codepoint requested outside of unicode range');
}
public function testEscapeJsEscapesOwaspRecommendedRanges()
{
// Exceptions to escaping ranges
$immune = [',', '.', '_'];
for ($chr = 0; $chr < 0xFF; $chr++) {
if (($chr >= 0x30 && $chr <= 0x39)
|| ($chr >= 0x41 && $chr <= 0x5A)
|| ($chr >= 0x61 && $chr <= 0x7A)
) {
$literal = $this->codepointToUtf8($chr);
$this->assertEquals($literal, $this->escaper->escapeJs($literal));
} else {
$literal = $this->codepointToUtf8($chr);
if (in_array($literal, $immune)) {
$this->assertEquals($literal, $this->escaper->escapeJs($literal));
} else {
$this->assertNotEquals(
$literal,
$this->escaper->escapeJs($literal),
$literal . ' should be escaped!'
);
}
}
}
}
/**
* @param string $data
* @param string $expected
* @dataProvider escapeJsDataProvider
*/
public function testEscapeJs($data, $expected)
{
$this->assertEquals($expected, $this->escaper->escapeJs($data));
}
/**
* @return array
*/
public function escapeJsDataProvider()
{
return [
'zero length string' => ['', ''],
'only digits' => ['123', '123'],
'<' => ['<', '\u003C'],
'>' => ['>', '\\u003E'],
'\'' => ['\'', '\\u0027'],
'"' => ['"', '\\u0022'],
'&' => ['&', '\\u0026'],
'Characters beyond ASCII value 255 to unicode escape' => ['Ā', '\\u0100'],
'Characters beyond Unicode BMP to unicode escape' => ["\xF0\x90\x80\x80", '\\uD800DC00'],
/* Immune chars excluded */
',' => [',', ','],
'.' => ['.', '.'],
'_' => ['_', '_'],
/* Basic alnums exluded */
'a' => ['a', 'a'],
'A' => ['A', 'A'],
'z' => ['z', 'z'],
'Z' => ['Z', 'Z'],
'0' => ['0', '0'],
'9' => ['9', '9'],
/* Basic control characters and null */
"\r" => ["\r", '\\u000D'],
"\n" => ["\n", '\\u000A'],
"\t" => ["\t", '\\u0009'],
"\0" => ["\0", '\\u0000'],
'Encode spaces for quoteless attribute protection' => [' ', '\\u0020'],
];
}
/**
* @covers \Magento\Framework\Escaper::escapeHtml
* @dataProvider escapeHtmlDataProvider
*/
public function testEscapeHtml($data, $expected, $allowedTags = [])
{
$actual = $this->escaper->escapeHtml($data, $allowedTags);
$this->assertEquals($expected, $actual);
}
/**
* @covers \Magento\Framework\Escaper::escapeHtml
* @dataProvider escapeHtmlInvalidDataProvider
*/
public function testEscapeHtmlWithInvalidData($data, $expected, $allowedTags = [])
{
$this->loggerMock->expects($this->once())
->method('critical');
$actual = $this->escaper->escapeHtml($data, $allowedTags);
$this->assertEquals($expected, $actual);
}
/**
* @return array
*/
public function escapeHtmlDataProvider()
{
return [
'array -> [text with no tags, text with no allowed tags]' => [
'data' => ['one', '
"\'&<>"' ',
'expected' => '&
"'&<>"' ',
'allowedTags' => ['br'],
],
'text with multiple allowed tags, includes self closing tag' => [
'data' => 'some text in tags
',
'expected' => 'some text in tags
',
'allowedTags' => ['span', 'br'],
],
'text with multiple allowed tags and allowed attribute in double quotes' => [
'data' => 'Only 2 in stock',
'expected' => 'Only 2 in stock',
'allowedTags' => ['span', 'b'],
],
'text with multiple allowed tags and allowed attribute in single quotes' => [
'data' => 'Only 2 in stock',
'expected' => 'Only 2 in stock',
'allowedTags' => ['span', 'b'],
],
'text with multiple allowed tags with allowed attribute' => [
'data' => 'Only registered users can write reviews. Please Sign in or '
. 'create an account',
'expected' => 'Only registered users can write reviews. Please Sign in or '
. 'create an account',
'allowedTags' => ['a'],
],
'text with not allowed attribute in single quotes' => [
'data' => 'Only 2 in stock',
'expected' => 'Only 2 in stock',
'allowedTags' => ['span', 'b'],
],
'text with allowed and not allowed tags' => [
'data' => 'Only registered users can write reviews. Please Sign inthree '
. 'or create an account',
'expected' => 'Only registered users can write reviews. Please Sign inthree or '
. 'create an account',
'allowedTags' => ['a'],
],
'text with allowed and not allowed tags, with allowed and not allowed attributes' => [
'data' => 'Some test text in span tag text in strong tag '
. 'Click here',
'expected' => 'Some test text in span tag text in strong tag '
. 'Click herealert(1)',
'allowedTags' => ['a', 'span'],
],
'text with html comment' => [
'data' => 'Only 2 in stock ',
'expected' => 'Only 2 in stock ',
'allowedTags' => ['span', 'b'],
],
'text with non ascii characters' => [
'data' => ['абвгд', 'مثال', '幸福'],
'expected' => ['абвгд', 'مثال', '幸福'],
'allowedTags' => [],
],
'html and body tags' => [
'data' => '