diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php index 64e01e6..a24330d 100644 --- a/core/lib/Drupal/Core/Form/FormBuilder.php +++ b/core/lib/Drupal/Core/Form/FormBuilder.php @@ -725,7 +725,14 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) { protected function buildFormAction() { // @todo Use instead of the master request in // https://www.drupal.org/node/2505339. - $request_uri = $this->requestStack->getMasterRequest()->getRequestUri(); + $request = $this->requestStack->getMasterRequest(); + $request_uri = $request->getRequestUri(); + + // Prevent cross site requests via the Form API by using an absolute URL + // when the request uri starts with multiple slashes.. + if (strpos($request_uri, '//') === 0) { + $request_uri = $request->getUri(); + } // @todo Remove this parsing once these are removed from the request in // https://www.drupal.org/node/2504709. diff --git a/core/modules/system/src/Tests/Form/ExternalFormUrlTest.php b/core/modules/system/src/Tests/Form/ExternalFormUrlTest.php new file mode 100644 index 0000000..f179ec9 --- /dev/null +++ b/core/modules/system/src/Tests/Form/ExternalFormUrlTest.php @@ -0,0 +1,108 @@ + 'textfield', + '#title' => 'What do you think?', + ]; + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) {} + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) {} + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + $this->installSchema('system', ['key_value_expire', 'sequences']); + $this->installEntitySchema('user'); + + $test_user = User::create([ + 'name' => 'foobar', + 'mail' => 'foobar@example.com', + ]); + $test_user->save(); + \Drupal::service('current_user')->setAccount($test_user); + + } + + /** + * Tests form behaviour. + */ + public function testActionUrlBehavior() { + // Create a new request which has a request uri with multiple leading + // slashes and make it the master request. + $request_stack = \Drupal::service('request_stack'); + $original_request = $request_stack->pop(); + $request = Request::create($original_request->getSchemeAndHttpHost() . '//example.org'); + $request_stack->push($request); + + $form = \Drupal::formBuilder()->getForm($this); + $markup = \Drupal::service('renderer')->renderRoot($form); + + $this->setRawContent($markup); + $elements = $this->xpath('//form/@action'); + $action = (string) $elements[0]; + $this->assertEqual($original_request->getSchemeAndHttpHost() . '//example.org', $action); + + // Create a new request which has a request uri with a single leading slash + // and make it the master request. + $request_stack = \Drupal::service('request_stack'); + $original_request = $request_stack->pop(); + $request = Request::create($original_request->getSchemeAndHttpHost() . '/example.org'); + $request_stack->push($request); + + $form = \Drupal::formBuilder()->getForm($this); + $markup = \Drupal::service('renderer')->renderRoot($form); + + $this->setRawContent($markup); + $elements = $this->xpath('//form/@action'); + $action = (string) $elements[0]; + $this->assertEqual('/example.org', $action); + } + +}