core/lib/Drupal/Component/Utility/Xss.php | 51 +-
.../ckeditor/Plugin/CKEditorPlugin/Internal.php | 3 +-
.../lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php | 3 +-
.../Drupal/ckeditor/Tests/CKEditorLoadingTest.php | 2 +
core/modules/editor/editor.api.php | 27 +
core/modules/editor/editor.module | 94 ++++
core/modules/editor/editor.routing.yml | 7 +
core/modules/editor/js/editor.js | 72 ++-
.../editor/lib/Drupal/editor/Annotation/Editor.php | 9 +-
.../editor/lib/Drupal/editor/EditorController.php | 43 +-
.../lib/Drupal/editor/EditorXssFilter/Standard.php | 136 +++++
.../lib/Drupal/editor/EditorXssFilterInterface.php | 47 ++
.../editor/lib/Drupal/editor/Plugin/EditorBase.php | 4 +-
.../lib/Drupal/editor/Plugin/EditorManager.php | 1 +
.../Drupal/editor/Plugin/InPlaceEditor/Editor.php | 3 +-
.../lib/Drupal/editor/Tests/EditorLoadingTest.php | 2 +
.../lib/Drupal/editor/Tests/EditorManagerTest.php | 1 +
.../lib/Drupal/editor/Tests/EditorSecurityTest.php | 415 +++++++++++++++
.../editor/Tests/EditorXssFilter/StandardTest.php | 553 ++++++++++++++++++++
.../editor/tests/modules/editor_test.module | 17 +
.../editor_test/EditorXssFilter/Insecure.php | 26 +
.../editor_test/Plugin/Editor/UnicornEditor.php | 5 +-
core/modules/filter/filter.module | 37 +-
.../lib/Drupal/filter/Entity/FilterFormat.php | 3 +-
.../Drupal/filter/Plugin/Filter/FilterAutoP.php | 2 +-
.../Drupal/filter/Plugin/Filter/FilterCaption.php | 2 +-
.../lib/Drupal/filter/Plugin/Filter/FilterHtml.php | 2 +-
.../filter/Plugin/Filter/FilterHtmlCorrector.php | 2 +-
.../filter/Plugin/Filter/FilterHtmlEscape.php | 2 +-
.../filter/Plugin/Filter/FilterHtmlImageSecure.php | 2 +-
.../lib/Drupal/filter/Plugin/Filter/FilterNull.php | 2 +-
.../lib/Drupal/filter/Plugin/Filter/FilterUrl.php | 2 +-
.../lib/Drupal/filter/Plugin/FilterInterface.php | 48 +-
.../lib/Drupal/filter/Tests/FilterAPITest.php | 21 +-
.../lib/Drupal/filter/Tests/FilterSecurityTest.php | 7 +-
.../Plugin/Filter/FilterTestReplace.php | 2 +-
.../Filter/FilterTestRestrictTagsAndAttributes.php | 2 +-
.../Plugin/Filter/FilterTestUncacheable.php | 2 +-
.../Drupal/Tests/Component/Utility/XssTest.php | 77 ++-
39 files changed, 1631 insertions(+), 105 deletions(-)
diff --git a/core/lib/Drupal/Component/Utility/Xss.php b/core/lib/Drupal/Component/Utility/Xss.php
index 0daab37..ef35e2a 100644
--- a/core/lib/Drupal/Component/Utility/Xss.php
+++ b/core/lib/Drupal/Component/Utility/Xss.php
@@ -13,6 +13,18 @@
class Xss {
/**
+ * Indicates that XSS filtering must be applied in whitelist mode: only
+ * specified HTML tags are allowed.
+ */
+ const FILTER_MODE_WHITELIST = TRUE;
+
+ /**
+ * Indicates that XSS filtering must be applied in blacklist mode: only
+ * specified HTML tags are disallowed.
+ */
+ const FILTER_MODE_BLACKLIST = FALSE;
+
+ /**
* The list of html tags allowed by filterAdmin().
*
* @var array
@@ -35,10 +47,14 @@ class Xss {
* javascript:).
*
* @param $string
- * The string with raw HTML in it. It will be stripped of everything that can
- * cause an XSS attack.
- * @param array $allowed_tags
- * An array of allowed tags.
+ * The string with raw HTML in it. It will be stripped of everything that
+ * can cause an XSS attack.
+ * @param array $html_tags
+ * An array of HTML tags.
+ * @param bool $mode
+ * (optional) Defaults to FILTER_MODE_WHITELIST ($html_tags is used as a
+ * whitelist of allowed tags), but can also be set to FILTER_MODE_BLACKLIST
+ * ($html_tags is used as a blacklist of disallowed tags).
*
* @return string
* An XSS safe version of $string, or an empty string if $string is not
@@ -48,14 +64,14 @@ class Xss {
*
* @ingroup sanitization
*/
- public static function filter($string, $allowed_tags = array('a', 'em', 'strong', 'cite', 'blockquote', 'code', 'ul', 'ol', 'li', 'dl', 'dt', 'dd')) {
+ public static function filter($string, $html_tags = array('a', 'em', 'strong', 'cite', 'blockquote', 'code', 'ul', 'ol', 'li', 'dl', 'dt', 'dd'), $mode = Xss::FILTER_MODE_WHITELIST) {
// Only operate on valid UTF-8 strings. This is necessary to prevent cross
// site scripting issues on Internet Explorer 6.
if (!Unicode::validateUtf8($string)) {
return '';
}
// Store the text format.
- static::split($allowed_tags, TRUE);
+ static::split($html_tags, TRUE, $mode);
// Remove NULL characters (ignored by some browsers).
$string = str_replace(chr(0), '', $string);
// Remove Netscape 4 JS entities.
@@ -80,7 +96,7 @@ public static function filter($string, $allowed_tags = array('a', 'em', 'strong'
<[^>]*(>|$) # a string that starts with a <, up until the > or the end of the string
| # or
> # just a >
- )%x', '\Drupal\Component\Utility\Xss::split', $string);
+ )%x', 'static::split', $string);
}
/**
@@ -112,17 +128,22 @@ public static function filterAdmin($string) {
* If $store is TRUE then the array contains the allowed tags.
* If $store is FALSE then the array has one element, the HTML tag to process.
* @param bool $store
- * Whether to store $m.
+ * Whether to store $matches.
+ * @param bool $mode
+ * (optional) Ignored when $store is FALSE, otherwise used to determine
+ * whether $matches is a list of allowed (if FILTER_MODE_WHITELIST) or
+ * disallowed (if FILTER_MODE_BLACKLIST) HTML tags.
*
* @return string
* If the element isn't allowed, an empty string. Otherwise, the cleaned up
* version of the HTML element.
*/
- protected static function split($matches, $store = FALSE) {
- static $allowed_html;
+ protected static function split($matches, $store = FALSE, $mode = Xss::FILTER_MODE_WHITELIST) {
+ static $html_tags, $split_mode;
if ($store) {
- $allowed_html = array_flip($matches);
+ $html_tags = array_flip($matches);
+ $split_mode = $mode;
return;
}
@@ -151,8 +172,12 @@ protected static function split($matches, $store = FALSE) {
$elem = '!--';
}
- if (!isset($allowed_html[strtolower($elem)])) {
- // Disallowed HTML element.
+ // When in whitelist mode, an element is disallowed when not listed.
+ if ($split_mode === static::FILTER_MODE_WHITELIST && !isset($html_tags[strtolower($elem)])) {
+ return '';
+ }
+ // When in blacklist mode, an element is disallowed when listed.
+ elseif ($split_mode === static::FILTER_MODE_BLACKLIST && isset($html_tags[strtolower($elem)])) {
return '';
}
diff --git a/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/CKEditorPlugin/Internal.php b/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/CKEditorPlugin/Internal.php
index e0e23aa..c683e3e 100644
--- a/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/CKEditorPlugin/Internal.php
+++ b/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/CKEditorPlugin/Internal.php
@@ -10,6 +10,7 @@
use Drupal\ckeditor\CKEditorPluginBase;
use Drupal\Component\Utility\NestedArray;
use Drupal\editor\Entity\Editor;
+use Drupal\filter\Plugin\FilterInterface;
/**
* Defines the "internal" plugin (i.e. core plugins part of our CKEditor build).
@@ -287,7 +288,7 @@ protected function generateAllowedContentSetting(Editor $editor) {
// When nothing is disallowed, set allowedContent to true.
$format = entity_load('filter_format', $editor->format);
$filter_types = $format->getFilterTypes();
- if (!in_array(FILTER_TYPE_HTML_RESTRICTOR, $filter_types)) {
+ if (!in_array(FilterInterface::TYPE_HTML_RESTRICTOR, $filter_types)) {
return TRUE;
}
// Generate setting that accurately reflects allowed tags and attributes.
diff --git a/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php b/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php
index 6b880e3..21db722 100644
--- a/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php
+++ b/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php
@@ -24,7 +24,8 @@
* id = "ckeditor",
* label = @Translation("CKEditor"),
* supports_content_filtering = TRUE,
- * supports_inline_editing = TRUE
+ * supports_inline_editing = TRUE,
+ * is_xss_safe = FALSE
* )
*/
class CKEditor extends EditorBase implements ContainerFactoryPluginInterface {
diff --git a/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorLoadingTest.php b/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorLoadingTest.php
index e97c7e7..fa3fc7a 100644
--- a/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorLoadingTest.php
+++ b/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorLoadingTest.php
@@ -104,6 +104,7 @@ function testLoading() {
'editor' => 'ckeditor',
'editorSettings' => $ckeditor_plugin->getJSSettings($editor),
'editorSupportsContentFiltering' => TRUE,
+ 'isXssSafe' => FALSE,
)));
$this->assertTrue($editor_settings_present, "Text Editor module's JavaScript settings are on the page.");
$this->assertIdentical($expected, $settings['editor'], "Text Editor module's JavaScript settings on the page are correct.");
@@ -131,6 +132,7 @@ function testLoading() {
'editor' => 'ckeditor',
'editorSettings' => $ckeditor_plugin->getJSSettings($editor),
'editorSupportsContentFiltering' => TRUE,
+ 'isXssSafe' => FALSE,
)));
$this->assertTrue($editor_settings_present, "Text Editor module's JavaScript settings are on the page.");
$this->assertIdentical($expected, $settings['editor'], "Text Editor module's JavaScript settings on the page are correct.");
diff --git a/core/modules/editor/editor.api.php b/core/modules/editor/editor.api.php
index fe9548b..681399a 100644
--- a/core/modules/editor/editor.api.php
+++ b/core/modules/editor/editor.api.php
@@ -5,6 +5,8 @@
* Documentation for Text Editor API.
*/
+use Drupal\filter\FilterFormatInterface;
+
/**
* @addtogroup hooks
* @{
@@ -104,5 +106,30 @@ function hook_editor_js_settings_alter(array &$settings, array $formats) {
}
/**
+ * Modifies the text editor XSS filter that will used for the given text format.
+ *
+ * Is only called when an EditorXssFilter will effectively be used; this hook
+ * does not allow one to alter that decision.
+ *
+ * @param string &$editor_xss_filter_class
+ * The text editor XSS filter class that will be used.
+ * @param \Drupal\filter\FilterFormatInterface $format
+ * The text format configuration entity. Provides context based upon which
+ * one may want to adjust the filtering.
+ * @param \Drupal\filter\FilterFormatInterface $original_format|null
+ * (optional) The original text format configuration entity (when switching
+ * text formats/editors). Also provides context based upon which one may want
+ * to adjust the filtering.
+ *
+ * @see \Drupal\editor\EditorXssFilterInterface
+ */
+function hook_editor_xss_filter_alter(&$editor_xss_filter_class, FilterFormatInterface $format, FilterFormatInterface $original_format = NULL) {
+ $filters = $format->filters()->getAll();
+ if (isset($filters['filter_wysiwyg']) && $filters['filter_wysiwyg']->status) {
+ $editor_xss_filter_class = '\Drupal\filter_wysiwyg\EditorXssFilter\WysiwygFilter';
+ }
+}
+
+/**
* @} End of "addtogroup hooks".
*/
diff --git a/core/modules/editor/editor.module b/core/modules/editor/editor.module
index 1358910..c73c071 100644
--- a/core/modules/editor/editor.module
+++ b/core/modules/editor/editor.module
@@ -10,6 +10,8 @@
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\EntityInterface;
use Drupal\field\Field;
+use Drupal\filter\FilterFormatInterface;
+use Drupal\filter\Plugin\FilterInterface;
/**
* Implements hook_help().
@@ -378,10 +380,102 @@ function editor_pre_render_format($element) {
$manager = \Drupal::service('plugin.manager.editor');
$element['#attached'] = NestedArray::mergeDeep($element['#attached'], $manager->getAttachments($format_ids));
+ // Apply XSS filters when editing content if necessary. Some types of text
+ // editors cannot guarantee that the end user won't become a victim of XSS.
+ if (!empty($element['value']['#value'])) {
+ $original = $element['value']['#value'];
+ $format = entity_load('filter_format', $element['format']['format']['#value']);
+
+ // Ensure XSS-safety for the current text format/editor.
+ $filtered = editor_filter_xss($original, $format);
+ if ($filtered !== FALSE) {
+ $element['value']['#value'] = $filtered;
+ }
+
+ // Only when the user has access to multiple text formats, we must add data-
+ // attributes for the original value and change tracking, because they are
+ // only necessary when the end user can switch between text formats/editors.
+ if ($element['format']['format']['#access']) {
+ $element['value']['#attributes']['data-editor-value-is-changed'] = 'false';
+ $element['value']['#attributes']['data-editor-value-original'] = $original;
+ }
+ }
+
return $element;
}
/**
+ * Applies text editor XSS filtering.
+ *
+ * @param string $html
+ * The HTML string that will be passed to the text editor.
+ * @param \Drupal\filter\FilterFormatInterface $format
+ * The text format whose text editor will be used.
+ * @param \Drupal\filter\FilterFormatInterface $original_format|null
+ * (optional) The original text format (i.e. when switching text formats,
+ * $format is the text format that is going to be used, $original_format is
+ * the one that was being used initially, the one that is stored in the
+ * database when editing).
+ *
+ * @return string|false
+ * FALSE when no XSS filtering needs to be applied (either because no text
+ * editor is associated with the text format, or because the text editor is
+ * safe from XSS attacks, or because the text format does not use any XSS
+ * protection filters), otherwise the XSS filtered string.
+ *
+ * @see https://drupal.org/node/2099741
+ */
+function editor_filter_xss($html, FilterFormatInterface $format, FilterFormatInterface $original_format = NULL) {
+ $editor = editor_load($format->id());
+
+ // If no text editor is associated with this text format, then we don't need
+ // text editor XSS filtering either.
+ if (!isset($editor)) {
+ return FALSE;
+ }
+
+ // If the text editor associated with this text format guarantees security,
+ // then we also don't need text editor XSS filtering.
+ $definition = \Drupal::service('plugin.manager.editor')->getDefinition($editor->editor);
+ if ($definition['is_xss_safe'] === TRUE) {
+ return FALSE;
+ }
+
+ // If there is no filter preventing XSS attacks in the text format being used,
+ // then no text editor XSS filtering is needed either. (Because then the
+ // editing user can already be attacked by merely viewing the content.)
+ // e.g.: an admin user creates content in Full HTML and then edits it, no text
+ // format switching happens; in this case, no text editor XSS filtering is
+ // desirable, because it would strip style attributes, amongst others.
+ $current_filter_types = $format->getFilterTypes();
+ if (!in_array(FilterInterface::TYPE_HTML_RESTRICTOR, $current_filter_types, TRUE)) {
+ if ($original_format === NULL) {
+ return FALSE;
+ }
+ // Unless we are switching from another text format, in which case we must
+ // first check whether a filter preventing XSS attacks is used in that text
+ // format, and if so, we must still apply XSS filtering.
+ // e.g.: an anonymous user creates content in Restricted HTML, an admin user
+ // edits it (then no XSS filtering is applied because no text editor is
+ // used), and switches to Full HTML (for which a text editor is used). Then
+ // we must apply XSS filtering to protect the admin user.
+ else {
+ $original_filter_types = $original_format->getFilterTypes();
+ if (!in_array(FilterInterface::TYPE_HTML_RESTRICTOR, $original_filter_types, TRUE)) {
+ return FALSE;
+ }
+ }
+ }
+
+ // Otherwise, apply the text editor XSS filter. We use the default one unless
+ // a module tells us to use a different one.
+ $editor_xss_filter_class = '\Drupal\editor\EditorXssFilter\Standard';
+ \Drupal::moduleHandler()->alter('editor_xss_filter', $editor_xss_filter_class, $format, $original_format);
+
+ return call_user_func($editor_xss_filter_class . '::filterXss', $html, $format, $original_format);
+}
+
+/**
* Implements hook_entity_insert().
*/
function editor_entity_insert(EntityInterface $entity) {
diff --git a/core/modules/editor/editor.routing.yml b/core/modules/editor/editor.routing.yml
index bf9d360..2a753f5 100644
--- a/core/modules/editor/editor.routing.yml
+++ b/core/modules/editor/editor.routing.yml
@@ -1,3 +1,10 @@
+editor.filter_xss:
+ path: '/editor/filter_xss/{filter_format}'
+ defaults:
+ _controller: '\Drupal\editor\EditorController::filterXss'
+ requirements:
+ _entity_access: 'filter_format.view'
+
editor.field_untransformed_text:
path: '/editor/{entity_type}/{entity}/{field_name}/{langcode}/{view_mode_id}'
defaults:
diff --git a/core/modules/editor/js/editor.js b/core/modules/editor/js/editor.js
index ebaf5e4..d538b68 100644
--- a/core/modules/editor/js/editor.js
+++ b/core/modules/editor/js/editor.js
@@ -34,14 +34,22 @@
* attached.
*/
function changeTextEditor($formatSelector, activeFormatID, newFormatID) {
+ var originalFormatID = activeFormatID;
var field = findFieldForFormatSelector($formatSelector);
// Detach the current editor (if any) and attach a new editor.
if (drupalSettings.editor.formats[activeFormatID]) {
Drupal.editorDetach(field, drupalSettings.editor.formats[activeFormatID]);
}
+ // When no text editor is currently active, stop tracking changes.
+ else if (!drupalSettings.editor.formats[activeFormatID]) {
+ $(field).off('.editor');
+ }
activeFormatID = newFormatID;
+
+ // Attach the new text editor (if any).
if (drupalSettings.editor.formats[activeFormatID]) {
- Drupal.editorAttach(field, drupalSettings.editor.formats[activeFormatID]);
+ var format = drupalSettings.editor.formats[activeFormatID];
+ filterXssWhenSwitching(field, format, originalFormatID, Drupal.editorAttach);
}
$formatSelector.attr('data-editor-active-text-format', newFormatID);
}
@@ -135,10 +143,22 @@
$this.attr('data-editor-active-text-format', activeFormatID);
var field = findFieldForFormatSelector($this);
- // Directly attach this editor, if the text format is enabled.
+ // Directly attach this text editor, if the text format is enabled.
if (settings.editor.formats[activeFormatID]) {
+ // XSS protection for the current text format/editor is performed on the
+ // server side, so we don't need to do anything special here.
Drupal.editorAttach(field, settings.editor.formats[activeFormatID]);
}
+ // When there is no text editor for this text format, still track changes,
+ // because the user has the ability to switch to some text editor, other-
+ // wise this code would not be executed.
+ else {
+ $(field).on('change.editor keypress.editor', function () {
+ field.setAttribute('data-editor-value-is-changed', 'true');
+ // Just knowing that the value was changed is enough, stop tracking.
+ $(field).off('.editor');
+ });
+ }
// Attach onChange handler to text format selector element.
if ($this.is('select')) {
@@ -200,6 +220,10 @@
// happen within the text editor.
Drupal.editors[format.editor].onChange(field, function () {
$(field).trigger('formUpdated');
+
+ // Keep track of changes, so we know what to do when switching text
+ // formats and guaranteeing XSS protection.
+ field.setAttribute('data-editor-value-is-changed', 'true');
});
}
};
@@ -214,7 +238,51 @@
}
Drupal.editors[format.editor].detach(field, format, trigger);
+
+ // Restore the original value if the user didn't make any changes yet.
+ if (field.getAttribute('data-editor-value-is-changed') === 'false') {
+ field.value = field.getAttribute('data-editor-value-original');
+ }
}
};
+ /**
+ * Filter away XSS attack vectors when switching text formats.
+ *
+ * @param DOM field
+ * The textarea DOM element.
+ * @param Object format
+ * The text format that's being activated, from drupalSettings.editor.formats.
+ * @param String originalFormatID
+ * The text format ID of the original text format.
+ * @param Function callback
+ * A callback to be called (with no parameters) after the field's value has
+ * been XSS filtered.
+ */
+ function filterXssWhenSwitching (field, format, originalFormatID, callback) {
+ // A text editor that already is XSS-safe needs no additional measures.
+ if (format.editor.isXssSafe) {
+ callback(field, format);
+ }
+ // Otherwise, ensure XSS safety: let the server XSS filter this value.
+ else {
+ $.ajax({
+ url: Drupal.url('editor/filter_xss/' + format.format),
+ type: 'POST',
+ data: {
+ 'value': field.value,
+ 'original_format_id': originalFormatID
+ },
+ dataType: 'json',
+ success: function (xssFilteredValue) {
+ // If the server returns false, then no XSS filtering is needed.
+ if (xssFilteredValue !== false) {
+ field.value = xssFilteredValue;
+ }
+ callback(field, format);
+ }
+ });
+ }
+ }
+
})(jQuery, Drupal, drupalSettings);
diff --git a/core/modules/editor/lib/Drupal/editor/Annotation/Editor.php b/core/modules/editor/lib/Drupal/editor/Annotation/Editor.php
index be126b4..32ac3db 100644
--- a/core/modules/editor/lib/Drupal/editor/Annotation/Editor.php
+++ b/core/modules/editor/lib/Drupal/editor/Annotation/Editor.php
@@ -42,8 +42,15 @@ class Editor extends Plugin {
/**
* Whether the editor supports the inline editing provided by the Edit module.
*
- * @var boolean
+ * @var bool
*/
public $supports_inline_editing;
+ /**
+ * Whether this text editor is not vulnerable to XSS attacks.
+ *
+ * @var bool
+ */
+ public $is_xss_safe;
+
}
diff --git a/core/modules/editor/lib/Drupal/editor/EditorController.php b/core/modules/editor/lib/Drupal/editor/EditorController.php
index 27c874d..0e65571 100644
--- a/core/modules/editor/lib/Drupal/editor/EditorController.php
+++ b/core/modules/editor/lib/Drupal/editor/EditorController.php
@@ -10,17 +10,21 @@
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\OpenModalDialogCommand;
use Drupal\Core\Ajax\CloseModalDialogCommand;
+use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityInterface;
use Drupal\editor\Ajax\GetUntransformedTextCommand;
use Drupal\editor\Form\EditorImageDialog;
use Drupal\editor\Form\EditorLinkDialog;
-use Drupal\filter\Entity\FilterFormat;
-use Symfony\Component\DependencyInjection\ContainerAware;
+use Drupal\filter\Plugin\FilterInterface;
+use Drupal\filter\FilterFormatInterface;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Returns responses for Editor module routes.
*/
-class EditorController extends ContainerAware {
+class EditorController extends ControllerBase {
/**
* Returns an Ajax response to render a text field without transformation filters.
@@ -43,10 +47,41 @@ public function getUntransformedText(EntityInterface $entity, $field_name, $lang
// Direct text editing is only supported for single-valued fields.
$field = $entity->getTranslation($langcode)->$field_name;
- $editable_text = check_markup($field->value, $field->format, $langcode, FALSE, array(FILTER_TYPE_TRANSFORM_REVERSIBLE, FILTER_TYPE_TRANSFORM_IRREVERSIBLE));
+ $editable_text = check_markup($field->value, $field->format, $langcode, FALSE, array(FilterInterface::TYPE_TRANSFORM_REVERSIBLE, FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE));
$response->addCommand(new GetUntransformedTextCommand($editable_text));
return $response;
}
+ /**
+ * Apply the necessary XSS filtering for using a certain text format's editor.
+ *
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ * The current request object.
+ * @param \Drupal\filter\FilterFormatInterface $filter_format
+ * The text format whose text editor (if any) will be used.
+ *
+ * @return \Symfony\Component\HttpFoundation\JsonResponse
+ * A JSON response containing the XSS-filtered value.
+ *
+ * @see editor_filter_xss()
+ */
+ public function filterXss(Request $request, FilterFormatInterface $filter_format) {
+ $value = $request->request->get('value');
+ if (!isset($value)) {
+ throw new NotFoundHttpException();
+ }
+
+ // The original_format parameter will only exist when switching text format.
+ $original_format_id = $request->request->get('original_format_id');
+ $original_format = NULL;
+ if (isset($original_format_id)) {
+ $original_format = $this->entityManager()
+ ->getStorageController('filter_format')
+ ->load($original_format_id);
+ }
+
+ return new JsonResponse(editor_filter_xss($value, $filter_format, $original_format));
+ }
+
}
diff --git a/core/modules/editor/lib/Drupal/editor/EditorXssFilter/Standard.php b/core/modules/editor/lib/Drupal/editor/EditorXssFilter/Standard.php
new file mode 100644
index 0000000..3f93863
--- /dev/null
+++ b/core/modules/editor/lib/Drupal/editor/EditorXssFilter/Standard.php
@@ -0,0 +1,136 @@
+,