added maxDepth parameter to the parser
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
package net.woggioni.worth.exception;
|
||||||
|
|
||||||
|
public class MaxDepthExceededException extends WorthException {
|
||||||
|
public MaxDepthExceededException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,7 @@
|
|||||||
package net.woggioni.worth.serialization;
|
package net.woggioni.worth.serialization;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.woggioni.worth.exception.MaxDepthExceededException;
|
||||||
import net.woggioni.worth.exception.NotImplementedException;
|
import net.woggioni.worth.exception.NotImplementedException;
|
||||||
import net.woggioni.worth.utils.WorthUtils;
|
import net.woggioni.worth.utils.WorthUtils;
|
||||||
import net.woggioni.worth.value.*;
|
import net.woggioni.worth.value.*;
|
||||||
@@ -13,6 +14,8 @@ import java.io.Reader;
|
|||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
|
|
||||||
|
import static net.woggioni.worth.utils.WorthUtils.newThrowable;
|
||||||
|
|
||||||
public class ValueParser implements Parser {
|
public class ValueParser implements Parser {
|
||||||
|
|
||||||
protected final Value.Configuration cfg;
|
protected final Value.Configuration cfg;
|
||||||
@@ -64,7 +67,16 @@ public class ValueParser implements Parser {
|
|||||||
|
|
||||||
protected ValueParser(Value.Configuration cfg) {
|
protected ValueParser(Value.Configuration cfg) {
|
||||||
this.cfg = cfg;
|
this.cfg = cfg;
|
||||||
stack = new ArrayDeque<>();
|
stack = new ArrayDeque<>() {
|
||||||
|
@Override
|
||||||
|
public void push(StackLevel stackLevel) {
|
||||||
|
if(size() == cfg.maxDepth) {
|
||||||
|
throw newThrowable(MaxDepthExceededException.class,
|
||||||
|
"Objects is too deep, max allowed depth is %d", cfg.maxDepth);
|
||||||
|
}
|
||||||
|
super.push(stackLevel);
|
||||||
|
}
|
||||||
|
};
|
||||||
stack.push(new ArrayStackLevel());
|
stack.push(new ArrayStackLevel());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package net.woggioni.worth.xface;
|
package net.woggioni.worth.xface;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
import net.woggioni.worth.exception.TypeException;
|
import net.woggioni.worth.exception.TypeException;
|
||||||
import net.woggioni.worth.value.NullValue;
|
import net.woggioni.worth.value.NullValue;
|
||||||
import net.woggioni.worth.value.ObjectValue;
|
import net.woggioni.worth.value.ObjectValue;
|
||||||
@@ -85,11 +86,21 @@ public interface Value {
|
|||||||
throw new TypeException("Not an object");
|
throw new TypeException("Not an object");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Builder
|
||||||
class Configuration {
|
class Configuration {
|
||||||
public ObjectValue.Implementation objectValueImplementation = ObjectValue.Implementation.valueOf(
|
|
||||||
|
@Builder.Default
|
||||||
|
public final ObjectValue.Implementation objectValueImplementation = ObjectValue.Implementation.valueOf(
|
||||||
System.getProperty(ObjectValue.class.getName() + ".implementation", "TreeMap"));
|
System.getProperty(ObjectValue.class.getName() + ".implementation", "TreeMap"));
|
||||||
public boolean useReferences = Boolean.valueOf(
|
|
||||||
|
@Builder.Default
|
||||||
|
public final boolean useReferences = Boolean.valueOf(
|
||||||
System.getProperty(Value.class.getName() + ".useReferences", "false"));
|
System.getProperty(Value.class.getName() + ".useReferences", "false"));
|
||||||
|
|
||||||
|
@Builder.Default
|
||||||
|
public final int maxDepth =
|
||||||
|
Integer.parseInt(System.getProperty(Value.class.getName() + ".maxDepth", "1048576"));
|
||||||
}
|
}
|
||||||
Configuration configuration = new Configuration();
|
|
||||||
|
Configuration configuration = Configuration.builder().build();
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,59 @@
|
|||||||
|
package net.woggioni.worth.serialization;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import net.woggioni.worth.antlr.JSONLexer;
|
||||||
|
import net.woggioni.worth.antlr.JSONListenerImpl;
|
||||||
|
import net.woggioni.worth.exception.MaxDepthExceededException;
|
||||||
|
import net.woggioni.worth.serialization.json.JSONParser;
|
||||||
|
import net.woggioni.worth.xface.Parser;
|
||||||
|
import net.woggioni.worth.xface.Value;
|
||||||
|
import org.antlr.v4.runtime.CharStream;
|
||||||
|
import org.antlr.v4.runtime.CharStreams;
|
||||||
|
import org.antlr.v4.runtime.CommonTokenStream;
|
||||||
|
import org.antlr.v4.runtime.tree.ParseTreeWalker;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
|
public class JsonBombTest {
|
||||||
|
|
||||||
|
private InputStream infiniteJson() {
|
||||||
|
return new InputStream() {
|
||||||
|
int index = 0;
|
||||||
|
final String monomer = "{\"key\":[";
|
||||||
|
@Override
|
||||||
|
public int read() {
|
||||||
|
return (int) monomer.charAt(index++ % monomer.length());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = StackOverflowError.class)
|
||||||
|
@SneakyThrows
|
||||||
|
public void jackson() {
|
||||||
|
ObjectMapper om = new ObjectMapper();
|
||||||
|
om.readTree(infiniteJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = MaxDepthExceededException.class)
|
||||||
|
@SneakyThrows
|
||||||
|
public void worth() {
|
||||||
|
Value.Configuration cfg = Value.Configuration.builder().maxDepth(1024).build();
|
||||||
|
Parser parser = JSONParser.newInstance(cfg);
|
||||||
|
parser.parse(infiniteJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = OutOfMemoryError.class)
|
||||||
|
@SneakyThrows
|
||||||
|
public void antlr() {
|
||||||
|
CharStream inputStream = CharStreams.fromReader(new InputStreamReader(infiniteJson()));
|
||||||
|
JSONLexer lexer = new JSONLexer(inputStream);
|
||||||
|
CommonTokenStream commonTokenStream = new CommonTokenStream(lexer);
|
||||||
|
net.woggioni.worth.antlr.JSONParser parser = new net.woggioni.worth.antlr.JSONParser(commonTokenStream);
|
||||||
|
JSONListenerImpl listener = new JSONListenerImpl();
|
||||||
|
ParseTreeWalker walker = new ParseTreeWalker();
|
||||||
|
walker.walk(listener, parser.json());
|
||||||
|
}
|
||||||
|
}
|
@@ -32,8 +32,9 @@ public class ObjectValueImplementationTest {
|
|||||||
ObjectValue obj = ObjectValue.newInstance();
|
ObjectValue obj = ObjectValue.newInstance();
|
||||||
Assert.assertEquals(expectedClass, obj.getClass());
|
Assert.assertEquals(expectedClass, obj.getClass());
|
||||||
mapping.forEach(tuple -> {
|
mapping.forEach(tuple -> {
|
||||||
Value.Configuration cfg = new Value.Configuration();
|
Value.Configuration cfg = Value.Configuration.builder()
|
||||||
cfg.objectValueImplementation = tuple._1;
|
.objectValueImplementation(tuple._1)
|
||||||
|
.build();
|
||||||
ObjectValue obj2 = ObjectValue.newInstance(cfg);
|
ObjectValue obj2 = ObjectValue.newInstance(cfg);
|
||||||
Assert.assertEquals(tuple._2, obj2.getClass());
|
Assert.assertEquals(tuple._2, obj2.getClass());
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user