Skip to content

Commit 943ae33

Browse files
committed
Add additional tests and address review feedback
1 parent 57e795a commit 943ae33

File tree

2 files changed

+48
-7
lines changed

2 files changed

+48
-7
lines changed

extensions/src/main/java/dev/cel/extensions/CelStringExtensions.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -475,10 +475,7 @@ private static String quote(String s) {
475475
sb.append('"');
476476
for (int i = 0; i < s.length(); ) {
477477
int codePoint = s.codePointAt(i);
478-
if (!Character.isValidCodePoint(codePoint)
479-
|| Character.isLowSurrogate(s.charAt(i))
480-
|| (Character.isHighSurrogate(s.charAt(i))
481-
&& (i + 1 >= s.length() || !Character.isLowSurrogate(s.charAt(i + 1))))) {
478+
if (isMalformedUtf16(s, i, codePoint)) {
482479
sb.append('\uFFFD');
483480
i++;
484481
continue;
@@ -521,6 +518,19 @@ private static String quote(String s) {
521518
return sb.toString();
522519
}
523520

521+
private static boolean isMalformedUtf16(String s, int index, int codePoint) {
522+
char currentChar = s.charAt(index);
523+
if (!Character.isValidCodePoint(codePoint)) {
524+
return true;
525+
}
526+
if (Character.isLowSurrogate(currentChar)) {
527+
return true;
528+
}
529+
// Check for unpaired high surrogate
530+
return Character.isHighSurrogate(currentChar)
531+
&& (index + 1 >= s.length() || !Character.isLowSurrogate(s.charAt(index + 1)));
532+
}
533+
524534
private static String replaceAll(Object[] objects) {
525535
return replace((String) objects[0], (String) objects[1], (String) objects[2], -1);
526536
}

extensions/src/test/java/dev/cel/extensions/CelStringExtensionsTest.java

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
import dev.cel.runtime.CelEvaluationException;
3434
import dev.cel.runtime.CelRuntime;
3535
import dev.cel.runtime.CelRuntimeFactory;
36+
37+
import java.nio.charset.StandardCharsets;
3638
import java.util.List;
3739
import org.junit.Test;
3840
import org.junit.runner.RunWith;
@@ -1485,19 +1487,23 @@ public void reverse_success(String string, String expectedResult) throws Excepti
14851487
}
14861488

14871489
@Test
1488-
public void reverse_unicode() throws Exception {
1490+
@TestParameters("{string: '😁😑😦', expectedResult: '😦😑😁'}")
1491+
@TestParameters("{string: '\u180e\u200b\u200c\u200d\u2060\ufeff', expectedResult: '\ufeff\u2060\u200d\u200c\u200b\u180e'}")
1492+
public void reverse_unicode(String string, String expectedResult) throws Exception {
14891493
CelAbstractSyntaxTree ast = COMPILER.compile("s.reverse()").getAst();
14901494
CelRuntime.Program program = RUNTIME.createProgram(ast);
14911495

1492-
Object evaluatedResult = program.eval(ImmutableMap.of("s", "😁😑😦"));
1496+
Object evaluatedResult = program.eval(ImmutableMap.of("s", string));
14931497

1494-
assertThat(evaluatedResult).isEqualTo("😦😑😁");
1498+
assertThat(evaluatedResult).isEqualTo(expectedResult);
14951499
}
14961500

14971501
@Test
14981502
@TestParameters("{string: 'hello', expectedResult: '\"hello\"'}")
14991503
@TestParameters("{string: '', expectedResult: '\"\"'}")
15001504
@TestParameters("{string: 'contains \\\"quotes\\\"', expectedResult: '\"contains \\\\\\\"quotes\\\\\\\"\"'}")
1505+
@TestParameters("{string: 'ends with \\\\', expectedResult: '\"ends with \\\\\\\\\"'}")
1506+
@TestParameters("{string: '\\\\ starts with', expectedResult: '\"\\\\\\\\ starts with\"'}")
15011507
public void quote_success(String string, String expectedResult) throws Exception {
15021508
CelAbstractSyntaxTree ast = COMPILER.compile("strings.quote(s)").getAst();
15031509
CelRuntime.Program program = RUNTIME.createProgram(ast);
@@ -1507,6 +1513,18 @@ public void quote_success(String string, String expectedResult) throws Exception
15071513
assertThat(evaluatedResult).isEqualTo(expectedResult);
15081514
}
15091515

1516+
@Test
1517+
public void quote_singleWithDoubleQuotes() throws Exception {
1518+
CelAbstractSyntaxTree ast = COMPILER.compile(
1519+
"strings.quote('single-quote with \"double quote\"') == \"\\\"single-quote with \\\\\\\"double quote\\\\\\\"\\\"\""
1520+
).getAst();
1521+
CelRuntime.Program program = RUNTIME.createProgram(ast);
1522+
1523+
Object evaluatedResult = program.eval();
1524+
1525+
assertThat(evaluatedResult).isEqualTo(true);
1526+
}
1527+
15101528
@Test
15111529
public void quote_escapesSpecialCharacters() throws Exception {
15121530
CelAbstractSyntaxTree ast = COMPILER.compile("strings.quote(s)").getAst();
@@ -1521,6 +1539,19 @@ public void quote_escapesSpecialCharacters() throws Exception {
15211539
.isEqualTo("\"\\abell\\vvtab\\bback\\ffeed\\rret\\nline\\ttab\\\\slash 가 😁\"");
15221540
}
15231541

1542+
@Test
1543+
public void quote_escapesMalformed() throws Exception {
1544+
CelAbstractSyntaxTree ast = COMPILER.compile("strings.quote(s)").getAst();
1545+
CelRuntime.Program program = RUNTIME.createProgram(ast);
1546+
1547+
Object evaluatedResult =
1548+
program.eval(
1549+
ImmutableMap.of(
1550+
"s", new String(new byte[]{'f','i','l','l','e','r',' ',(byte)0x9f}, StandardCharsets.UTF_8)));
1551+
1552+
assertThat(evaluatedResult).isEqualTo("\"filler \uFFFD\"");
1553+
}
1554+
15241555
@Test
15251556
public void stringExtension_compileUnallowedFunction_throws() {
15261557
CelCompiler celCompiler =

0 commit comments

Comments
 (0)