4 * This class is adapted from code coming from Zend Framework.
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
10 class Twig_Test_EscapingTest
extends PHPUnit_Framework_TestCase
13 * All character encodings supported by htmlspecialchars()
15 protected $htmlSpecialChars = array(
23 protected $htmlAttrSpecialChars = array(
25 /* Characters beyond ASCII value 255 to unicode escape */
27 /* Immune chars excluded */
32 /* Basic alnums excluded */
39 /* Basic control characters and null */
43 "\0" => '�', // should use Unicode replacement char
44 /* Encode chars as named entities where possible */
49 /* Encode spaces for quoteless attribute protection */
53 protected $jsSpecialChars = array(
54 /* HTML special chars - escape without exception to hex */
60 /* Characters beyond ASCII value 255 to unicode escape */
62 /* Immune chars excluded */
66 /* Basic alnums excluded */
73 /* Basic control characters and null */
78 /* Encode spaces for quoteless attribute protection */
82 protected $urlSpecialChars = array(
83 /* HTML special chars - escape without exception to percent encoding */
89 /* Characters beyond ASCII value 255 to hex sequence */
91 /* Punctuation and unreserved check */
99 /* Basic alnums excluded */
106 /* Basic control characters and null */
111 /* PHP quirks from the past */
117 protected $cssSpecialChars = array(
118 /* HTML special chars - escape without exception to hex */
124 /* Characters beyond ASCII value 255 to unicode escape */
126 /* Immune chars excluded */
130 /* Basic alnums excluded */
137 /* Basic control characters and null */
142 /* Encode spaces for quoteless attribute protection */
148 public function setUp()
150 $this->env
= new Twig_Environment();
153 public function testHtmlEscapingConvertsSpecialChars()
155 foreach ($this->htmlSpecialChars
as $key => $value) {
156 $this->assertEquals($value, twig_escape_filter($this->env
, $key, 'html'), 'Failed to escape: '.$key);
160 public function testHtmlAttributeEscapingConvertsSpecialChars()
162 foreach ($this->htmlAttrSpecialChars
as $key => $value) {
163 $this->assertEquals($value, twig_escape_filter($this->env
, $key, 'html_attr'), 'Failed to escape: '.$key);
167 public function testJavascriptEscapingConvertsSpecialChars()
169 foreach ($this->jsSpecialChars
as $key => $value) {
170 $this->assertEquals($value, twig_escape_filter($this->env
, $key, 'js'), 'Failed to escape: '.$key);
174 public function testJavascriptEscapingReturnsStringIfZeroLength()
176 $this->assertEquals('', twig_escape_filter($this->env
, '', 'js'));
179 public function testJavascriptEscapingReturnsStringIfContainsOnlyDigits()
181 $this->assertEquals('123', twig_escape_filter($this->env
, '123', 'js'));
184 public function testCssEscapingConvertsSpecialChars()
186 foreach ($this->cssSpecialChars
as $key => $value) {
187 $this->assertEquals($value, twig_escape_filter($this->env
, $key, 'css'), 'Failed to escape: '.$key);
191 public function testCssEscapingReturnsStringIfZeroLength()
193 $this->assertEquals('', twig_escape_filter($this->env
, '', 'css'));
196 public function testCssEscapingReturnsStringIfContainsOnlyDigits()
198 $this->assertEquals('123', twig_escape_filter($this->env
, '123', 'css'));
201 public function testUrlEscapingConvertsSpecialChars()
203 foreach ($this->urlSpecialChars
as $key => $value) {
204 $this->assertEquals($value, twig_escape_filter($this->env
, $key, 'url'), 'Failed to escape: '.$key);
209 * Range tests to confirm escaped range of characters is within OWASP recommendation
213 * Only testing the first few 2 ranges on this prot. function as that's all these
214 * other range tests require
216 public function testUnicodeCodepointConversionToUtf8()
219 $codepoints = array(0x20, 0x7e, 0x799);
221 foreach ($codepoints as $value) {
222 $result .= $this->codepointToUtf8($value);
224 $this->assertEquals($expected, $result);
228 * Convert a Unicode Codepoint to a literal UTF-8 character.
230 * @param int Unicode codepoint in hex notation
231 * @return string UTF-8 literal string
233 protected function codepointToUtf8($codepoint)
235 if ($codepoint < 0x80) {
236 return chr($codepoint);
238 if ($codepoint < 0x800) {
239 return chr($codepoint >> 6 & 0x3f | 0xc0)
240 . chr($codepoint & 0x3f | 0x80);
242 if ($codepoint < 0x10000) {
243 return chr($codepoint >> 12 & 0x0f | 0xe0)
244 . chr($codepoint >> 6 & 0x3f | 0x80)
245 . chr($codepoint & 0x3f | 0x80);
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);
253 throw new Exception('Codepoint requested outside of Unicode range');
256 public function testJavascriptEscapingEscapesOwaspRecommendedRanges()
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'));
266 $literal = $this->codepointToUtf8($chr);
267 if (in_array($literal, $immune)) {
268 $this->assertEquals($literal, twig_escape_filter($this->env
, $literal, 'js'));
270 $this->assertNotEquals(
272 twig_escape_filter($this->env
, $literal, 'js'),
273 "$literal should be escaped!");
279 public function testHtmlAttributeEscapingEscapesOwaspRecommendedRanges()
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'));
289 $literal = $this->codepointToUtf8($chr);
290 if (in_array($literal, $immune)) {
291 $this->assertEquals($literal, twig_escape_filter($this->env
, $literal, 'html_attr'));
293 $this->assertNotEquals(
295 twig_escape_filter($this->env
, $literal, 'html_attr'),
296 "$literal should be escaped!");
302 public function testCssEscapingEscapesOwaspRecommendedRanges()
304 $immune = array(); // 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'));
312 $literal = $this->codepointToUtf8($chr);
313 $this->assertNotEquals(
315 twig_escape_filter($this->env
, $literal, 'css'),
316 "$literal should be escaped!");