Allow safe server-generated item metadata
This commit is contained in:
1 parent
f3555f9a0d
commit
c32f2200dd
1 file changed
+165
-143
@@ -1,143 +1,165 @@
|
||||
package com.ruinscraft.panilla.api.nbt.checks;
|
||||
|
||||
import com.ruinscraft.panilla.api.IPanilla;
|
||||
import com.ruinscraft.panilla.api.exception.FailedNbt;
|
||||
import com.ruinscraft.panilla.api.exception.NbtNotPermittedException;
|
||||
import com.ruinscraft.panilla.api.nbt.INbtTagCompound;
|
||||
import com.ruinscraft.panilla.api.nbt.INbtTagList;
|
||||
import com.ruinscraft.panilla.api.nbt.NbtDataType;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class NbtChecks {
|
||||
|
||||
private static final Map<String, NbtCheck> checks = new HashMap<>();
|
||||
|
||||
static {
|
||||
// vanilla
|
||||
register(new NbtCheck_Unbreakable());
|
||||
register(new NbtCheck_CanDestroy());
|
||||
register(new NbtCheck_CanPlaceOn());
|
||||
register(new NbtCheck_BlockEntityTag());
|
||||
register(new NbtCheck_BlockStateTag());
|
||||
register(new NbtCheck_ench());
|
||||
register(new NbtCheck_Enchantments());
|
||||
register(new NbtCheck_StoredEnchantments());
|
||||
register(new NbtCheck_RepairCost());
|
||||
register(new NbtCheck_AttributeModifiers());
|
||||
register(new NbtCheck_CustomPotionEffects());
|
||||
register(new NbtCheck_Potion());
|
||||
register(new NbtCheck_CustomPotionColor());
|
||||
register(new NbtCheck_display());
|
||||
register(new NbtCheck_HideFlags());
|
||||
register(new NbtCheck_resolved());
|
||||
register(new NbtCheck_generation());
|
||||
register(new NbtCheck_author());
|
||||
register(new NbtCheck_title());
|
||||
register(new NbtCheck_pages());
|
||||
register(new NbtCheck_SkullOwner());
|
||||
register(new NbtCheck_SkullProfile());
|
||||
register(new NbtCheck_Explosion());
|
||||
register(new NbtCheck_Fireworks());
|
||||
register(new NbtCheck_EntityTag());
|
||||
register(new NbtCheck_BucketVariantTag());
|
||||
register(new NbtCheck_map());
|
||||
register(new NbtCheck_map_scale_direction());
|
||||
register(new NbtCheck_Decorations());
|
||||
register(new NbtCheck_Effects());
|
||||
register(new NbtCheck_CustomModelData());
|
||||
register(new NbtCheck_HasVisualFire()); // 1.17
|
||||
register(new NbtCheck_ChargedProjectiles());
|
||||
register(new NbtCheck_Items());
|
||||
|
||||
// non-vanilla
|
||||
register(new NbtCheck_weBrushJson());
|
||||
}
|
||||
|
||||
private static void register(NbtCheck check) {
|
||||
checks.put(check.getName(), check);
|
||||
}
|
||||
|
||||
public static Map<String, NbtCheck> getChecks() {
|
||||
return checks;
|
||||
}
|
||||
|
||||
public static void checkPacketPlayIn(int slot, INbtTagCompound tag, String nmsItemClassName, String nmsPacketClassName,
|
||||
IPanilla panilla) throws NbtNotPermittedException {
|
||||
FailedNbt failedNbt = checkAll(tag, nmsItemClassName, panilla);
|
||||
|
||||
if (FailedNbt.fails(failedNbt)) {
|
||||
throw new NbtNotPermittedException(nmsPacketClassName, false, failedNbt, slot);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkPacketPlayOut(int slot, INbtTagCompound tag, String nmsItemClassName, String nmsPacketClassName,
|
||||
IPanilla panilla) throws NbtNotPermittedException {
|
||||
FailedNbt failedNbt = checkAll(tag, nmsItemClassName, panilla);
|
||||
|
||||
if (FailedNbt.fails(failedNbt)) {
|
||||
throw new NbtNotPermittedException(nmsPacketClassName, false, failedNbt, slot);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean tagMeetsKeyThreshold(INbtTagCompound tag, IPanilla panilla) {
|
||||
int maxNonMinecraftKeys = panilla.getPConfig().maxNonMinecraftNbtKeys;
|
||||
|
||||
if (tag.getNonMinecraftKeys().size() > maxNonMinecraftKeys) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (String key : tag.getKeys()) {
|
||||
if (tag.hasKeyOfType(key, NbtDataType.COMPOUND)) {
|
||||
INbtTagCompound subTag = tag.getCompound(key);
|
||||
|
||||
if (!tagMeetsKeyThreshold(subTag, panilla)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static FailedNbt checkAll(INbtTagCompound tag, String nmsItemClassName, IPanilla panilla) {
|
||||
if (!tagMeetsKeyThreshold(tag, panilla)) {
|
||||
return FailedNbt.FAIL_KEY_THRESHOLD;
|
||||
}
|
||||
|
||||
for (String key : tag.getKeys()) {
|
||||
if (panilla.getPConfig().nbtWhitelist.contains(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tag.hasKeyOfType(key, NbtDataType.LIST)) {
|
||||
INbtTagList list = tag.getList(key);
|
||||
|
||||
if (list.size() > 128) {
|
||||
return new FailedNbt((key), NbtCheck.NbtCheckResult.CRITICAL);
|
||||
}
|
||||
}
|
||||
|
||||
NbtCheck check = checks.get(key);
|
||||
|
||||
if (check == null) {
|
||||
// a non-minecraft NBT tag
|
||||
continue;
|
||||
}
|
||||
|
||||
if (check.getTolerance().ordinal() > panilla.getPConfig().strictness.ordinal()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NbtCheck.NbtCheckResult result = check.check(tag, nmsItemClassName, panilla);
|
||||
|
||||
if (result != NbtCheck.NbtCheckResult.PASS) {
|
||||
return new FailedNbt(key, result);
|
||||
}
|
||||
}
|
||||
|
||||
return FailedNbt.NO_FAIL;
|
||||
}
|
||||
|
||||
}
|
||||
package com.ruinscraft.panilla.api.nbt.checks;
|
||||
|
||||
import com.ruinscraft.panilla.api.IPanilla;
|
||||
import com.ruinscraft.panilla.api.exception.FailedNbt;
|
||||
import com.ruinscraft.panilla.api.exception.NbtNotPermittedException;
|
||||
import com.ruinscraft.panilla.api.nbt.INbtTagCompound;
|
||||
import com.ruinscraft.panilla.api.nbt.INbtTagList;
|
||||
import com.ruinscraft.panilla.api.nbt.NbtDataType;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class NbtChecks {
|
||||
|
||||
private static final Map<String, NbtCheck> checks = new HashMap<>();
|
||||
private static final int MAX_HIDE_FLAGS = 0x7F;
|
||||
|
||||
static {
|
||||
// vanilla
|
||||
register(new NbtCheck_Unbreakable());
|
||||
register(new NbtCheck_CanDestroy());
|
||||
register(new NbtCheck_CanPlaceOn());
|
||||
register(new NbtCheck_BlockEntityTag());
|
||||
register(new NbtCheck_BlockStateTag());
|
||||
register(new NbtCheck_ench());
|
||||
register(new NbtCheck_Enchantments());
|
||||
register(new NbtCheck_StoredEnchantments());
|
||||
register(new NbtCheck_RepairCost());
|
||||
register(new NbtCheck_AttributeModifiers());
|
||||
register(new NbtCheck_CustomPotionEffects());
|
||||
register(new NbtCheck_Potion());
|
||||
register(new NbtCheck_CustomPotionColor());
|
||||
register(new NbtCheck_display());
|
||||
register(new NbtCheck_HideFlags());
|
||||
register(new NbtCheck_resolved());
|
||||
register(new NbtCheck_generation());
|
||||
register(new NbtCheck_author());
|
||||
register(new NbtCheck_title());
|
||||
register(new NbtCheck_pages());
|
||||
register(new NbtCheck_SkullOwner());
|
||||
register(new NbtCheck_SkullProfile());
|
||||
register(new NbtCheck_Explosion());
|
||||
register(new NbtCheck_Fireworks());
|
||||
register(new NbtCheck_EntityTag());
|
||||
register(new NbtCheck_BucketVariantTag());
|
||||
register(new NbtCheck_map());
|
||||
register(new NbtCheck_map_scale_direction());
|
||||
register(new NbtCheck_Decorations());
|
||||
register(new NbtCheck_Effects());
|
||||
register(new NbtCheck_CustomModelData());
|
||||
register(new NbtCheck_HasVisualFire()); // 1.17
|
||||
register(new NbtCheck_ChargedProjectiles());
|
||||
register(new NbtCheck_Items());
|
||||
|
||||
// non-vanilla
|
||||
register(new NbtCheck_weBrushJson());
|
||||
}
|
||||
|
||||
private static void register(NbtCheck check) {
|
||||
checks.put(check.getName(), check);
|
||||
}
|
||||
|
||||
public static Map<String, NbtCheck> getChecks() {
|
||||
return checks;
|
||||
}
|
||||
|
||||
public static void checkPacketPlayIn(int slot, INbtTagCompound tag, String nmsItemClassName, String nmsPacketClassName,
|
||||
IPanilla panilla) throws NbtNotPermittedException {
|
||||
FailedNbt failedNbt = checkAll(tag, nmsItemClassName, panilla);
|
||||
|
||||
if (FailedNbt.fails(failedNbt)) {
|
||||
throw new NbtNotPermittedException(nmsPacketClassName, false, failedNbt, slot);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkPacketPlayOut(int slot, INbtTagCompound tag, String nmsItemClassName, String nmsPacketClassName,
|
||||
IPanilla panilla) throws NbtNotPermittedException {
|
||||
FailedNbt failedNbt = checkAll(tag, nmsItemClassName, panilla, true);
|
||||
|
||||
if (FailedNbt.fails(failedNbt)) {
|
||||
throw new NbtNotPermittedException(nmsPacketClassName, false, failedNbt, slot);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean tagMeetsKeyThreshold(INbtTagCompound tag, IPanilla panilla) {
|
||||
int maxNonMinecraftKeys = panilla.getPConfig().maxNonMinecraftNbtKeys;
|
||||
|
||||
if (tag.getNonMinecraftKeys().size() > maxNonMinecraftKeys) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (String key : tag.getKeys()) {
|
||||
if (tag.hasKeyOfType(key, NbtDataType.COMPOUND)) {
|
||||
INbtTagCompound subTag = tag.getCompound(key);
|
||||
|
||||
if (!tagMeetsKeyThreshold(subTag, panilla)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static FailedNbt checkAll(INbtTagCompound tag, String nmsItemClassName, IPanilla panilla) {
|
||||
return checkAll(tag, nmsItemClassName, panilla, false);
|
||||
}
|
||||
|
||||
private static FailedNbt checkAll(INbtTagCompound tag, String nmsItemClassName, IPanilla panilla,
|
||||
boolean serverGeneratedItem) {
|
||||
if (!tagMeetsKeyThreshold(tag, panilla)) {
|
||||
return FailedNbt.FAIL_KEY_THRESHOLD;
|
||||
}
|
||||
|
||||
for (String key : tag.getKeys()) {
|
||||
if (panilla.getPConfig().nbtWhitelist.contains(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (serverGeneratedItem && isSafeServerGeneratedMetadata(tag, key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tag.hasKeyOfType(key, NbtDataType.LIST)) {
|
||||
INbtTagList list = tag.getList(key);
|
||||
|
||||
if (list.size() > 128) {
|
||||
return new FailedNbt((key), NbtCheck.NbtCheckResult.CRITICAL);
|
||||
}
|
||||
}
|
||||
|
||||
NbtCheck check = checks.get(key);
|
||||
|
||||
if (check == null) {
|
||||
// a non-minecraft NBT tag
|
||||
continue;
|
||||
}
|
||||
|
||||
if (check.getTolerance().ordinal() > panilla.getPConfig().strictness.ordinal()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NbtCheck.NbtCheckResult result = check.check(tag, nmsItemClassName, panilla);
|
||||
|
||||
if (result != NbtCheck.NbtCheckResult.PASS) {
|
||||
return new FailedNbt(key, result);
|
||||
}
|
||||
}
|
||||
|
||||
return FailedNbt.NO_FAIL;
|
||||
}
|
||||
|
||||
private static boolean isSafeServerGeneratedMetadata(INbtTagCompound tag, String key) {
|
||||
if ("Unbreakable".equals(key)) {
|
||||
return tag.hasKeyOfType(key, NbtDataType.BYTE) && tag.getInt(key) >= 0 && tag.getInt(key) <= 1;
|
||||
}
|
||||
|
||||
if ("HideFlags".equals(key)) {
|
||||
return tag.hasKeyOfType(key, NbtDataType.INT) && tag.getInt(key) >= 0 && tag.getInt(key) <= MAX_HIDE_FLAGS;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user