Added the Search API Synonym module to deal specifically with licence and license...
[yaffs-website] / vendor / twig / twig / test / Twig / Tests / escapingTest.php
1 <?php
2
3 /**
4  * This class is adapted from code coming from Zend Framework.
5  *
6  * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (https://www.zend.com)
7  * @license   https://framework.zend.com/license/new-bsd New BSD License
8  */
9 class Twig_Test_EscapingTest extends \PHPUnit\Framework\TestCase
10 {
11     /**
12      * All character encodings supported by htmlspecialchars().
13      */
14     protected $htmlSpecialChars = array(
15         '\'' => '&#039;',
16         '"' => '&quot;',
17         '<' => '&lt;',
18         '>' => '&gt;',
19         '&' => '&amp;',
20     );
21
22     protected $htmlAttrSpecialChars = array(
23         '\'' => '&#x27;',
24         /* Characters beyond ASCII value 255 to unicode escape */
25         'Ā' => '&#x0100;',
26         /* Immune chars excluded */
27         ',' => ',',
28         '.' => '.',
29         '-' => '-',
30         '_' => '_',
31         /* Basic alnums excluded */
32         'a' => 'a',
33         'A' => 'A',
34         'z' => 'z',
35         'Z' => 'Z',
36         '0' => '0',
37         '9' => '9',
38         /* Basic control characters and null */
39         "\r" => '&#x0D;',
40         "\n" => '&#x0A;',
41         "\t" => '&#x09;',
42         "\0" => '&#xFFFD;', // should use Unicode replacement char
43         /* Encode chars as named entities where possible */
44         '<' => '&lt;',
45         '>' => '&gt;',
46         '&' => '&amp;',
47         '"' => '&quot;',
48         /* Encode spaces for quoteless attribute protection */
49         ' ' => '&#x20;',
50     );
51
52     protected $jsSpecialChars = array(
53         /* HTML special chars - escape without exception to hex */
54         '<' => '\\u003C',
55         '>' => '\\u003E',
56         '\'' => '\\u0027',
57         '"' => '\\u0022',
58         '&' => '\\u0026',
59         '/' => '\\/',
60         /* Characters beyond ASCII value 255 to unicode escape */
61         'Ā' => '\\u0100',
62         '😀' => '\\uD83D\\uDE00',
63         /* Immune chars excluded */
64         ',' => ',',
65         '.' => '.',
66         '_' => '_',
67         /* Basic alnums excluded */
68         'a' => 'a',
69         'A' => 'A',
70         'z' => 'z',
71         'Z' => 'Z',
72         '0' => '0',
73         '9' => '9',
74         /* Basic control characters and null */
75         "\r" => '\r',
76         "\n" => '\n',
77         "\x08" => '\b',
78         "\t" => '\t',
79         "\x0C" => '\f',
80         "\0" => '\\u0000',
81         /* Encode spaces for quoteless attribute protection */
82         ' ' => '\\u0020',
83     );
84
85     protected $urlSpecialChars = array(
86         /* HTML special chars - escape without exception to percent encoding */
87         '<' => '%3C',
88         '>' => '%3E',
89         '\'' => '%27',
90         '"' => '%22',
91         '&' => '%26',
92         /* Characters beyond ASCII value 255 to hex sequence */
93         'Ā' => '%C4%80',
94         /* Punctuation and unreserved check */
95         ',' => '%2C',
96         '.' => '.',
97         '_' => '_',
98         '-' => '-',
99         ':' => '%3A',
100         ';' => '%3B',
101         '!' => '%21',
102         /* Basic alnums excluded */
103         'a' => 'a',
104         'A' => 'A',
105         'z' => 'z',
106         'Z' => 'Z',
107         '0' => '0',
108         '9' => '9',
109         /* Basic control characters and null */
110         "\r" => '%0D',
111         "\n" => '%0A',
112         "\t" => '%09',
113         "\0" => '%00',
114         /* PHP quirks from the past */
115         ' ' => '%20',
116         '~' => '~',
117         '+' => '%2B',
118     );
119
120     protected $cssSpecialChars = array(
121         /* HTML special chars - escape without exception to hex */
122         '<' => '\\3C ',
123         '>' => '\\3E ',
124         '\'' => '\\27 ',
125         '"' => '\\22 ',
126         '&' => '\\26 ',
127         /* Characters beyond ASCII value 255 to unicode escape */
128         'Ā' => '\\100 ',
129         /* Immune chars excluded */
130         ',' => '\\2C ',
131         '.' => '\\2E ',
132         '_' => '\\5F ',
133         /* Basic alnums excluded */
134         'a' => 'a',
135         'A' => 'A',
136         'z' => 'z',
137         'Z' => 'Z',
138         '0' => '0',
139         '9' => '9',
140         /* Basic control characters and null */
141         "\r" => '\\D ',
142         "\n" => '\\A ',
143         "\t" => '\\9 ',
144         "\0" => '\\0 ',
145         /* Encode spaces for quoteless attribute protection */
146         ' ' => '\\20 ',
147     );
148
149     protected $env;
150
151     protected function setUp()
152     {
153         $this->env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
154     }
155
156     public function testHtmlEscapingConvertsSpecialChars()
157     {
158         foreach ($this->htmlSpecialChars as $key => $value) {
159             $this->assertEquals($value, twig_escape_filter($this->env, $key, 'html'), 'Failed to escape: '.$key);
160         }
161     }
162
163     public function testHtmlAttributeEscapingConvertsSpecialChars()
164     {
165         foreach ($this->htmlAttrSpecialChars as $key => $value) {
166             $this->assertEquals($value, twig_escape_filter($this->env, $key, 'html_attr'), 'Failed to escape: '.$key);
167         }
168     }
169
170     public function testJavascriptEscapingConvertsSpecialChars()
171     {
172         foreach ($this->jsSpecialChars as $key => $value) {
173             $this->assertEquals($value, twig_escape_filter($this->env, $key, 'js'), 'Failed to escape: '.$key);
174         }
175     }
176
177     public function testJavascriptEscapingReturnsStringIfZeroLength()
178     {
179         $this->assertEquals('', twig_escape_filter($this->env, '', 'js'));
180     }
181
182     public function testJavascriptEscapingReturnsStringIfContainsOnlyDigits()
183     {
184         $this->assertEquals('123', twig_escape_filter($this->env, '123', 'js'));
185     }
186
187     public function testCssEscapingConvertsSpecialChars()
188     {
189         foreach ($this->cssSpecialChars as $key => $value) {
190             $this->assertEquals($value, twig_escape_filter($this->env, $key, 'css'), 'Failed to escape: '.$key);
191         }
192     }
193
194     public function testCssEscapingReturnsStringIfZeroLength()
195     {
196         $this->assertEquals('', twig_escape_filter($this->env, '', 'css'));
197     }
198
199     public function testCssEscapingReturnsStringIfContainsOnlyDigits()
200     {
201         $this->assertEquals('123', twig_escape_filter($this->env, '123', 'css'));
202     }
203
204     public function testUrlEscapingConvertsSpecialChars()
205     {
206         foreach ($this->urlSpecialChars as $key => $value) {
207             $this->assertEquals($value, twig_escape_filter($this->env, $key, 'url'), 'Failed to escape: '.$key);
208         }
209     }
210
211     /**
212      * Range tests to confirm escaped range of characters is within OWASP recommendation.
213      */
214
215     /**
216      * Only testing the first few 2 ranges on this prot. function as that's all these
217      * other range tests require.
218      */
219     public function testUnicodeCodepointConversionToUtf8()
220     {
221         $expected = ' ~ޙ';
222         $codepoints = array(0x20, 0x7e, 0x799);
223         $result = '';
224         foreach ($codepoints as $value) {
225             $result .= $this->codepointToUtf8($value);
226         }
227         $this->assertEquals($expected, $result);
228     }
229
230     /**
231      * Convert a Unicode Codepoint to a literal UTF-8 character.
232      *
233      * @param int $codepoint Unicode codepoint in hex notation
234      *
235      * @return string UTF-8 literal string
236      */
237     protected function codepointToUtf8($codepoint)
238     {
239         if ($codepoint < 0x80) {
240             return chr($codepoint);
241         }
242         if ($codepoint < 0x800) {
243             return chr($codepoint >> 6 & 0x3f | 0xc0)
244                 .chr($codepoint & 0x3f | 0x80);
245         }
246         if ($codepoint < 0x10000) {
247             return chr($codepoint >> 12 & 0x0f | 0xe0)
248                 .chr($codepoint >> 6 & 0x3f | 0x80)
249                 .chr($codepoint & 0x3f | 0x80);
250         }
251         if ($codepoint < 0x110000) {
252             return chr($codepoint >> 18 & 0x07 | 0xf0)
253                 .chr($codepoint >> 12 & 0x3f | 0x80)
254                 .chr($codepoint >> 6 & 0x3f | 0x80)
255                 .chr($codepoint & 0x3f | 0x80);
256         }
257         throw new Exception('Codepoint requested outside of Unicode range.');
258     }
259
260     public function testJavascriptEscapingEscapesOwaspRecommendedRanges()
261     {
262         $immune = array(',', '.', '_'); // Exceptions to escaping ranges
263         for ($chr = 0; $chr < 0xFF; ++$chr) {
264             if ($chr >= 0x30 && $chr <= 0x39
265             || $chr >= 0x41 && $chr <= 0x5A
266             || $chr >= 0x61 && $chr <= 0x7A) {
267                 $literal = $this->codepointToUtf8($chr);
268                 $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'js'));
269             } else {
270                 $literal = $this->codepointToUtf8($chr);
271                 if (in_array($literal, $immune)) {
272                     $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'js'));
273                 } else {
274                     $this->assertNotEquals(
275                         $literal,
276                         twig_escape_filter($this->env, $literal, 'js'),
277                         "$literal should be escaped!");
278                 }
279             }
280         }
281     }
282
283     public function testHtmlAttributeEscapingEscapesOwaspRecommendedRanges()
284     {
285         $immune = array(',', '.', '-', '_'); // Exceptions to escaping ranges
286         for ($chr = 0; $chr < 0xFF; ++$chr) {
287             if ($chr >= 0x30 && $chr <= 0x39
288             || $chr >= 0x41 && $chr <= 0x5A
289             || $chr >= 0x61 && $chr <= 0x7A) {
290                 $literal = $this->codepointToUtf8($chr);
291                 $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'html_attr'));
292             } else {
293                 $literal = $this->codepointToUtf8($chr);
294                 if (in_array($literal, $immune)) {
295                     $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'html_attr'));
296                 } else {
297                     $this->assertNotEquals(
298                         $literal,
299                         twig_escape_filter($this->env, $literal, 'html_attr'),
300                         "$literal should be escaped!");
301                 }
302             }
303         }
304     }
305
306     public function testCssEscapingEscapesOwaspRecommendedRanges()
307     {
308         // CSS has no exceptions to escaping ranges
309         for ($chr = 0; $chr < 0xFF; ++$chr) {
310             if ($chr >= 0x30 && $chr <= 0x39
311             || $chr >= 0x41 && $chr <= 0x5A
312             || $chr >= 0x61 && $chr <= 0x7A) {
313                 $literal = $this->codepointToUtf8($chr);
314                 $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'css'));
315             } else {
316                 $literal = $this->codepointToUtf8($chr);
317                 $this->assertNotEquals(
318                     $literal,
319                     twig_escape_filter($this->env, $literal, 'css'),
320                     "$literal should be escaped!");
321             }
322         }
323     }
324 }