Skip to content

Commit a0bebc7

Browse files
authored
Fix String length calculation in presence of special characters (#108)
* add the reproducer to the testsuite * comprhensive tests for special characters
1 parent cf644a2 commit a0bebc7

File tree

4 files changed

+79
-3
lines changed

4 files changed

+79
-3
lines changed

core/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@
5555
<version>2.20.0</version>
5656
<scope>test</scope>
5757
</dependency>
58+
<dependency>
59+
<groupId>org.instancio</groupId>
60+
<artifactId>instancio-junit</artifactId>
61+
<version>5.5.1</version>
62+
<scope>test</scope>
63+
</dependency>
5864
</dependencies>
5965

6066
<build>

core/src/main/java/com/styra/opa/wasm/OpaPolicy.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.io.FileNotFoundException;
1414
import java.io.InputStream;
1515
import java.nio.ByteBuffer;
16+
import java.nio.charset.StandardCharsets;
1617
import java.nio.file.Path;
1718
import java.util.ArrayList;
1819
import java.util.HashMap;
@@ -60,9 +61,10 @@ public OpaPolicy entrypoint(String entrypoint) {
6061
}
6162

6263
private int loadJson(String data) {
63-
var dataStrAddr = wasm.exports().opaMalloc(data.length());
64-
wasm.memory().writeCString(dataStrAddr, data);
65-
var dstAddr = wasm.exports().opaJsonParse(dataStrAddr, data.length());
64+
var dataBytes = data.getBytes(StandardCharsets.UTF_8);
65+
var dataStrAddr = wasm.exports().opaMalloc(dataBytes.length);
66+
wasm.memory().write(dataStrAddr, dataBytes);
67+
var dstAddr = wasm.exports().opaJsonParse(dataStrAddr, dataBytes.length);
6668
wasm.exports().opaFree(dataStrAddr);
6769
return dstAddr;
6870
}

core/src/test/java/com/styra/opa/wasm/OpaTest.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,34 @@
44

55
import com.dylibso.chicory.runtime.ByteBufferMemory;
66
import com.dylibso.chicory.wasm.types.MemoryLimits;
7+
import com.fasterxml.jackson.core.JsonProcessingException;
78
import java.io.FileInputStream;
89
import java.nio.file.Path;
10+
import org.instancio.Instancio;
11+
import org.instancio.junit.Given;
12+
import org.instancio.junit.GivenProvider;
13+
import org.instancio.junit.InstancioExtension;
14+
import org.instancio.junit.InstancioSource;
915
import org.junit.jupiter.api.Assertions;
1016
import org.junit.jupiter.api.BeforeAll;
1117
import org.junit.jupiter.api.Test;
18+
import org.junit.jupiter.api.extension.ExtendWith;
19+
import org.junit.jupiter.params.ParameterizedTest;
1220

21+
@ExtendWith(InstancioExtension.class)
1322
public class OpaTest {
1423
static Path wasmFile;
1524
static Path issue69WasmFile;
25+
static OpaPolicy issue107Policy;
1626

1727
@BeforeAll
1828
public static void beforeAll() throws Exception {
1929
wasmFile = OpaCli.compile("base", "opa/wasm/test/allowed").resolve("policy.wasm");
2030
issue69WasmFile = OpaCli.compile("issue69", "authz/allow").resolve("policy.wasm");
31+
issue107Policy =
32+
OpaPolicy.builder()
33+
.withPolicy(OpaCli.compile("issue107", "test").resolve("policy.wasm"))
34+
.build();
2135
}
2236

2337
@Test
@@ -106,6 +120,51 @@ public void issue69() throws Exception {
106120
Assertions.assertFalse(Utils.getResult(policy.evaluate()).asBoolean());
107121
}
108122

123+
@Test
124+
public void issue107() {
125+
var policy = issue107Policy;
126+
127+
policy.input("{\"role\": \"admin\",\"name\": \"Doe\"}");
128+
var result = policy.evaluate();
129+
Assertions.assertTrue(Utils.getResult(result).get("allow").asBoolean());
130+
131+
var input = "{\"role\": \"admin\",\"name\": \"Štěpán\"}";
132+
133+
policy.input(input);
134+
result = policy.evaluate();
135+
Assertions.assertTrue(Utils.getResult(result).get("allow").asBoolean());
136+
}
137+
138+
static class InputStringProvider implements GivenProvider {
139+
@Override
140+
public Object provide(ElementContext context) {
141+
String randomName = Instancio.gen().string().unicode().mixedCase().get();
142+
try {
143+
String input = "{\"role\": \"admin\",\"name\": \"" + randomName + "\"}";
144+
DefaultMappers.jsonMapper.readTree(input);
145+
return randomName;
146+
} catch (JsonProcessingException e) {
147+
// the generated input name breaks the json escaping
148+
return provide(context);
149+
}
150+
}
151+
}
152+
153+
@InstancioSource(samples = 1000)
154+
@ParameterizedTest
155+
public void issue107Parametrized(@Given(InputStringProvider.class) String name) {
156+
var policy = issue107Policy;
157+
158+
policy.input("{\"role\": \"admin\",\"name\": \"" + name + "\"}");
159+
var result = policy.evaluate();
160+
Assertions.assertTrue(Utils.getResult(result).get("allow").asBoolean());
161+
162+
policy.input("{\"role\": \"notadmin\",\"name\": \"" + name + "\"}");
163+
164+
result = policy.evaluate();
165+
Assertions.assertFalse(Utils.getResult(result).get("allow").asBoolean());
166+
}
167+
109168
@Test
110169
public void issue78Sprintf() throws Exception {
111170
var policyWasm =
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package test
2+
3+
import rego.v1
4+
5+
default allow := false
6+
7+
allow if {
8+
input.role == "admin"
9+
}

0 commit comments

Comments
 (0)