Hello, Dumbo Octopus!

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

Hello, Dumbo Octopus!

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

Hello, Dumbo Octopus!

alert(0)'; /** * User with access to Restricted HTML text format without text editor. */ protected $untrustedUser; /** * User with access to Restricted HTML text format with text editor. */ protected $normalUser; /** * User with access to Restricted HTML text format, dangerous tags allowed * with text editor. */ protected $trustedUser; /** * User with access to all text formats and text editors. */ protected $privilegedUser; /** * Define this test class. */ public static function getinfo() { return array( 'name' => 'Wysiwyg XSS filtering', 'description' => 'Ensure Wysiwyg runs the configured filters', 'group' => 'wysiwyg', ); } /** * {@inheritdoc} */ public function setUp(array $modules = array()) { $modules[] = 'wysiwyg_test'; parent::setUp($modules); // 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 = new stdClass(); $format->format = 'restricted_without_editor'; $format->name = 'Restricted HTML, without text editor'; $format->filters = array( 'filter_html' => array( 'status' => 1, 'settings' => array( 'allowed_html' => '


', 'filter_html_help' => 1, 'filter_html_nofollow' => 0, ), ), ); filter_format_save($format); $format = new stdClass(); $format->format = 'restricted_with_editor'; $format->name = 'Restricted HTML, with text editor'; $format->filters = array( 'filter_html' => array( 'status' => 1, 'settings' => array( 'allowed_html' => '


', 'filter_html_help' => 1, 'filter_html_nofollow' => 0, ), ), ); filter_format_save($format); db_insert('wysiwyg')->fields(array( 'format' => 'restricted_with_editor', 'editor' => 'unicorn', 'settings' => serialize(array( 'default' => 1, )), ))->execute(); $format = new stdClass(); $format->format = 'restricted_plus_dangerous_tag_with_editor'; $format->name = 'Restricted HTML, dangerous tag allowed, with text editor'; $format->filters = array( 'filter_html' => array( 'status' => 1, 'settings' => array( 'allowed_html' => '


', 'filter_html_help' => 1, 'filter_html_nofollow' => 0, ), ), ); filter_format_save($format); db_insert('wysiwyg')->fields(array( 'format' => 'restricted_plus_dangerous_tag_with_editor', 'editor' => 'unicorn', 'settings' => serialize(array( 'default' => 1, )), ))->execute(); $format = new stdClass(); $format->format = 'unrestricted_without_editor'; $format->name = 'Unrestricted HTML, without text editor'; $format->filters = array(); filter_format_save($format); $format = new stdClass(); $format->format = 'unrestricted_with_editor'; $format->name = 'Unrestricted HTML, with text editor'; $format->filters = array(); filter_format_save($format); db_insert('wysiwyg')->fields(array( 'format' => 'unrestricted_with_editor', 'editor' => 'unicorn', 'settings' => serialize(array( 'default' => 1, )), ))->execute(); filter_formats_reset(); wysiwyg_profile_cache_clear(); // Create node type. $this->drupalCreateContentType(array( 'type' => 'textblob', 'name' => 'Textblob', )); // 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(array( 'create textblob content', 'edit any textblob content', 'use text format restricted_without_editor', )); $this->normalUser = $this->drupalCreateUser(array( 'create textblob content', 'edit any textblob content', 'use text format restricted_with_editor', )); $this->trustedUser = $this->drupalCreateUser(array( 'create textblob content', 'edit any textblob content', 'use text format restricted_plus_dangerous_tag_with_editor', )); $this->privilegedUser = $this->drupalCreateUser(array( 'create textblob content', 'edit any textblob 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 "textblob" node for each possible text format, with the same // sample content, to do our tests on. $samples = array( array('author' => $this->untrustedUser->uid, 'format' => 'restricted_without_editor'), array('author' => $this->normalUser->uid, 'format' => 'restricted_with_editor'), array('author' => $this->trustedUser->uid, 'format' => 'restricted_plus_dangerous_tag_with_editor'), array('author' => $this->privilegedUser->uid, 'format' => 'unrestricted_without_editor'), array('author' => $this->privilegedUser->uid, 'format' => 'unrestricted_with_editor'), ); foreach ($samples as $sample) { $this->drupalCreateNode(array( 'type' => 'textblob', 'body' => array( LANGUAGE_NONE => array(array('value' => self::$sampleContent, 'format' => $sample['format'])), ), 'uid' => $sample['author'], )); } } /** * Test filtering on the initial content loaded into the form fields. */ public function testInitialSecurity() { $expected = array( array( 'node_id' => 1, 'format' => 'restricted_without_editor', // No text editor => no XSS filtering. 'value' => '', 'users' => array( $this->untrustedUser, $this->privilegedUser, ), ), array( 'node_id' => 2, 'format' => 'restricted_with_editor', // Text editor => XSS filtering. 'value' => self::$sampleContentSecured, 'users' => array( $this->normalUser, $this->privilegedUser, ), ), array( 'node_id' => 3, 'format' => 'restricted_plus_dangerous_tag_with_editor', // Text editor => XSS filtering. 'value' => self::$sampleContentSecuredEmbedAllowed, 'users' => array( $this->trustedUser, $this->privilegedUser, ), ), array( 'node_id' => 4, 'format' => 'unrestricted_without_editor', // No text editor => no XSS filtering. 'value' => '', 'users' => array( $this->privilegedUser, ), ), array( 'node_id' => 5, 'format' => 'unrestricted_with_editor', // Text editor, no security filter => no XSS filtering. 'value' => '', 'users' => array( $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.', array( '%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-und-0-value"]'); // Verify the filtered attribute value. This is different from D8 which // puts the original content in an attribute and filtered content as the // field value. Wysiwyg does the opposite to not unintentionally modify // the data if JavaScript is not there to swap it back before a save. $this->assertIdentical($case['value'], (string) $dom_node[0]['data-wysiwyg-value-filtered'], '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