123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- <?php
-
- /**
- * This class is adapted from code coming from Zend Framework.
- *
- * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
- * @license http://framework.zend.com/license/new-bsd New BSD License
- */
-
- class Twig_Test_EscapingTest extends PHPUnit_Framework_TestCase
- {
- /**
- * All character encodings supported by htmlspecialchars()
- */
- protected $htmlSpecialChars = array(
- '\'' => ''',
- '"' => '"',
- '<' => '<',
- '>' => '>',
- '&' => '&'
- );
-
- protected $htmlAttrSpecialChars = array(
- '\'' => ''',
- /* Characters beyond ASCII value 255 to unicode escape */
- 'Ā' => 'Ā',
- /* Immune chars excluded */
- ',' => ',',
- '.' => '.',
- '-' => '-',
- '_' => '_',
- /* Basic alnums exluded */
- 'a' => 'a',
- 'A' => 'A',
- 'z' => 'z',
- 'Z' => 'Z',
- '0' => '0',
- '9' => '9',
- /* Basic control characters and null */
- "\r" => '
',
- "\n" => '
',
- "\t" => '	',
- "\0" => '�', // should use Unicode replacement char
- /* Encode chars as named entities where possible */
- '<' => '<',
- '>' => '>',
- '&' => '&',
- '"' => '"',
- /* Encode spaces for quoteless attribute protection */
- ' ' => ' ',
- );
-
- protected $jsSpecialChars = array(
- /* HTML special chars - escape without exception to hex */
- '<' => '\\x3C',
- '>' => '\\x3E',
- '\'' => '\\x27',
- '"' => '\\x22',
- '&' => '\\x26',
- /* Characters beyond ASCII value 255 to unicode escape */
- 'Ā' => '\\u0100',
- /* Immune chars excluded */
- ',' => ',',
- '.' => '.',
- '_' => '_',
- /* Basic alnums exluded */
- 'a' => 'a',
- 'A' => 'A',
- 'z' => 'z',
- 'Z' => 'Z',
- '0' => '0',
- '9' => '9',
- /* Basic control characters and null */
- "\r" => '\\x0D',
- "\n" => '\\x0A',
- "\t" => '\\x09',
- "\0" => '\\x00',
- /* Encode spaces for quoteless attribute protection */
- ' ' => '\\x20',
- );
-
- protected $urlSpecialChars = array(
- /* HTML special chars - escape without exception to percent encoding */
- '<' => '%3C',
- '>' => '%3E',
- '\'' => '%27',
- '"' => '%22',
- '&' => '%26',
- /* Characters beyond ASCII value 255 to hex sequence */
- 'Ā' => '%C4%80',
- /* Punctuation and unreserved check */
- ',' => '%2C',
- '.' => '.',
- '_' => '_',
- '-' => '-',
- ':' => '%3A',
- ';' => '%3B',
- '!' => '%21',
- /* Basic alnums excluded */
- 'a' => 'a',
- 'A' => 'A',
- 'z' => 'z',
- 'Z' => 'Z',
- '0' => '0',
- '9' => '9',
- /* Basic control characters and null */
- "\r" => '%0D',
- "\n" => '%0A',
- "\t" => '%09',
- "\0" => '%00',
- /* PHP quirks from the past */
- ' ' => '%20',
- '~' => '~',
- '+' => '%2B',
- );
-
- protected $cssSpecialChars = array(
- /* HTML special chars - escape without exception to hex */
- '<' => '\\3C ',
- '>' => '\\3E ',
- '\'' => '\\27 ',
- '"' => '\\22 ',
- '&' => '\\26 ',
- /* Characters beyond ASCII value 255 to unicode escape */
- 'Ā' => '\\100 ',
- /* Immune chars excluded */
- ',' => '\\2C ',
- '.' => '\\2E ',
- '_' => '\\5F ',
- /* Basic alnums exluded */
- 'a' => 'a',
- 'A' => 'A',
- 'z' => 'z',
- 'Z' => 'Z',
- '0' => '0',
- '9' => '9',
- /* Basic control characters and null */
- "\r" => '\\D ',
- "\n" => '\\A ',
- "\t" => '\\9 ',
- "\0" => '\\0 ',
- /* Encode spaces for quoteless attribute protection */
- ' ' => '\\20 ',
- );
-
- protected $env;
-
- public function setUp()
- {
- $this->env = new Twig_Environment();
- }
-
- public function testHtmlEscapingConvertsSpecialChars()
- {
- foreach ($this->htmlSpecialChars as $key => $value) {
- $this->assertEquals($value, twig_escape_filter($this->env, $key, 'html'), 'Failed to escape: '.$key);
- }
- }
-
- public function testHtmlAttributeEscapingConvertsSpecialChars()
- {
- foreach ($this->htmlAttrSpecialChars as $key => $value) {
- $this->assertEquals($value, twig_escape_filter($this->env, $key, 'html_attr'), 'Failed to escape: '.$key);
- }
- }
-
- public function testJavascriptEscapingConvertsSpecialChars()
- {
- foreach ($this->jsSpecialChars as $key => $value) {
- $this->assertEquals($value, twig_escape_filter($this->env, $key, 'js'), 'Failed to escape: '.$key);
- }
- }
-
- public function testJavascriptEscapingReturnsStringIfZeroLength()
- {
- $this->assertEquals('', twig_escape_filter($this->env, '', 'js'));
- }
-
- public function testJavascriptEscapingReturnsStringIfContainsOnlyDigits()
- {
- $this->assertEquals('123', twig_escape_filter($this->env, '123', 'js'));
- }
-
- public function testCssEscapingConvertsSpecialChars()
- {
- foreach ($this->cssSpecialChars as $key => $value) {
- $this->assertEquals($value, twig_escape_filter($this->env, $key, 'css'), 'Failed to escape: '.$key);
- }
- }
-
- public function testCssEscapingReturnsStringIfZeroLength()
- {
- $this->assertEquals('', twig_escape_filter($this->env, '', 'css'));
- }
-
- public function testCssEscapingReturnsStringIfContainsOnlyDigits()
- {
- $this->assertEquals('123', twig_escape_filter($this->env, '123', 'css'));
- }
-
- public function testUrlEscapingConvertsSpecialChars()
- {
- foreach ($this->urlSpecialChars as $key => $value) {
- $this->assertEquals($value, twig_escape_filter($this->env, $key, 'url'), 'Failed to escape: '.$key);
- }
- }
-
- /**
- * Range tests to confirm escaped range of characters is within OWASP recommendation
- */
-
- /**
- * Only testing the first few 2 ranges on this prot. function as that's all these
- * other range tests require
- */
- public function testUnicodeCodepointConversionToUtf8()
- {
- $expected = " ~ޙ";
- $codepoints = array(0x20, 0x7e, 0x799);
- $result = '';
- foreach ($codepoints as $value) {
- $result .= $this->codepointToUtf8($value);
- }
- $this->assertEquals($expected, $result);
- }
-
- /**
- * Convert a Unicode Codepoint to a literal UTF-8 character.
- *
- * @param int 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 testJavascriptEscapingEscapesOwaspRecommendedRanges()
- {
- $immune = array(',', '.', '_'); // Exceptions to escaping ranges
- 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, twig_escape_filter($this->env, $literal, 'js'));
- } else {
- $literal = $this->codepointToUtf8($chr);
- if (in_array($literal, $immune)) {
- $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'js'));
- } else {
- $this->assertNotEquals(
- $literal,
- twig_escape_filter($this->env, $literal, 'js'),
- "$literal should be escaped!");
- }
- }
- }
- }
-
- public function testHtmlAttributeEscapingEscapesOwaspRecommendedRanges()
- {
- $immune = array(',', '.', '-', '_'); // Exceptions to escaping ranges
- 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, twig_escape_filter($this->env, $literal, 'html_attr'));
- } else {
- $literal = $this->codepointToUtf8($chr);
- if (in_array($literal, $immune)) {
- $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'html_attr'));
- } else {
- $this->assertNotEquals(
- $literal,
- twig_escape_filter($this->env, $literal, 'html_attr'),
- "$literal should be escaped!");
- }
- }
- }
- }
-
- public function testCssEscapingEscapesOwaspRecommendedRanges()
- {
- $immune = array(); // CSS has no exceptions to escaping ranges
- 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, twig_escape_filter($this->env, $literal, 'css'));
- } else {
- $literal = $this->codepointToUtf8($chr);
- $this->assertNotEquals(
- $literal,
- twig_escape_filter($this->env, $literal, 'css'),
- "$literal should be escaped!");
- }
- }
- }
- }
|