X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs-website;a=blobdiff_plain;f=web%2Fcore%2Fmodules%2Feditor%2Ftests%2Fsrc%2FFunctional%2FEditorSecurityTest.php;fp=web%2Fcore%2Fmodules%2Feditor%2Ftests%2Fsrc%2FFunctional%2FEditorSecurityTest.php;h=55095c98c60241bc49522e1ae69b3f57cc99f721;hp=0000000000000000000000000000000000000000;hb=0bf8d09d2542548982e81a441b1f16e75873a04f;hpb=74df008bdbb3a11eeea356744f39b802369bda3c diff --git a/web/core/modules/editor/tests/src/Functional/EditorSecurityTest.php b/web/core/modules/editor/tests/src/Functional/EditorSecurityTest.php new file mode 100644 index 000000000..55095c98c --- /dev/null +++ b/web/core/modules/editor/tests/src/Functional/EditorSecurityTest.php @@ -0,0 +1,453 @@ +Hello, Dumbo Octopus!

'; + + /** + * The secured sample content to use in most tests. + * + * @var string + */ + protected static $sampleContentSecured = '

Hello, Dumbo Octopus!

alert(0)'; + + /** + * The secured sample content to use in tests when the tag is allowed. + * + * @var string + */ + protected static $sampleContentSecuredEmbedAllowed = '

Hello, Dumbo Octopus!

alert(0)'; + + /** + * Modules to enable. + * + * @var array + */ + public static $modules = ['filter', 'editor', 'editor_test', 'node']; + + /** + * User with access to Restricted HTML text format without text editor. + * + * @var \Drupal\user\UserInterface + */ + protected $untrustedUser; + + /** + * User with access to Restricted HTML text format with text editor. + * + * @var \Drupal\user\UserInterface + */ + protected $normalUser; + + /** + * User with access to Restricted HTML text format, dangerous tags allowed + * with text editor. + * + * @var \Drupal\user\UserInterface + */ + protected $trustedUser; + + /** + * User with access to all text formats and text editors. + * + * @var \Drupal\user\UserInterface + */ + protected $privilegedUser; + + protected function setUp() { + parent::setUp(); + + // Create 5 text formats, to cover all potential use cases: + // 1. restricted_without_editor (untrusted: anonymous) + // 2. restricted_with_editor (normal: authenticated) + // 3. restricted_plus_dangerous_tag_with_editor (privileged: trusted) + // 4. unrestricted_without_editor (privileged: admin) + // 5. unrestricted_with_editor (privileged: admin) + // With text formats 2, 3 and 5, we also associate a text editor that does + // not guarantee XSS safety. "restricted" means the text format has XSS + // filters on output, "unrestricted" means the opposite. + $format = FilterFormat::create([ + 'format' => 'restricted_without_editor', + 'name' => 'Restricted HTML, without text editor', + 'weight' => 0, + 'filters' => [ + // A filter of the FilterInterface::TYPE_HTML_RESTRICTOR type. + 'filter_html' => [ + 'status' => 1, + 'settings' => [ + 'allowed_html' => '


', + ], + ], + ], + ]); + $format->save(); + $format = FilterFormat::create([ + 'format' => 'restricted_with_editor', + 'name' => 'Restricted HTML, with text editor', + 'weight' => 1, + 'filters' => [ + // A filter of the FilterInterface::TYPE_HTML_RESTRICTOR type. + 'filter_html' => [ + 'status' => 1, + 'settings' => [ + 'allowed_html' => '


', + ], + ], + ], + ]); + $format->save(); + $editor = Editor::create([ + 'format' => 'restricted_with_editor', + 'editor' => 'unicorn', + ]); + $editor->save(); + $format = FilterFormat::create([ + 'format' => 'restricted_plus_dangerous_tag_with_editor', + 'name' => 'Restricted HTML, dangerous tag allowed, with text editor', + 'weight' => 1, + 'filters' => [ + // A filter of the FilterInterface::TYPE_HTML_RESTRICTOR type. + 'filter_html' => [ + 'status' => 1, + 'settings' => [ + 'allowed_html' => '


', + ], + ], + ], + ]); + $format->save(); + $editor = Editor::create([ + 'format' => 'restricted_plus_dangerous_tag_with_editor', + 'editor' => 'unicorn', + ]); + $editor->save(); + $format = FilterFormat::create([ + 'format' => 'unrestricted_without_editor', + 'name' => 'Unrestricted HTML, without text editor', + 'weight' => 0, + 'filters' => [], + ]); + $format->save(); + $format = FilterFormat::create([ + 'format' => 'unrestricted_with_editor', + 'name' => 'Unrestricted HTML, with text editor', + 'weight' => 1, + 'filters' => [], + ]); + $format->save(); + $editor = Editor::create([ + 'format' => 'unrestricted_with_editor', + 'editor' => 'unicorn', + ]); + $editor->save(); + + // Create node type. + $this->drupalCreateContentType([ + 'type' => 'article', + 'name' => 'Article', + ]); + + // Create 4 users, each with access to different text formats/editors: + // - "untrusted": restricted_without_editor + // - "normal": restricted_with_editor, + // - "trusted": restricted_plus_dangerous_tag_with_editor + // - "privileged": restricted_without_editor, restricted_with_editor, + // restricted_plus_dangerous_tag_with_editor, + // unrestricted_without_editor and unrestricted_with_editor + $this->untrustedUser = $this->drupalCreateUser([ + 'create article content', + 'edit any article content', + 'use text format restricted_without_editor', + ]); + $this->normalUser = $this->drupalCreateUser([ + 'create article content', + 'edit any article content', + 'use text format restricted_with_editor', + ]); + $this->trustedUser = $this->drupalCreateUser([ + 'create article content', + 'edit any article content', + 'use text format restricted_plus_dangerous_tag_with_editor', + ]); + $this->privilegedUser = $this->drupalCreateUser([ + 'create article content', + 'edit any article content', + 'use text format restricted_without_editor', + 'use text format restricted_with_editor', + 'use text format restricted_plus_dangerous_tag_with_editor', + 'use text format unrestricted_without_editor', + 'use text format unrestricted_with_editor', + ]); + + // Create an "article" node for each possible text format, with the same + // sample content, to do our tests on. + $samples = [ + ['author' => $this->untrustedUser->id(), 'format' => 'restricted_without_editor'], + ['author' => $this->normalUser->id(), 'format' => 'restricted_with_editor'], + ['author' => $this->trustedUser->id(), 'format' => 'restricted_plus_dangerous_tag_with_editor'], + ['author' => $this->privilegedUser->id(), 'format' => 'unrestricted_without_editor'], + ['author' => $this->privilegedUser->id(), 'format' => 'unrestricted_with_editor'], + ]; + foreach ($samples as $sample) { + $this->drupalCreateNode([ + 'type' => 'article', + 'body' => [ + ['value' => self::$sampleContent, 'format' => $sample['format']], + ], + 'uid' => $sample['author'], + ]); + } + } + + /** + * Tests initial security: is the user safe without switching text formats? + * + * Tests 8 scenarios. Tests only with a text editor that is not XSS-safe. + */ + public function testInitialSecurity() { + $expected = [ + [ + 'node_id' => 1, + 'format' => 'restricted_without_editor', + // No text editor => no XSS filtering. + 'value' => self::$sampleContent, + 'users' => [ + $this->untrustedUser, + $this->privilegedUser, + ], + ], + [ + 'node_id' => 2, + 'format' => 'restricted_with_editor', + // Text editor => XSS filtering. + 'value' => self::$sampleContentSecured, + 'users' => [ + $this->normalUser, + $this->privilegedUser, + ], + ], + [ + 'node_id' => 3, + 'format' => 'restricted_plus_dangerous_tag_with_editor', + // Text editor => XSS filtering. + 'value' => self::$sampleContentSecuredEmbedAllowed, + 'users' => [ + $this->trustedUser, + $this->privilegedUser, + ], + ], + [ + 'node_id' => 4, + 'format' => 'unrestricted_without_editor', + // No text editor => no XSS filtering. + 'value' => self::$sampleContent, + 'users' => [ + $this->privilegedUser, + ], + ], + [ + 'node_id' => 5, + 'format' => 'unrestricted_with_editor', + // Text editor, no security filter => no XSS filtering. + 'value' => self::$sampleContent, + 'users' => [ + $this->privilegedUser, + ], + ], + ]; + + // Log in as each user that may edit the content, and assert the value. + foreach ($expected as $case) { + foreach ($case['users'] as $account) { + $this->pass(format_string('Scenario: sample %sample_id, %format.', [ + '%sample_id' => $case['node_id'], + '%format' => $case['format'], + ])); + $this->drupalLogin($account); + $this->drupalGet('node/' . $case['node_id'] . '/edit'); + $dom_node = $this->xpath('//textarea[@id="edit-body-0-value"]'); + $this->assertIdentical($case['value'], $dom_node[0]->getText(), 'The value was correctly filtered for XSS attack vectors.'); + } + } + } + + /** + * 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