[WEB-617] fix: link behaviour fixed on formatting (#3855)

* fix: link behaviour fixed on formatting

* chore: added harmful script checks for links
This commit is contained in:
M. Palanikannan 2024-03-06 14:22:02 +05:30 committed by GitHub
parent 87eadc3c5d
commit 126d01bdc5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 71 additions and 46 deletions

View File

@ -15,9 +15,15 @@ export function clickHandler(options: ClickHandlerOptions): Plugin {
return false;
}
const eventTarget = event.target as HTMLElement;
let a = event.target as HTMLElement;
const els = [];
if (eventTarget.nodeName !== "A") {
while (a.nodeName !== "DIV") {
els.push(a);
a = a.parentNode as HTMLElement;
}
if (!els.find((value) => value.nodeName === "A")) {
return false;
}
@ -28,9 +34,7 @@ export function clickHandler(options: ClickHandlerOptions): Plugin {
const target = link?.target ?? attrs.target;
if (link && href) {
if (view.editable) {
window.open(href, target);
}
window.open(href, target);
return true;
}

View File

@ -33,16 +33,8 @@ export function pasteHandler(options: PasteHandlerOptions): Plugin {
return false;
}
const html = event.clipboardData?.getData("text/html");
const hrefRegex = /href="([^"]*)"/;
const existingLink = html?.match(hrefRegex);
const url = existingLink ? existingLink[1] : link.href;
options.editor.commands.setMark(options.type, {
href: url,
href: link.href,
});
return true;

View File

@ -1,41 +1,76 @@
import { Mark, markPasteRule, mergeAttributes } from "@tiptap/core";
import { Mark, markPasteRule, mergeAttributes, PasteRuleMatch } from "@tiptap/core";
import { Plugin } from "@tiptap/pm/state";
import { find, registerCustomProtocol, reset } from "linkifyjs";
import { autolink } from "src/ui/extensions/custom-link/helpers/autolink";
import { clickHandler } from "src/ui/extensions/custom-link/helpers/clickHandler";
import { pasteHandler } from "src/ui/extensions/custom-link/helpers/pasteHandler";
import { autolink } from "./helpers/autolink";
import { clickHandler } from "./helpers/clickHandler";
import { pasteHandler } from "./helpers/pasteHandler";
export interface LinkProtocolOptions {
scheme: string;
optionalSlashes?: boolean;
}
export const pasteRegex =
/https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)/gi;
export interface LinkOptions {
/**
* If enabled, it adds links as you type.
*/
autolink: boolean;
inclusive: boolean;
/**
* An array of custom protocols to be registered with linkifyjs.
*/
protocols: Array<LinkProtocolOptions | string>;
/**
* If enabled, links will be opened on click.
*/
openOnClick: boolean;
/**
* If enabled, links will be inclusive i.e. if you move your cursor to the
* link text, and start typing, it'll be a part of the link itself.
*/
inclusive: boolean;
/**
* Adds a link to the current selection if the pasted content only contains an url.
*/
linkOnPaste: boolean;
/**
* A list of HTML attributes to be rendered.
*/
HTMLAttributes: Record<string, any>;
/**
* A validation function that modifies link verification for the auto linker.
* @param url - The url to be validated.
* @returns - True if the url is valid, false otherwise.
*/
validate?: (url: string) => boolean;
}
declare module "@tiptap/core" {
interface Commands<ReturnType> {
link: {
/**
* Set a link mark
*/
setLink: (attributes: {
href: string;
target?: string | null;
rel?: string | null;
class?: string | null;
}) => ReturnType;
/**
* Toggle a link mark
*/
toggleLink: (attributes: {
href: string;
target?: string | null;
rel?: string | null;
class?: string | null;
}) => ReturnType;
/**
* Unset a link mark
*/
unsetLink: () => ReturnType;
};
}
@ -150,37 +185,31 @@ export const CustomLinkExtension = Mark.create<LinkOptions>({
addPasteRules() {
return [
markPasteRule({
find: (text) =>
find(text)
.filter((link) => {
if (this.options.validate) {
return this.options.validate(link.value);
}
return true;
})
.filter((link) => link.isLink)
.map((link) => ({
text: link.value,
index: link.start,
data: link,
})),
type: this.type,
getAttributes: (match, pasteEvent) => {
const html = pasteEvent?.clipboardData?.getData("text/html");
const hrefRegex = /href="([^"]*)"/;
find: (text) => {
const foundLinks: PasteRuleMatch[] = [];
const existingLink = html?.match(hrefRegex);
if (text) {
const links = find(text).filter((item) => item.isLink);
if (existingLink) {
return {
href: existingLink[1],
};
if (links.length) {
links.forEach((link) =>
foundLinks.push({
text: link.value,
data: {
href: link.href,
},
index: link.start,
})
);
}
}
return {
href: match.data?.href,
};
return foundLinks;
},
type: this.type,
getAttributes: (match) => ({
href: match.data?.href,
}),
}),
];
},