fix(editor): Fixing XSS vulnerability in toast messages (#10329)

Co-authored-by: Adi <aditya@netroy.in>
This commit is contained in:
Milorad FIlipović 2024-08-08 16:28:51 +02:00 committed by GitHub
parent b6c47c0e32
commit 38bdd9f5d0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 25 additions and 18 deletions

View file

@ -94,6 +94,7 @@ async function send() {
message: Number(form.value.value) >= 8 ? i18n.baseText('prompts.npsSurvey.reviewUs') : '', message: Number(form.value.value) >= 8 ? i18n.baseText('prompts.npsSurvey.reviewUs') : '',
type: 'success', type: 'success',
duration: 15000, duration: 15000,
dangerouslyUseHTMLString: true,
}); });
setTimeout(() => { setTimeout(() => {

View file

@ -16,6 +16,7 @@ import { useUIStore } from '@/stores/ui.store';
import { useTelemetry } from './useTelemetry'; import { useTelemetry } from './useTelemetry';
import { useRootStore } from '@/stores/root.store'; import { useRootStore } from '@/stores/root.store';
import { isFullExecutionResponse } from '@/utils/typeGuards'; import { isFullExecutionResponse } from '@/utils/typeGuards';
import { sanitizeHtml } from '@/utils/htmlUtils';
export const useExecutionDebugging = () => { export const useExecutionDebugging = () => {
const telemetry = useTelemetry(); const telemetry = useTelemetry();
@ -61,7 +62,7 @@ export const useExecutionDebugging = () => {
h( h(
'ul', 'ul',
{ class: 'mt-l ml-l' }, { class: 'mt-l ml-l' },
matchingPinnedNodeNames.map((name) => h('li', name)), matchingPinnedNodeNames.map((name) => h('li', sanitizeHtml(name))),
), ),
]); ]);

View file

@ -328,6 +328,7 @@ export function usePushConnection({ router }: { router: ReturnType<typeof useRou
message: `${action} <a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.wait/" target="_blank">More info</a>`, message: `${action} <a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.wait/" target="_blank">More info</a>`,
type: 'success', type: 'success',
duration: 0, duration: 0,
dangerouslyUseHTMLString: true,
}); });
} else if (runDataExecuted.finished !== true) { } else if (runDataExecuted.finished !== true) {
titleChange.titleSet(workflow.name as string, 'ERROR'); titleChange.titleSet(workflow.name as string, 'ERROR');
@ -438,7 +439,6 @@ export function usePushConnection({ router }: { router: ReturnType<typeof useRou
message: runDataExecutedErrorMessage, message: runDataExecutedErrorMessage,
type: 'error', type: 'error',
duration: 0, duration: 0,
dangerouslyUseHTMLString: true,
}); });
} }
} }

View file

@ -18,7 +18,7 @@ export interface NotificationErrorWithNodeAndDescription extends ApplicationErro
} }
const messageDefaults: Partial<Omit<NotificationOptions, 'message'>> = { const messageDefaults: Partial<Omit<NotificationOptions, 'message'>> = {
dangerouslyUseHTMLString: true, dangerouslyUseHTMLString: false,
position: 'bottom-right', position: 'bottom-right',
}; };
@ -32,28 +32,28 @@ export function useToast() {
const i18n = useI18n(); const i18n = useI18n();
function showMessage(messageData: Partial<NotificationOptions>, track = true) { function showMessage(messageData: Partial<NotificationOptions>, track = true) {
messageData = { ...messageDefaults, ...messageData }; const { message, title } = messageData;
const params = { ...messageDefaults, ...messageData };
Object.defineProperty(messageData, 'message', { if (typeof message === 'string') {
value: params.message = sanitizeHtml(message);
typeof messageData.message === 'string' }
? sanitizeHtml(messageData.message)
: messageData.message,
writable: true,
enumerable: true,
});
const notification = Notification(messageData); if (typeof title === 'string') {
params.title = sanitizeHtml(title);
}
if (messageData.duration === 0) { const notification = Notification(params);
if (params.duration === 0) {
stickyNotificationQueue.push(notification); stickyNotificationQueue.push(notification);
} }
if (messageData.type === 'error' && track) { if (params.type === 'error' && track) {
telemetry.track('Instance FE emitted error', { telemetry.track('Instance FE emitted error', {
error_title: messageData.title, error_title: params.title,
error_message: messageData.message, error_message: params.message,
caused_by_credential: causedByCredential(messageData.message as string), caused_by_credential: causedByCredential(params.message as string),
workflow_id: workflowsStore.workflowId, workflow_id: workflowsStore.workflowId,
}); });
} }
@ -133,6 +133,7 @@ export function useToast() {
${collapsableDetails(error)}`, ${collapsableDetails(error)}`,
type: 'error', type: 'error',
duration: 0, duration: 0,
dangerouslyUseHTMLString: true,
}, },
false, false,
); );

View file

@ -18,6 +18,10 @@ export function sanitizeHtml(dirtyHtml: string) {
} }
if (ALLOWED_HTML_ATTRIBUTES.includes(name) || name.startsWith('data-')) { if (ALLOWED_HTML_ATTRIBUTES.includes(name) || name.startsWith('data-')) {
// href is allowed but we need to sanitize certain protocols
if (name === 'href' && !value.match(/^https?:\/\//gm)) {
return '';
}
return `${name}="${friendlyAttrValue(value)}"`; return `${name}="${friendlyAttrValue(value)}"`;
} }