diff --git a/src/test/system/text_formatting_test.js b/src/test/system/text_formatting_test.js
index 0b099c5f0..ce7e775e6 100644
--- a/src/test/system/text_formatting_test.js
+++ b/src/test/system/text_formatting_test.js
@@ -58,6 +58,18 @@ testGroup("Text formatting", { template: "editor_empty" }, () => {
expectDocument("ahttp://example.com\n")
})
+ test("inserting a javascript: link is forbidden", async () => {
+ await typeCharacters("XSS")
+ await moveCursor("left")
+ await expandSelection("left")
+ await clickToolbarButton({ attribute: "href" })
+ assert.ok(isToolbarDialogActive({ attribute: "href" }))
+ await typeInToolbarDialog("javascript:alert('XSS')", { attribute: "href" })
+ assert.textAttributes([ 0, 1 ], {})
+ assert.textAttributes([ 1, 2 ], { frozen: true })
+ assert.textAttributes([ 2, 3 ], {})
+ })
+
test("editing a link", async () => {
insertString("a")
const text = Text.textForStringWithAttributes("bc", { href: "http://example.com" })
diff --git a/src/trix/config/toolbar.js b/src/trix/config/toolbar.js
index d45c68bee..1eb44fd3f 100644
--- a/src/trix/config/toolbar.js
+++ b/src/trix/config/toolbar.js
@@ -35,7 +35,7 @@ export default {
-
+
diff --git a/src/trix/controllers/toolbar_controller.js b/src/trix/controllers/toolbar_controller.js
index a1178fea7..7b1b4ebcb 100644
--- a/src/trix/controllers/toolbar_controller.js
+++ b/src/trix/controllers/toolbar_controller.js
@@ -2,6 +2,8 @@ import BasicObject from "trix/core/basic_object"
import { findClosestElementFromNode, handleEvent, triggerEvent } from "trix/core/helpers"
+import DOMPurify from "dompurify"
+
const attributeButtonSelector = "[data-trix-attribute]"
const actionButtonSelector = "[data-trix-action]"
const toolbarButtonSelector = `${attributeButtonSelector}, ${actionButtonSelector}`
@@ -205,7 +207,10 @@ export default class ToolbarController extends BasicObject {
setAttribute(dialogElement) {
const attributeName = getAttributeName(dialogElement)
const input = getInputForDialog(dialogElement, attributeName)
- if (input.willValidate && !input.checkValidity()) {
+
+ input.willValidate && input.setCustomValidity("")
+ if (input.willValidate && !input.checkValidity() || !this.safeAttribute(input)) {
+ input.setCustomValidity("Invalid value")
input.setAttribute("data-trix-validate", "")
input.classList.add("trix-validate")
return input.focus()
@@ -215,6 +220,14 @@ export default class ToolbarController extends BasicObject {
}
}
+ safeAttribute(input) {
+ if (input.hasAttribute("data-trix-validate-href")) {
+ return DOMPurify.isValidAttribute("a", "href", input.value)
+ } else {
+ return true
+ }
+ }
+
removeAttribute(dialogElement) {
const attributeName = getAttributeName(dialogElement)
this.delegate?.toolbarDidRemoveAttribute(attributeName)