diff -r 07239de796bb -r e756a8c72c3d cms/drupal/modules/simpletest/tests/common.test --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cms/drupal/modules/simpletest/tests/common.test Fri Sep 08 12:04:06 2017 +0200 @@ -0,0 +1,3182 @@ + 'drupal_alter() tests', + 'description' => 'Confirm that alteration of arguments passed to drupal_alter() works correctly.', + 'group' => 'System', + ); + } + + function setUp() { + parent::setUp('common_test'); + } + + function testDrupalAlter() { + // This test depends on Bartik, so make sure that it is always the current + // active theme. + global $theme, $base_theme_info; + $theme = 'bartik'; + $base_theme_info = array(); + + $array = array('foo' => 'bar'); + $entity = new stdClass(); + $entity->foo = 'bar'; + + // Verify alteration of a single argument. + $array_copy = $array; + $array_expected = array('foo' => 'Drupal theme'); + drupal_alter('drupal_alter', $array_copy); + $this->assertEqual($array_copy, $array_expected, 'Single array was altered.'); + + $entity_copy = clone $entity; + $entity_expected = clone $entity; + $entity_expected->foo = 'Drupal theme'; + drupal_alter('drupal_alter', $entity_copy); + $this->assertEqual($entity_copy, $entity_expected, 'Single object was altered.'); + + // Verify alteration of multiple arguments. + $array_copy = $array; + $array_expected = array('foo' => 'Drupal theme'); + $entity_copy = clone $entity; + $entity_expected = clone $entity; + $entity_expected->foo = 'Drupal theme'; + $array2_copy = $array; + $array2_expected = array('foo' => 'Drupal theme'); + drupal_alter('drupal_alter', $array_copy, $entity_copy, $array2_copy); + $this->assertEqual($array_copy, $array_expected, 'First argument to drupal_alter() was altered.'); + $this->assertEqual($entity_copy, $entity_expected, 'Second argument to drupal_alter() was altered.'); + $this->assertEqual($array2_copy, $array2_expected, 'Third argument to drupal_alter() was altered.'); + + // Verify alteration order when passing an array of types to drupal_alter(). + // common_test_module_implements_alter() places 'block' implementation after + // other modules. + $array_copy = $array; + $array_expected = array('foo' => 'Drupal block theme'); + drupal_alter(array('drupal_alter', 'drupal_alter_foo'), $array_copy); + $this->assertEqual($array_copy, $array_expected, 'hook_TYPE_alter() implementations ran in correct order.'); + } +} + +/** + * Tests for URL generation functions. + * + * url() calls module_implements(), which may issue a db query, which requires + * inheriting from a web test case rather than a unit test case. + */ +class CommonURLUnitTest extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'URL generation tests', + 'description' => 'Confirm that url(), drupal_get_query_parameters(), drupal_http_build_query(), and l() work correctly with various input.', + 'group' => 'System', + ); + } + + /** + * Confirm that invalid text given as $path is filtered. + */ + function testLXSS() { + $text = $this->randomName(); + $path = ""; + $link = l($text, $path); + $sanitized_path = check_url(url($path)); + $this->assertTrue(strpos($link, $sanitized_path) !== FALSE, format_string('XSS attack @path was filtered', array('@path' => $path))); + } + + /* + * Tests for active class in l() function. + */ + function testLActiveClass() { + $link = l($this->randomName(), $_GET['q']); + $this->assertTrue($this->hasClass($link, 'active'), format_string('Class @class is present on link to the current page', array('@class' => 'active'))); + } + + /** + * Tests for custom class in l() function. + */ + function testLCustomClass() { + $class = $this->randomName(); + $link = l($this->randomName(), $_GET['q'], array('attributes' => array('class' => array($class)))); + $this->assertTrue($this->hasClass($link, $class), format_string('Custom class @class is present on link when requested', array('@class' => $class))); + $this->assertTrue($this->hasClass($link, 'active'), format_string('Class @class is present on link to the current page', array('@class' => 'active'))); + } + + private function hasClass($link, $class) { + return preg_match('|class="([^\"\s]+\s+)*' . $class . '|', $link); + } + + /** + * Test drupal_get_query_parameters(). + */ + function testDrupalGetQueryParameters() { + $original = array( + 'a' => 1, + 'b' => array( + 'd' => 4, + 'e' => array( + 'f' => 5, + ), + ), + 'c' => 3, + 'q' => 'foo/bar', + ); + + // Default arguments. + $result = $_GET; + unset($result['q']); + $this->assertEqual(drupal_get_query_parameters(), $result, "\$_GET['q'] was removed."); + + // Default exclusion. + $result = $original; + unset($result['q']); + $this->assertEqual(drupal_get_query_parameters($original), $result, "'q' was removed."); + + // First-level exclusion. + $result = $original; + unset($result['b']); + $this->assertEqual(drupal_get_query_parameters($original, array('b')), $result, "'b' was removed."); + + // Second-level exclusion. + $result = $original; + unset($result['b']['d']); + $this->assertEqual(drupal_get_query_parameters($original, array('b[d]')), $result, "'b[d]' was removed."); + + // Third-level exclusion. + $result = $original; + unset($result['b']['e']['f']); + $this->assertEqual(drupal_get_query_parameters($original, array('b[e][f]')), $result, "'b[e][f]' was removed."); + + // Multiple exclusions. + $result = $original; + unset($result['a'], $result['b']['e'], $result['c']); + $this->assertEqual(drupal_get_query_parameters($original, array('a', 'b[e]', 'c')), $result, "'a', 'b[e]', 'c' were removed."); + } + + /** + * Test drupal_http_build_query(). + */ + function testDrupalHttpBuildQuery() { + $this->assertEqual(drupal_http_build_query(array('a' => ' &#//+%20@۞')), 'a=%20%26%23//%2B%2520%40%DB%9E', 'Value was properly encoded.'); + $this->assertEqual(drupal_http_build_query(array(' &#//+%20@۞' => 'a')), '%20%26%23%2F%2F%2B%2520%40%DB%9E=a', 'Key was properly encoded.'); + $this->assertEqual(drupal_http_build_query(array('a' => '1', 'b' => '2', 'c' => '3')), 'a=1&b=2&c=3', 'Multiple values were properly concatenated.'); + $this->assertEqual(drupal_http_build_query(array('a' => array('b' => '2', 'c' => '3'), 'd' => 'foo')), 'a%5Bb%5D=2&a%5Bc%5D=3&d=foo', 'Nested array was properly encoded.'); + } + + /** + * Test drupal_parse_url(). + */ + function testDrupalParseUrl() { + // Relative URL. + $url = 'foo/bar?foo=bar&bar=baz&baz#foo'; + $result = array( + 'path' => 'foo/bar', + 'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''), + 'fragment' => 'foo', + ); + $this->assertEqual(drupal_parse_url($url), $result, 'Relative URL parsed correctly.'); + + // Relative URL that is known to confuse parse_url(). + $url = 'foo/bar:1'; + $result = array( + 'path' => 'foo/bar:1', + 'query' => array(), + 'fragment' => '', + ); + $this->assertEqual(drupal_parse_url($url), $result, 'Relative URL parsed correctly.'); + + // Absolute URL. + $url = '/foo/bar?foo=bar&bar=baz&baz#foo'; + $result = array( + 'path' => '/foo/bar', + 'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''), + 'fragment' => 'foo', + ); + $this->assertEqual(drupal_parse_url($url), $result, 'Absolute URL parsed correctly.'); + + // External URL testing. + $url = 'http://drupal.org/foo/bar?foo=bar&bar=baz&baz#foo'; + + // Test that drupal can recognize an absolute URL. Used to prevent attack vectors. + $this->assertTrue(url_is_external($url), 'Correctly identified an external URL.'); + + // External URL without an explicit protocol. + $url = '//drupal.org/foo/bar?foo=bar&bar=baz&baz#foo'; + $this->assertTrue(url_is_external($url), 'Correctly identified an external URL without a protocol part.'); + + // Internal URL starting with a slash. + $url = '/drupal.org'; + $this->assertFalse(url_is_external($url), 'Correctly identified an internal URL with a leading slash.'); + + // Test the parsing of absolute URLs. + $url = 'http://drupal.org/foo/bar?foo=bar&bar=baz&baz#foo'; + $result = array( + 'path' => 'http://drupal.org/foo/bar', + 'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''), + 'fragment' => 'foo', + ); + $this->assertEqual(drupal_parse_url($url), $result, 'External URL parsed correctly.'); + + // Verify proper parsing of URLs when clean URLs are disabled. + $result = array( + 'path' => 'foo/bar', + 'query' => array('bar' => 'baz'), + 'fragment' => 'foo', + ); + // Non-clean URLs #1: Absolute URL generated by url(). + $url = $GLOBALS['base_url'] . '/?q=foo/bar&bar=baz#foo'; + $this->assertEqual(drupal_parse_url($url), $result, 'Absolute URL with clean URLs disabled parsed correctly.'); + + // Non-clean URLs #2: Relative URL generated by url(). + $url = '?q=foo/bar&bar=baz#foo'; + $this->assertEqual(drupal_parse_url($url), $result, 'Relative URL with clean URLs disabled parsed correctly.'); + + // Non-clean URLs #3: URL generated by url() on non-Apache webserver. + $url = 'index.php?q=foo/bar&bar=baz#foo'; + $this->assertEqual(drupal_parse_url($url), $result, 'Relative URL on non-Apache webserver with clean URLs disabled parsed correctly.'); + + // Test that drupal_parse_url() does not allow spoofing a URL to force a malicious redirect. + $parts = drupal_parse_url('forged:http://cwe.mitre.org/data/definitions/601.html'); + $this->assertFalse(valid_url($parts['path'], TRUE), 'drupal_parse_url() correctly parsed a forged URL.'); + } + + /** + * Test url() with/without query, with/without fragment, absolute on/off and + * assert all that works when clean URLs are on and off. + */ + function testUrl() { + global $base_url; + + foreach (array(FALSE, TRUE) as $absolute) { + // Get the expected start of the path string. + $base = $absolute ? $base_url . '/' : base_path(); + $absolute_string = $absolute ? 'absolute' : NULL; + + // Disable Clean URLs. + $GLOBALS['conf']['clean_url'] = 0; + + $url = $base . '?q=node/123'; + $result = url('node/123', array('absolute' => $absolute)); + $this->assertEqual($url, $result, "$url == $result"); + + $url = $base . '?q=node/123#foo'; + $result = url('node/123', array('fragment' => 'foo', 'absolute' => $absolute)); + $this->assertEqual($url, $result, "$url == $result"); + + $url = $base . '?q=node/123&foo'; + $result = url('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute)); + $this->assertEqual($url, $result, "$url == $result"); + + $url = $base . '?q=node/123&foo=bar&bar=baz'; + $result = url('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute)); + $this->assertEqual($url, $result, "$url == $result"); + + $url = $base . '?q=node/123&foo#bar'; + $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute)); + $this->assertEqual($url, $result, "$url == $result"); + + $url = $base . '?q=node/123&foo#0'; + $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => '0', 'absolute' => $absolute)); + $this->assertEqual($url, $result, "$url == $result"); + + $url = $base . '?q=node/123&foo'; + $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => '', 'absolute' => $absolute)); + $this->assertEqual($url, $result, "$url == $result"); + + $url = $base; + $result = url('', array('absolute' => $absolute)); + $this->assertEqual($url, $result, "$url == $result"); + + // Enable Clean URLs. + $GLOBALS['conf']['clean_url'] = 1; + + $url = $base . 'node/123'; + $result = url('node/123', array('absolute' => $absolute)); + $this->assertEqual($url, $result, "$url == $result"); + + $url = $base . 'node/123#foo'; + $result = url('node/123', array('fragment' => 'foo', 'absolute' => $absolute)); + $this->assertEqual($url, $result, "$url == $result"); + + $url = $base . 'node/123?foo'; + $result = url('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute)); + $this->assertEqual($url, $result, "$url == $result"); + + $url = $base . 'node/123?foo=bar&bar=baz'; + $result = url('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute)); + $this->assertEqual($url, $result, "$url == $result"); + + $url = $base . 'node/123?foo#bar'; + $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute)); + $this->assertEqual($url, $result, "$url == $result"); + + $url = $base; + $result = url('', array('absolute' => $absolute)); + $this->assertEqual($url, $result, "$url == $result"); + } + } + + /** + * Test external URL handling. + */ + function testExternalUrls() { + $test_url = 'http://drupal.org/'; + + // Verify external URL can contain a fragment. + $url = $test_url . '#drupal'; + $result = url($url); + $this->assertEqual($url, $result, 'External URL with fragment works without a fragment in $options.'); + + // Verify fragment can be overidden in an external URL. + $url = $test_url . '#drupal'; + $fragment = $this->randomName(10); + $result = url($url, array('fragment' => $fragment)); + $this->assertEqual($test_url . '#' . $fragment, $result, 'External URL fragment is overidden with a custom fragment in $options.'); + + // Verify external URL can contain a query string. + $url = $test_url . '?drupal=awesome'; + $result = url($url); + $this->assertEqual($url, $result, 'External URL with query string works without a query string in $options.'); + + // Verify external URL can be extended with a query string. + $url = $test_url; + $query = array($this->randomName(5) => $this->randomName(5)); + $result = url($url, array('query' => $query)); + $this->assertEqual($url . '?' . http_build_query($query, '', '&'), $result, 'External URL can be extended with a query string in $options.'); + + // Verify query string can be extended in an external URL. + $url = $test_url . '?drupal=awesome'; + $query = array($this->randomName(5) => $this->randomName(5)); + $result = url($url, array('query' => $query)); + $this->assertEqual($url . '&' . http_build_query($query, '', '&'), $result, 'External URL query string can be extended with a custom query string in $options.'); + + // Verify that an internal URL does not result in an external URL without + // protocol part. + $url = '/drupal.org'; + $result = url($url); + $this->assertTrue(strpos($result, '//') === FALSE, 'Internal URL does not turn into an external URL.'); + + // Verify that an external URL without protocol part is recognized as such. + $url = '//drupal.org'; + $result = url($url); + $this->assertEqual($url, $result, 'External URL without protocol is not altered.'); + } +} + +/** + * Tests url_is_external(). + */ +class UrlIsExternalUnitTest extends DrupalUnitTestCase { + + public static function getInfo() { + return array( + 'name' => 'External URL checking', + 'description' => 'Performs tests on url_is_external().', + 'group' => 'System', + ); + } + + /** + * Tests if each URL is external or not. + */ + function testUrlIsExternal() { + foreach ($this->examples() as $path => $expected) { + $this->assertIdentical(url_is_external($path), $expected, $path); + } + } + + /** + * Provides data for testUrlIsExternal(). + * + * @return array + * An array of test data, keyed by a path, with the expected value where + * TRUE is external, and FALSE is not external. + */ + protected function examples() { + return array( + // Simple external URLs. + 'http://example.com' => TRUE, + 'https://example.com' => TRUE, + 'http://drupal.org/foo/bar?foo=bar&bar=baz&baz#foo' => TRUE, + '//drupal.org' => TRUE, + // Some browsers ignore or strip leading control characters. + "\x00//www.example.com" => TRUE, + "\x08//www.example.com" => TRUE, + "\x1F//www.example.com" => TRUE, + "\n//www.example.com" => TRUE, + // JSON supports decoding directly from UTF-8 code points. + json_decode('"\u00AD"') . "//www.example.com" => TRUE, + json_decode('"\u200E"') . "//www.example.com" => TRUE, + json_decode('"\uE0020"') . "//www.example.com" => TRUE, + json_decode('"\uE000"') . "//www.example.com" => TRUE, + // Backslashes should be normalized to forward. + '\\\\example.com' => TRUE, + // Local URLs. + 'node' => FALSE, + '/system/ajax' => FALSE, + '?q=foo:bar' => FALSE, + 'node/edit:me' => FALSE, + '/drupal.org' => FALSE, + '' => FALSE, + ); + } +} + +/** + * Tests for check_plain(), filter_xss(), format_string(), and check_url(). + */ +class CommonXssUnitTest extends DrupalUnitTestCase { + + public static function getInfo() { + return array( + 'name' => 'String filtering tests', + 'description' => 'Confirm that check_plain(), filter_xss(), format_string() and check_url() work correctly, including invalid multi-byte sequences.', + 'group' => 'System', + ); + } + + /** + * Check that invalid multi-byte sequences are rejected. + */ + function testInvalidMultiByte() { + // Ignore PHP 5.3+ invalid multibyte sequence warning. + $text = @check_plain("Foo\xC0barbaz"); + $this->assertEqual($text, '', 'check_plain() rejects invalid sequence "Foo\xC0barbaz"'); + // Ignore PHP 5.3+ invalid multibyte sequence warning. + $text = @check_plain("\xc2\""); + $this->assertEqual($text, '', 'check_plain() rejects invalid sequence "\xc2\""'); + $text = check_plain("Fooÿñ"); + $this->assertEqual($text, "Fooÿñ", 'check_plain() accepts valid sequence "Fooÿñ"'); + $text = filter_xss("Foo\xC0barbaz"); + $this->assertEqual($text, '', 'filter_xss() rejects invalid sequence "Foo\xC0barbaz"'); + $text = filter_xss("Fooÿñ"); + $this->assertEqual($text, "Fooÿñ", 'filter_xss() accepts valid sequence Fooÿñ'); + } + + /** + * Check that special characters are escaped. + */ + function testEscaping() { + $text = check_plain("