Skip to content

[Routing] fix encoding of static text #4530

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions src/Symfony/Component/Routing/Generator/UrlGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,33 @@
class UrlGenerator implements UrlGeneratorInterface
{
protected $context;

/**
* This array defines the characters (besides alphanumeric ones) that will not be percent-encoded in the path segment of the generated URL.
*
* PHP's rawurlencode() encodes all chars except "a-zA-Z0-9-._~" according to RFC 3986. But we want to allow some chars
* to be used in their literal form (reasons below). Other chars inside the path must of course be encoded, e.g.
* "?" and "#" (would be interpreted wrongly as query and fragment identifier),
* "'" and """ (are used as delimiters in HTML).
*/
protected $decodedChars = array(
// %2F is not valid in a URL, so we don't encode it (which is fine as the requirements explicitly allowed it)
// the slash can be used to designate a hierarchical structure and we want allow using it with this meaning
// some webservers don't allow the slash in encoded form in the path for security reasons anyway
// see http://stackoverflow.com/questions/4069002/http-400-if-2f-part-of-get-url-in-jboss
'%2F' => '/',
// the following chars are general delimiters in the URI specification but have only special meaning in the authority component
// so they can safely be used in the path in unencoded form
'%40' => '@',
'%3A' => ':',
// these chars are only sub-delimiters that have no predefined meaning and can therefore be used literally
// so URI producing applications can use these chars to delimit subcomponents in a path segment without being encoded for better readability
'%3B' => ';',
'%2C' => ',',
'%3D' => '=',
'%2B' => '+',
'%21' => '!',
'%2A' => '*',
'%7C' => '|',
);

protected $routes;
Expand Down Expand Up @@ -129,7 +153,7 @@ protected function doGenerate($variables, $defaults, $requirements, $tokens, $pa
}

if (!$isEmpty || !$optional) {
$url = $token[1].strtr(rawurlencode($tparams[$token[3]]), $this->decodedChars).$url;
$url = $token[1].$tparams[$token[3]].$url;
}

$optional = false;
Expand All @@ -144,14 +168,15 @@ protected function doGenerate($variables, $defaults, $requirements, $tokens, $pa
$url = '/';
}

// do not encode the contexts base url as it is already encoded (see Symfony\Component\HttpFoundation\Request)
$url = $this->context->getBaseUrl().strtr(rawurlencode($url), $this->decodedChars);

// add a query string if needed
$extra = array_diff_key($originParameters, $variables, $defaults);
if ($extra && $query = http_build_query($extra, '', '&')) {
$url .= '?'.$query;
}

$url = $this->context->getBaseUrl().$url;

if ($this->context->getHost()) {
$scheme = $this->context->getScheme();
if (isset($requirements['_scheme']) && ($req = strtolower($requirements['_scheme'])) && $scheme != $req) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,22 @@ public function testWithAnIntegerAsADefaultValue()
$this->assertEquals('/app.php/foo', $this->getGenerator($routes)->generate('test', array('default' => 'foo')));
}

public function testUrlEncoding()
{
// This tests the encoding of reserved characters that are used for delimiting of URI components (defined in RFC 3986)
// and other special ASCII chars. These chars are tested as static text path, variable path and query param.
$chars = '@:[]/()*\'" +,;-._~&$<>|{}%\\^`!?foo=bar#id';
$routes = $this->getRoutes('test', new Route("/$chars/{varpath}", array(), array('varpath' => '.+')));
$this->assertSame('/app.php/@:%5B%5D/%28%29*%27%22%20+,;-._~%26%24%3C%3E|%7B%7D%25%5C%5E%60!%3Ffoo=bar%23id'
. '/@:%5B%5D/%28%29*%27%22%20+,;-._~%26%24%3C%3E|%7B%7D%25%5C%5E%60!%3Ffoo=bar%23id'
. '?query=%40%3A%5B%5D%2F%28%29%2A%27%22+%2B%2C%3B-._%7E%26%24%3C%3E%7C%7B%7D%25%5C%5E%60%21%3Ffoo%3Dbar%23id',
$this->getGenerator($routes)->generate('test', array(
'varpath' => $chars,
'query' => $chars
))
);
}

protected function getGenerator(RouteCollection $routes, array $parameters = array())
{
$context = new RequestContext('/app.php');
Expand Down