1<?php 2 3namespace Drupal\KernelTests\Core\Form; 4 5use Drupal\Core\Form\FormInterface; 6use Drupal\Core\Form\FormStateInterface; 7use Drupal\KernelTests\KernelTestBase; 8use Drupal\user\Entity\User; 9use Symfony\Component\HttpFoundation\Request; 10 11/** 12 * Ensures that form actions can't be tricked into sending to external URLs. 13 * 14 * @group system 15 */ 16class ExternalFormUrlTest extends KernelTestBase implements FormInterface { 17 18 /** 19 * {@inheritdoc} 20 */ 21 protected static $modules = ['user', 'system']; 22 23 /** 24 * {@inheritdoc} 25 */ 26 public function getFormId() { 27 return 'external_form_url_test'; 28 } 29 30 /** 31 * {@inheritdoc} 32 */ 33 public function buildForm(array $form, FormStateInterface $form_state) { 34 $form['something'] = [ 35 '#type' => 'textfield', 36 '#title' => 'What do you think?', 37 ]; 38 return $form; 39 } 40 41 /** 42 * {@inheritdoc} 43 */ 44 public function validateForm(array &$form, FormStateInterface $form_state) {} 45 46 /** 47 * {@inheritdoc} 48 */ 49 public function submitForm(array &$form, FormStateInterface $form_state) {} 50 51 /** 52 * {@inheritdoc} 53 */ 54 protected function setUp(): void { 55 parent::setUp(); 56 $this->installSchema('system', ['sequences']); 57 $this->installEntitySchema('user'); 58 59 $test_user = User::create([ 60 'name' => 'foobar', 61 'mail' => 'foobar@example.com', 62 ]); 63 $test_user->save(); 64 \Drupal::service('current_user')->setAccount($test_user); 65 } 66 67 /** 68 * Tests form behavior. 69 */ 70 public function testActionUrlBehavior() { 71 // Create a new request which has a request uri with multiple leading 72 // slashes and make it the master request. 73 $request_stack = \Drupal::service('request_stack'); 74 /** @var \Symfony\Component\HttpFoundation\RequestStack $original_request */ 75 $original_request = $request_stack->pop(); 76 // Just request some more so there is no request left. 77 $request_stack->pop(); 78 $request_stack->pop(); 79 $request = Request::create($original_request->getSchemeAndHttpHost() . '//example.org'); 80 $request_stack->push($request); 81 82 $form = \Drupal::formBuilder()->getForm($this); 83 $markup = \Drupal::service('renderer')->renderRoot($form); 84 85 $this->setRawContent($markup); 86 $elements = $this->xpath('//form/@action'); 87 $action = (string) $elements[0]; 88 $this->assertEquals($original_request->getSchemeAndHttpHost() . '//example.org', $action); 89 90 // Create a new request which has a request uri with a single leading slash 91 // and make it the master request. 92 $request_stack = \Drupal::service('request_stack'); 93 $original_request = $request_stack->pop(); 94 $request = Request::create($original_request->getSchemeAndHttpHost() . '/example.org'); 95 $request_stack->push($request); 96 97 $form = \Drupal::formBuilder()->getForm($this); 98 $markup = \Drupal::service('renderer')->renderRoot($form); 99 100 $this->setRawContent($markup); 101 $elements = $this->xpath('//form/@action'); 102 $action = (string) $elements[0]; 103 $this->assertEquals('/example.org', $action); 104 } 105 106} 107