Skip to content

Commit 1b57c23

Browse files
authored
fix: prevent undesirable coercion of JSON values (#238)
The Jackson JSON serialization library will attempt to coerce JSON values to Java data types when it is inappropriate. This leads to element properties being incorrect for the UTAM JSON grammar. For example, `"public": "true"` will incorrectly be coerced into a boolean value, when it should throw an error. Moreover, newer versions of Jackson (`2.16` and higher) will allow more liberal deserialization of properties that should have object values. This allows an invalid UTAM construct line `"selector": "some-selector"`, which should error, as the value of the `selector` property is expected to be an object, not a string value. This change fixes both issues, correctly throwing errors in cases where the wrong type of a property has been authored into the UTAM PO JSON declarative description file.
1 parent 38fd54d commit 1b57c23

File tree

10 files changed

+77
-4
lines changed

10 files changed

+77
-4
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@
128128
<selenium.version>4.21.0</selenium.version>
129129
<appium.version>9.2.2</appium.version>
130130
<slf4j.version>2.0.9</slf4j.version>
131-
<jackson.version>2.16.0</jackson.version>
131+
<jackson.version>2.18.4</jackson.version>
132132
<google.java.format.version>1.19.1</google.java.format.version>
133133
<picocli-version>4.7.5</picocli-version>
134134
<testng.version>7.8.0</testng.version>

utam-compiler/src/main/java/utam/compiler/grammar/JsonDeserializer.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@
2424
import com.fasterxml.jackson.databind.DeserializationContext;
2525
import com.fasterxml.jackson.databind.JsonNode;
2626
import com.fasterxml.jackson.databind.ObjectMapper;
27+
import com.fasterxml.jackson.databind.cfg.CoercionAction;
28+
import com.fasterxml.jackson.databind.cfg.CoercionInputShape;
2729
import com.fasterxml.jackson.databind.module.SimpleModule;
30+
import com.fasterxml.jackson.databind.type.LogicalType;
2831
import java.io.IOException;
2932
import java.util.ArrayList;
3033
import java.util.List;
@@ -86,6 +89,9 @@ private static ObjectMapper getObjectMapperWithSettings() {
8689
mapper.disable(ALLOW_COMMENTS);
8790
mapper.enable(ACCEPT_SINGLE_VALUE_AS_ARRAY);
8891
mapper.enable(STRICT_DUPLICATE_DETECTION);
92+
mapper
93+
.coercionConfigFor(LogicalType.Boolean)
94+
.setCoercion(CoercionInputShape.String, CoercionAction.Fail);
8995
return mapper;
9096
}
9197

utam-compiler/src/main/java/utam/compiler/grammar/UtamSelector.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package utam.compiler.grammar;
99

1010
import static utam.compiler.diagnostics.ValidationUtilities.VALIDATION;
11+
import static utam.compiler.grammar.JsonDeserializer.isEmptyNode;
1112
import static utam.compiler.grammar.JsonDeserializer.readNode;
1213
import static utam.core.element.Locator.SELECTOR_INTEGER_PARAMETER;
1314
import static utam.core.element.Locator.SELECTOR_STRING_PARAMETER;
@@ -84,6 +85,9 @@ class UtamSelector extends UtamRootSelector {
8485
*/
8586
static UtamSelector processSelectorNode(JsonNode node, String elementName) {
8687
String parserContext = String.format("element \"%s\"", elementName);
88+
if (!isEmptyNode(node) && !node.isObject()) {
89+
throw new UtamCompilationError(VALIDATION.getErrorMessage(1000, parserContext));
90+
}
8791
UtamSelector selector =
8892
readNode(node, UtamSelector.class, VALIDATION.getErrorMessage(1000, parserContext));
8993
if (selector != null) {

utam-compiler/src/test/java/utam/compiler/grammar/UtamElementBasicTests.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,28 @@ public void testBasicElementWithFilterAndNonListSelectorThrows() {
5757
containsString("error 302: element \"test\" filter: filter can only be set for list"));
5858
}
5959

60+
/** The validateSimpleElement method with string nullable type should throw */
61+
@Test
62+
public void testBasicElementInvalidNullableTypeThrows() {
63+
UtamError e = expectThrows(UtamCompilationError.class, () -> getContext("nullableWrongType"));
64+
assertThat(
65+
e.getMessage(),
66+
containsString(
67+
"error 200: root elements: incorrect format of elements \n"
68+
+ "Cannot coerce String value (\"true\") to `java.lang.Boolean` value"));
69+
}
70+
71+
/** The validateSimpleElement method with string public type should throw */
72+
@Test
73+
public void testBasicElementInvalidPublicTypeThrows() {
74+
UtamError e = expectThrows(UtamCompilationError.class, () -> getContext("publicWrongType"));
75+
assertThat(
76+
e.getMessage(),
77+
containsString(
78+
"error 200: root elements: incorrect format of elements \n"
79+
+ "Cannot coerce String value (\"true\") to `java.lang.Boolean` value"));
80+
}
81+
6082
@Test
6183
public void testEmptyNestedElementsThrows() {
6284
UtamError e = expectThrows(UtamError.class, () -> getContext("emptyNestedElementsArray"));

utam-compiler/src/test/java/utam/compiler/grammar/UtamElementWaitTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,6 @@ public void testWaitNotBoolean() {
117117
e.getMessage(),
118118
containsString(
119119
"error 200: root elements: incorrect format of elements \n"
120-
+ "Cannot deserialize value of type `java.lang.Boolean` from String \"string\""));
120+
+ "Cannot coerce String value (\"true\") to `java.lang.Boolean` value"));
121121
}
122122
}

utam-compiler/src/test/java/utam/compiler/grammar/UtamSelectorTests.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,14 @@ public void testArgsWithDuplicateNamesThrows() {
107107
+ " \"str\" is already declared"));
108108
}
109109

110+
@Test
111+
public void testReturnAllWrongArgTypeProvided() {
112+
Exception e = expectCompilerErrorFromFile("selectorReturnAllWrong");
113+
assertThat(
114+
e.getMessage(),
115+
containsString("error 1000: element \"test\": format of selector is incorrect;"));
116+
}
117+
110118
@Test
111119
public void testGetBuilderString() {
112120
final String prefix = LocatorBy.class.getSimpleName();
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"elements" : [
3+
{
4+
"name": "basicPublicElement",
5+
"selector": {
6+
"css": "basicElement"
7+
},
8+
"nullable": "true"
9+
}
10+
]
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"elements" : [
3+
{
4+
"name": "basicPublicElement",
5+
"selector": {
6+
"css": "basicElement"
7+
},
8+
"public": "true"
9+
}
10+
]
11+
}

utam-compiler/src/test/resources/validate/basic_element/waitWrongType.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"selector": {
66
"css": "basicElement"
77
},
8-
"wait": "string"
8+
"wait": "true"
99
}
1010
]
11-
}
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"elements": [
3+
{
4+
"name": "test",
5+
"selector": {
6+
"css": "selector",
7+
"returnAll": "true"
8+
}
9+
}
10+
]
11+
}

0 commit comments

Comments
 (0)