diff --git a/src/bootstrap.js b/src/bootstrap.js index f011a25a1..eeacabc9c 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -50,6 +50,9 @@ const cacheEnabled = document.head.querySelector( const cacheTimeout = document.head.querySelector( "meta[name='screen-cache-timeout']" ); +const secureHandlerToggleVisibleMeta = document.head.querySelector( + "meta[name='screen-secure-handler-toggle-visible']" +); // Get the current protocol, hostname, and port const { protocol, hostname, port } = window.location; @@ -73,7 +76,10 @@ window.ProcessMaker = { alert(message, variant) {}, screen: { cacheEnabled: cacheEnabled ? cacheEnabled.content === "true" : false, - cacheTimeout: cacheTimeout ? Number(cacheTimeout.content) : 0 + cacheTimeout: cacheTimeout ? Number(cacheTimeout.content) : 0, + secureHandlerToggleVisible: !!Number( + secureHandlerToggleVisibleMeta?.content + ) } }; window.Echo = { diff --git a/src/components/inspector/button/handler-event-property.js b/src/components/inspector/button/handler-event-property.js index 17a4885cc..baa732e30 100644 --- a/src/components/inspector/button/handler-event-property.js +++ b/src/components/inspector/button/handler-event-property.js @@ -1,9 +1,21 @@ export const handlerEventProperty = { - type: 'CodeEditor', - field: 'handler', + type: "CodeEditor", + field: "handler", config: { - label: 'Click Handler', - helper: 'The handler is a JavaScript function that will be executed when the button is clicked.', - dataFeature: 'i1177', - }, + label: "Click Handler", + helper: + "The handler is a JavaScript function that will be executed when the button is clicked.", + dataFeature: "i1177" + } +}; + +export const handlerSecurityProperty = { + type: "FormCheckbox", + field: "handlerSecurityEnabled", + config: { + label: "Secure Handler Execution", + toggle: true, + helper: + "When enabled, the handler runs inside a sandboxed worker. Disable to allow full JavaScript access." + } }; diff --git a/src/components/renderer/form-button.vue b/src/components/renderer/form-button.vue index 9d09e717c..8f1c06fc0 100644 --- a/src/components/renderer/form-button.vue +++ b/src/components/renderer/form-button.vue @@ -2,12 +2,12 @@
+ + + diff --git a/src/components/vue-form-builder.vue b/src/components/vue-form-builder.vue index b45adf4c9..1f616fb13 100644 --- a/src/components/vue-form-builder.vue +++ b/src/components/vue-form-builder.vue @@ -810,6 +810,13 @@ export default { }, showToolbar() { return this.screenType === formTypes.form; + }, + secureHandlerToggleVisible() { + return _.get( + globalObject, + "ProcessMaker.screen.secureHandlerToggleVisible", + false + ); } }, watch: { @@ -1220,6 +1227,13 @@ export default { (control) => control.component === this.inspection.component ) || { inspector: [] }; return control.inspector.filter((input) => { + if ( + !this.secureHandlerToggleVisible && + typeof input === "object" && + input.field === "handlerSecurityEnabled" + ) { + return false; + } if (accordionFields.includes(input.field)) { return true; } diff --git a/src/form-builder-controls.js b/src/form-builder-controls.js index 0dd899444..5bf4f9151 100755 --- a/src/form-builder-controls.js +++ b/src/form-builder-controls.js @@ -14,7 +14,7 @@ import FormListTable from './components/renderer/form-list-table'; import FormAnalyticsChart from "./components/renderer/form-analytics-chart"; import FormCollectionRecordControl from './components/renderer/form-collection-record-control.vue'; import FormCollectionViewControl from './components/renderer/form-collection-view-control.vue'; -import { handlerEventProperty } from './components/inspector/button/handler-event-property'; +import { handlerEventProperty, handlerSecurityProperty } from './components/inspector/button/handler-event-property'; import {DataTypeProperty, DataFormatProperty, DataTypeDateTimeProperty} from './VariableDataTypeProperties'; import { FormInput, @@ -763,6 +763,7 @@ export default [ fieldValue: null, tooltip: {}, handler: '', + handlerSecurityEnabled: true, }, inspector: [ { @@ -786,6 +787,7 @@ export default [ }, buttonTypeEvent, handlerEventProperty, + handlerSecurityProperty, LoadingSubmitButtonProperty, LabelSubmitButtonProperty, tooltipProperty, diff --git a/src/main.js b/src/main.js index 0d2c5344d..18ba1e940 100644 --- a/src/main.js +++ b/src/main.js @@ -152,6 +152,10 @@ const cacheEnabled = document.head.querySelector( const cacheTimeout = document.head.querySelector( "meta[name='screen-cache-timeout']" ); +const secureHandlerToggleVisibleMeta = document.head.querySelector( + "meta[name='screen-secure-handler-toggle-visible']" +); + // Get the current protocol, hostname, and port const { protocol, hostname, port } = window.location; window.ProcessMaker = { @@ -302,7 +306,10 @@ window.ProcessMaker = { }, screen: { cacheEnabled: cacheEnabled ? cacheEnabled.content === "true" : false, - cacheTimeout: cacheTimeout ? Number(cacheTimeout.content) : 0 + cacheTimeout: cacheTimeout ? Number(cacheTimeout.content) : 0, + secureHandlerToggleVisible: !!Number( + secureHandlerToggleVisibleMeta?.content + ) } }; window.Echo = { diff --git a/src/mixins/extensions/LoadFieldComponents.js b/src/mixins/extensions/LoadFieldComponents.js index 52632aecb..2166fd91b 100644 --- a/src/mixins/extensions/LoadFieldComponents.js +++ b/src/mixins/extensions/LoadFieldComponents.js @@ -55,11 +55,19 @@ export default { componentName === "FormTextArea" || componentName === "FormInput" ) { - properties["@input"] = `updateScreenData('${safeDotName}', '${element.config.name}')`; - properties["@change"] = `updateScreenDataNow('${safeDotName}', '${element.config.name}')`; + properties[ + "@input" + ] = `updateScreenData('${safeDotName}', '${element.config.name}')`; + properties[ + "@change" + ] = `updateScreenDataNow('${safeDotName}', '${element.config.name}')`; } else { - properties["@input"] = `updateScreenDataNow('${safeDotName}', '${element.config.name}')`; - properties["@change"] = `updateScreenDataNow('${safeDotName}', '${element.config.name}')`; + properties[ + "@input" + ] = `updateScreenDataNow('${safeDotName}', '${element.config.name}')`; + properties[ + "@change" + ] = `updateScreenDataNow('${safeDotName}', '${element.config.name}')`; } // Process the FormSelectList@reset event properties[ @@ -100,12 +108,17 @@ export default { properties[":readonly"] = isCalcProp || element.config.readonly; properties[":disabled"] = isCalcProp || element.config.disabled; // Events - properties['@submit'] = 'submitForm'; + properties["@submit"] = "submitForm"; // Add handler event if Button - if(componentName === 'FormButton') { - properties[':handler'] = this.byRef(element.config.handler); + if (componentName === "FormButton") { + properties[":handler"] = this.byRef(element.config.handler); + const handlerSecurity = + element.config.handlerSecurityEnabled === undefined + ? true + : element.config.handlerSecurityEnabled; + properties[":handler-security-enabled"] = this.byRef(handlerSecurity); } - }, + } }, mounted() { this.extensions.push({