CVE-2025-58059
Description
Valtimo is a platform for Business Process Automation. In versions before 12.16.0.RELEASE, and from 13.0.0.RELEASE to before 13.1.2.RELEASE, any admin that can create or modify and execute process-definitions could gain access to sensitive data or resources. This includes but is not limited to: running executables on the application host, inspecting and extracting data from the host environment or application properties, spring beans (application context, database pooling). The following conditions have to be met in order to perform this attack: the user must be logged in, have the admin role, and must have some knowledge about running scripts via a the Camunda/Operator engine. Version 12.16.0 and 13.1.2 have been patched. It is strongly advised to upgrade. If no scripting is needed in any of the processes, it could be possible to disable it altogether via the ProcessEngineConfiguration. However, this workaround could lead to unexpected side-effects.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
com.ritense.valtimo:coreMaven | < 12.16.0.RELEASE | 12.16.0.RELEASE |
com.ritense.valtimo:coreMaven | >= 13.0.0.RELEASE, < 13.1.2.RELEASE | 13.1.2.RELEASE |
Affected products
1- Range: 11.3.2.RELEASE, 12.11.0.RELEASE, 12.14.0.RELEASE, …
Patches
33c3335f1acd7Process engine js sandboxing
8 files changed · +290 −1
core/src/main/java/com/ritense/valtimo/autoconfigure/CamundaContextConfiguration.java+12 −1 modified@@ -17,9 +17,12 @@ package com.ritense.valtimo.autoconfigure; import com.ritense.valtimo.CamundaWhitelistedBeansPlugin; +import com.ritense.valtimo.ScriptingWhitelistPlugin; import com.ritense.valtimo.contract.annotation.ProcessBean; import java.util.Map; +import java.util.Set; import org.camunda.bpm.spring.boot.starter.configuration.Ordering; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.ApplicationContext; @@ -28,16 +31,24 @@ import org.springframework.core.annotation.Order; @AutoConfiguration -@ConditionalOnProperty(prefix = "valtimo.camunda", name = "bean-whitelisting", havingValue = "true", matchIfMissing = true) public class CamundaContextConfiguration { @Bean @Order(Ordering.DEFAULT_ORDER - 1) + @ConditionalOnProperty(prefix = "valtimo.camunda", name = "bean-whitelisting", havingValue = "true", matchIfMissing = true) public CamundaWhitelistedBeansPlugin camundaWhitelistedBeansPlugin( @Lazy @ProcessBean Map<String, Object> processBeans, ApplicationContext applicationContext ) { return new CamundaWhitelistedBeansPlugin(processBeans, applicationContext); } + @Bean + @Order(Ordering.DEFAULT_ORDER - 1) + public ScriptingWhitelistPlugin scriptingWhitelistPlugin( + @Value("valtimo.camunda.scripting.allowedClasses") Set<String> allowedScriptingClasses + ) { + return new ScriptingWhitelistPlugin(allowedScriptingClasses); + } + }
core/src/main/kotlin/com/ritense/valtimo/AllowedClassesScriptEngineResolver.kt+65 −0 added@@ -0,0 +1,65 @@ +/* + * Copyright 2015-2025 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.ritense.valtimo + +import com.oracle.truffle.js.scriptengine.GraalJSScriptEngine +import org.camunda.bpm.engine.impl.scripting.engine.DefaultScriptEngineResolver +import org.graalvm.polyglot.Context +import org.graalvm.polyglot.HostAccess +import javax.script.ScriptEngine +import javax.script.ScriptEngineManager + +class AllowedClassesScriptEngineResolver( + scriptEngineManager: ScriptEngineManager, + otherAllowedClasses: Set<String> = emptySet() +) : DefaultScriptEngineResolver(scriptEngineManager) { + val ALL_ALLOWED = ALLOWED + otherAllowedClasses + + override fun getJavaScriptScriptEngine(language: String?): ScriptEngine { + val ctx = Context.newBuilder("js") + .allowHostAccess(HostAccess.ALL) + .allowHostClassLookup(ALL_ALLOWED::contains) + + return GraalJSScriptEngine.create(null, ctx) + } + + companion object { + private val ALLOWED = mutableSetOf<String?>( + "java.util.ArrayList", + "org.joda.time.DateTime", + "java.util.Date", + "java.lang.Math", + "org.camunda.spin.Spin", + //java.time classes + "java.time.Clock", + "java.time.Duration", + "java.time.Instant", + "java.time.LocalDate", + "java.time.LocalDateTime", + "java.time.LocalTime", + "java.time.MonthDay", + "java.time.OffsetDateTime", + "java.time.OffsetTime", + "java.time.Period", + "java.time.Year", + "java.time.YearMonth", + "java.time.ZonedDateTime", + "java.time.ZoneId", + "java.time.ZoneOffset", + ) + } +} \ No newline at end of file
core/src/main/kotlin/com/ritense/valtimo/ScriptingWhitelistPlugin.kt+39 −0 added@@ -0,0 +1,39 @@ +/* + * Copyright 2015-2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.ritense.valtimo + +import mu.KotlinLogging +import org.camunda.bpm.engine.impl.cfg.AbstractProcessEnginePlugin +import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl +import javax.script.ScriptEngineManager + +class ScriptingWhitelistPlugin( + private val allowedScriptingClasses: Set<String> = emptySet() +) : AbstractProcessEnginePlugin() { + override fun preInit(processEngineConfiguration: ProcessEngineConfigurationImpl?) { + logger.debug { "Registering allowed classes for scripting..." } + requireNotNull(processEngineConfiguration) { "No process engine configuration found. Failed to register allowed scripting classes." } + + processEngineConfiguration.setConfigureScriptEngineHostAccess(false) + processEngineConfiguration.setEnableScriptEngineLoadExternalResources(false) + processEngineConfiguration.setScriptEngineResolver(AllowedClassesScriptEngineResolver(ScriptEngineManager(), allowedScriptingClasses)) + } + + companion object { + val logger = KotlinLogging.logger {} + } +} \ No newline at end of file
core/src/test/kotlin/com/ritense/valtimo/scripttask/JavascriptScriptTaskProcessIntTest.kt+44 −0 modified@@ -20,7 +20,10 @@ import com.ritense.authorization.AuthorizationContext.Companion.runWithoutAuthor import com.ritense.valtimo.BaseIntegrationTest import com.ritense.valtimo.service.CamundaProcessService import org.camunda.bpm.engine.HistoryService +import org.camunda.bpm.engine.ScriptEvaluationException import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertDoesNotThrow +import org.junit.jupiter.api.assertThrows import org.springframework.beans.factory.annotation.Autowired import org.springframework.transaction.annotation.Transactional import java.util.UUID @@ -52,4 +55,45 @@ class JavascriptScriptTaskProcessIntTest : BaseIntegrationTest() { .value assertEquals(3, c) } + + @Test + fun `whitelisted classes should be allowed in script tasks`() { + + assertDoesNotThrow { + runWithoutAuthorization { + camundaProcessService.startProcess( + "javascript-script-task-process-allowed", + UUID.randomUUID().toString(), + emptyMap() + ).processInstanceDto + } + } + } + + @Test + fun `default whitelisted classes should be allowed in script tasks`() { + + assertDoesNotThrow { + runWithoutAuthorization { + camundaProcessService.startProcess( + "javascript-script-task-process-default-allowed", + UUID.randomUUID().toString(), + emptyMap() + ).processInstanceDto + } + } + } + + @Test + fun `non-whitelisted classes should not be allowed in script tasks`() { + assertThrows<ScriptEvaluationException> { + runWithoutAuthorization { + camundaProcessService.startProcess( + "javascript-script-task-process-unallowed", + UUID.randomUUID().toString(), + emptyMap() + ).processInstanceDto + } + } + } } \ No newline at end of file
core/src/test/resources/bpmn/javascript-script-task-process-allowed.bpmn+42 −0 added@@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_18vfwpp" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.23.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.19.0"> + <bpmn:process id="javascript-script-task-process-allowed" name="Javascript script task process" isExecutable="true" camunda:historyTimeToLive="180"> + <bpmn:startEvent id="StartEvent_1"> + <bpmn:outgoing>Flow_0kymcf3</bpmn:outgoing> + </bpmn:startEvent> + <bpmn:sequenceFlow id="Flow_0kymcf3" sourceRef="StartEvent_1" targetRef="Activity_1wy7vpn" /> + <bpmn:endEvent id="Event_0k1400n"> + <bpmn:incoming>Flow_1muqoyt</bpmn:incoming> + </bpmn:endEvent> + <bpmn:sequenceFlow id="Flow_1muqoyt" sourceRef="Activity_1wy7vpn" targetRef="Event_0k1400n" /> + <bpmn:scriptTask id="Activity_1wy7vpn" name="Sum" scriptFormat="javascript"> + <bpmn:incoming>Flow_0kymcf3</bpmn:incoming> + <bpmn:outgoing>Flow_1muqoyt</bpmn:outgoing> + <bpmn:script>const Math = Java.type("java.lang.Math"); + +const result = Math.pow(2,3);</bpmn:script> + </bpmn:scriptTask> + </bpmn:process> + <bpmndi:BPMNDiagram id="BPMNDiagram_1"> + <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="javascript-script-task-process-allowed"> + <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1"> + <dc:Bounds x="179" y="99" width="36" height="36" /> + </bpmndi:BPMNShape> + <bpmndi:BPMNShape id="Event_0k1400n_di" bpmnElement="Event_0k1400n"> + <dc:Bounds x="432" y="99" width="36" height="36" /> + </bpmndi:BPMNShape> + <bpmndi:BPMNShape id="Activity_0zbtk6u_di" bpmnElement="Activity_1wy7vpn"> + <dc:Bounds x="270" y="77" width="100" height="80" /> + <bpmndi:BPMNLabel /> + </bpmndi:BPMNShape> + <bpmndi:BPMNEdge id="Flow_0kymcf3_di" bpmnElement="Flow_0kymcf3"> + <di:waypoint x="215" y="117" /> + <di:waypoint x="270" y="117" /> + </bpmndi:BPMNEdge> + <bpmndi:BPMNEdge id="Flow_1muqoyt_di" bpmnElement="Flow_1muqoyt"> + <di:waypoint x="370" y="117" /> + <di:waypoint x="432" y="117" /> + </bpmndi:BPMNEdge> + </bpmndi:BPMNPlane> + </bpmndi:BPMNDiagram> +</bpmn:definitions>
core/src/test/resources/bpmn/javascript-script-task-process-default-allowed.bpmn+42 −0 added@@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_18vfwpp" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.23.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.19.0"> + <bpmn:process id="javascript-script-task-process-default-allowed" name="Javascript script task process" isExecutable="true" camunda:historyTimeToLive="180"> + <bpmn:startEvent id="StartEvent_1"> + <bpmn:outgoing>Flow_0kymcf3</bpmn:outgoing> + </bpmn:startEvent> + <bpmn:sequenceFlow id="Flow_0kymcf3" sourceRef="StartEvent_1" targetRef="Activity_1wy7vpn" /> + <bpmn:endEvent id="Event_0k1400n"> + <bpmn:incoming>Flow_1muqoyt</bpmn:incoming> + </bpmn:endEvent> + <bpmn:sequenceFlow id="Flow_1muqoyt" sourceRef="Activity_1wy7vpn" targetRef="Event_0k1400n" /> + <bpmn:scriptTask id="Activity_1wy7vpn" name="Sum" scriptFormat="javascript"> + <bpmn:incoming>Flow_0kymcf3</bpmn:incoming> + <bpmn:outgoing>Flow_1muqoyt</bpmn:outgoing> + <bpmn:script>const LocalDateTime = Java.type("java.time.LocalDateTime"); + +const result = LocalDateTime.now()</bpmn:script> + </bpmn:scriptTask> + </bpmn:process> + <bpmndi:BPMNDiagram id="BPMNDiagram_1"> + <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="javascript-script-task-process-default-allowed"> + <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1"> + <dc:Bounds x="179" y="99" width="36" height="36" /> + </bpmndi:BPMNShape> + <bpmndi:BPMNShape id="Event_0k1400n_di" bpmnElement="Event_0k1400n"> + <dc:Bounds x="432" y="99" width="36" height="36" /> + </bpmndi:BPMNShape> + <bpmndi:BPMNShape id="Activity_0zbtk6u_di" bpmnElement="Activity_1wy7vpn"> + <dc:Bounds x="270" y="77" width="100" height="80" /> + <bpmndi:BPMNLabel /> + </bpmndi:BPMNShape> + <bpmndi:BPMNEdge id="Flow_0kymcf3_di" bpmnElement="Flow_0kymcf3"> + <di:waypoint x="215" y="117" /> + <di:waypoint x="270" y="117" /> + </bpmndi:BPMNEdge> + <bpmndi:BPMNEdge id="Flow_1muqoyt_di" bpmnElement="Flow_1muqoyt"> + <di:waypoint x="370" y="117" /> + <di:waypoint x="432" y="117" /> + </bpmndi:BPMNEdge> + </bpmndi:BPMNPlane> + </bpmndi:BPMNDiagram> +</bpmn:definitions>
core/src/test/resources/bpmn/javascript-script-task-process-unallowed.bpmn+42 −0 added@@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_18vfwpp" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.23.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.19.0"> + <bpmn:process id="javascript-script-task-process-unallowed" name="Javascript script task process" isExecutable="true" camunda:historyTimeToLive="180"> + <bpmn:startEvent id="StartEvent_1"> + <bpmn:outgoing>Flow_0kymcf3</bpmn:outgoing> + </bpmn:startEvent> + <bpmn:sequenceFlow id="Flow_0kymcf3" sourceRef="StartEvent_1" targetRef="Activity_1wy7vpn" /> + <bpmn:endEvent id="Event_0k1400n"> + <bpmn:incoming>Flow_1muqoyt</bpmn:incoming> + </bpmn:endEvent> + <bpmn:sequenceFlow id="Flow_1muqoyt" sourceRef="Activity_1wy7vpn" targetRef="Event_0k1400n" /> + <bpmn:scriptTask id="Activity_1wy7vpn" name="Sum" scriptFormat="javascript"> + <bpmn:incoming>Flow_0kymcf3</bpmn:incoming> + <bpmn:outgoing>Flow_1muqoyt</bpmn:outgoing> + <bpmn:script>const HashMap = Java.type("java.util.HashMap"); + +const result = new HashMap()</bpmn:script> + </bpmn:scriptTask> + </bpmn:process> + <bpmndi:BPMNDiagram id="BPMNDiagram_1"> + <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="javascript-script-task-process-unallowed"> + <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1"> + <dc:Bounds x="179" y="99" width="36" height="36" /> + </bpmndi:BPMNShape> + <bpmndi:BPMNShape id="Event_0k1400n_di" bpmnElement="Event_0k1400n"> + <dc:Bounds x="432" y="99" width="36" height="36" /> + </bpmndi:BPMNShape> + <bpmndi:BPMNShape id="Activity_0zbtk6u_di" bpmnElement="Activity_1wy7vpn"> + <dc:Bounds x="270" y="77" width="100" height="80" /> + <bpmndi:BPMNLabel /> + </bpmndi:BPMNShape> + <bpmndi:BPMNEdge id="Flow_0kymcf3_di" bpmnElement="Flow_0kymcf3"> + <di:waypoint x="215" y="117" /> + <di:waypoint x="270" y="117" /> + </bpmndi:BPMNEdge> + <bpmndi:BPMNEdge id="Flow_1muqoyt_di" bpmnElement="Flow_1muqoyt"> + <di:waypoint x="370" y="117" /> + <di:waypoint x="432" y="117" /> + </bpmndi:BPMNEdge> + </bpmndi:BPMNPlane> + </bpmndi:BPMNDiagram> +</bpmn:definitions>
core/src/test/resources/config/application.yml+4 −0 modified@@ -51,6 +51,10 @@ valtimo: reminderTemplate: bpc-task-reminder plugin: encryption-secret: "abcdefghijklmnop" + camunda: + scripting: + allowedClasses: + - java.lang.Math aws: profile: ritense
2423d1551549Add a check to only add the operaton role when it hasn't been created yet (#1949)
1 file changed · +22 −5
core/src/main/resources/config/liquibase/changelog/20250812-operaton-migration.xml+22 −5 modified@@ -20,19 +20,36 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd"> - <changeSet id="1" author="Ritense"> + <changeSet id="2" author="Ritense"> + <preConditions onFail="MARK_RAN"> + <sqlCheck expectedResult="0">select count(*) from act_id_group where id_ = 'operaton-admin'</sqlCheck> + </preConditions> + <sql> + insert into act_id_group (id_, name_, type_, rev_) + values ('operaton-admin', 'operaton BPM Administrators', 'SYSTEM', 1) + </sql> + </changeSet> + + <changeSet id="3" author="Ritense"> <preConditions onFail="MARK_RAN"> <not> <sqlCheck expectedResult="0">select count(*) from act_id_group where id_ = 'camunda-admin'</sqlCheck> </not> </preConditions> <sql> - insert into act_id_group (id_, name_, type_, rev_) - values ('operaton-admin', 'operaton BPM Administrators', 'SYSTEM', 1) + insert into act_id_membership (user_id_, group_id_) + select user_id_, 'operaton-admin' + from act_id_membership m1 + where group_id_ = 'camunda-admin' + and not exists ( + select * + from act_id_membership m2 + where group_id_ = 'operaton-admin' + and m1.user_id_ = m2.user_id_ + ) </sql> <sql> - update act_id_membership - set group_id_ = 'operaton-admin' + delete from act_id_membership where group_id_ = 'camunda-admin' </sql> <sql>
45eb60b0b2dfProcess engine js sandboxing
8 files changed · +290 −1
core/src/main/java/com/ritense/valtimo/autoconfigure/CamundaContextConfiguration.java+12 −1 modified@@ -17,9 +17,12 @@ package com.ritense.valtimo.autoconfigure; import com.ritense.valtimo.CamundaWhitelistedBeansPlugin; +import com.ritense.valtimo.ScriptingWhitelistPlugin; import com.ritense.valtimo.contract.annotation.ProcessBean; import java.util.Map; +import java.util.Set; import org.camunda.bpm.spring.boot.starter.configuration.Ordering; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.ApplicationContext; @@ -28,16 +31,24 @@ import org.springframework.core.annotation.Order; @AutoConfiguration -@ConditionalOnProperty(prefix = "valtimo.camunda", name = "bean-whitelisting", havingValue = "true", matchIfMissing = true) public class CamundaContextConfiguration { @Bean @Order(Ordering.DEFAULT_ORDER - 1) + @ConditionalOnProperty(prefix = "valtimo.camunda", name = "bean-whitelisting", havingValue = "true", matchIfMissing = true) public CamundaWhitelistedBeansPlugin camundaWhitelistedBeansPlugin( @Lazy @ProcessBean Map<String, Object> processBeans, ApplicationContext applicationContext ) { return new CamundaWhitelistedBeansPlugin(processBeans, applicationContext); } + @Bean + @Order(Ordering.DEFAULT_ORDER - 1) + public ScriptingWhitelistPlugin scriptingWhitelistPlugin( + @Value("valtimo.camunda.scripting.allowedClasses") Set<String> allowedScriptingClasses + ) { + return new ScriptingWhitelistPlugin(allowedScriptingClasses); + } + }
core/src/main/kotlin/com/ritense/valtimo/AllowedClassesScriptEngineResolver.kt+65 −0 added@@ -0,0 +1,65 @@ +/* + * Copyright 2015-2025 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.ritense.valtimo + +import com.oracle.truffle.js.scriptengine.GraalJSScriptEngine +import org.camunda.bpm.engine.impl.scripting.engine.DefaultScriptEngineResolver +import org.graalvm.polyglot.Context +import org.graalvm.polyglot.HostAccess +import javax.script.ScriptEngine +import javax.script.ScriptEngineManager + +class AllowedClassesScriptEngineResolver( + scriptEngineManager: ScriptEngineManager, + otherAllowedClasses: Set<String> = emptySet() +) : DefaultScriptEngineResolver(scriptEngineManager) { + val ALL_ALLOWED = ALLOWED + otherAllowedClasses + + override fun getJavaScriptScriptEngine(language: String?): ScriptEngine { + val ctx = Context.newBuilder("js") + .allowHostAccess(HostAccess.ALL) + .allowHostClassLookup(ALL_ALLOWED::contains) + + return GraalJSScriptEngine.create(null, ctx) + } + + companion object { + private val ALLOWED = mutableSetOf<String?>( + "java.util.ArrayList", + "org.joda.time.DateTime", + "java.util.Date", + "java.lang.Math", + "org.camunda.spin.Spin", + //java.time classes + "java.time.Clock", + "java.time.Duration", + "java.time.Instant", + "java.time.LocalDate", + "java.time.LocalDateTime", + "java.time.LocalTime", + "java.time.MonthDay", + "java.time.OffsetDateTime", + "java.time.OffsetTime", + "java.time.Period", + "java.time.Year", + "java.time.YearMonth", + "java.time.ZonedDateTime", + "java.time.ZoneId", + "java.time.ZoneOffset", + ) + } +} \ No newline at end of file
core/src/main/kotlin/com/ritense/valtimo/ScriptingWhitelistPlugin.kt+39 −0 added@@ -0,0 +1,39 @@ +/* + * Copyright 2015-2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.ritense.valtimo + +import mu.KotlinLogging +import org.camunda.bpm.engine.impl.cfg.AbstractProcessEnginePlugin +import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl +import javax.script.ScriptEngineManager + +class ScriptingWhitelistPlugin( + private val allowedScriptingClasses: Set<String> = emptySet() +) : AbstractProcessEnginePlugin() { + override fun preInit(processEngineConfiguration: ProcessEngineConfigurationImpl?) { + logger.debug { "Registering allowed classes for scripting..." } + requireNotNull(processEngineConfiguration) { "No process engine configuration found. Failed to register allowed scripting classes." } + + processEngineConfiguration.setConfigureScriptEngineHostAccess(false) + processEngineConfiguration.setEnableScriptEngineLoadExternalResources(false) + processEngineConfiguration.setScriptEngineResolver(AllowedClassesScriptEngineResolver(ScriptEngineManager(), allowedScriptingClasses)) + } + + companion object { + val logger = KotlinLogging.logger {} + } +} \ No newline at end of file
core/src/test/kotlin/com/ritense/valtimo/scripttask/JavascriptScriptTaskProcessIntTest.kt+44 −0 modified@@ -20,7 +20,10 @@ import com.ritense.authorization.AuthorizationContext.Companion.runWithoutAuthor import com.ritense.valtimo.BaseIntegrationTest import com.ritense.valtimo.service.CamundaProcessService import org.camunda.bpm.engine.HistoryService +import org.camunda.bpm.engine.ScriptEvaluationException import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertDoesNotThrow +import org.junit.jupiter.api.assertThrows import org.springframework.beans.factory.annotation.Autowired import org.springframework.transaction.annotation.Transactional import java.util.UUID @@ -52,4 +55,45 @@ class JavascriptScriptTaskProcessIntTest : BaseIntegrationTest() { .value assertEquals(3, c) } + + @Test + fun `whitelisted classes should be allowed in script tasks`() { + + assertDoesNotThrow { + runWithoutAuthorization { + camundaProcessService.startProcess( + "javascript-script-task-process-allowed", + UUID.randomUUID().toString(), + emptyMap() + ).processInstanceDto + } + } + } + + @Test + fun `default whitelisted classes should be allowed in script tasks`() { + + assertDoesNotThrow { + runWithoutAuthorization { + camundaProcessService.startProcess( + "javascript-script-task-process-default-allowed", + UUID.randomUUID().toString(), + emptyMap() + ).processInstanceDto + } + } + } + + @Test + fun `non-whitelisted classes should not be allowed in script tasks`() { + assertThrows<ScriptEvaluationException> { + runWithoutAuthorization { + camundaProcessService.startProcess( + "javascript-script-task-process-unallowed", + UUID.randomUUID().toString(), + emptyMap() + ).processInstanceDto + } + } + } } \ No newline at end of file
core/src/test/resources/bpmn/javascript-script-task-process-allowed.bpmn+42 −0 added@@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_18vfwpp" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.23.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.19.0"> + <bpmn:process id="javascript-script-task-process-allowed" name="Javascript script task process" isExecutable="true" camunda:historyTimeToLive="180"> + <bpmn:startEvent id="StartEvent_1"> + <bpmn:outgoing>Flow_0kymcf3</bpmn:outgoing> + </bpmn:startEvent> + <bpmn:sequenceFlow id="Flow_0kymcf3" sourceRef="StartEvent_1" targetRef="Activity_1wy7vpn" /> + <bpmn:endEvent id="Event_0k1400n"> + <bpmn:incoming>Flow_1muqoyt</bpmn:incoming> + </bpmn:endEvent> + <bpmn:sequenceFlow id="Flow_1muqoyt" sourceRef="Activity_1wy7vpn" targetRef="Event_0k1400n" /> + <bpmn:scriptTask id="Activity_1wy7vpn" name="Sum" scriptFormat="javascript"> + <bpmn:incoming>Flow_0kymcf3</bpmn:incoming> + <bpmn:outgoing>Flow_1muqoyt</bpmn:outgoing> + <bpmn:script>const Math = Java.type("java.lang.Math"); + +const result = Math.pow(2,3);</bpmn:script> + </bpmn:scriptTask> + </bpmn:process> + <bpmndi:BPMNDiagram id="BPMNDiagram_1"> + <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="javascript-script-task-process-allowed"> + <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1"> + <dc:Bounds x="179" y="99" width="36" height="36" /> + </bpmndi:BPMNShape> + <bpmndi:BPMNShape id="Event_0k1400n_di" bpmnElement="Event_0k1400n"> + <dc:Bounds x="432" y="99" width="36" height="36" /> + </bpmndi:BPMNShape> + <bpmndi:BPMNShape id="Activity_0zbtk6u_di" bpmnElement="Activity_1wy7vpn"> + <dc:Bounds x="270" y="77" width="100" height="80" /> + <bpmndi:BPMNLabel /> + </bpmndi:BPMNShape> + <bpmndi:BPMNEdge id="Flow_0kymcf3_di" bpmnElement="Flow_0kymcf3"> + <di:waypoint x="215" y="117" /> + <di:waypoint x="270" y="117" /> + </bpmndi:BPMNEdge> + <bpmndi:BPMNEdge id="Flow_1muqoyt_di" bpmnElement="Flow_1muqoyt"> + <di:waypoint x="370" y="117" /> + <di:waypoint x="432" y="117" /> + </bpmndi:BPMNEdge> + </bpmndi:BPMNPlane> + </bpmndi:BPMNDiagram> +</bpmn:definitions>
core/src/test/resources/bpmn/javascript-script-task-process-default-allowed.bpmn+42 −0 added@@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_18vfwpp" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.23.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.19.0"> + <bpmn:process id="javascript-script-task-process-default-allowed" name="Javascript script task process" isExecutable="true" camunda:historyTimeToLive="180"> + <bpmn:startEvent id="StartEvent_1"> + <bpmn:outgoing>Flow_0kymcf3</bpmn:outgoing> + </bpmn:startEvent> + <bpmn:sequenceFlow id="Flow_0kymcf3" sourceRef="StartEvent_1" targetRef="Activity_1wy7vpn" /> + <bpmn:endEvent id="Event_0k1400n"> + <bpmn:incoming>Flow_1muqoyt</bpmn:incoming> + </bpmn:endEvent> + <bpmn:sequenceFlow id="Flow_1muqoyt" sourceRef="Activity_1wy7vpn" targetRef="Event_0k1400n" /> + <bpmn:scriptTask id="Activity_1wy7vpn" name="Sum" scriptFormat="javascript"> + <bpmn:incoming>Flow_0kymcf3</bpmn:incoming> + <bpmn:outgoing>Flow_1muqoyt</bpmn:outgoing> + <bpmn:script>const LocalDateTime = Java.type("java.time.LocalDateTime"); + +const result = LocalDateTime.now()</bpmn:script> + </bpmn:scriptTask> + </bpmn:process> + <bpmndi:BPMNDiagram id="BPMNDiagram_1"> + <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="javascript-script-task-process-default-allowed"> + <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1"> + <dc:Bounds x="179" y="99" width="36" height="36" /> + </bpmndi:BPMNShape> + <bpmndi:BPMNShape id="Event_0k1400n_di" bpmnElement="Event_0k1400n"> + <dc:Bounds x="432" y="99" width="36" height="36" /> + </bpmndi:BPMNShape> + <bpmndi:BPMNShape id="Activity_0zbtk6u_di" bpmnElement="Activity_1wy7vpn"> + <dc:Bounds x="270" y="77" width="100" height="80" /> + <bpmndi:BPMNLabel /> + </bpmndi:BPMNShape> + <bpmndi:BPMNEdge id="Flow_0kymcf3_di" bpmnElement="Flow_0kymcf3"> + <di:waypoint x="215" y="117" /> + <di:waypoint x="270" y="117" /> + </bpmndi:BPMNEdge> + <bpmndi:BPMNEdge id="Flow_1muqoyt_di" bpmnElement="Flow_1muqoyt"> + <di:waypoint x="370" y="117" /> + <di:waypoint x="432" y="117" /> + </bpmndi:BPMNEdge> + </bpmndi:BPMNPlane> + </bpmndi:BPMNDiagram> +</bpmn:definitions>
core/src/test/resources/bpmn/javascript-script-task-process-unallowed.bpmn+42 −0 added@@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_18vfwpp" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.23.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.19.0"> + <bpmn:process id="javascript-script-task-process-unallowed" name="Javascript script task process" isExecutable="true" camunda:historyTimeToLive="180"> + <bpmn:startEvent id="StartEvent_1"> + <bpmn:outgoing>Flow_0kymcf3</bpmn:outgoing> + </bpmn:startEvent> + <bpmn:sequenceFlow id="Flow_0kymcf3" sourceRef="StartEvent_1" targetRef="Activity_1wy7vpn" /> + <bpmn:endEvent id="Event_0k1400n"> + <bpmn:incoming>Flow_1muqoyt</bpmn:incoming> + </bpmn:endEvent> + <bpmn:sequenceFlow id="Flow_1muqoyt" sourceRef="Activity_1wy7vpn" targetRef="Event_0k1400n" /> + <bpmn:scriptTask id="Activity_1wy7vpn" name="Sum" scriptFormat="javascript"> + <bpmn:incoming>Flow_0kymcf3</bpmn:incoming> + <bpmn:outgoing>Flow_1muqoyt</bpmn:outgoing> + <bpmn:script>const HashMap = Java.type("java.util.HashMap"); + +const result = new HashMap()</bpmn:script> + </bpmn:scriptTask> + </bpmn:process> + <bpmndi:BPMNDiagram id="BPMNDiagram_1"> + <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="javascript-script-task-process-unallowed"> + <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1"> + <dc:Bounds x="179" y="99" width="36" height="36" /> + </bpmndi:BPMNShape> + <bpmndi:BPMNShape id="Event_0k1400n_di" bpmnElement="Event_0k1400n"> + <dc:Bounds x="432" y="99" width="36" height="36" /> + </bpmndi:BPMNShape> + <bpmndi:BPMNShape id="Activity_0zbtk6u_di" bpmnElement="Activity_1wy7vpn"> + <dc:Bounds x="270" y="77" width="100" height="80" /> + <bpmndi:BPMNLabel /> + </bpmndi:BPMNShape> + <bpmndi:BPMNEdge id="Flow_0kymcf3_di" bpmnElement="Flow_0kymcf3"> + <di:waypoint x="215" y="117" /> + <di:waypoint x="270" y="117" /> + </bpmndi:BPMNEdge> + <bpmndi:BPMNEdge id="Flow_1muqoyt_di" bpmnElement="Flow_1muqoyt"> + <di:waypoint x="370" y="117" /> + <di:waypoint x="432" y="117" /> + </bpmndi:BPMNEdge> + </bpmndi:BPMNPlane> + </bpmndi:BPMNDiagram> +</bpmn:definitions>
core/src/test/resources/config/application.yml+4 −0 modified@@ -51,6 +51,10 @@ valtimo: reminderTemplate: bpc-task-reminder plugin: encryption-secret: "abcdefghijklmnop" + camunda: + scripting: + allowedClasses: + - java.lang.Math aws: profile: ritense
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-w48j-pp7j-fj55ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-58059ghsaADVISORY
- github.com/valtimo-platform/valtimo-backend-libraries/commit/45eb60b0b2df5964fb9917295d0dceb1fff8dd85nvdWEB
- github.com/valtimo-platform/valtimo-backend-libraries/security/advisories/GHSA-w48j-pp7j-fj55nvdWEB
News mentions
0No linked articles in our index yet.