mirror of
https://github.com/moodle/moodle.git
synced 2025-01-19 06:18:28 +01:00
Merge branch 'MDL-78714-401' of https://github.com/andrewnicols/moodle into MOODLE_401_STABLE
This commit is contained in:
commit
4732fa0b13
2
lib/editor/tiny/amd/build/editor.min.js
vendored
2
lib/editor/tiny/amd/build/editor.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -241,8 +241,17 @@ const getStandardConfig = (target, tinyMCE, options, plugins) => {
|
||||
// eslint-disable-next-line camelcase
|
||||
a11y_advanced_options: true,
|
||||
|
||||
// Ensure that scripts are recognized as valid elements.
|
||||
// eslint-disable-next-line camelcase
|
||||
extended_valid_elements: 'script[*]',
|
||||
|
||||
// Disable XSS Sanitisation.
|
||||
// We do this in PHP.
|
||||
// https://www.tiny.cloud/docs/tinymce/6/security/#turning-dompurify-off
|
||||
// Note: This feature has been backported from TinyMCE 6.4.0.
|
||||
// eslint-disable-next-line camelcase
|
||||
xss_sanitization: false,
|
||||
|
||||
// Disable quickbars entirely.
|
||||
// The UI is not ideal and we'll wait for it to improve in future before we enable it in Moodle.
|
||||
// eslint-disable-next-line camelcase
|
||||
|
@ -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 => {
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,5 +1,17 @@
|
||||
# This is a description for the TinyMCE 6 library integration with Moodle.
|
||||
|
||||
Please note that we have a clone of the official TinyMCE repository which contains the working build and branch for each release. This ensures build repeatability and gives us the ability to patch stable versions of Moodle for security fixes where relevant.
|
||||
|
||||
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)
|
||||
- MDL-77470: Fix for CVE-2022-23494
|
||||
|
||||
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).
|
||||
@ -10,7 +22,7 @@
|
||||
|
||||
2. Check out a clean copy of TinyMCE of the target version.
|
||||
|
||||
```
|
||||
```../../
|
||||
tinymce=`mktemp -d`
|
||||
cd "${tinymce}"
|
||||
git clone https://github.com/tinymce/tinymce.git
|
||||
@ -25,21 +37,43 @@
|
||||
sed -i 's/"target.*es.*",/"target": "es2020",/' tsconfig.shared.json
|
||||
```
|
||||
|
||||
4. Rebuild TinyMCE
|
||||
4. Install dependencies
|
||||
|
||||
```
|
||||
yarn
|
||||
yarn build
|
||||
yarn
|
||||
```
|
||||
|
||||
5. Remove the old TinyMCE configuration and replace it with the newly built version.
|
||||
5. Check in the base changes
|
||||
|
||||
```
|
||||
rm -rf "${MOODLEDIR}/js"
|
||||
cp -r modules/tinymce/js "${MOODLEDIR}/js"
|
||||
git commit -m 'MDL: Add build configuration'
|
||||
```
|
||||
|
||||
6. Check the (Release notes)[https://www.tiny.cloud/docs/tinymce/6/release-notes/] for any new plugins, premium plugins, menu items, or buttons and add them to classes/manager.php
|
||||
6. Apply any necessary security patches.
|
||||
7. Rebuild TinyMCE
|
||||
|
||||
```
|
||||
yarn
|
||||
yarn build
|
||||
```
|
||||
|
||||
8. Remove the old TinyMCE configuration and replace it with the newly built version.
|
||||
|
||||
```
|
||||
rm -rf "${MOODLEDIR}/js"
|
||||
cp -r modules/tinymce/js "${MOODLEDIR}/js"
|
||||
```
|
||||
|
||||
9. Push the build to MoodleHQ for future change support
|
||||
|
||||
```
|
||||
# Tag the next Moodle version.
|
||||
git tag v4.2.0
|
||||
git remote add moodlehq --tags
|
||||
git push moodlehq MOODLE_402_STABLE
|
||||
```
|
||||
|
||||
10. Check the (Release notes)[https://www.tiny.cloud/docs/tinymce/6/release-notes/] for any new plugins, premium plugins, menu items, or buttons and add them to classes/manager.php
|
||||
|
||||
## Update procedure for included TinyMCE translations
|
||||
|
@ -26917,7 +26917,6 @@
|
||||
var _attr = attr, name = _attr.name, namespaceURI = _attr.namespaceURI;
|
||||
value = name === 'value' ? attr.value : stringTrim(attr.value);
|
||||
lcName = transformCaseFunc(name);
|
||||
var initValue = value;
|
||||
hookEvent.attrName = lcName;
|
||||
hookEvent.attrValue = value;
|
||||
hookEvent.keepAttr = true;
|
||||
@ -26927,8 +26926,8 @@
|
||||
if (hookEvent.forceKeepAttr) {
|
||||
continue;
|
||||
}
|
||||
_removeAttribute(name, currentNode);
|
||||
if (!hookEvent.keepAttr) {
|
||||
_removeAttribute(name, currentNode);
|
||||
continue;
|
||||
}
|
||||
if (regExpTest(/\/>/i, value)) {
|
||||
@ -26941,19 +26940,16 @@
|
||||
}
|
||||
var lcTag = transformCaseFunc(currentNode.nodeName);
|
||||
if (!_isValidAttribute(lcTag, lcName, value)) {
|
||||
_removeAttribute(name, currentNode);
|
||||
continue;
|
||||
}
|
||||
if (value !== initValue) {
|
||||
try {
|
||||
if (namespaceURI) {
|
||||
currentNode.setAttributeNS(namespaceURI, name, value);
|
||||
} else {
|
||||
currentNode.setAttribute(name, value);
|
||||
}
|
||||
} catch (_) {
|
||||
_removeAttribute(name, currentNode);
|
||||
try {
|
||||
if (namespaceURI) {
|
||||
currentNode.setAttributeNS(namespaceURI, name, value);
|
||||
} else {
|
||||
currentNode.setAttribute(name, value);
|
||||
}
|
||||
arrayPop(DOMPurify.removed);
|
||||
} catch (_) {
|
||||
}
|
||||
}
|
||||
_executeHook('afterSanitizeAttributes', currentNode, null);
|
||||
|
File diff suppressed because one or more lines are too long
3
lib/editor/tiny/js/tinymce/tinymce.d.ts
vendored
3
lib/editor/tiny/js/tinymce/tinymce.d.ts
vendored
@ -1281,6 +1281,7 @@ interface DomParserSettings {
|
||||
preserve_cdata?: boolean;
|
||||
remove_trailing_brs?: boolean;
|
||||
root_name?: string;
|
||||
sanitize?: boolean;
|
||||
validate?: boolean;
|
||||
inline_styles?: boolean;
|
||||
blob_cache?: BlobCache;
|
||||
@ -1870,6 +1871,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>;
|
||||
@ -1954,6 +1956,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 {
|
||||
|
@ -7046,6 +7046,10 @@
|
||||
processor: 'boolean',
|
||||
default: true
|
||||
});
|
||||
registerOption('xss_sanitization', {
|
||||
processor: 'boolean',
|
||||
default: true
|
||||
});
|
||||
editor.on('ScriptsLoaded', () => {
|
||||
registerOption('directionality', {
|
||||
processor: 'string',
|
||||
@ -7145,6 +7149,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'));
|
||||
@ -15463,7 +15468,6 @@
|
||||
var _attr = attr, name = _attr.name, namespaceURI = _attr.namespaceURI;
|
||||
value = name === 'value' ? attr.value : stringTrim(attr.value);
|
||||
lcName = transformCaseFunc(name);
|
||||
var initValue = value;
|
||||
hookEvent.attrName = lcName;
|
||||
hookEvent.attrValue = value;
|
||||
hookEvent.keepAttr = true;
|
||||
@ -15473,8 +15477,8 @@
|
||||
if (hookEvent.forceKeepAttr) {
|
||||
continue;
|
||||
}
|
||||
_removeAttribute(name, currentNode);
|
||||
if (!hookEvent.keepAttr) {
|
||||
_removeAttribute(name, currentNode);
|
||||
continue;
|
||||
}
|
||||
if (regExpTest(/\/>/i, value)) {
|
||||
@ -15487,19 +15491,16 @@
|
||||
}
|
||||
var lcTag = transformCaseFunc(currentNode.nodeName);
|
||||
if (!_isValidAttribute(lcTag, lcName, value)) {
|
||||
_removeAttribute(name, currentNode);
|
||||
continue;
|
||||
}
|
||||
if (value !== initValue) {
|
||||
try {
|
||||
if (namespaceURI) {
|
||||
currentNode.setAttributeNS(namespaceURI, name, value);
|
||||
} else {
|
||||
currentNode.setAttribute(name, value);
|
||||
}
|
||||
} catch (_) {
|
||||
_removeAttribute(name, currentNode);
|
||||
try {
|
||||
if (namespaceURI) {
|
||||
currentNode.setAttributeNS(namespaceURI, name, value);
|
||||
} else {
|
||||
currentNode.setAttribute(name, value);
|
||||
}
|
||||
arrayPop(DOMPurify.removed);
|
||||
} catch (_) {
|
||||
}
|
||||
}
|
||||
_executeHook('afterSanitizeAttributes', currentNode, null);
|
||||
@ -16596,6 +16597,7 @@
|
||||
const defaultedSettings = {
|
||||
validate: true,
|
||||
root_name: 'body',
|
||||
sanitize: true,
|
||||
...settings
|
||||
};
|
||||
const parser = new DOMParser();
|
||||
@ -16606,8 +16608,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;
|
||||
@ -16720,7 +16724,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()) {
|
||||
@ -16729,7 +16733,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,
|
||||
@ -16764,10 +16769,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;
|
||||
}
|
||||
};
|
||||
@ -16778,7 +16783,7 @@
|
||||
const processedEventArgs = withSerializedContent(args.content, content => fireBeforeSetContent(editor, {
|
||||
...args,
|
||||
content
|
||||
}));
|
||||
}), shouldSanitizeXss(editor));
|
||||
if (processedEventArgs.isDefaultPrevented()) {
|
||||
fireSetContent(editor, processedEventArgs);
|
||||
return Result.error(undefined);
|
||||
@ -24409,7 +24414,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();
|
||||
@ -26655,6 +26660,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()
|
||||
|
2
lib/editor/tiny/js/tinymce/tinymce.min.js
vendored
2
lib/editor/tiny/js/tinymce/tinymce.min.js
vendored
File diff suppressed because one or more lines are too long
1
lib/editor/tiny/readme_moodle.txt
Normal file
1
lib/editor/tiny/readme_moodle.txt
Normal file
@ -0,0 +1 @@
|
||||
For instructions on how to import TinyMCE into Moodle, see js/tinymce/readme_moodle.md.
|
Loading…
x
Reference in New Issue
Block a user