Skip to content

Commit f906198

Browse files
committed
Make sure that empty CDATA is properly parsed
There is a bug in the default Java XML Stream Reader in which an empty CDATA throws an IndexOutOfBoundsException when the schema is being validated. Therefore, we are creating our own delegating FlowableXMLStreamReader and handle the edge case for an empty CDATA.
1 parent c74491c commit f906198

File tree

11 files changed

+153
-3
lines changed

11 files changed

+153
-3
lines changed

modules/flowable-bpmn-converter/src/main/java/org/flowable/bpmn/converter/BpmnXMLConverter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ public BpmnModel convertToBpmnModel(InputStreamProvider inputStreamProvider, boo
281281
if (!enableSafeBpmnXml) {
282282
validateModel(inputStreamProvider);
283283
} else {
284-
validateModel(xif.createXMLStreamReader(in));
284+
validateModel(new FlowableXMLStreamReader(xif.createXMLStreamReader(in)));
285285
}
286286
} catch (UnsupportedEncodingException e) {
287287
throw new XMLException("The bpmn 2.0 xml is not properly encoded", e);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/* Licensed under the Apache License, Version 2.0 (the "License");
2+
* you may not use this file except in compliance with the License.
3+
* You may obtain a copy of the License at
4+
*
5+
* http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*/
13+
package org.flowable.bpmn.converter;
14+
15+
import javax.xml.stream.XMLStreamException;
16+
import javax.xml.stream.XMLStreamReader;
17+
import javax.xml.stream.util.StreamReaderDelegate;
18+
19+
/**
20+
* @author Filip Hrisafov
21+
*/
22+
public class FlowableXMLStreamReader extends StreamReaderDelegate {
23+
24+
public FlowableXMLStreamReader(XMLStreamReader reader) {
25+
super(reader);
26+
}
27+
28+
@Override
29+
public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length) throws XMLStreamException {
30+
try {
31+
return super.getTextCharacters(sourceStart, target, targetStart, length);
32+
} catch (IndexOutOfBoundsException e) {
33+
if (length == 0) {
34+
// The default java stream reader has a bug where an empty CDATA will throw an IndexOutOfBoundsException
35+
// When the length to copy is 0, then we should not copy anything and just return 0
36+
return 0;
37+
}
38+
throw e;
39+
}
40+
}
41+
}

modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/CmmnXmlConverter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ public CmmnModel convertToCmmnModel(InputStreamProvider inputStreamProvider, boo
175175
if (!enableSafeBpmnXml) {
176176
validateModel(inputStreamProvider);
177177
} else {
178-
validateModel(xif.createXMLStreamReader(in));
178+
validateModel(new FlowableXMLStreamReader(xif.createXMLStreamReader(in)));
179179
}
180180
} catch (UnsupportedEncodingException e) {
181181
throw new CmmnXMLException("The CMMN 1.1 xml is not properly encoded", e);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/* Licensed under the Apache License, Version 2.0 (the "License");
2+
* you may not use this file except in compliance with the License.
3+
* You may obtain a copy of the License at
4+
*
5+
* http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*/
13+
package org.flowable.cmmn.converter;
14+
15+
import javax.xml.stream.XMLStreamException;
16+
import javax.xml.stream.XMLStreamReader;
17+
import javax.xml.stream.util.StreamReaderDelegate;
18+
19+
/**
20+
* @author Filip Hrisafov
21+
*/
22+
public class FlowableXMLStreamReader extends StreamReaderDelegate {
23+
24+
public FlowableXMLStreamReader(XMLStreamReader reader) {
25+
super(reader);
26+
}
27+
28+
@Override
29+
public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length) throws XMLStreamException {
30+
try {
31+
return super.getTextCharacters(sourceStart, target, targetStart, length);
32+
} catch (IndexOutOfBoundsException e) {
33+
if (length == 0) {
34+
// The default java stream reader has a bug where an empty CDATA will throw an IndexOutOfBoundsException
35+
// When the length to copy is 0, then we should not copy anything and just return 0
36+
return 0;
37+
}
38+
throw e;
39+
}
40+
}
41+
}

modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/deployer/CmmnDeployer.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,8 @@ public String xmlEncoding() {
431431

432432
@Override
433433
public boolean validateXml() {
434-
return !cmmnEngineConfiguration.isDisableCmmnXmlValidation();
434+
// On redeploy, we assume it is validated at the first deploy
435+
return newDeployment && !cmmnEngineConfiguration.isDisableCmmnXmlValidation();
435436
}
436437

437438
@Override

modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/repository/DeploymentTest.java

+13
Original file line numberDiff line numberDiff line change
@@ -244,4 +244,17 @@ public void deployingCaseModelWithWarningsShouldNotFail() {
244244
}
245245

246246
}
247+
248+
@Test
249+
public void deployingCaseModelWithEmptyCDATAShouldNotFail() {
250+
org.flowable.cmmn.api.repository.CmmnDeployment cmmnDeployment = cmmnRepositoryService.createDeployment()
251+
.addClasspathResource("org/flowable/cmmn/test/repository/DeploymentTest.testCaseDefinitionWithEmptyCDATA.cmmn")
252+
.deploy();
253+
autoCleanupDeploymentIds.add(cmmnDeployment.getId());
254+
CaseDefinition caseDefinition = cmmnRepositoryService.createCaseDefinitionQuery()
255+
.caseDefinitionKey("myCase")
256+
.singleResult();
257+
assertThat(caseDefinition).isNotNull();
258+
259+
}
247260
}

modules/flowable-cmmn-engine/src/test/resources/flowable.cmmn.cfg.xml

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040
<property name="enableEntityLinks" value="true" />
4141
<property name="enableCaseDefinitionHistoryLevel" value="true" />
42+
<property name="enableSafeCmmnXml" value="true" />
4243

4344
<property name="enableHistoricTaskLogging" value="true" />
4445

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<definitions
3+
xmlns="http://www.omg.org/spec/CMMN/20151109/MODEL"
4+
xmlns:flowable="http://flowable.org/cmmn"
5+
targetNamespace="http://flowable.org/cmmn">
6+
7+
<case id="myCase" name="My Invalid Case Model">
8+
<casePlanModel id="myPlanModel" name="My CasePlanModel">
9+
<planItem id="planItem1" name="Task 1" definitionRef="CasePageTask_1"/>
10+
<task id="CasePageTask_1" name="Case page" flowable:type="casePage">
11+
<extensionElements>
12+
<flowable:translation key="label" locale="it_it"><![CDATA[]]></flowable:translation>
13+
</extensionElements>
14+
</task>
15+
</casePlanModel>
16+
</case>
17+
18+
</definitions>

modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/deployment/BpmnDeploymentTest.java

+13
Original file line numberDiff line numberDiff line change
@@ -337,4 +337,17 @@ public void testV5Deployment() {
337337
.as("Expected deployment exception because v5 compatibility handler is not enabled");
338338
}
339339

340+
@Test
341+
public void deployingModelWithEmptyCDATAShouldNotFail() {
342+
org.flowable.engine.repository.Deployment deployment = repositoryService.createDeployment()
343+
.addClasspathResource("org/flowable/engine/test/bpmn/deployment/BpmnDeploymentTest.testBpmnWithEmptyCDATA.bpmn20.xml")
344+
.deploy();
345+
deploymentIdsForAutoCleanup.add(deployment.getId());
346+
347+
ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
348+
.processDefinitionKey("myProcess")
349+
.singleResult();
350+
assertThat(definition).isNotNull();
351+
}
352+
340353
}

modules/flowable-engine/src/test/resources/flowable.cfg.xml

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
<property name="asyncHistoryEnabled" value="false" />
7171
<property name="enableEntityLinks" value="true" />
7272
<property name="enableProcessDefinitionHistoryLevel" value="true" />
73+
<property name="enableSafeBpmnXml" value="true"/>
7374

7475
<property name="enableProcessDefinitionInfoCache" value="true" />
7576

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<definitions id="definitions"
3+
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
4+
xmlns:flowable="http://flowable.org/bpmn"
5+
targetNamespace="Examples">
6+
7+
<process id="myProcess">
8+
9+
<startEvent id="start" />
10+
11+
<sequenceFlow id="flow1" sourceRef="start" targetRef="end" >
12+
<extensionElements>
13+
<flowable:translation key="label" locale="it_it"><![CDATA[]]></flowable:translation>
14+
</extensionElements>
15+
</sequenceFlow>
16+
17+
<endEvent id="end" />
18+
19+
</process>
20+
21+
</definitions>

0 commit comments

Comments
 (0)