escapingTest.php

File

drupal/core/vendor/twig/twig/test/Twig/Tests/escapingTest.php
View source
<?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(
    '\'' => '&#039;',
    '"' => '&quot;',
    '<' => '&lt;',
    '>' => '&gt;',
    '&' => '&amp;',
  );
  protected $htmlAttrSpecialChars = array(
    '\'' => '&#x27;',
    /* Characters beyond ASCII value 255 to unicode escape */
    'Ā' => '&#x0100;',
    /* Immune chars excluded */
    ',' => ',',
    '.' => '.',
    '-' => '-',
    '_' => '_',
    /* Basic alnums excluded */
    'a' => 'a',
    'A' => 'A',
    'z' => 'z',
    'Z' => 'Z',
    '0' => '0',
    '9' => '9',
    /* Basic control characters and null */
    "\r" => '&#x0D;',
    "\n" => '&#x0A;',
    "\t" => '&#x09;',
    "\0" => '&#xFFFD;',
    // should use Unicode replacement char

    /* Encode chars as named entities where possible */
    '<' => '&lt;',
    '>' => '&gt;',
    '&' => '&amp;',
    '"' => '&quot;',
    /* Encode spaces for quoteless attribute protection */
    ' ' => '&#x20;',
  );
  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 excluded */
    '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 excluded */
    '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 & 0xf | 0xe0) . chr($codepoint >> 6 & 0x3f | 0x80) . chr($codepoint & 0x3f | 0x80);
    }
    if ($codepoint < 0x110000) {
      return chr($codepoint >> 18 & 0x7 | 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!");
      }
    }
  }

}

Classes

Namesort descending Description
Twig_Test_EscapingTest This class is adapted from code coming from Zend Framework.