You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-11-29 22:48:10 +02:00
Mobile: Rich Text Editor: Avoid rendering links with unknown protocols (#12943)
Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
This commit is contained in:
@@ -288,6 +288,26 @@ describe('RichTextEditor', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should avoid rendering URLs with unknown protocols', async () => {
|
||||
let body = '[link](unknown://test)';
|
||||
|
||||
render(<WrappedEditor
|
||||
noteBody={body}
|
||||
onBodyChange={newBody => { body = newBody; }}
|
||||
/>);
|
||||
|
||||
const renderedLink = await findElement<HTMLAnchorElement>('a[href][data-original-href]');
|
||||
expect(renderedLink.getAttribute('href')).toBe('#');
|
||||
expect(renderedLink.getAttribute('data-original-href')).toBe('unknown://test');
|
||||
|
||||
const window = await getEditorWindow();
|
||||
mockTyping(window, ' testing');
|
||||
|
||||
await waitFor(async () => {
|
||||
expect(body.trim()).toBe('[link](unknown://test) testing');
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
MarkupLanguage.Markdown, MarkupLanguage.Html,
|
||||
])('should preserve image attachments on edit (case %#)', async (markupLanguage) => {
|
||||
|
||||
@@ -10,6 +10,24 @@ import convertHtmlToMarkdown from './convertHtmlToMarkdown';
|
||||
import { ExportedWebViewGlobals as MarkdownEditorWebViewGlobals } from '../../markdownEditorBundle/types';
|
||||
import { EditorEventType } from '@joplin/editor/events';
|
||||
|
||||
const postprocessHtml = (html: HTMLElement) => {
|
||||
// Fix resource URLs
|
||||
const resources = html.querySelectorAll<HTMLImageElement>('img[data-resource-id]');
|
||||
for (const resource of resources) {
|
||||
const resourceId = resource.getAttribute('data-resource-id');
|
||||
resource.src = `:/${resourceId}`;
|
||||
}
|
||||
|
||||
// Restore HREFs
|
||||
const links = html.querySelectorAll<HTMLAnchorElement>('a[href="#"][data-original-href]');
|
||||
for (const link of links) {
|
||||
link.href = link.getAttribute('data-original-href');
|
||||
link.removeAttribute('data-original-href');
|
||||
}
|
||||
|
||||
return html;
|
||||
};
|
||||
|
||||
const wrapHtmlForMarkdownConversion = (html: HTMLElement) => {
|
||||
// Add a container element -- when converting to HTML, Turndown
|
||||
// sometimes doesn't process the toplevel element in the same way
|
||||
|
||||
@@ -3,6 +3,8 @@ import { nodeSpecs as joplinEditableNodes } from './plugins/joplinEditablePlugin
|
||||
import { tableNodes } from 'prosemirror-tables';
|
||||
import { nodeSpecs as listNodes } from './plugins/listPlugin';
|
||||
import { nodeSpecs as resourcePlaceholderNodes } from './plugins/resourcePlaceholderPlugin';
|
||||
import { hasProtocol } from '@joplin/utils/url';
|
||||
import { isResourceUrl } from '@joplin/lib/models/utils/resourceUtils';
|
||||
import { nodeSpecs as detailsNodes } from './plugins/detailsPlugin';
|
||||
|
||||
// For reference, see:
|
||||
@@ -259,8 +261,15 @@ const marks = {
|
||||
tag: 'a[href]',
|
||||
getAttrs: node => {
|
||||
const resourceId = node.getAttribute('data-resource-id');
|
||||
const href = node.getAttribute('href');
|
||||
let href = node.getAttribute('href');
|
||||
const isResourceLink = resourceId && href === '#';
|
||||
if (isResourceLink) {
|
||||
href = `:/${resourceId}`;
|
||||
}
|
||||
|
||||
if (href === '#' && node.hasAttribute('data-original-href')) {
|
||||
href = node.getAttribute('data-original-href');
|
||||
}
|
||||
|
||||
return {
|
||||
href: isResourceLink ? `:/${resourceId}` : href,
|
||||
@@ -269,10 +278,29 @@ const marks = {
|
||||
};
|
||||
},
|
||||
}],
|
||||
toDOM: node => [
|
||||
'a',
|
||||
{ href: node.attrs.href, title: node.attrs.title, 'data-resource-id': node.attrs.dataResourceId },
|
||||
],
|
||||
toDOM: node => {
|
||||
const isSafeForRendering = (href: string) => {
|
||||
return hasProtocol(href, ['http', 'https', 'joplin']) || isResourceUrl(href);
|
||||
};
|
||||
|
||||
// Avoid rendering URLs with unknown protocols (avoid rendering or pasting unsafe HREFs).
|
||||
// Note that URL click handling is handled elsewhere and does not use the HTML "href" attribute.
|
||||
// However "href" may be used by the right-click menu on web:
|
||||
const safeHref = isSafeForRendering(node.attrs.href) ? node.attrs.href : '#';
|
||||
|
||||
return [
|
||||
'a',
|
||||
{
|
||||
href: safeHref,
|
||||
...(safeHref !== node.attrs.href ? {
|
||||
'data-original-href': node.attrs.href,
|
||||
} : {}),
|
||||
|
||||
title: node.attrs.title,
|
||||
'data-resource-id': node.attrs.dataResourceId,
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
} satisfies Record<string, MarkSpec>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user