Verified Commit c15356bb authored by godotislate's avatar godotislate
Browse files

fix: #3592341 Fix dot-segment encoding for chained ../ and ./ in generated URLs

By: znerol
By: smustgrave
By: dww
By: godotislate
(cherry picked from commit 01058045)
parent ea59f00a
Loading
Loading
Loading
Loading
Loading
+9 −6
Original line number Diff line number Diff line
@@ -331,14 +331,17 @@ public function generateFromRoute(string $name, array $parameters = [], array $o
      // them as they are not used for this purpose here otherwise we would
      // generate a URI that, when followed by a user agent (e.g. browser), does
      // not match this route.
      $path = strtr($path, ['/../' => '/%2E%2E/', '/./' => '/%2E/']);
      if (str_ends_with($path, '/..')) {
        $path = substr($path, 0, -2) . '%2E%2E';
      $segments = explode('/', $path);
      foreach ($segments as $i => $segment) {
        if ($segment === '.') {
          $segments[$i] = '%2E';
        }
      elseif (str_ends_with($path, '/.')) {
        $path = substr($path, 0, -1) . '%2E';
        elseif ($segment === '..') {
          $segments[$i] = '%2E%2E';
        }
      }
      $path = implode('/', $segments);
    }

    if (!empty($options['prefix'])) {
      $path = ltrim($path, '/');
+24 −0
Original line number Diff line number Diff line
@@ -99,12 +99,16 @@ protected function setUp(): void {
    $third_route = new Route('/test/two/');
    $fourth_route = new Route('/test/four', [], [], [], '', ['https']);
    $none_route = new Route('', [], [], ['_no_path' => TRUE]);
    $path_route = new Route('/test-path/{path}/bar', [], ['path' => '.+']);
    $tail_route = new Route('/test-tail/{path}', [], ['path' => '.+']);

    $routes->add('test_1', $first_route);
    $routes->add('test_2', $second_route);
    $routes->add('test_3', $third_route);
    $routes->add('test_4', $fourth_route);
    $routes->add('<none>', $none_route);
    $routes->add('test_path', $path_route);
    $routes->add('test_tail', $tail_route);

    // Create a route provider stub.
    $provider = $this->getMockBuilder('Drupal\Core\Routing\RouteProvider')
@@ -135,6 +139,14 @@ protected function setUp(): void {
        'route_name' => '<none>',
        'return' => $none_route,
      ],
      [
        'route_name' => 'test_path',
        'return' => $path_route,
      ],
      [
        'route_name' => 'test_tail',
        'return' => $tail_route,
      ],
    ];
    foreach ($return_map_values as $values) {
      $route_name_return_map[] = [$values['route_name'], $values['return']];
@@ -547,6 +559,18 @@ public function testGenerateWithPathProcessorChangingOptions(): void {
    $this->assertGenerateFromRoute('test_2', ['Lassie' => 5], $options, '/goodbye/cruel/world?zoo=5#foo', (new BubbleableMetadata())->setCacheMaxAge(Cache::PERMANENT));
  }

  /**
   * Tests generate with chained relative path segments.
   */
  public function testEncodingOfChainedRelativePathSegments(): void {
    $this->assertGenerateFromRoute('test_path', ['path' => '../../..'], [], '/test-path/%2E%2E/%2E%2E/%2E%2E/bar', (new BubbleableMetadata())->setCacheMaxAge(Cache::PERMANENT));
    $this->assertGenerateFromRoute('test_path', ['path' => '././.'], [], '/test-path/%2E/%2E/%2E/bar', (new BubbleableMetadata())->setCacheMaxAge(Cache::PERMANENT));
    $this->assertGenerateFromRoute('test_path', ['path' => '../././..'], [], '/test-path/%2E%2E/%2E/%2E/%2E%2E/bar', (new BubbleableMetadata())->setCacheMaxAge(Cache::PERMANENT));

    $this->assertGenerateFromRoute('test_tail', ['path' => '../../..'], [], '/test-tail/%2E%2E/%2E%2E/%2E%2E', (new BubbleableMetadata())->setCacheMaxAge(Cache::PERMANENT));
    $this->assertGenerateFromRoute('test_tail', ['path' => '././.'], [], '/test-tail/%2E/%2E/%2E', (new BubbleableMetadata())->setCacheMaxAge(Cache::PERMANENT));
  }

  /**
   * Asserts \Drupal\Core\Routing\UrlGenerator::generateFromRoute()'s output.
   *