function EditorSecurityTest::testSwitchingSecurity

Same name in other branches
  1. 9 core/modules/editor/tests/src/Functional/EditorSecurityTest.php \Drupal\Tests\editor\Functional\EditorSecurityTest::testSwitchingSecurity()
  2. 8.9.x core/modules/editor/tests/src/Functional/EditorSecurityTest.php \Drupal\Tests\editor\Functional\EditorSecurityTest::testSwitchingSecurity()
  3. 11.x core/modules/editor/tests/src/Functional/EditorSecurityTest.php \Drupal\Tests\editor\Functional\EditorSecurityTest::testSwitchingSecurity()

Tests administrator security: is the user safe when switching text formats?

Tests 24 scenarios. Tests only with a text editor that is not XSS-safe.

When changing from a more restrictive text format with a text editor (or a text format without a text editor) to a less restrictive text format, it is possible that a malicious user could trigger an XSS.

E.g. when switching a piece of text that uses the Restricted HTML text format and contains a <script> tag to the Full HTML text format, the <script> tag would be executed. Unless we apply appropriate filtering.

File

core/modules/editor/tests/src/Functional/EditorSecurityTest.php, line 307

Class

EditorSecurityTest
Tests XSS protection for content creators when using text editors.

Namespace

Drupal\Tests\editor\Functional

Code

public function testSwitchingSecurity() : void {
    $expected = [
        [
            'node_id' => 1,
            // No text editor => no XSS filtering.
'value' => self::$sampleContent,
            'format' => 'restricted_without_editor',
            'switch_to' => [
                'restricted_with_editor' => self::$sampleContentSecured,
                // Intersection of restrictions => most strict XSS filtering.
'restricted_plus_dangerous_tag_with_editor' => self::$sampleContentSecured,
                // No text editor => no XSS filtering.
'unrestricted_without_editor' => FALSE,
                'unrestricted_with_editor' => self::$sampleContentSecured,
            ],
        ],
        [
            'node_id' => 2,
            // Text editor => XSS filtering.
'value' => self::$sampleContentSecured,
            'format' => 'restricted_with_editor',
            'switch_to' => [
                // No text editor => no XSS filtering.
'restricted_without_editor' => FALSE,
                // Intersection of restrictions => most strict XSS filtering.
'restricted_plus_dangerous_tag_with_editor' => self::$sampleContentSecured,
                // No text editor => no XSS filtering.
'unrestricted_without_editor' => FALSE,
                'unrestricted_with_editor' => self::$sampleContentSecured,
            ],
        ],
        [
            'node_id' => 3,
            // Text editor => XSS filtering.
'value' => self::$sampleContentSecuredEmbedAllowed,
            'format' => 'restricted_plus_dangerous_tag_with_editor',
            'switch_to' => [
                // No text editor => no XSS filtering.
'restricted_without_editor' => FALSE,
                // Intersection of restrictions => most strict XSS filtering.
'restricted_with_editor' => self::$sampleContentSecured,
                // No text editor => no XSS filtering.
'unrestricted_without_editor' => FALSE,
                // Intersection of restrictions => most strict XSS filtering.
'unrestricted_with_editor' => self::$sampleContentSecured,
            ],
        ],
        [
            'node_id' => 4,
            // No text editor => no XSS filtering.
'value' => self::$sampleContent,
            'format' => 'unrestricted_without_editor',
            'switch_to' => [
                // No text editor => no XSS filtering.
'restricted_without_editor' => FALSE,
                'restricted_with_editor' => self::$sampleContentSecured,
                // Intersection of restrictions => most strict XSS filtering.
'restricted_plus_dangerous_tag_with_editor' => self::$sampleContentSecured,
                // From no editor, no security filters, to editor, still no security
                // filters: resulting content when viewed was already vulnerable, so
                // it must be intentional.
'unrestricted_with_editor' => FALSE,
            ],
        ],
        [
            'node_id' => 5,
            // Text editor => XSS filtering.
'value' => self::$sampleContentSecured,
            'format' => 'unrestricted_with_editor',
            'switch_to' => [
                // From editor, no security filters to security filters, no editor: no
                // risk.
'restricted_without_editor' => FALSE,
                'restricted_with_editor' => self::$sampleContentSecured,
                // Intersection of restrictions => most strict XSS filtering.
'restricted_plus_dangerous_tag_with_editor' => self::$sampleContentSecured,
                // From no editor, no security filters, to editor, still no security
                // filters: resulting content when viewed was already vulnerable, so
                // it must be intentional.
'unrestricted_without_editor' => FALSE,
            ],
        ],
    ];
    // Log in as the privileged user, and for every sample, do the following:
    // - switch to every other text format/editor
    // - assert the XSS-filtered values that we get from the server
    $this->drupalLogin($this->privilegedUser);
    $cookies = $this->getSessionCookies();
    foreach ($expected as $case) {
        $this->drupalGet('node/' . $case['node_id'] . '/edit');
        // Verify data- attributes.
        $body = $this->assertSession()
            ->fieldExists('edit-body-0-value');
        $this->assertSame(self::$sampleContent, $body->getAttribute('data-editor-value-original'), 'The data-editor-value-original attribute is correctly set.');
        $this->assertSame('false', (string) $body->getAttribute('data-editor-value-is-changed'), 'The data-editor-value-is-changed attribute is correctly set.');
        // Switch to every other text format/editor and verify the results.
        foreach ($case['switch_to'] as $format => $expected_filtered_value) {
            $post = [
                'value' => self::$sampleContent,
                'original_format_id' => $case['format'],
            ];
            $client = $this->getHttpClient();
            $response = $client->post($this->buildUrl('/editor/filter_xss/' . $format), [
                'body' => http_build_query($post),
                'cookies' => $cookies,
                'headers' => [
                    'Accept' => 'application/json',
                    'Content-Type' => 'application/x-www-form-urlencoded',
                ],
                'http_errors' => FALSE,
            ]);
            $this->assertEquals(200, $response->getStatusCode());
            $json = Json::decode($response->getBody());
            $this->assertSame($expected_filtered_value, $json, 'The value was correctly filtered for XSS attack vectors.');
        }
    }
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.