'URL generation tests', 'description' => 'Confirm that url(), drupal_get_query_parameters(), drupal_http_build_query(), and l() work correctly with various input.', 'group' => 'Common', ); } /** * Confirms that invalid URLs are 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(), current_path()); $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(), current_path(), 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'))); } /** * Checks for class existence in link. * * @param $link * URL to search. * @param $class * Element class to search for. * * @return bool * TRUE if the class is found, FALSE otherwise. */ private function hasClass($link, $class) { return preg_match('|class="([^\"\s]+\s+)*' . $class . '|', $link); } /** * Tests drupal_get_query_parameters(). */ function testDrupalGetQueryParameters() { $original = array( 'a' => 1, 'b' => array( 'd' => 4, 'e' => array( 'f' => 5, ), ), 'c' => 3, ); // 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."); } /** * Tests 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[b]=2&a[c]=3&d=foo', 'Nested array was properly encoded.'); } /** * Tests drupal_parse_url(). */ function testDrupalParseUrl() { // Relative, absolute, and external URLs, without/with explicit script path, // without/with Drupal path. foreach (array('', '/', 'http://drupal.org/') as $absolute) { foreach (array('', 'index.php/') as $script) { foreach (array('', 'foo/bar') as $path) { $url = $absolute . $script . $path . '?foo=bar&bar=baz&baz#foo'; $expected = array( 'path' => $absolute . $script . $path, 'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''), 'fragment' => 'foo', ); $this->assertEqual(drupal_parse_url($url), $expected, '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.'); // Test that drupal can recognize an absolute URL. Used to prevent attack vectors. $url = 'http://drupal.org/foo/bar?foo=bar&bar=baz&baz#foo'; $this->assertTrue(url_is_external($url), 'Correctly identified an external URL.'); // 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.'); } /** * Tests url() functionality. * * Tests url() with/without query, with/without fragment, absolute on/off and * asserts all that works when clean URLs are on and off. */ function testUrl() { global $base_url, $script_path; $script_path_original = $script_path; foreach (array('', 'index.php/') as $script_path) { foreach (array(FALSE, TRUE) as $absolute) { // Get the expected start of the path string. $base = ($absolute ? $base_url . '/' : base_path()) . $script_path; $absolute_string = $absolute ? 'absolute' : NULL; $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"); } } $script_path = $script_path_original; } /** * Tests 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.'); } }