From 8b2c60040e017073464be5c5199faadf3c0f3cf4 Mon Sep 17 00:00:00 2001 From: lmasroca Date: Fri, 12 Jun 2026 13:53:09 -0300 Subject: [PATCH 1/4] Added failing RestPath test to surface non-ASCII encoding bug. --- .../evomaster/core/problem/rest/RestPathTest.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/RestPathTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/RestPathTest.kt index 7ddf3d809b..b1898193b2 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/RestPathTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/RestPathTest.kt @@ -605,4 +605,19 @@ internal class RestPathTest{ assertEquals(y, resolvedY) } + @Test + fun testPathParamWithNonAscii() { + // Non-ASCII characters (e.g. from AnyCharacterRxGene sampling the full + // Unicode range) must also be percent-encoded in path parameters. These + // are not encoded by URI(null, null, s, null, null) in path parameters, + // causing them to appear raw in the generated URL. + val key = PathParam("key", CustomMutationRateGene("d_", StringGene("key", "key-聚"), 1.0)) + + val resolved = RestPath("/api/{key}").resolveOnlyPath(listOf(key)) + + assertFalse(resolved.contains("聚"), + "Resolved path must not contain a raw non-ASCII character, got: $resolved") + assertTrue(resolved.contains("%E8%81%9A"), + "Resolved path must percent-encode non-ASCII characters, got: $resolved") + } } \ No newline at end of file From 915f1cdae47334fec43eb9d6d9d7224bd7fddf7d Mon Sep 17 00:00:00 2001 From: lmasroca Date: Fri, 12 Jun 2026 14:17:46 -0300 Subject: [PATCH 2/4] Possible fix to non-ASCII encoding issue in RestPath. --- .../org/evomaster/core/problem/rest/data/RestPath.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestPath.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestPath.kt index 8331f87ca2..2836c9eaea 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestPath.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestPath.kt @@ -354,7 +354,15 @@ class RestPath(path: String) { val data = dynamicResolutionOnlyPathData(params, mapOf()) assert(data.size == 1) assert(!data[0].second) - return data[0].first + return data[0].first.map { c -> + // The URI calls in dynamicResolutionOnlyPathData do not encode non-ASCII characters in path segments. + if (c.code > 127) { + // non-ASCII + encode(c.toString()) + } else { + c.toString() + } + }.joinToString( "") } From 15c8abffd4dc559d34c4d4e7db2bd622f006db30 Mon Sep 17 00:00:00 2001 From: lmasroca Date: Thu, 18 Jun 2026 14:46:51 -0300 Subject: [PATCH 3/4] refactored the fix using toASCIIString() --- .../evomaster/core/problem/rest/data/RestPath.kt | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestPath.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestPath.kt index 2836c9eaea..4e1439b4e6 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestPath.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestPath.kt @@ -354,15 +354,7 @@ class RestPath(path: String) { val data = dynamicResolutionOnlyPathData(params, mapOf()) assert(data.size == 1) assert(!data[0].second) - return data[0].first.map { c -> - // The URI calls in dynamicResolutionOnlyPathData do not encode non-ASCII characters in path segments. - if (c.code > 127) { - // non-ASCII - encode(c.toString()) - } else { - c.toString() - } - }.joinToString( "") + return data[0].first } @@ -421,7 +413,7 @@ class RestPath(path: String) { why not using URI also for Query part??? it seems unclear how to properly build it as a single string... */ - val entry = URI(null, null, path.toString(), null, null).rawPath + val entry = URI(null, null, path.toString(), null, null).toASCIIString() data.add(Pair(entry, false)) path.setLength(0) // clear it data.add(Pair(variable, true)) @@ -454,7 +446,7 @@ class RestPath(path: String) { } if(path.isNotEmpty()){ - val entry = URI(null, null, path.toString(), null, null).rawPath + val entry = URI(null, null, path.toString(), null, null).toASCIIString() data.add(Pair(entry, false)) } From 31cdb27d44e665ff7ea5328e6d007c793d8ff438 Mon Sep 17 00:00:00 2001 From: lmasroca Date: Fri, 19 Jun 2026 15:03:37 -0300 Subject: [PATCH 4/4] Added non-ASCII rest path parameter character encoding test to TestCaseWriterTest. --- .../core/output/TestCaseWriterTest.kt | 28 +++++++++++++++++++ .../core/problem/rest/RestPathTest.kt | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/core/src/test/kotlin/org/evomaster/core/output/TestCaseWriterTest.kt b/core/src/test/kotlin/org/evomaster/core/output/TestCaseWriterTest.kt index d2454da456..86fb37fc90 100644 --- a/core/src/test/kotlin/org/evomaster/core/output/TestCaseWriterTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/output/TestCaseWriterTest.kt @@ -16,6 +16,7 @@ import org.evomaster.core.output.service.RestTestCaseWriter import org.evomaster.core.problem.enterprise.SampleType import org.evomaster.core.problem.rest.data.* import org.evomaster.core.problem.rest.param.BodyParam +import org.evomaster.core.problem.rest.param.PathParam import org.evomaster.core.search.EvaluatedIndividual import org.evomaster.core.search.FitnessValue import org.evomaster.core.search.gene.* @@ -28,6 +29,7 @@ import org.evomaster.core.search.gene.collection.EnumGene import org.evomaster.core.search.gene.numeric.IntegerGene import org.evomaster.core.search.gene.string.StringGene import org.evomaster.core.search.gene.utils.GeneUtils +import org.evomaster.core.search.gene.wrapper.CustomMutationRateGene import org.evomaster.core.search.gene.wrapper.OptionalGene import org.evomaster.core.sql.schema.TableId import org.junit.jupiter.api.Assertions.* @@ -1679,4 +1681,30 @@ public void test() throws Exception { assertFalse(lines.toString().contains(".body()")) } + + @Test + fun testNonAsciiInPathParamIsEncoded() { + // non-ASCII characters in path parameter values must be percent-encoded in generated test output. + val format = OutputFormat.KOTLIN_JUNIT_5 + + val pathParam = PathParam("key", CustomMutationRateGene("key", StringGene("key", "聚"), 1.0)) + val action = RestCallAction("1", HttpVerb.GET, RestPath("/api/{key}"), mutableListOf(pathParam)) + val individual = RestIndividual(mutableListOf(action), SampleType.RANDOM) + TestUtils.doInitializeIndividualForTesting(individual) + + val result = RestCallResult(action.getLocalId()) + result.setTimedout(false) + result.setStatusCode(200) + val ei = EvaluatedIndividual(FitnessValue(0.0), individual, listOf(result)) + + val writer = RestTestCaseWriter(getConfig(format), PartialOracles()) + val lines = writer.convertToCompilableTestCode(TestCase(test = ei, name = "test"), "baseUrlOfSut") + val output = lines.toString() + + assertFalse(output.contains("聚"), + "Non-ASCII character must not appear raw in generated test output, got:\n$output") + + assertTrue(output.contains("%E8%81%9A"), + "Non-ASCII character must be percent-encoded as %E8%81%9A in generated test output, got:\n$output") + } } diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/RestPathTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/RestPathTest.kt index b1898193b2..4ed357800f 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/RestPathTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/RestPathTest.kt @@ -613,7 +613,7 @@ internal class RestPathTest{ // causing them to appear raw in the generated URL. val key = PathParam("key", CustomMutationRateGene("d_", StringGene("key", "key-聚"), 1.0)) - val resolved = RestPath("/api/{key}").resolveOnlyPath(listOf(key)) + val resolved = RestPath("/api/{key}").resolve(listOf(key)) assertFalse(resolved.contains("聚"), "Resolved path must not contain a raw non-ASCII character, got: $resolved")