config service: validate config values
This commit is contained in:
@@ -26,6 +26,10 @@ package net.runelite.http.service.config;
|
|||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
import com.google.gson.JsonSyntaxException;
|
import com.google.gson.JsonSyntaxException;
|
||||||
import com.mongodb.client.MongoClient;
|
import com.mongodb.client.MongoClient;
|
||||||
import com.mongodb.client.MongoCollection;
|
import com.mongodb.client.MongoCollection;
|
||||||
@@ -52,6 +56,9 @@ import org.springframework.stereotype.Service;
|
|||||||
@Service
|
@Service
|
||||||
public class ConfigService
|
public class ConfigService
|
||||||
{
|
{
|
||||||
|
private static final int MAX_DEPTH = 8;
|
||||||
|
private static final int MAX_VALUE_LENGTH = 262144;
|
||||||
|
|
||||||
private final Gson GSON = RuneLiteAPI.GSON;
|
private final Gson GSON = RuneLiteAPI.GSON;
|
||||||
private final UpdateOptions upsertUpdateOptions = new UpdateOptions().upsert(true);
|
private final UpdateOptions upsertUpdateOptions = new UpdateOptions().upsert(true);
|
||||||
|
|
||||||
@@ -139,6 +146,11 @@ public class ConfigService
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!validateJson(value))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Object jsonValue = parseJsonString(value);
|
Object jsonValue = parseJsonString(value);
|
||||||
mongoCollection.updateOne(eq("_userId", userId),
|
mongoCollection.updateOne(eq("_userId", userId),
|
||||||
set(split[0] + "." + split[1].replace('.', ':'), jsonValue),
|
set(split[0] + "." + split[1].replace('.', ':'), jsonValue),
|
||||||
@@ -205,4 +217,71 @@ public class ConfigService
|
|||||||
}
|
}
|
||||||
return jsonValue;
|
return jsonValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static boolean validateJson(String value)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// I couldn't figure out a better way to do this than a second json parse
|
||||||
|
JsonElement jsonElement = RuneLiteAPI.GSON.fromJson(value, JsonElement.class);
|
||||||
|
return validateObject(jsonElement, 1);
|
||||||
|
}
|
||||||
|
catch (JsonSyntaxException ex)
|
||||||
|
{
|
||||||
|
// the client submits the string representation of objects which is not always valid json,
|
||||||
|
// eg. a value with a ':' in it. We just ignore it now. We can't json encode the values client
|
||||||
|
// side due to them already being strings, which prevents gson from being able to convert them
|
||||||
|
// to ints/floats/maps etc.
|
||||||
|
return value.length() < MAX_VALUE_LENGTH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean validateObject(JsonElement jsonElement, int depth)
|
||||||
|
{
|
||||||
|
if (depth >= MAX_DEPTH)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jsonElement.isJsonObject())
|
||||||
|
{
|
||||||
|
JsonObject jsonObject = jsonElement.getAsJsonObject();
|
||||||
|
|
||||||
|
for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet())
|
||||||
|
{
|
||||||
|
JsonElement element = entry.getValue();
|
||||||
|
|
||||||
|
if (!validateObject(element, depth + 1))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (jsonElement.isJsonArray())
|
||||||
|
{
|
||||||
|
JsonArray jsonArray = jsonElement.getAsJsonArray();
|
||||||
|
|
||||||
|
for (int i = 0; i < jsonArray.size(); ++i)
|
||||||
|
{
|
||||||
|
JsonElement element = jsonArray.get(i);
|
||||||
|
|
||||||
|
if (!validateObject(element, depth + 1))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (jsonElement.isJsonPrimitive())
|
||||||
|
{
|
||||||
|
JsonPrimitive jsonPrimitive = jsonElement.getAsJsonPrimitive();
|
||||||
|
String value = jsonPrimitive.getAsString();
|
||||||
|
if (value.length() >= MAX_VALUE_LENGTH)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ package net.runelite.http.service.config;
|
|||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class ConfigServiceTest
|
public class ConfigServiceTest
|
||||||
@@ -40,4 +41,15 @@ public class ConfigServiceTest
|
|||||||
assertEquals("test", ConfigService.parseJsonString("\"test\""));
|
assertEquals("test", ConfigService.parseJsonString("\"test\""));
|
||||||
assertEquals(ImmutableMap.of("key", "value"), ConfigService.parseJsonString("{\"key\": \"value\"}"));
|
assertEquals(ImmutableMap.of("key", "value"), ConfigService.parseJsonString("{\"key\": \"value\"}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateJson()
|
||||||
|
{
|
||||||
|
assertTrue(ConfigService.validateJson("1"));
|
||||||
|
assertTrue(ConfigService.validateJson("3.14"));
|
||||||
|
assertTrue(ConfigService.validateJson("test"));
|
||||||
|
assertTrue(ConfigService.validateJson("\"test\""));
|
||||||
|
assertTrue(ConfigService.validateJson("key:value"));
|
||||||
|
assertTrue(ConfigService.validateJson("{\"key\": \"value\"}"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user