From 9b7d5dce1faf07306e6202ac6df0642eac55acbc Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 30 Oct 2023 09:27:06 +0100 Subject: [PATCH] Configure accepted iframe message origins. --- .../Squidex/wwwroot/scripts/editor-sdk.d.ts | 54 ++++-- .../src/Squidex/wwwroot/scripts/editor-sdk.js | 171 ++++++++++-------- 2 files changed, 129 insertions(+), 96 deletions(-) diff --git a/backend/src/Squidex/wwwroot/scripts/editor-sdk.d.ts b/backend/src/Squidex/wwwroot/scripts/editor-sdk.d.ts index 51e76a843..45cd14c4d 100644 --- a/backend/src/Squidex/wwwroot/scripts/editor-sdk.d.ts +++ b/backend/src/Squidex/wwwroot/scripts/editor-sdk.d.ts @@ -1,3 +1,10 @@ +type PluginOptions = { + /** + * Defines the accepted origins for incoming messages. + */ + acceptedOrigins?: string[]; +} + declare class EditorPlugin { /** * Get the current context. @@ -12,14 +19,14 @@ declare class EditorPlugin { /** * Register an function that is called when the sidebar is initialized. * - * @param {Function} callback: The callback to invoke. + * @param callback: The callback to invoke. */ onInit(callback: () => void): void; /** * Register an function that is called whenever the value of the content has changed. * - * @param {Function} callback: The callback to invoke. Argument 1: Content value (any). + * @param callback: The callback to invoke. Argument 1: Content value (any). */ onContentChanged(callback: (content: any) => void): void; @@ -30,6 +37,13 @@ declare class EditorPlugin { } declare class SquidexFormField { + /** + * The constructor. + * + * @param options: The plugin options. + */ + constructor(options?: PluginOptions); + /** * Get the current value. */ @@ -77,7 +91,7 @@ declare class SquidexFormField { /** * Notifies the parent to navigate to the path. * - * @param {string} url: The url to navigate to. + * @param url: The url to navigate to. */ navigate(url: string): void; @@ -94,101 +108,101 @@ declare class SquidexFormField { /** * Notifies the control container that the value has been changed. * - * @param {any} newValue: The new field value. + * @param newValue: The new field value. */ valueChanged(newValue: any): void; /** * Shows an info alert. * - * @param {string} text: The info text. + * @param text: The info text. */ notifyInfo(text: string): void; /** * Shows an error alert. * - * @param {string} text: error info text. + * @param text: error info text. */ notifyError(text: string): void; /** * Shows an confirm dialog. * - * @param {string} title The title of the dialog. - * @param {string} text The text of the dialog. - * @param {function} callback The callback to invoke when the dialog is completed or closed. + * @param title The title of the dialog. + * @param text The text of the dialog. + * @param callback The callback to invoke when the dialog is completed or closed. */ confirm(title: string, text: string, callback: (result: boolean) => void): void; /** * Shows the dialog to pick assets. * - * @param {function} callback The callback to invoke when the dialog is completed or closed. + * @param callback The callback to invoke when the dialog is completed or closed. */ pickAssets(callback: (assets: any[]) => void): void; /** * Shows the dialog to pick contents. * - * @param {string} schemas: The list of schema names. - * @param {function} callback The callback to invoke when the dialog is completed or closed. + * @param schemas: The list of schema names. + * @param callback The callback to invoke when the dialog is completed or closed. */ pickContents(schemas: string[], callback: (assets: any[]) => void): void; /** * Register an function that is called when the field is initialized. * - * @param {Function} callback: The callback to invoke. + * @param callback: The callback to invoke. */ onInit(callback: () => void): void; /** * Register an function that is called when the field is moved. * - * @param {Function} callback: The callback to invoke. Argument 1: New position (number). + * @param callback: The callback to invoke. Argument 1: New position (number). */ onMoved(callback: (index: number) => void): void; /** * Register an function that is called whenever the field is disabled or enabled. * - * @param {Function} callback: The callback to invoke. Argument 1: New disabled state (boolean, disabled = true, enabled = false). + * @param callback: The callback to invoke. Argument 1: New disabled state (boolean, disabled = true, enabled = false). */ onDisabled(callback: (isDisabled: boolean) => void): void; /** * Register an function that is called whenever the field language is changed. * - * @param {Function} callback: The callback to invoke. Argument 1: Language code (string). + * @param callback: The callback to invoke. Argument 1: Language code (string). */ onLanguageChanged(callback: (language: string) => void): void; /** * Register an function that is called whenever the value of the field has changed. * - * @param {Function} callback: The callback to invoke. Argument 1: Field value (any). + * @param callback: The callback to invoke. Argument 1: Field value (any). */ onValueChanged(callback: (value: any) => void): void; /** * Register an function that is called whenever the value of the content has changed. * - * @param {Function} callback: The callback to invoke. Argument 1: Content value (any). + * @param callback: The callback to invoke. Argument 1: Content value (any). */ onFormValueChanged(callback: (value: any) => void): void; /** * Register an function that is called whenever the fullscreen mode has changed. * - * @param {Function} callback: The callback to invoke. Argument 1: Fullscreen state (boolean, fullscreen on = true, fullscreen off = false). + * @param callback: The callback to invoke. Argument 1: Fullscreen state (boolean, fullscreen on = true, fullscreen off = false). */ onFullscreen(callback: (isFullscreen: boolean) => void): void; /** * Register an function that is called whenever the expanded mode has changed. * - * @param {Function} callback: The callback to invoke. Argument 1: Expanded state (boolean, expanded on = true, expanded off = false). + * @param callback: The callback to invoke. Argument 1: Expanded state (boolean, expanded on = true, expanded off = false). */ onExpanded(callback: (isExpanded: boolean) => void): void; diff --git a/backend/src/Squidex/wwwroot/scripts/editor-sdk.js b/backend/src/Squidex/wwwroot/scripts/editor-sdk.js index e0d131830..1383c8b56 100644 --- a/backend/src/Squidex/wwwroot/scripts/editor-sdk.js +++ b/backend/src/Squidex/wwwroot/scripts/editor-sdk.js @@ -53,13 +53,19 @@ function isArrayOfStrings(value) { return true; } -function SquidexPlugin() { +/** + * Creates a new plugin for sidebars or widgets. + * + * @param {object} options with the accepted origins. + */ +function SquidexPlugin(options) { var initHandler; var initCalled = false; var contentHandler; var content; var context; var timer; + var acceptedOrigins = options && isArrayOfStrings(options.acceptedOrigins) ? options.acceptedOrigins : null; function raiseContentChanged() { if (contentHandler && content) { @@ -75,18 +81,24 @@ function SquidexPlugin() { } function eventListener(event) { - if (event.source !== window) { - var type = event.data.type; - - if (type === 'contentChanged') { - content = event.data.content; + if (acceptedOrigins && acceptedOrigins.indexOf(event.origin) < 0) { + return; + } - raiseContentChanged(); - } else if (type === 'init') { - context = event.data.context; + if (event.source === window) { + return; + } - raiseInit(); - } + var type = event.data.type; + + if (type === 'contentChanged') { + content = event.data.content; + + raiseContentChanged(); + } else if (type === 'init') { + context = event.data.context; + + raiseInit(); } } @@ -114,7 +126,7 @@ function SquidexPlugin() { /** * Register an function that is called when the sidebar is initialized. * - * @param {Function} callback: The callback to invoke. + * @param {function} callback: The callback to invoke. */ onInit: function (callback) { if (!isFunction(callback)) { @@ -129,7 +141,7 @@ function SquidexPlugin() { /** * Register an function that is called whenever the value of the content has changed. * - * @param {Function} callback: The callback to invoke. Argument 1: Content value (any). + * @param {function} callback: The callback to invoke. Argument 1: Content value (any). */ onContentChanged: function (callback) { if (!isFunction(callback)) { @@ -156,7 +168,7 @@ function SquidexPlugin() { return editor; } -function SquidexFormField() { +function SquidexFormField(options) { var context; var currentConfirm; var currentPickAssets; @@ -178,6 +190,7 @@ function SquidexFormField() { var timer; var value; var valueHandler; + var acceptedOrigins = options && isArrayOfStrings(options.acceptedOrigins) ? options.acceptedOrigins : null; function raiseDisabled() { if (disabledHandler) { @@ -229,81 +242,87 @@ function SquidexFormField() { } function eventListener(event) { - if (event.source !== window) { - var type = event.data.type; + if (acceptedOrigins && acceptedOrigins.indexOf(event.origin) < 0) { + return; + } - console.log('Received Message: ' + type); + if (event.source === window) { + return; + } + + var type = event.data.type; - if (type === 'disabled') { - var newDisabled = event.data.isDisabled; + if (type === 'disabled') { + var newDisabled = event.data.isDisabled; - if (disabled !== newDisabled) { - disabled = newDisabled; + if (disabled !== newDisabled) { + disabled = newDisabled; - raiseDisabled(); - } - } else if (type === 'moved') { - var newIndex = event.data.index; + raiseDisabled(); + } + } else if (type === 'moved') { + var newIndex = event.data.index; - if (index !== newIndex) { - index = newIndex; + if (index !== newIndex) { + index = newIndex; - raisedMoved(); - } - } else if (type === 'languageChanged') { - var newLanguage = event.data.language; + raisedMoved(); + } + } else if (type === 'languageChanged') { + var newLanguage = event.data.language; - if (language !== newLanguage) { - language = newLanguage; + if (language !== newLanguage) { + language = newLanguage; - raiseLanguageChanged(); - } - } else if (type === 'valueChanged') { - value = event.data.value; + raiseLanguageChanged(); + } + } else if (type === 'valueChanged') { + value = event.data.value; - raiseValueChanged(); - } else if (type === 'formValueChanged') { - formValue = event.data.formValue; + raiseValueChanged(); + } else if (type === 'formValueChanged') { + formValue = event.data.formValue; - raiseFormValueChanged(); - } else if (type === 'fullscreenChanged') { - fullscreen = event.data.fullscreen; + raiseFormValueChanged(); + } else if (type === 'fullscreenChanged') { + fullscreen = event.data.fullscreen; - raiseFullscreen(); - } else if (type === 'expandedChanged') { - expanded = event.data.expanded; + raiseFullscreen(); + } else if (type === 'expandedChanged') { + expanded = event.data.expanded; - raiseExpanded(); - } else if (type === 'init') { - context = event.data.context; + raiseExpanded(); + } else if (type === 'init') { + context = event.data.context; - raiseInit(); - } else if (type === 'confirmResult') { - var correlationId = event.data.correlationId; + raiseInit(); + } else if (type === 'confirmResult') { + var correlationId = event.data.correlationId; - if (currentConfirm && currentConfirm.correlationId === correlationId) { - if (typeof currentConfirm.callback === 'function') { - currentConfirm.callback(event.data.result); - } + if (currentConfirm && currentConfirm.correlationId === correlationId) { + if (typeof currentConfirm.callback === 'function') { + currentConfirm.callback(event.data.result); } - } else if (type === 'pickAssetsResult') { - var correlationId = event.data.correlationId; + } + } else if (type === 'pickAssetsResult') { + var correlationId = event.data.correlationId; - if (currentPickAssets && currentPickAssets.correlationId === correlationId) { - if (typeof currentPickAssets.callback === 'function') { - currentPickAssets.callback(event.data.result); - } + if (currentPickAssets && currentPickAssets.correlationId === correlationId) { + if (typeof currentPickAssets.callback === 'function') { + currentPickAssets.callback(event.data.result); } - } else if (type === 'pickContentsResult') { - var correlationId = event.data.correlationId; + } + } else if (type === 'pickContentsResult') { + var correlationId = event.data.correlationId; - if (currentPickContents && currentPickContents.correlationId === correlationId) { - if (typeof currentPickContents.callback === 'function') { - currentPickContents.callback(event.data.result); - } + if (currentPickContents && currentPickContents.correlationId === correlationId) { + if (typeof currentPickContents.callback === 'function') { + currentPickContents.callback(event.data.result); } } } + + console.log('Received Message: ' + type); } window.addEventListener('message', eventListener, false); @@ -512,7 +531,7 @@ function SquidexFormField() { /** * Register an function that is called when the field is initialized. * - * @param {Function} callback: The callback to invoke. + * @param {function} callback: The callback to invoke. */ onInit: function (callback) { if (!isFunction(callback)) { @@ -527,7 +546,7 @@ function SquidexFormField() { /** * Register an function that is called when the field is moved. * - * @param {Function} callback: The callback to invoke. Argument 1: New position (number). + * @param {function} callback: The callback to invoke. Argument 1: New position (number). */ onMoved: function (callback) { if (!isFunction(callback)) { @@ -542,7 +561,7 @@ function SquidexFormField() { /** * Register an function that is called whenever the field is disabled or enabled. * - * @param {Function} callback: The callback to invoke. Argument 1: New disabled state (boolean, disabled = true, enabled = false). + * @param {function} callback: The callback to invoke. Argument 1: New disabled state (boolean, disabled = true, enabled = false). */ onDisabled: function (callback) { if (!isFunction(callback)) { @@ -557,7 +576,7 @@ function SquidexFormField() { /** * Register an function that is called whenever the field language is changed. * - * @param {Function} callback: The callback to invoke. Argument 1: Language code (string). + * @param {function} callback: The callback to invoke. Argument 1: Language code (string). */ onLanguageChanged: function (callback) { if (!isFunction(callback)) { @@ -572,7 +591,7 @@ function SquidexFormField() { /** * Register an function that is called whenever the value of the field has changed. * - * @param {Function} callback: The callback to invoke. Argument 1: Field value (any). + * @param {function} callback: The callback to invoke. Argument 1: Field value (any). */ onValueChanged: function (callback) { if (!isFunction(callback)) { @@ -587,7 +606,7 @@ function SquidexFormField() { /** * Register an function that is called whenever the value of the content has changed. * - * @param {Function} callback: The callback to invoke. Argument 1: Content value (any). + * @param {function} callback: The callback to invoke. Argument 1: Content value (any). */ onFormValueChanged: function (callback) { if (!isFunction(callback)) { @@ -602,7 +621,7 @@ function SquidexFormField() { /** * Register an function that is called whenever the fullscreen mode has changed. * - * @param {Function} callback: The callback to invoke. Argument 1: Fullscreen state (boolean, fullscreen on = true, fullscreen off = false). + * @param {function} callback: The callback to invoke. Argument 1: Fullscreen state (boolean, fullscreen on = true, fullscreen off = false). */ onFullscreen: function (callback) { if (!isFunction(callback)) { @@ -617,7 +636,7 @@ function SquidexFormField() { /** * Register an function that is called whenever the expanded mode has changed. * - * @param {Function} callback: The callback to invoke. Argument 1: Expanded state (boolean, expanded on = true, expanded off = false). + * @param {function} callback: The callback to invoke. Argument 1: Expanded state (boolean, expanded on = true, expanded off = false). */ onExpanded: function (callback) { if (!isFunction(callback)) {