fix(housekeeping): protect room owner mutations

This commit is contained in:
simoleo89
2026-06-14 22:17:47 +02:00
parent 93c4565660
commit ec24283e0f
7 changed files with 106 additions and 6 deletions
@@ -42,11 +42,14 @@ public class HousekeepingDeleteRoomEvent extends MessageHandler {
Room room = Emulator.getGameEnvironment().getRoomManager().loadRoom(roomId, false);
if (room != null) {
room.ejectAll();
room.preventUnloading = false;
room.dispose();
Emulator.getGameEnvironment().getRoomManager().uncacheRoom(room);
if (room == null) {
this.client.sendResponse(new HousekeepingActionResultComposer(ACTION_KEY, false, 0, "housekeeping.error.room_not_found"));
return;
}
if (!HousekeepingRoomGuard.canManageRoom(this.client.getHabbo(), room)) {
this.client.sendResponse(new HousekeepingActionResultComposer(ACTION_KEY, false, 0, "housekeeping.error.rank_too_high"));
return;
}
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection();
@@ -63,6 +66,11 @@ public class HousekeepingDeleteRoomEvent extends MessageHandler {
return;
}
room.ejectAll();
room.preventUnloading = false;
room.dispose();
Emulator.getGameEnvironment().getRoomManager().uncacheRoom(room);
com.eu.habbo.habbohotel.modtool.HousekeepingAuditLog.log(
this.client.getHabbo().getHabboInfo().getId(),
this.client.getHabbo().getHabboInfo().getUsername(),
@@ -34,6 +34,11 @@ public class HousekeepingKickAllFromRoomEvent extends MessageHandler {
return;
}
if (!HousekeepingRoomGuard.canManageRoom(this.client.getHabbo(), room)) {
this.client.sendResponse(new HousekeepingActionResultComposer(ACTION_KEY, false, 0, "housekeeping.error.rank_too_high"));
return;
}
room.ejectAll();
com.eu.habbo.habbohotel.modtool.HousekeepingAuditLog.log(
@@ -31,7 +31,7 @@ public class HousekeepingMuteRoomEvent extends MessageHandler {
int roomId = this.packet.readInt();
int minutes = this.packet.readInt();
if (roomId <= 0) {
if (roomId <= 0 || minutes < 0) {
this.client.sendResponse(new HousekeepingActionResultComposer(ACTION_KEY, false, 0, "housekeeping.error.invalid_input"));
return;
}
@@ -43,6 +43,11 @@ public class HousekeepingMuteRoomEvent extends MessageHandler {
return;
}
if (!HousekeepingRoomGuard.canManageRoom(this.client.getHabbo(), room)) {
this.client.sendResponse(new HousekeepingActionResultComposer(ACTION_KEY, false, 0, "housekeeping.error.rank_too_high"));
return;
}
room.setMuted(minutes > 0);
com.eu.habbo.habbohotel.modtool.HousekeepingAuditLog.log(
@@ -0,0 +1,13 @@
package com.eu.habbo.messages.incoming.housekeeping;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.users.Habbo;
final class HousekeepingRoomGuard {
private HousekeepingRoomGuard() {
}
static boolean canManageRoom(Habbo operator, Room room) {
return room != null && HousekeepingTargetRankGuard.canTargetUser(operator, room.getOwnerId());
}
}
@@ -41,6 +41,11 @@ public class HousekeepingRoomStateEvent extends MessageHandler {
return;
}
if (!HousekeepingRoomGuard.canManageRoom(this.client.getHabbo(), room)) {
this.client.sendResponse(new HousekeepingActionResultComposer(actionKey, false, 0, "housekeeping.error.rank_too_high"));
return;
}
room.setState(open ? RoomState.OPEN : RoomState.LOCKED);
room.save();
@@ -2,6 +2,7 @@ package com.eu.habbo.messages.incoming.housekeeping;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.permissions.Permission;
import com.eu.habbo.habbohotel.rooms.Room;
import com.eu.habbo.habbohotel.users.HabboInfo;
import com.eu.habbo.messages.incoming.MessageHandler;
import com.eu.habbo.messages.outgoing.housekeeping.HousekeepingActionResultComposer;
@@ -39,6 +40,19 @@ public class HousekeepingTransferRoomOwnershipEvent extends MessageHandler {
return;
}
Room room = Emulator.getGameEnvironment().getRoomManager().loadRoom(roomId, false);
if (room == null) {
this.client.sendResponse(new HousekeepingActionResultComposer(ACTION_KEY, false, 0, "housekeeping.error.room_not_found"));
return;
}
if (!HousekeepingRoomGuard.canManageRoom(this.client.getHabbo(), room) ||
!HousekeepingTargetRankGuard.canTargetUser(this.client.getHabbo(), newOwnerId)) {
this.client.sendResponse(new HousekeepingActionResultComposer(ACTION_KEY, false, 0, "housekeeping.error.rank_too_high"));
return;
}
HabboInfo newOwner = Emulator.getGameEnvironment().getHabboManager().getHabboInfo(newOwnerId);
if (newOwner == null) {
@@ -62,6 +76,9 @@ public class HousekeepingTransferRoomOwnershipEvent extends MessageHandler {
return;
}
room.setOwnerId(newOwnerId);
room.setOwnerName(newOwner.getUsername());
com.eu.habbo.habbohotel.modtool.HousekeepingAuditLog.log(
this.client.getHabbo().getHabboInfo().getId(),
this.client.getHabbo().getHabboInfo().getUsername(),
@@ -0,0 +1,47 @@
package com.eu.habbo.messages.incoming.housekeeping;
import org.junit.jupiter.api.Test;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertTrue;
class HousekeepingRoomGuardContractTest {
@Test
void destructiveRoomActionsRespectOwnerRankCeiling() throws Exception {
Path base = Path.of("src/main/java/com/eu/habbo/messages/incoming/housekeeping");
for (String handler : List.of(
"HousekeepingDeleteRoomEvent.java",
"HousekeepingKickAllFromRoomEvent.java",
"HousekeepingMuteRoomEvent.java",
"HousekeepingRoomStateEvent.java",
"HousekeepingTransferRoomOwnershipEvent.java"
)) {
String source = Files.readString(base.resolve(handler));
assertTrue(source.contains("HousekeepingRoomGuard.canManageRoom(this.client.getHabbo(), room)"),
handler + " must reject room mutations when the room owner is peer-or-higher ranked");
assertTrue(source.contains("housekeeping.error.rank_too_high"),
handler + " must surface a rank-ceiling error for protected room owners");
}
}
@Test
void roomGuardDelegatesToTargetRankGuard() throws Exception {
String source = Files.readString(Path.of("src/main/java/com/eu/habbo/messages/incoming/housekeeping/HousekeepingRoomGuard.java"));
assertTrue(source.contains("HousekeepingTargetRankGuard.canTargetUser(operator, room.getOwnerId())"),
"room-owner checks must use the same core-rank peer override as user moderation");
}
@Test
void roomMuteRejectsNegativeDurations() throws Exception {
String source = Files.readString(Path.of("src/main/java/com/eu/habbo/messages/incoming/housekeeping/HousekeepingMuteRoomEvent.java"));
assertTrue(source.contains("minutes < 0"),
"room mute should reject negative duration values instead of treating them as unmute");
}
}