From 11514611f71e1b41c002ca118aba49e68be47acc Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Tue, 3 Sep 2024 18:41:47 +0200 Subject: [PATCH] fix: Always make sure we put valid utf8-encoded strings into attribute values (#661) --- .../io/appium/uiautomator2/utils/XMLHelpers.java | 14 +++++++++++++- .../appium/uiautomator2/utils/XMLHelpersTests.java | 8 ++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/appium/uiautomator2/utils/XMLHelpers.java b/app/src/main/java/io/appium/uiautomator2/utils/XMLHelpers.java index 8810d871d..dd08eec24 100644 --- a/app/src/main/java/io/appium/uiautomator2/utils/XMLHelpers.java +++ b/app/src/main/java/io/appium/uiautomator2/utils/XMLHelpers.java @@ -18,6 +18,7 @@ import androidx.annotation.Nullable; +import java.nio.charset.StandardCharsets; import java.util.regex.Pattern; public abstract class XMLHelpers { @@ -50,7 +51,18 @@ public static String toNodeName(String str) { @Nullable public static String toSafeString(@Nullable Object source, String replacement) { return source == null ? null : XML10_PATTERN - .matcher(String.valueOf(source)) + .matcher(toSafeUtf8String(String.valueOf(source))) .replaceAll(replacement); } + + @Nullable + public static String toSafeUtf8String(@Nullable String source) { + if (source == null) { + return null; + } + + // This method always replaces malformed-input and unmappable-character sequences + // with this charset's default replacement byte array. + return new String(source.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); + } } diff --git a/app/src/test/java/io/appium/uiautomator2/utils/XMLHelpersTests.java b/app/src/test/java/io/appium/uiautomator2/utils/XMLHelpersTests.java index b7d6271e1..7815528f7 100644 --- a/app/src/test/java/io/appium/uiautomator2/utils/XMLHelpersTests.java +++ b/app/src/test/java/io/appium/uiautomator2/utils/XMLHelpersTests.java @@ -34,6 +34,8 @@ import static org.junit.Assert.assertEquals; +import static io.appium.uiautomator2.utils.XMLHelpers.toSafeString; + import android.os.SystemClock; import java.io.ByteArrayInputStream; @@ -238,6 +240,12 @@ private static List findNodesUsingXpath1(String xml, String xpathSelect } } + @Test + public void createsSafeXmlString() { + String text = toSafeString("°C\u000b", "?"); + assertEquals(text, "°C?"); + } + @Test public void parsesComplexXpath1() { String query = "(//android.widget.TextView[@text='some, text']/following::android.widget.ImageButton)[1]";