fix(room-settings): bound inputs

This commit is contained in:
simoleo89
2026-06-17 21:12:06 +02:00
parent 0109c25c80
commit 18d90a635e
3 changed files with 164 additions and 28 deletions
@@ -0,0 +1,74 @@
package com.eu.habbo.messages.incoming.rooms;
import com.eu.habbo.habbohotel.rooms.RoomState;
final class RoomSettingsInputGuard {
static final int MAX_PASSWORD_LENGTH = 64;
static final int MAX_TAGS = 2;
static final int MIN_USERS_MAX = 1;
static final int MAX_USERS_MAX = 200;
static final int MIN_WALL_OR_FLOOR_SIZE = -2;
static final int MAX_WALL_OR_FLOOR_SIZE = 1;
static final int MIN_CHAT_DISTANCE = 1;
static final int MAX_CHAT_DISTANCE = 99;
private RoomSettingsInputGuard() {
}
static boolean isValidRoomState(int value) {
return value >= 0 && value < RoomState.values().length;
}
static RoomState roomState(int value) {
RoomState[] states = RoomState.values();
return states[value];
}
static boolean isValidUsersMax(int value) {
return isInRange(value, MIN_USERS_MAX, MAX_USERS_MAX);
}
static boolean isValidTagCount(int value) {
return isInRange(value, 0, MAX_TAGS);
}
static boolean isValidTradeMode(int value) {
return isInRange(value, 0, 2);
}
static boolean isValidModerationOption(int value) {
return isInRange(value, 0, 2);
}
static boolean isValidWallOrFloorSize(int value) {
return isInRange(value, MIN_WALL_OR_FLOOR_SIZE, MAX_WALL_OR_FLOOR_SIZE);
}
static boolean isValidChatMode(int value) {
return isInRange(value, 0, 2);
}
static boolean isValidChatWeight(int value) {
return isInRange(value, 0, 2);
}
static boolean isValidChatSpeed(int value) {
return isInRange(value, 0, 2);
}
static boolean isValidChatDistance(int value) {
return isInRange(value, MIN_CHAT_DISTANCE, MAX_CHAT_DISTANCE);
}
static boolean isValidChatProtection(int value) {
return isInRange(value, 0, 2);
}
static boolean isSafePassword(String password) {
return password != null && password.length() <= MAX_PASSWORD_LENGTH;
}
private static boolean isInRange(int value, int min, int max) {
return value >= min && value <= max;
}
}
@@ -15,15 +15,6 @@ import java.util.Set;
public class RoomSettingsSaveEvent extends MessageHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(RoomSettingsSaveEvent.class);
private static final int MAX_ROOM_PASSWORD_LENGTH = 64;
private static final int MAX_TAGS = 2;
private static final int MIN_USERS_MAX = 1;
private static final int MAX_USERS_MAX = 200;
private static final int MIN_THICKNESS = -2;
private static final int MAX_THICKNESS = 1;
private static final int MAX_OPTION_LEVEL = 2;
private static final int MIN_CHAT_DISTANCE = 1;
private static final int MAX_CHAT_DISTANCE = 99;
@Override
public void handle() throws Exception {
@@ -57,13 +48,13 @@ public class RoomSettingsSaveEvent extends MessageHandler {
}
int stateId = this.packet.readInt();
if (stateId < 0 || stateId >= RoomState.values().length) {
if (!RoomSettingsInputGuard.isValidRoomState(stateId)) {
return;
}
RoomState state = RoomState.values()[stateId];
RoomState state = RoomSettingsInputGuard.roomState(stateId);
String password = this.packet.readString();
if (password.length() > MAX_ROOM_PASSWORD_LENGTH) {
if (!RoomSettingsInputGuard.isSafePassword(password)) {
return;
}
if (state == RoomState.PASSWORD && password.isEmpty() && (room.getPassword() == null || room.getPassword().isEmpty())) {
@@ -72,7 +63,7 @@ public class RoomSettingsSaveEvent extends MessageHandler {
}
int usersMax = this.packet.readInt();
if (usersMax < MIN_USERS_MAX || usersMax > MAX_USERS_MAX) {
if (!RoomSettingsInputGuard.isValidUsersMax(usersMax)) {
return;
}
@@ -80,7 +71,7 @@ public class RoomSettingsSaveEvent extends MessageHandler {
StringBuilder tags = new StringBuilder();
Set<String> uniqueTags = new HashSet<>();
int count = this.packet.readInt();
if (count < 0 || count > MAX_TAGS) {
if (!RoomSettingsInputGuard.isValidTagCount(count)) {
return;
}
for (int i = 0; i < count; i++) {
@@ -152,17 +143,17 @@ public class RoomSettingsSaveEvent extends MessageHandler {
int chatDistance = this.packet.readInt();
int chatProtection = this.packet.readInt();
if (!isInRange(tradeMode, 0, MAX_OPTION_LEVEL)
|| !isInRange(wallSize, MIN_THICKNESS, MAX_THICKNESS)
|| !isInRange(floorSize, MIN_THICKNESS, MAX_THICKNESS)
|| !isInRange(muteOption, 0, MAX_OPTION_LEVEL)
|| !isInRange(kickOption, 0, MAX_OPTION_LEVEL)
|| !isInRange(banOption, 0, MAX_OPTION_LEVEL)
|| !isInRange(chatMode, 0, MAX_OPTION_LEVEL)
|| !isInRange(chatWeight, 0, MAX_OPTION_LEVEL)
|| !isInRange(chatSpeed, 0, MAX_OPTION_LEVEL)
|| !isInRange(chatDistance, MIN_CHAT_DISTANCE, MAX_CHAT_DISTANCE)
|| !isInRange(chatProtection, 0, MAX_OPTION_LEVEL)) {
if (!RoomSettingsInputGuard.isValidTradeMode(tradeMode)
|| !RoomSettingsInputGuard.isValidWallOrFloorSize(wallSize)
|| !RoomSettingsInputGuard.isValidWallOrFloorSize(floorSize)
|| !RoomSettingsInputGuard.isValidModerationOption(muteOption)
|| !RoomSettingsInputGuard.isValidModerationOption(kickOption)
|| !RoomSettingsInputGuard.isValidModerationOption(banOption)
|| !RoomSettingsInputGuard.isValidChatMode(chatMode)
|| !RoomSettingsInputGuard.isValidChatWeight(chatWeight)
|| !RoomSettingsInputGuard.isValidChatSpeed(chatSpeed)
|| !RoomSettingsInputGuard.isValidChatDistance(chatDistance)
|| !RoomSettingsInputGuard.isValidChatProtection(chatProtection)) {
return;
}
@@ -198,7 +189,4 @@ public class RoomSettingsSaveEvent extends MessageHandler {
}
}
private static boolean isInRange(int value, int min, int max) {
return value >= min && value <= max;
}
}
@@ -0,0 +1,74 @@
package com.eu.habbo.messages.incoming.rooms;
import com.eu.habbo.habbohotel.rooms.RoomState;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
class RoomSettingsInputGuardTest {
@Test
void validatesRoomStateBeforeIndexing() {
assertFalse(RoomSettingsInputGuard.isValidRoomState(-1));
assertFalse(RoomSettingsInputGuard.isValidRoomState(RoomState.values().length));
assertTrue(RoomSettingsInputGuard.isValidRoomState(RoomState.PASSWORD.getState()));
assertEquals(RoomState.PASSWORD, RoomSettingsInputGuard.roomState(RoomState.PASSWORD.getState()));
}
@Test
void validatesRoomCapacityAndCosmeticSizes() {
assertFalse(RoomSettingsInputGuard.isValidUsersMax(-50));
assertFalse(RoomSettingsInputGuard.isValidUsersMax(500));
assertTrue(RoomSettingsInputGuard.isValidUsersMax(200));
assertFalse(RoomSettingsInputGuard.isValidWallOrFloorSize(-50));
assertFalse(RoomSettingsInputGuard.isValidWallOrFloorSize(50));
assertTrue(RoomSettingsInputGuard.isValidWallOrFloorSize(-2));
assertTrue(RoomSettingsInputGuard.isValidWallOrFloorSize(1));
}
@Test
void validatesRoomOptionEnums() {
assertFalse(RoomSettingsInputGuard.isValidTradeMode(-1));
assertFalse(RoomSettingsInputGuard.isValidTradeMode(99));
assertTrue(RoomSettingsInputGuard.isValidTradeMode(2));
assertFalse(RoomSettingsInputGuard.isValidModerationOption(-1));
assertFalse(RoomSettingsInputGuard.isValidModerationOption(99));
assertTrue(RoomSettingsInputGuard.isValidModerationOption(2));
assertFalse(RoomSettingsInputGuard.isValidChatMode(-1));
assertFalse(RoomSettingsInputGuard.isValidChatMode(99));
assertTrue(RoomSettingsInputGuard.isValidChatMode(2));
assertFalse(RoomSettingsInputGuard.isValidChatWeight(-1));
assertFalse(RoomSettingsInputGuard.isValidChatWeight(99));
assertTrue(RoomSettingsInputGuard.isValidChatWeight(2));
assertFalse(RoomSettingsInputGuard.isValidChatSpeed(-1));
assertFalse(RoomSettingsInputGuard.isValidChatSpeed(99));
assertTrue(RoomSettingsInputGuard.isValidChatSpeed(2));
assertFalse(RoomSettingsInputGuard.isValidChatProtection(-1));
assertFalse(RoomSettingsInputGuard.isValidChatProtection(99));
assertTrue(RoomSettingsInputGuard.isValidChatProtection(2));
}
@Test
void validatesChatDistanceSafely() {
assertFalse(RoomSettingsInputGuard.isValidChatDistance(0));
assertFalse(RoomSettingsInputGuard.isValidChatDistance(Integer.MIN_VALUE));
assertFalse(RoomSettingsInputGuard.isValidChatDistance(500));
assertTrue(RoomSettingsInputGuard.isValidChatDistance(1));
assertTrue(RoomSettingsInputGuard.isValidChatDistance(99));
}
@Test
void validatesTagCount() {
assertFalse(RoomSettingsInputGuard.isValidTagCount(-1));
assertFalse(RoomSettingsInputGuard.isValidTagCount(3));
assertTrue(RoomSettingsInputGuard.isValidTagCount(0));
assertTrue(RoomSettingsInputGuard.isValidTagCount(2));
}
@Test
void rejectsOversizedPasswords() {
assertTrue(RoomSettingsInputGuard.isSafePassword("short-secret"));
assertFalse(RoomSettingsInputGuard.isSafePassword("x".repeat(RoomSettingsInputGuard.MAX_PASSWORD_LENGTH + 1)));
}
}