mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 04:04:06 -08:00
fix(editor): Prevent clipboard xss injection (#10894)
This commit is contained in:
parent
48294e7ec1
commit
e20ab59c1d
|
@ -1,4 +1,4 @@
|
|||
import { render, within } from '@testing-library/vue';
|
||||
import { render } from '@testing-library/vue';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { defineComponent, h, ref } from 'vue';
|
||||
import { useClipboard } from '@/composables/useClipboard';
|
||||
|
@ -8,13 +8,9 @@ const testValue = 'This is a test';
|
|||
const TestComponent = defineComponent({
|
||||
setup() {
|
||||
const pasted = ref('');
|
||||
const htmlContent = ref<HTMLElement>();
|
||||
const clipboard = useClipboard({
|
||||
onPaste(data) {
|
||||
pasted.value = data;
|
||||
if (htmlContent.value) {
|
||||
htmlContent.value.innerHTML = data;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -27,7 +23,6 @@ const TestComponent = defineComponent({
|
|||
},
|
||||
}),
|
||||
h('div', { 'data-test-id': 'paste' }, pasted.value),
|
||||
h('div', { 'data-test-id': 'xss-attack', ref: htmlContent }),
|
||||
]);
|
||||
},
|
||||
});
|
||||
|
@ -73,12 +68,4 @@ describe('useClipboard()', () => {
|
|||
expect(pasteElement.textContent).toEqual(testValue);
|
||||
});
|
||||
});
|
||||
|
||||
it('sanitizes HTML', async () => {
|
||||
const unsafeHtml = 'https://www.ex.com/sfefdfd<img/src/onerror=alert(1)>fdf/xdfef.json';
|
||||
const { getByTestId } = render(TestComponent);
|
||||
|
||||
await userEvent.paste(unsafeHtml);
|
||||
expect(within(getByTestId('xss-attack')).queryByRole('img')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import { useClipboard as useClipboardCore } from '@vueuse/core';
|
||||
import { useDebounce } from '@/composables/useDebounce';
|
||||
import { sanitizeIfString } from '@/utils/htmlUtils';
|
||||
|
||||
type ClipboardEventFn = (data: string, event?: ClipboardEvent) => void;
|
||||
|
||||
|
@ -43,7 +42,7 @@ export function useClipboard(
|
|||
|
||||
const clipboardData = event.clipboardData;
|
||||
if (clipboardData !== null) {
|
||||
const clipboardValue = sanitizeIfString(clipboardData.getData('text/plain'));
|
||||
const clipboardValue = clipboardData.getData('text/plain');
|
||||
onPasteCallback.value(clipboardValue, event);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,4 +37,18 @@ describe('sanitizeHtml', () => {
|
|||
const result = sanitizeHtml(dirtyHtml);
|
||||
expect(result).toBe('<a>Click me</a>');
|
||||
});
|
||||
|
||||
test.each([
|
||||
[
|
||||
'https://www.ex.com/sfefdfd<img/src/onerror=alert(1)>fdf/xdfef.json',
|
||||
'https://www.ex.com/sfefdfdfdf/xdfef.json',
|
||||
],
|
||||
[
|
||||
// eslint-disable-next-line n8n-local-rules/no-unneeded-backticks
|
||||
`https://www.ex.com/sfefdfd<details title='"><img/src/onerror=alert(document.domain)>/ '>/c.json`,
|
||||
'https://www.ex.com/sfefdfd<details title=""><img/src/onerror=alert(document.domain)>/">/c.json',
|
||||
],
|
||||
])('should escape js code %s to equal %s', (dirtyURL, expected) => {
|
||||
expect(sanitizeHtml(dirtyURL)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import xss, { friendlyAttrValue } from 'xss';
|
||||
import xss, { escapeAttrValue } from 'xss';
|
||||
import { ALLOWED_HTML_ATTRIBUTES, ALLOWED_HTML_TAGS } from '@/constants';
|
||||
|
||||
/*
|
||||
|
@ -22,7 +22,7 @@ export function sanitizeHtml(dirtyHtml: string) {
|
|||
if (name === 'href' && !value.match(/^https?:\/\//gm)) {
|
||||
return '';
|
||||
}
|
||||
return `${name}="${friendlyAttrValue(value)}"`;
|
||||
return `${name}="${escapeAttrValue(value)}"`;
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
Loading…
Reference in a new issue