MDL-78714 editor_tiny: Add xss_sanitize option to TinyMCE
To address a potential data loss issue, a feature introduced in TinyMCE 6.4.0 to disable client-side XSS sanitisation must be backported.
This commit is contained in:
@@ -1044,8 +1044,13 @@
|
||||
};
|
||||
|
||||
const parseAndSanitize = (editor, context, html) => {
|
||||
const getEditorOption = editor.options.get;
|
||||
const sanitize = getEditorOption('xss_sanitization');
|
||||
const validate = shouldFilterHtml(editor);
|
||||
return Parser(editor.schema, { validate }).parse(html, { context });
|
||||
return Parser(editor.schema, {
|
||||
sanitize,
|
||||
validate
|
||||
}).parse(html, { context });
|
||||
};
|
||||
|
||||
const setup$1 = editor => {
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -5,6 +5,13 @@ Please note that we have a clone of the official TinyMCE repository which contai
|
||||
Each Moodle branch has a similar branch in the https://github.com/moodlehq/tinymce.
|
||||
The Moodle `master` branch is named as the upcoming STABLE branch name, for example during the development of Moodle 4.2.0, the upcoming STABLE branch name will be MOODLE_402_STABLE.
|
||||
|
||||
## Patches included in this release
|
||||
|
||||
- MDL-78714: Add support for disabling XSS Sanitisation (TINY-9600)
|
||||
|
||||
Please note: TinyMCE issue numbers are related to bugs in their private issue
|
||||
tracker. See git history of their repository for relevant information.
|
||||
|
||||
## Upgrade procedure for TinyMCE Editor
|
||||
|
||||
1. Store an environment variable to the Tiny directory in the Moodle repository (the current directory).
|
||||
|
||||
+6
-3
@@ -1300,18 +1300,19 @@ interface DomParserSettings {
|
||||
allow_html_in_named_anchor?: boolean;
|
||||
allow_script_urls?: boolean;
|
||||
allow_unsafe_link_target?: boolean;
|
||||
blob_cache?: BlobCache;
|
||||
convert_fonts_to_spans?: boolean;
|
||||
document?: Document;
|
||||
fix_list_elements?: boolean;
|
||||
font_size_legacy_values?: string;
|
||||
forced_root_block?: boolean | string;
|
||||
forced_root_block_attrs?: Record<string, string>;
|
||||
inline_styles?: boolean;
|
||||
preserve_cdata?: boolean;
|
||||
remove_trailing_brs?: boolean;
|
||||
root_name?: string;
|
||||
sanitize?: boolean;
|
||||
validate?: boolean;
|
||||
inline_styles?: boolean;
|
||||
blob_cache?: BlobCache;
|
||||
document?: Document;
|
||||
}
|
||||
interface DomParser {
|
||||
schema: Schema;
|
||||
@@ -1904,6 +1905,7 @@ interface BaseEditorOptions {
|
||||
visual_anchor_class?: string;
|
||||
visual_table_class?: string;
|
||||
width?: number | string;
|
||||
xss_sanitization?: boolean;
|
||||
disable_nodechange?: boolean;
|
||||
forced_plugins?: string | string[];
|
||||
plugin_base_urls?: Record<string, string>;
|
||||
@@ -1993,6 +1995,7 @@ interface EditorOptions extends NormalizedEditorOptions {
|
||||
visual_anchor_class: string;
|
||||
visual_table_class: string;
|
||||
width: number | string;
|
||||
xss_sanitization: boolean;
|
||||
}
|
||||
declare type StyleMap = Record<string, string | number>;
|
||||
interface StylesSettings {
|
||||
|
||||
@@ -7124,6 +7124,10 @@
|
||||
processor: 'boolean',
|
||||
default: true
|
||||
});
|
||||
registerOption('xss_sanitization', {
|
||||
processor: 'boolean',
|
||||
default: true
|
||||
});
|
||||
editor.on('ScriptsLoaded', () => {
|
||||
registerOption('directionality', {
|
||||
processor: 'string',
|
||||
@@ -7223,6 +7227,7 @@
|
||||
const getEditableClass = option('editable_class');
|
||||
const getNonEditableRegExps = option('noneditable_regexp');
|
||||
const shouldPreserveCData = option('preserve_cdata');
|
||||
const shouldSanitizeXss = option('xss_sanitization');
|
||||
const hasTextPatternsLookup = editor => editor.options.isSet('text_patterns_lookup');
|
||||
const getFontStyleValues = editor => Tools.explode(editor.options.get('font_size_style_values'));
|
||||
const getFontSizeClasses = editor => Tools.explode(editor.options.get('font_size_classes'));
|
||||
@@ -16725,6 +16730,7 @@
|
||||
const defaultedSettings = {
|
||||
validate: true,
|
||||
root_name: 'body',
|
||||
sanitize: true,
|
||||
...settings
|
||||
};
|
||||
const parser = new DOMParser();
|
||||
@@ -16735,8 +16741,10 @@
|
||||
const content = isSpecialRoot ? `<${ rootName }>${ html }</${ rootName }>` : html;
|
||||
const wrappedHtml = format === 'xhtml' ? `<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>${ content }</body></html>` : `<body>${ content }</body>`;
|
||||
const body = parser.parseFromString(wrappedHtml, mimeType).body;
|
||||
purify.sanitize(body, getPurifyConfig(defaultedSettings, mimeType));
|
||||
purify.removed = [];
|
||||
if (defaultedSettings.sanitize) {
|
||||
purify.sanitize(body, getPurifyConfig(defaultedSettings, mimeType));
|
||||
purify.removed = [];
|
||||
}
|
||||
return isSpecialRoot ? body.firstChild : body;
|
||||
};
|
||||
const addNodeFilter = nodeFilterRegistry.addFilter;
|
||||
@@ -16855,7 +16863,7 @@
|
||||
};
|
||||
|
||||
const serializeContent = content => isTreeNode(content) ? HtmlSerializer({ validate: false }).serialize(content) : content;
|
||||
const withSerializedContent = (content, fireEvent) => {
|
||||
const withSerializedContent = (content, fireEvent, sanitize) => {
|
||||
const serializedContent = serializeContent(content);
|
||||
const eventArgs = fireEvent(serializedContent);
|
||||
if (eventArgs.isDefaultPrevented()) {
|
||||
@@ -16864,7 +16872,8 @@
|
||||
if (eventArgs.content !== serializedContent) {
|
||||
const rootNode = DomParser({
|
||||
validate: false,
|
||||
forced_root_block: false
|
||||
forced_root_block: false,
|
||||
sanitize
|
||||
}).parse(eventArgs.content, { context: content.name });
|
||||
return {
|
||||
...eventArgs,
|
||||
@@ -16899,10 +16908,10 @@
|
||||
if (args.no_events) {
|
||||
return content;
|
||||
} else {
|
||||
const processedEventArgs = withSerializedContent(content, c => fireGetContent(editor, {
|
||||
const processedEventArgs = withSerializedContent(content, content => fireGetContent(editor, {
|
||||
...args,
|
||||
content: c
|
||||
}));
|
||||
content
|
||||
}), shouldSanitizeXss(editor));
|
||||
return processedEventArgs.content;
|
||||
}
|
||||
};
|
||||
@@ -16913,7 +16922,7 @@
|
||||
const processedEventArgs = withSerializedContent(args.content, content => fireBeforeSetContent(editor, {
|
||||
...args,
|
||||
content
|
||||
}));
|
||||
}), shouldSanitizeXss(editor));
|
||||
if (processedEventArgs.isDefaultPrevented()) {
|
||||
fireSetContent(editor, processedEventArgs);
|
||||
return Result.error(undefined);
|
||||
@@ -24566,7 +24575,7 @@
|
||||
};
|
||||
|
||||
const preProcess = (editor, html) => {
|
||||
const parser = DomParser({}, editor.schema);
|
||||
const parser = DomParser({ sanitize: shouldSanitizeXss(editor) }, editor.schema);
|
||||
parser.addNodeFilter('meta', nodes => {
|
||||
Tools.each(nodes, node => {
|
||||
node.remove();
|
||||
@@ -26859,6 +26868,7 @@
|
||||
remove_trailing_brs: getOption('remove_trailing_brs'),
|
||||
inline_styles: getOption('inline_styles'),
|
||||
root_name: getRootName(editor),
|
||||
sanitize: getOption('xss_sanitization'),
|
||||
validate: true,
|
||||
blob_cache: blobCache,
|
||||
document: editor.getDoc()
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user