From 29cc466dd72da8de753fc1202860203f11643396 Mon Sep 17 00:00:00 2001 From: Burak KALAYCI Date: Mon, 21 Sep 2026 12:53:37 +0300 Subject: [PATCH] Don't fail XML save when sample data contains illegal XML characters Woodstox rejects NUL and C0 controls by default. Replace them so JTL recording and JMX save keep working after the xmlpull migration. Closes #6761 --- .../apache/jmeter/save/JMeterStaxDriver.kt | 18 ++++- .../save/SaveServiceInvalidXmlCharTest.java | 72 +++++++++++++++++++ xdocs/changes.xml | 1 + 3 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 src/core/src/test/java/org/apache/jmeter/save/SaveServiceInvalidXmlCharTest.java diff --git a/src/core/src/main/kotlin/org/apache/jmeter/save/JMeterStaxDriver.kt b/src/core/src/main/kotlin/org/apache/jmeter/save/JMeterStaxDriver.kt index fef5c9383a8..74c8d7b4be3 100644 --- a/src/core/src/main/kotlin/org/apache/jmeter/save/JMeterStaxDriver.kt +++ b/src/core/src/main/kotlin/org/apache/jmeter/save/JMeterStaxDriver.kt @@ -17,6 +17,8 @@ package org.apache.jmeter.save +import com.ctc.wstx.api.InvalidCharHandler +import com.ctc.wstx.api.WstxOutputProperties import com.thoughtworks.xstream.io.xml.StaxDriver import javax.xml.stream.XMLOutputFactory @@ -24,6 +26,18 @@ public class JMeterStaxDriver( public val xmlHeader: Boolean = true, public val indent: Boolean = true, ) : StaxDriver() { - override fun createOutputFactory(): XMLOutputFactory = - XMLOutputFactoryDelegate(super.createOutputFactory(), xmlHeader = xmlHeader, indent = indent) + override fun createOutputFactory(): XMLOutputFactory { + val factory = super.createOutputFactory() + // Woodstox rejects XML 1.0-illegal characters (NUL, C0 controls) by default. + // Replace them so JTL/JMX save of binary payloads does not fail (#6761). + factory.setProperty( + WstxOutputProperties.P_OUTPUT_INVALID_CHAR_HANDLER, + InvalidCharHandler.ReplacingHandler(INVALID_XML_CHAR_REPLACEMENT) + ) + return XMLOutputFactoryDelegate(factory, xmlHeader = xmlHeader, indent = indent) + } + + private companion object { + const val INVALID_XML_CHAR_REPLACEMENT = ' ' + } } diff --git a/src/core/src/test/java/org/apache/jmeter/save/SaveServiceInvalidXmlCharTest.java b/src/core/src/test/java/org/apache/jmeter/save/SaveServiceInvalidXmlCharTest.java new file mode 100644 index 00000000000..58e64ed83ef --- /dev/null +++ b/src/core/src/test/java/org/apache/jmeter/save/SaveServiceInvalidXmlCharTest.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 org.apache.jmeter.save; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import java.io.ByteArrayOutputStream; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; + +import org.apache.jmeter.junit.JMeterTestCase; +import org.apache.jmeter.samplers.SampleEvent; +import org.apache.jmeter.samplers.SampleResult; +import org.apache.jmeter.samplers.SampleSaveConfiguration; +import org.apache.jmeter.testelement.property.StringProperty; +import org.junit.jupiter.api.Test; + +/** + * Regression for #6761: + * Woodstox rejects characters that are illegal in XML 1.0 when saving JTL/JMX. + */ +public class SaveServiceInvalidXmlCharTest extends JMeterTestCase { + + private static final String ILLEGAL_XML_CHARS = "pre\u0000mid\u001fsuf"; + + @Test + void saveSampleResultAllowsIllegalXmlCharsInSamplerData() { + SampleSaveConfiguration saveConfig = new SampleSaveConfiguration(); + saveConfig.setSamplerData(true); + + SampleResult result = new SampleResult(); + result.setSaveConfig(saveConfig); + result.setSampleLabel("binary-body"); + result.setSamplerData(ILLEGAL_XML_CHARS); + + StringWriter writer = new StringWriter(); + assertDoesNotThrow(() -> SaveService.saveSampleResult(new SampleEvent(result, "tg"), writer)); + + String xml = writer.toString(); + assertFalse(xml.isEmpty(), "JTL output should not be empty"); + assertFalse(xml.indexOf('\u0000') >= 0, "NUL must not appear in well-formed XML"); + assertFalse(xml.indexOf('\u001f') >= 0, "C1 control 0x1F must not appear in XML 1.0"); + } + + @Test + void saveElementAllowsNulInStringProperty() { + StringProperty property = new StringProperty("bin", ILLEGAL_XML_CHARS); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + assertDoesNotThrow(() -> SaveService.saveElement(property, out)); + + String xml = out.toString(StandardCharsets.UTF_8); + assertFalse(xml.isEmpty(), "JMX fragment should not be empty"); + assertFalse(xml.indexOf('\u0000') >= 0, "NUL must not appear in well-formed XML"); + assertFalse(xml.indexOf('\u001f') >= 0, "C1 control 0x1F must not appear in XML 1.0"); + } +} diff --git a/xdocs/changes.xml b/xdocs/changes.xml index 56b5657563c..7b9fe512b71 100644 --- a/xdocs/changes.xml +++ b/xdocs/changes.xml @@ -126,6 +126,7 @@ Summary
  • 5937Remove deprecated Log4j package scanning and configure plugin metadata processing to improve startup time and avoid deprecation warnings. Contributed by Piotr P. Karwasz (github.com/piotrgithub)
  • 6620Fix report generation paths so dashboard output files are created in the correct location after internal refactoring.
  • 6456Handle malformed percent-encoded URLs gracefully when recording HTTP traffic, logging a warning instead of failing the recording.
  • +
  • 6761Allow XML save of sample results and test plans that contain characters illegal in XML 1.0 (NUL and C0 controls) after the Woodstox migration.