Improved sanitization of href's in editor

This commit is contained in:
Tom Moor
2022-07-05 10:14:16 +02:00
parent 2f3dcb2520
commit 4e189b8970
9 changed files with 94 additions and 45 deletions

View File

@@ -1,12 +0,0 @@
export default function isUrl(text: string) {
if (text.match(/\n/)) {
return false;
}
try {
const url = new URL(text);
return url.hostname !== "";
} catch (err) {
return false;
}
}

View File

@@ -13,7 +13,7 @@ import { EditorState, Plugin } from "prosemirror-state";
import { Decoration, DecorationSet } from "prosemirror-view";
import * as React from "react";
import ReactDOM from "react-dom";
import { isExternalUrl } from "../../utils/urls";
import { isExternalUrl, sanitizeHref } from "../../utils/urls";
import findLinkNodes from "../queries/findLinkNodes";
import { EventType, Dispatch } from "../types";
import Mark from "./Mark";
@@ -80,6 +80,7 @@ export default class Link extends Mark {
"a",
{
...node.attrs,
href: sanitizeHref(node.attrs.href),
rel: "noopener noreferrer nofollow",
},
0,
@@ -196,18 +197,25 @@ export default class Link extends Mark {
? event.target.parentNode.href
: "");
const isHashtag = href.startsWith("#");
if (isHashtag && this.options.onClickHashtag) {
event.stopPropagation();
event.preventDefault();
this.options.onClickHashtag(href, event);
try {
const isHashtag = href.startsWith("#");
if (isHashtag && this.options.onClickHashtag) {
event.stopPropagation();
event.preventDefault();
this.options.onClickHashtag(href, event);
}
if (this.options.onClickLink) {
event.stopPropagation();
event.preventDefault();
this.options.onClickLink(href, event);
}
} catch (err) {
this.editor.props.onShowToast(
this.options.dictionary.openLinkError
);
}
if (this.options.onClickLink) {
event.stopPropagation();
event.preventDefault();
this.options.onClickLink(href, event);
}
return true;
}

View File

@@ -4,6 +4,7 @@ import { NodeSpec, NodeType, Node as ProsemirrorNode } from "prosemirror-model";
import * as React from "react";
import { Trans } from "react-i18next";
import { bytesToHumanReadable } from "../../utils/files";
import { sanitizeHref } from "../../utils/urls";
import toggleWrap from "../commands/toggleWrap";
import FileExtension from "../components/FileExtension";
import Widget from "../components/Widget";
@@ -56,7 +57,7 @@ export default class Attachment extends Node {
{
class: `attachment`,
id: node.attrs.id,
href: node.attrs.href,
href: sanitizeHref(node.attrs.href),
download: node.attrs.title,
"data-size": node.attrs.size,
},

View File

@@ -2,6 +2,7 @@ import Token from "markdown-it/lib/token";
import { NodeSpec, NodeType, Node as ProsemirrorNode } from "prosemirror-model";
import { EditorState } from "prosemirror-state";
import * as React from "react";
import { sanitizeHref } from "../../utils/urls";
import DisabledEmbed from "../components/DisabledEmbed";
import { MarkdownSerializerState } from "../lib/markdown/serializer";
import embedsRule from "../rules/embeds";
@@ -47,7 +48,11 @@ export default class Embed extends Node {
],
toDOM: (node) => [
"iframe",
{ class: "embed", src: node.attrs.href, contentEditable: "false" },
{
class: "embed",
src: sanitizeHref(node.attrs.href),
contentEditable: "false",
},
0,
],
toPlainText: (node) => node.attrs.href,

View File

@@ -1,9 +1,9 @@
import { toggleMark } from "prosemirror-commands";
import { Plugin } from "prosemirror-state";
import { isInTable } from "prosemirror-tables";
import { isUrl } from "../../utils/urls";
import Extension from "../lib/Extension";
import isMarkdown from "../lib/isMarkdown";
import isUrl from "../lib/isUrl";
import selectionIsInCode from "../queries/isInCode";
import { LANGUAGES } from "./Prism";