Loading core/misc/ajax.js +6 −3 Original line number Diff line number Diff line Loading @@ -492,7 +492,7 @@ this.url = this.url.replace(/\/nojs(\/|$|\?|#)/, '/ajax$1'); // If the 'nojs' version of the URL is trusted, also trust the 'ajax' // version. if (drupalSettings.ajaxTrustedUrl[originalUrl]) { if (drupalSettings.ajaxTrustedUrl.hasOwnProperty(originalUrl)) { drupalSettings.ajaxTrustedUrl[this.url] = true; } Loading Loading @@ -569,7 +569,10 @@ // #ajax) can bypass header verification. This is especially useful // for Ajax with multipart forms. Because IFRAME transport is used, // the response headers cannot be accessed for verification. if (response !== null && !drupalSettings.ajaxTrustedUrl[ajax.url]) { if ( response !== null && !drupalSettings.ajaxTrustedUrl.hasOwnProperty(ajax.url) ) { if (xmlhttprequest.getResponseHeader('X-Drupal-Ajax-Token') !== '1') { const customMessage = Drupal.t( 'The response failed verification so will not be processed.', Loading Loading @@ -636,7 +639,7 @@ // Bind the ajaxSubmit function to the element event. $(ajax.element).on(elementSettings.event, function (event) { if ( !drupalSettings.ajaxTrustedUrl[ajax.url] && !drupalSettings.ajaxTrustedUrl.hasOwnProperty(ajax.url) && !Drupal.url.isLocal(ajax.url) ) { throw new Error( Loading core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml +14 −0 Original line number Diff line number Diff line Loading @@ -168,3 +168,17 @@ ajax_test.link_page.dialog_contents: _controller: '\Drupal\ajax_test\Controller\AjaxTestController::httpMethodsDialog' requirements: _access: 'TRUE' ajax_test.constructor: path: '/ajax-test/constructor' defaults: _controller: '\Drupal\ajax_test\Controller\AjaxTestController::constructor' requirements: _access: 'TRUE' ajax_test.property_link: path: '/ajax-test/property-link' defaults: _controller: '\Drupal\ajax_test\Controller\AjaxTestController::propertyLink' requirements: _access: 'TRUE' core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php +29 −0 Original line number Diff line number Diff line Loading @@ -10,7 +10,9 @@ use Drupal\Core\Ajax\CloseDialogCommand; use Drupal\Core\Ajax\HtmlCommand; use Drupal\Core\Url; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; /** * Provides content for dialog tests. Loading Loading @@ -526,4 +528,31 @@ public function linkPageDialogTitle(): string { return $title; } /** * Provides a valid AJAX response for the "constructor" URL. * * @return \Symfony\Component\HttpFoundation\Response * The Ajax link. */ public function constructor(): Response { return new JsonResponse(); } /** * Provides an Ajax link to a URL that is also a JS object property. * * @return array * The Ajax link. */ public function propertyLink(): array { return [ '#markup' => '<a class="use-ajax" href="constructor" data-ajax-http-method="GET">Ajax constructor</a>', '#attached' => [ 'library' => [ 'core/drupal.ajax', ], ], ]; } } core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxTest.php +17 −0 Original line number Diff line number Diff line Loading @@ -371,4 +371,21 @@ public function testAjaxFocus(): void { $this->assertEquals('edit-email-field-1', $has_focus_id); } /** * Tests URLs that are also a JS object property are not mistakenly trusted. */ public function testPropertyUrl(): void { $this->drupalGet('ajax-test/property-link'); $this->clickLink('Ajax constructor'); // The AJAX request should fail because the URL is not trusted. $this->failOnJavascriptConsoleErrors = FALSE; $this->assertSession() ->statusMessageContainsAfterWait("Oops, something went wrong. Check your browser's developer console for more details.", 'error'); // This is needed to avoid an unfinished AJAX request error from tearDown() // because this test intentionally does not complete all AJAX requests. $this->getSession()->executeScript("delete window.drupalActiveXhrCount"); } } Loading
core/misc/ajax.js +6 −3 Original line number Diff line number Diff line Loading @@ -492,7 +492,7 @@ this.url = this.url.replace(/\/nojs(\/|$|\?|#)/, '/ajax$1'); // If the 'nojs' version of the URL is trusted, also trust the 'ajax' // version. if (drupalSettings.ajaxTrustedUrl[originalUrl]) { if (drupalSettings.ajaxTrustedUrl.hasOwnProperty(originalUrl)) { drupalSettings.ajaxTrustedUrl[this.url] = true; } Loading Loading @@ -569,7 +569,10 @@ // #ajax) can bypass header verification. This is especially useful // for Ajax with multipart forms. Because IFRAME transport is used, // the response headers cannot be accessed for verification. if (response !== null && !drupalSettings.ajaxTrustedUrl[ajax.url]) { if ( response !== null && !drupalSettings.ajaxTrustedUrl.hasOwnProperty(ajax.url) ) { if (xmlhttprequest.getResponseHeader('X-Drupal-Ajax-Token') !== '1') { const customMessage = Drupal.t( 'The response failed verification so will not be processed.', Loading Loading @@ -636,7 +639,7 @@ // Bind the ajaxSubmit function to the element event. $(ajax.element).on(elementSettings.event, function (event) { if ( !drupalSettings.ajaxTrustedUrl[ajax.url] && !drupalSettings.ajaxTrustedUrl.hasOwnProperty(ajax.url) && !Drupal.url.isLocal(ajax.url) ) { throw new Error( Loading
core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml +14 −0 Original line number Diff line number Diff line Loading @@ -168,3 +168,17 @@ ajax_test.link_page.dialog_contents: _controller: '\Drupal\ajax_test\Controller\AjaxTestController::httpMethodsDialog' requirements: _access: 'TRUE' ajax_test.constructor: path: '/ajax-test/constructor' defaults: _controller: '\Drupal\ajax_test\Controller\AjaxTestController::constructor' requirements: _access: 'TRUE' ajax_test.property_link: path: '/ajax-test/property-link' defaults: _controller: '\Drupal\ajax_test\Controller\AjaxTestController::propertyLink' requirements: _access: 'TRUE'
core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php +29 −0 Original line number Diff line number Diff line Loading @@ -10,7 +10,9 @@ use Drupal\Core\Ajax\CloseDialogCommand; use Drupal\Core\Ajax\HtmlCommand; use Drupal\Core\Url; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; /** * Provides content for dialog tests. Loading Loading @@ -526,4 +528,31 @@ public function linkPageDialogTitle(): string { return $title; } /** * Provides a valid AJAX response for the "constructor" URL. * * @return \Symfony\Component\HttpFoundation\Response * The Ajax link. */ public function constructor(): Response { return new JsonResponse(); } /** * Provides an Ajax link to a URL that is also a JS object property. * * @return array * The Ajax link. */ public function propertyLink(): array { return [ '#markup' => '<a class="use-ajax" href="constructor" data-ajax-http-method="GET">Ajax constructor</a>', '#attached' => [ 'library' => [ 'core/drupal.ajax', ], ], ]; } }
core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxTest.php +17 −0 Original line number Diff line number Diff line Loading @@ -371,4 +371,21 @@ public function testAjaxFocus(): void { $this->assertEquals('edit-email-field-1', $has_focus_id); } /** * Tests URLs that are also a JS object property are not mistakenly trusted. */ public function testPropertyUrl(): void { $this->drupalGet('ajax-test/property-link'); $this->clickLink('Ajax constructor'); // The AJAX request should fail because the URL is not trusted. $this->failOnJavascriptConsoleErrors = FALSE; $this->assertSession() ->statusMessageContainsAfterWait("Oops, something went wrong. Check your browser's developer console for more details.", 'error'); // This is needed to avoid an unfinished AJAX request error from tearDown() // because this test intentionally does not complete all AJAX requests. $this->getSession()->executeScript("delete window.drupalActiveXhrCount"); } }