diff --git a/src/trix/config/html_sanitizer_allowed_attributes.js b/src/trix/config/html_sanitizer_allowed_attributes.js new file mode 100644 index 000000000..27bc6f871 --- /dev/null +++ b/src/trix/config/html_sanitizer_allowed_attributes.js @@ -0,0 +1,10 @@ +const attributes = [ + "style", + "href", + "src", + "width", + "height", + "class", +] + +export default attributes diff --git a/src/trix/config/html_sanitizer_allowed_elements.js b/src/trix/config/html_sanitizer_allowed_elements.js new file mode 100644 index 000000000..68580c96c --- /dev/null +++ b/src/trix/config/html_sanitizer_allowed_elements.js @@ -0,0 +1,3 @@ +const allowedElements = [] + +export default allowedElements diff --git a/src/trix/config/html_sanitizer_allowed_protocols.js b/src/trix/config/html_sanitizer_allowed_protocols.js new file mode 100644 index 000000000..286823a0f --- /dev/null +++ b/src/trix/config/html_sanitizer_allowed_protocols.js @@ -0,0 +1,3 @@ +const allowedProtocols = [] + +export default allowedProtocols diff --git a/src/trix/config/html_sanitizer_forbidden_elements.js b/src/trix/config/html_sanitizer_forbidden_elements.js new file mode 100644 index 000000000..7641ab742 --- /dev/null +++ b/src/trix/config/html_sanitizer_forbidden_elements.js @@ -0,0 +1,7 @@ +const forbiddenElements = [ + "script", + "iframe", + "form", +] + +export default forbiddenElements diff --git a/src/trix/config/html_sanitizer_forbidden_protocols.js b/src/trix/config/html_sanitizer_forbidden_protocols.js new file mode 100644 index 000000000..8b8f7b28d --- /dev/null +++ b/src/trix/config/html_sanitizer_forbidden_protocols.js @@ -0,0 +1,5 @@ +const forbiddenProtocols = [ + "javascript:", +] + +export default forbiddenProtocols diff --git a/src/trix/config/index.js b/src/trix/config/index.js index 27f7abc2f..202cc5cf9 100644 --- a/src/trix/config/index.js +++ b/src/trix/config/index.js @@ -10,3 +10,8 @@ export { default as parser } from "./parser" export { default as textAttributes } from "./text_attributes" export { default as toolbar } from "./toolbar" export { default as undo } from "./undo" +export { default as htmlSanitizerAllowedAttributes } from "./html_sanitizer_allowed_attributes" +export { default as htmlSanitizerAllowedElements } from "./html_sanitizer_allowed_elements" +export { default as htmlSanitizerAllowedProtocols } from "./html_sanitizer_allowed_protocols" +export { default as htmlSanitizerForbiddenElements } from "./html_sanitizer_forbidden_elements" +export { default as htmlSanitizerForbiddenProtocols } from "./html_sanitizer_forbidden_protocols" diff --git a/src/trix/models/html_sanitizer.js b/src/trix/models/html_sanitizer.js index 6e342e51a..92616075d 100644 --- a/src/trix/models/html_sanitizer.js +++ b/src/trix/models/html_sanitizer.js @@ -1,10 +1,13 @@ import BasicObject from "trix/core/basic_object" import { nodeIsAttachmentElement, removeNode, tagName, walkTree } from "trix/core/helpers" - -const DEFAULT_ALLOWED_ATTRIBUTES = "style href src width height class".split(" ") -const DEFAULT_FORBIDDEN_PROTOCOLS = "javascript:".split(" ") -const DEFAULT_FORBIDDEN_ELEMENTS = "script iframe form".split(" ") +import { + htmlSanitizerAllowedAttributes, + htmlSanitizerAllowedElements, + htmlSanitizerAllowedProtocols, + htmlSanitizerForbiddenElements, + htmlSanitizerForbiddenProtocols +} from "../config" export default class HTMLSanitizer extends BasicObject { static sanitize(html, options) { @@ -13,11 +16,13 @@ export default class HTMLSanitizer extends BasicObject { return sanitizer } - constructor(html, { allowedAttributes, forbiddenProtocols, forbiddenElements } = {}) { + constructor(html, { allowedAttributes, allowedElements, allowedProtocols, forbiddenProtocols, forbiddenElements } = {}) { super(...arguments) - this.allowedAttributes = allowedAttributes || DEFAULT_ALLOWED_ATTRIBUTES - this.forbiddenProtocols = forbiddenProtocols || DEFAULT_FORBIDDEN_PROTOCOLS - this.forbiddenElements = forbiddenElements || DEFAULT_FORBIDDEN_ELEMENTS + this.allowedAttributes = allowedAttributes || htmlSanitizerAllowedAttributes + this.allowedElements = allowedElements || htmlSanitizerAllowedElements + this.allowedProtocols = allowedProtocols || htmlSanitizerAllowedProtocols + this.forbiddenElements = forbiddenElements || htmlSanitizerForbiddenElements + this.forbiddenProtocols = forbiddenProtocols || htmlSanitizerForbiddenProtocols this.body = createBodyElementForHTML(html) } @@ -63,7 +68,7 @@ export default class HTMLSanitizer extends BasicObject { sanitizeElement(element) { if (element.hasAttribute("href")) { - if (this.forbiddenProtocols.includes(element.protocol)) { + if (this.forbiddenProtocols.includes(element.protocol) || this.allowedProtocols.length > 0 && !this.allowedProtocols.includes(element.protocol)) { element.removeAttribute("href") } } @@ -96,7 +101,7 @@ export default class HTMLSanitizer extends BasicObject { } elementIsForbidden(element) { - return this.forbiddenElements.includes(tagName(element)) + return this.forbiddenElements.includes(tagName(element)) || this.allowedElements.length > 0 && !this.allowedElements.includes(tagName(element)) } elementIsntSerializable(element) { diff --git a/src/trix/views/piece_view.js b/src/trix/views/piece_view.js index c03d6c266..306478183 100644 --- a/src/trix/views/piece_view.js +++ b/src/trix/views/piece_view.js @@ -111,17 +111,25 @@ export default class PieceView extends ObjectView { } createContainerElement() { + const attributes = {} + let groupTagName + for (const key in this.attributes) { const value = this.attributes[key] const config = getTextConfig(key) if (config) { - if (config.groupTagName) { - const attributes = {} + if (!groupTagName && config.groupTagName) { + attributes[key] = value + groupTagName = config.groupTagName + } else if (config.groupTagName && groupTagName === config.groupTagName) { attributes[key] = value - return makeElement(config.groupTagName, attributes) } } } + + if (Object.entries(attributes).length > 0 && groupTagName) { + return makeElement(groupTagName, attributes) + } } preserveSpaces(string) {