Commit 47280fc8 authored by catch's avatar catch
Browse files

Issue #3399951 by neclimdul, godotislate, catch, lauriii, smustgrave, xjm:...

Issue #3399951 by neclimdul, godotislate, catch, lauriii, smustgrave, xjm: ajax_page_state leaks through request in Views Ajax

(cherry picked from commit ef985e93)
parent e829c1e3
Loading
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -36,7 +36,7 @@ public function getQueryParameters() {
    $request = $this->requestStack->getCurrentRequest();
    if ($request) {
      return UrlHelper::filterQueryParameters(
        $request->query->all(), ['page', 'ajax_page_state']
        $request->query->all(), ['page']
      );
    }
    return [];
+1 −6
Original line number Diff line number Diff line
@@ -114,12 +114,7 @@ public static function fromRequest(Request $request) {
      throw new BadRequestHttpException("Invalid media library parameters specified.");
    }

    // Remove ajax_page_state as it is irrelevant.
    // @todo: Review other parameters passed
    // See https://www.drupal.org/project/drupal/issues/3396650
    if ($query->has('ajax_page_state')) {
      $query->remove('ajax_page_state');
    }
    // @todo: Review parameters passed and remove irrelevant ones in https://www.drupal.org/i/3396650

    // Once we have validated the required parameters, we restore the parameters
    // from the request since there might be additional values.
+0 −9
Original line number Diff line number Diff line
@@ -288,9 +288,6 @@ public function testFromRequest(array $query_overrides, $exception_expected) {

    $state = MediaLibraryState::fromRequest(new Request($query));
    $this->assertInstanceOf(MediaLibraryState::class, $state);

    // Assert ajax_page_state is no longer in the state.
    $this->assertFalse($state->has('ajax_page_state'));
  }

  /**
@@ -361,12 +358,6 @@ public function providerFromRequest() {
      TRUE,
    ];

    // Assert ajax_page_state is removed if in the query.
    $test_data['ajax_page_state'] = [
      ['ajax_page_state' => 'A long string that gets removed'],
      FALSE,
    ];

    return $test_data;
  }

+1 −1
Original line number Diff line number Diff line
@@ -68,7 +68,7 @@ public function testLibrariesAvailable() {
  public function testDrupalSettingsIsNotLoaded() {
    $this->drupalGet('node',
      [
        "query" =>
        'query' =>
          [
            'ajax_page_state' => [
              'libraries' => UrlHelper::compressQueryParameter('core/drupalSettings'),
+34 −15
Original line number Diff line number Diff line
@@ -8,7 +8,6 @@
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\EventSubscriber\AjaxResponseSubscriber;
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Path\CurrentPathStack;
@@ -27,6 +26,23 @@
 */
class ViewAjaxController implements ContainerInjectionInterface {

  /**
   * Parameters that should be filtered and ignored inside ajax requests.
   */
  public const FILTERED_QUERY_PARAMETERS = [
    'view_name',
    'view_display_id',
    'view_args',
    'view_path',
    'view_dom_id',
    'pager_element',
    'view_base_path',
    'ajax_page_state',
    '_drupal_ajax',
    FormBuilderInterface::AJAX_FORM_REQUEST,
    MainContentViewSubscriber::WRAPPER_FORMAT,
  ];

  /**
   * The entity storage for views.
   *
@@ -131,21 +147,13 @@ public function ajaxView(Request $request) {
      $response = new ViewAjaxResponse();

      // Remove all of this stuff from the query of the request so it doesn't
      // end up in pagers and tablesort URLs.
      // end up in pagers and tablesort URLs. Additionally we need to preserve
      // ajax_page_state and add it back after the request has been processed so
      // the related listener can behave correctly.
      // @todo Remove this parsing once these are removed from the request in
      //   https://www.drupal.org/node/2504709.
      foreach ([
        'view_name',
        'view_display_id',
        'view_args',
        'view_path',
        'view_dom_id',
        'pager_element',
        'view_base_path',
        AjaxResponseSubscriber::AJAX_REQUEST_PARAMETER,
        FormBuilderInterface::AJAX_FORM_REQUEST,
        MainContentViewSubscriber::WRAPPER_FORMAT,
      ] as $key) {
      $existing_page_state = $request->get('ajax_page_state');
      foreach (self::FILTERED_QUERY_PARAMETERS as $key) {
        $request->query->remove($key);
        $request->request->remove($key);
      }
@@ -169,7 +177,6 @@ public function ajaxView(Request $request) {
        // Add all POST data, because AJAX is sometimes a POST and many things,
        // such as tablesorts, exposed filters and paging assume GET.
        $param_union = $request_clone->request->all() + $request_clone->query->all();
        unset($param_union['ajax_page_state']);
        $request_clone->query->replace($param_union);

        // Overwrite the destination.
@@ -191,9 +198,21 @@ public function ajaxView(Request $request) {
        // Reuse the same DOM id so it matches that in drupalSettings.
        $view->dom_id = $dom_id;

        // Populate request attributes temporarily with ajax_page_state theme
        // and theme_token for theme negotiation.
        $theme_keys = [
          'theme' => TRUE,
          'theme_token' => TRUE,
        ];
        if (is_array($existing_page_state) &&
            ($temp_attributes = array_intersect_key($existing_page_state, $theme_keys))) {
          $request->attributes->set('ajax_page_state', $temp_attributes);
        }
        $preview = $view->preview($display_id, $args);
        $request->attributes->remove('ajax_page_state');
        $response->addCommand(new ReplaceCommand(".js-view-dom-id-$dom_id", $preview));
        $response->addCommand(new PrependCommand(".js-view-dom-id-$dom_id", ['#type' => 'status_messages']));
        $request->query->set('ajax_page_state', $existing_page_state);

        return $response;
      }
Loading