diff --git a/packages/frontend/@n8n/frontend-utils/src/htmlUtils.ts b/packages/frontend/@n8n/frontend-utils/src/htmlUtils.ts index 1296f913e58..414f1b15520 100644 --- a/packages/frontend/@n8n/frontend-utils/src/htmlUtils.ts +++ b/packages/frontend/@n8n/frontend-utils/src/htmlUtils.ts @@ -1,11 +1,19 @@ import { toValue, type MaybeRef } from 'vue'; -import xss, { escapeAttrValue } from 'xss'; +import xss, { escapeAttrValue, escapeHtml } from 'xss'; import { ALLOWED_HTML_ATTRIBUTES, ALLOWED_HTML_TAGS } from './constants/sanitization'; /* Constants and utility functions that help in HTML, CSS and DOM manipulation */ + +/** + * Escapes HTML entities in a string to prevent HTML injection. + * This should be used for user input that will be displayed in HTML contexts. + * Unlike sanitizeHtml, this completely escapes all HTML entities. + */ +export { escapeHtml }; + export function sanitizeHtml(dirtyHtml: string) { const sanitizedHtml = xss(dirtyHtml, { onTagAttr: (tag, name, value) => { diff --git a/packages/frontend/editor-ui/src/app/utils/htmlUtils.ts b/packages/frontend/editor-ui/src/app/utils/htmlUtils.ts index 7360627e47a..5be69366fb5 100644 --- a/packages/frontend/editor-ui/src/app/utils/htmlUtils.ts +++ b/packages/frontend/editor-ui/src/app/utils/htmlUtils.ts @@ -6,6 +6,7 @@ */ export { capitalizeFirstLetter, + escapeHtml, getBannerRowHeight, getScrollbarWidth, isEventTargetContainedBy, diff --git a/packages/frontend/editor-ui/src/features/core/dataTable/components/DataTableActions.vue b/packages/frontend/editor-ui/src/features/core/dataTable/components/DataTableActions.vue index 038f72eb3f2..3183d772725 100644 --- a/packages/frontend/editor-ui/src/features/core/dataTable/components/DataTableActions.vue +++ b/packages/frontend/editor-ui/src/features/core/dataTable/components/DataTableActions.vue @@ -14,6 +14,7 @@ import type { DataTable } from '@/features/core/dataTable/dataTable.types'; import type { IUser, UserAction } from '@n8n/design-system'; import { useI18n } from '@n8n/i18n'; import { computed } from 'vue'; +import { escapeHtml } from '@/app/utils/htmlUtils'; import { N8nActionToggle } from '@n8n/design-system'; import { useUIStore } from '@/app/stores/ui.store'; @@ -114,7 +115,7 @@ const onAction = async (action: string) => { case DATA_TABLE_CARD_ACTIONS.DELETE: { const promptResponse = await message.confirm( i18n.baseText('dataTable.delete.confirm.message', { - interpolate: { name: props.dataTable.name }, + interpolate: { name: escapeHtml(props.dataTable.name) }, }), i18n.baseText('dataTable.delete.confirm.title'), { diff --git a/packages/frontend/editor-ui/src/features/core/dataTable/composables/useDataTableOperations.ts b/packages/frontend/editor-ui/src/features/core/dataTable/composables/useDataTableOperations.ts index 20a6acc3fe5..3e5535e405b 100644 --- a/packages/frontend/editor-ui/src/features/core/dataTable/composables/useDataTableOperations.ts +++ b/packages/frontend/editor-ui/src/features/core/dataTable/composables/useDataTableOperations.ts @@ -23,6 +23,7 @@ import { useDataTableTypes } from '@/features/core/dataTable/composables/useData import { areValuesEqual } from '@/features/core/dataTable/utils/typeUtils'; import { isUnsafeNumberValue } from '@/features/core/dataTable/utils/columnUtils'; import { ResponseError } from '@n8n/rest-api-client'; +import { escapeHtml } from '@/app/utils/htmlUtils'; export type UseDataTableOperationsParams = { colDefs: Ref; @@ -116,7 +117,7 @@ export const useDataTableOperations = ({ const promptResponse = await message.confirm( i18n.baseText('dataTable.deleteColumn.confirm.message', { - interpolate: { name: columnToDelete.headerName ?? '' }, + interpolate: { name: escapeHtml(columnToDelete.headerName ?? '') }, }), i18n.baseText('dataTable.deleteColumn.confirm.title'), {