fix(wired): bound save payloads

This commit is contained in:
simoleo89
2026-06-17 18:06:36 +02:00
parent 416d0bb088
commit 240fede12a
6 changed files with 168 additions and 29 deletions
@@ -2,6 +2,7 @@ package com.eu.habbo.habbohotel.items.interactions;
import com.eu.habbo.Emulator; import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.items.Item; import com.eu.habbo.habbohotel.items.Item;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredInputGuard;
import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings; import com.eu.habbo.habbohotel.items.interactions.wired.WiredSettings;
import com.eu.habbo.habbohotel.rooms.Room; import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.rooms.RoomUnit; import com.eu.habbo.habbohotel.rooms.RoomUnit;
@@ -230,39 +231,18 @@ public abstract class InteractionWired extends InteractionDefault {
public static WiredSettings readSettings(ClientMessage packet, boolean isEffect) public static WiredSettings readSettings(ClientMessage packet, boolean isEffect)
{ {
int intParamCount = packet.readInt(); int[] intParams = WiredInputGuard.readIntParams(packet);
if (intParamCount < 0 || intParamCount > 100) { String stringParam = WiredInputGuard.readStringParam(packet);
throw new IllegalArgumentException("Invalid intParamCount: " + intParamCount); int[] itemIds = WiredInputGuard.readFurniIds(packet);
}
int[] intParams = new int[intParamCount];
for(int i = 0; i < intParamCount; i++)
{
intParams[i] = packet.readInt();
}
String stringParam = packet.readString();
int itemCount = packet.readInt();
int selectionLimit = Emulator.getConfig() != null ? Emulator.getConfig().getInt("hotel.wired.furni.selection.count", 5) : 5;
if (itemCount < 0 || itemCount > selectionLimit * 20) {
throw new IllegalArgumentException("Invalid itemCount: " + itemCount + " exceeds maximum allowed limit");
}
int[] itemIds = new int[itemCount];
for(int i = 0; i < itemCount; i++)
{
itemIds[i] = packet.readInt();
}
WiredSettings settings = new WiredSettings(intParams, stringParam, itemIds, -1); WiredSettings settings = new WiredSettings(intParams, stringParam, itemIds, -1);
if(isEffect) if(isEffect)
{ {
settings.setDelay(packet.readInt()); settings.setDelay(WiredInputGuard.normalizeDelay(packet.readInt()));
} }
settings.setStuffTypeSelectionCode(packet.readInt()); settings.setStuffTypeSelectionCode(WiredInputGuard.normalizeStuffSelectionCode(packet.readInt()));
return settings; return settings;
} }
} }
@@ -0,0 +1,90 @@
package com.eu.habbo.habbohotel.items.interactions.wired;
import com.eu.habbo.Emulator;
import com.eu.habbo.messages.ClientMessage;
import java.util.Arrays;
public final class WiredInputGuard {
public static final int MAX_INT_PARAMS = 100;
public static final int MAX_STRING_PARAM_LENGTH = 1024;
public static final int MAX_ABSOLUTE_FURNI_IDS = 100;
public static final int DEFAULT_MAX_DELAY = 20;
public static final int MAX_ABSOLUTE_DELAY = 3600;
public static final int MIN_STUFF_SELECTION_CODE = -1;
public static final int MAX_STUFF_SELECTION_CODE = 2;
private WiredInputGuard() {
}
public static int[] readIntParams(ClientMessage packet) {
int count = packet.readInt();
if (count < 0 || count > MAX_INT_PARAMS) {
throw new IllegalArgumentException("Invalid wired int param count");
}
int[] values = new int[count];
for (int i = 0; i < count; i++) {
values[i] = packet.readInt();
}
return values;
}
public static String readStringParam(ClientMessage packet) {
String value = packet.readString();
if (value == null || value.isEmpty()) {
return "";
}
return value.length() > MAX_STRING_PARAM_LENGTH
? value.substring(0, MAX_STRING_PARAM_LENGTH)
: value;
}
public static int[] readFurniIds(ClientMessage packet) {
int count = packet.readInt();
int maxCount = maxFurniSelectionCount();
if (count < 0 || count > maxCount) {
throw new IllegalArgumentException("Invalid wired furni selection count");
}
int[] values = new int[count];
int accepted = 0;
for (int i = 0; i < count; i++) {
int itemId = packet.readInt();
if (itemId > 0) {
values[accepted++] = itemId;
}
}
return accepted == values.length ? values : Arrays.copyOf(values, accepted);
}
public static int normalizeDelay(int delay) {
return Math.max(0, Math.min(delay, maxDelay()));
}
public static int normalizeStuffSelectionCode(int code) {
if (code < MIN_STUFF_SELECTION_CODE || code > MAX_STUFF_SELECTION_CODE) {
return MIN_STUFF_SELECTION_CODE;
}
return code;
}
public static int maxFurniSelectionCount() {
int selectionLimit = Emulator.getConfig() != null
? Emulator.getConfig().getInt("hotel.wired.furni.selection.count", 5)
: 5;
selectionLimit = Math.max(1, selectionLimit);
return Math.min(MAX_ABSOLUTE_FURNI_IDS, selectionLimit * 20);
}
public static int maxDelay() {
int configured = Emulator.getConfig() != null
? Emulator.getConfig().getInt("hotel.wired.max_delay", DEFAULT_MAX_DELAY)
: DEFAULT_MAX_DELAY;
configured = Math.max(0, configured);
return Math.min(MAX_ABSOLUTE_DELAY, configured);
}
}
@@ -31,7 +31,13 @@ public class WiredConditionSaveDataEvent extends MessageHandler {
if(saveMethod.isPresent()) { if(saveMethod.isPresent()) {
if (saveMethod.get().getParameterTypes()[0] == WiredSettings.class) { if (saveMethod.get().getParameterTypes()[0] == WiredSettings.class) {
WiredSettings settings = InteractionWired.readSettings(this.packet, false); WiredSettings settings;
try {
settings = InteractionWired.readSettings(this.packet, false);
} catch (IllegalArgumentException e) {
this.client.sendResponse(new UpdateFailedComposer("Invalid wired condition settings"));
return;
}
if (condition.saveData(settings)) { if (condition.saveData(settings)) {
this.client.sendResponse(new WiredSavedComposer()); this.client.sendResponse(new WiredSavedComposer());
@@ -28,7 +28,13 @@ public class WiredEffectSaveDataEvent extends MessageHandler {
if (effect == null && extra == null) if (effect == null && extra == null)
throw new WiredSaveException(String.format("Wired effect/extra with item id %s not found in room", itemId)); throw new WiredSaveException(String.format("Wired effect/extra with item id %s not found in room", itemId));
WiredSettings settings = InteractionWired.readSettings(this.packet, true); WiredSettings settings;
try {
settings = InteractionWired.readSettings(this.packet, true);
} catch (IllegalArgumentException e) {
this.client.sendResponse(new UpdateFailedComposer("Invalid wired effect settings"));
return;
}
boolean saved; boolean saved;
if (effect != null) { if (effect != null) {
@@ -22,7 +22,13 @@ public class WiredTriggerSaveDataEvent extends MessageHandler {
InteractionWiredTrigger trigger = room.getRoomSpecialTypes().getTrigger(itemId); InteractionWiredTrigger trigger = room.getRoomSpecialTypes().getTrigger(itemId);
if (trigger != null) { if (trigger != null) {
WiredSettings settings = InteractionWired.readSettings(this.packet, false); WiredSettings settings;
try {
settings = InteractionWired.readSettings(this.packet, false);
} catch (IllegalArgumentException e) {
this.client.sendResponse(new UpdateFailedComposer("Invalid wired trigger settings"));
return;
}
try { try {
boolean saved = trigger.saveData(settings, this.client); boolean saved = trigger.saveData(settings, this.client);
@@ -0,0 +1,51 @@
package com.eu.habbo.habbohotel.items.interactions.wired;
import com.eu.habbo.messages.ClientMessage;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
class WiredInputGuardTest {
@Test
void trimsOversizedStringParams() {
String input = "x".repeat(WiredInputGuard.MAX_STRING_PARAM_LENGTH + 1);
ClientMessage message = new ClientMessage(1, stringBuffer(input));
assertEquals(WiredInputGuard.MAX_STRING_PARAM_LENGTH,
WiredInputGuard.readStringParam(message).length());
}
@Test
void filtersNonPositiveFurniIds() {
ByteBuf buffer = Unpooled.buffer();
buffer.writeInt(4);
buffer.writeInt(1);
buffer.writeInt(0);
buffer.writeInt(-1);
buffer.writeInt(2);
assertArrayEquals(new int[]{1, 2}, WiredInputGuard.readFurniIds(new ClientMessage(1, buffer)));
}
@Test
void clampsDelayAndSelectionCode() {
assertEquals(0, WiredInputGuard.normalizeDelay(-10));
assertEquals(WiredInputGuard.DEFAULT_MAX_DELAY, WiredInputGuard.normalizeDelay(WiredInputGuard.DEFAULT_MAX_DELAY + 1));
assertEquals(-1, WiredInputGuard.normalizeStuffSelectionCode(99));
assertEquals(2, WiredInputGuard.normalizeStuffSelectionCode(2));
}
private static ByteBuf stringBuffer(String value) {
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
ByteBuf buffer = Unpooled.buffer();
buffer.writeShort(bytes.length);
buffer.writeBytes(bytes);
return buffer;
}
}