Yaffs site version 1.1
[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. (http://www.zend.com)
7  * @license   http://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         '<' => '\\x3C',
55         '>' => '\\x3E',
56         '\'' => '\\x27',
57         '"' => '\\x22',
58         '&' => '\\x26',
59         /* Characters beyond ASCII value 255 to unicode escape */
60         'Ā' => '\\u0100',
61         /* Immune chars excluded */
62         ',' => ',',
63         '.' => '.',
64         '_' => '_',
65         /* Basic alnums excluded */
66         'a' => 'a',
67         'A' => 'A',
68         'z' => 'z',
69         'Z' => 'Z',
70         '0' => '0',
71         '9' => '9',
72         /* Basic control characters and null */
73         "\r" => '\\x0D',
74         "\n" => '\\x0A',
75         "\t" => '\\x09',
76         "\0" => '\\x00',
77         /* Encode spaces for quoteless attribute protection */
78         ' ' => '\\x20',
79     );
80
81     protected $urlSpecialChars = array(
82         /* HTML special chars - escape without exception to percent encoding */
83         '<' => '%3C',
84         '>' => '%3E',
85         '\'' => '%27',
86         '"' => '%22',
87         '&' => '%26',
88         /* Characters beyond ASCII value 255 to hex sequence */
89         'Ā' => '%C4%80',
90         /* Punctuation and unreserved check */
91         ',' => '%2C',
92         '.' => '.',
93         '_' => '_',
94         '-' => '-',
95         ':' => '%3A',
96         ';' => '%3B',
97         '!' => '%21',
98         /* Basic alnums excluded */
99         'a' => 'a',
100         'A' => 'A',
101         'z' => 'z',
102         'Z' => 'Z',
103         '0' => '0',
104         '9' => '9',
105         /* Basic control characters and null */
106         "\r" => '%0D',
107         "\n" => '%0A',
108         "\t" => '%09',
109         "\0" => '%00',
110         /* PHP quirks from the past */
111         ' ' => '%20',
112         '~' => '~',
113         '+' => '%2B',
114     );
115
116     protected $cssSpecialChars = array(
117         /* HTML special chars - escape without exception to hex */
118         '<' => '\\3C ',
119         '>' => '\\3E ',
120         '\'' => '\\27 ',
121         '"' => '\\22 ',
122         '&' => '\\26 ',
123         /* Characters beyond ASCII value 255 to unicode escape */
124         'Ā' => '\\100 ',
125         /* Immune chars excluded */
126         ',' => '\\2C ',
127         '.' => '\\2E ',
128         '_' => '\\5F ',
129         /* Basic alnums excluded */
130         'a' => 'a',
131         'A' => 'A',
132         'z' => 'z',
133         'Z' => 'Z',
134         '0' => '0',
135         '9' => '9',
136         /* Basic control characters and null */
137         "\r" => '\\D ',
138         "\n" => '\\A ',
139         "\t" => '\\9 ',
140         "\0" => '\\0 ',
141         /* Encode spaces for quoteless attribute protection */
142         ' ' => '\\20 ',
143     );
144
145     protected $env;
146
147     protected function setUp()
148     {
149         $this->env = new Twig_Environment($this->getMockBuilder('Twig_LoaderInterface')->getMock());
150     }
151
152     public function testHtmlEscapingConvertsSpecialChars()
153     {
154         foreach ($this->htmlSpecialChars as $key => $value) {
155             $this->assertEquals($value, twig_escape_filter($this->env, $key, 'html'), 'Failed to escape: '.$key);
156         }
157     }
158
159     public function testHtmlAttributeEscapingConvertsSpecialChars()
160     {
161         foreach ($this->htmlAttrSpecialChars as $key => $value) {
162             $this->assertEquals($value, twig_escape_filter($this->env, $key, 'html_attr'), 'Failed to escape: '.$key);
163         }
164     }
165
166     public function testJavascriptEscapingConvertsSpecialChars()
167     {
168         foreach ($this->jsSpecialChars as $key => $value) {
169             $this->assertEquals($value, twig_escape_filter($this->env, $key, 'js'), 'Failed to escape: '.$key);
170         }
171     }
172
173     public function testJavascriptEscapingReturnsStringIfZeroLength()
174     {
175         $this->assertEquals('', twig_escape_filter($this->env, '', 'js'));
176     }
177
178     public function testJavascriptEscapingReturnsStringIfContainsOnlyDigits()
179     {
180         $this->assertEquals('123', twig_escape_filter($this->env, '123', 'js'));
181     }
182
183     public function testCssEscapingConvertsSpecialChars()
184     {
185         foreach ($this->cssSpecialChars as $key => $value) {
186             $this->assertEquals($value, twig_escape_filter($this->env, $key, 'css'), 'Failed to escape: '.$key);
187         }
188     }
189
190     public function testCssEscapingReturnsStringIfZeroLength()
191     {
192         $this->assertEquals('', twig_escape_filter($this->env, '', 'css'));
193     }
194
195     public function testCssEscapingReturnsStringIfContainsOnlyDigits()
196     {
197         $this->assertEquals('123', twig_escape_filter($this->env, '123', 'css'));
198     }
199
200     public function testUrlEscapingConvertsSpecialChars()
201     {
202         foreach ($this->urlSpecialChars as $key => $value) {
203             $this->assertEquals($value, twig_escape_filter($this->env, $key, 'url'), 'Failed to escape: '.$key);
204         }
205     }
206
207     /**
208      * Range tests to confirm escaped range of characters is within OWASP recommendation.
209      */
210
211     /**
212      * Only testing the first few 2 ranges on this prot. function as that's all these
213      * other range tests require.
214      */
215     public function testUnicodeCodepointConversionToUtf8()
216     {
217         $expected = ' ~ޙ';
218         $codepoints = array(0x20, 0x7e, 0x799);
219         $result = '';
220         foreach ($codepoints as $value) {
221             $result .= $this->codepointToUtf8($value);
222         }
223         $this->assertEquals($expected, $result);
224     }
225
226     /**
227      * Convert a Unicode Codepoint to a literal UTF-8 character.
228      *
229      * @param int $codepoint Unicode codepoint in hex notation
230      *
231      * @return string UTF-8 literal string
232      */
233     protected function codepointToUtf8($codepoint)
234     {
235         if ($codepoint < 0x80) {
236             return chr($codepoint);
237         }
238         if ($codepoint < 0x800) {
239             return chr($codepoint >> 6 & 0x3f | 0xc0)
240                 .chr($codepoint & 0x3f | 0x80);
241         }
242         if ($codepoint < 0x10000) {
243             return chr($codepoint >> 12 & 0x0f | 0xe0)
244                 .chr($codepoint >> 6 & 0x3f | 0x80)
245                 .chr($codepoint & 0x3f | 0x80);
246         }
247         if ($codepoint < 0x110000) {
248             return chr($codepoint >> 18 & 0x07 | 0xf0)
249                 .chr($codepoint >> 12 & 0x3f | 0x80)
250                 .chr($codepoint >> 6 & 0x3f | 0x80)
251                 .chr($codepoint & 0x3f | 0x80);
252         }
253         throw new Exception('Codepoint requested outside of Unicode range.');
254     }
255
256     public function testJavascriptEscapingEscapesOwaspRecommendedRanges()
257     {
258         $immune = array(',', '.', '_'); // Exceptions to escaping ranges
259         for ($chr = 0; $chr < 0xFF; ++$chr) {
260             if ($chr >= 0x30 && $chr <= 0x39
261             || $chr >= 0x41 && $chr <= 0x5A
262             || $chr >= 0x61 && $chr <= 0x7A) {
263                 $literal = $this->codepointToUtf8($chr);
264                 $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'js'));
265             } else {
266                 $literal = $this->codepointToUtf8($chr);
267                 if (in_array($literal, $immune)) {
268                     $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'js'));
269                 } else {
270                     $this->assertNotEquals(
271                         $literal,
272                         twig_escape_filter($this->env, $literal, 'js'),
273                         "$literal should be escaped!");
274                 }
275             }
276         }
277     }
278
279     public function testHtmlAttributeEscapingEscapesOwaspRecommendedRanges()
280     {
281         $immune = array(',', '.', '-', '_'); // Exceptions to escaping ranges
282         for ($chr = 0; $chr < 0xFF; ++$chr) {
283             if ($chr >= 0x30 && $chr <= 0x39
284             || $chr >= 0x41 && $chr <= 0x5A
285             || $chr >= 0x61 && $chr <= 0x7A) {
286                 $literal = $this->codepointToUtf8($chr);
287                 $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'html_attr'));
288             } else {
289                 $literal = $this->codepointToUtf8($chr);
290                 if (in_array($literal, $immune)) {
291                     $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'html_attr'));
292                 } else {
293                     $this->assertNotEquals(
294                         $literal,
295                         twig_escape_filter($this->env, $literal, 'html_attr'),
296                         "$literal should be escaped!");
297                 }
298             }
299         }
300     }
301
302     public function testCssEscapingEscapesOwaspRecommendedRanges()
303     {
304         // CSS has no exceptions to escaping ranges
305         for ($chr = 0; $chr < 0xFF; ++$chr) {
306             if ($chr >= 0x30 && $chr <= 0x39
307             || $chr >= 0x41 && $chr <= 0x5A
308             || $chr >= 0x61 && $chr <= 0x7A) {
309                 $literal = $this->codepointToUtf8($chr);
310                 $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'css'));
311             } else {
312                 $literal = $this->codepointToUtf8($chr);
313                 $this->assertNotEquals(
314                     $literal,
315                     twig_escape_filter($this->env, $literal, 'css'),
316                     "$literal should be escaped!");
317             }
318         }
319     }
320 }