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