From 60ccc8c80b23a2c068cb3f177402ded54e46347c Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sat, 13 Jun 2026 17:28:09 +0200 Subject: [PATCH] fix(items): require seed ownership for monsterplants Reject monsterplant seed redemption when the caller does not own the placed seed. Without this guard, a user in the same room could trigger ToggleFloorItemEvent against another user's seed and have the server delete that item while creating the monsterplant pet for the attacker. Add a contract test covering the ownership guard before createMonsterplant is reached. --- .../rooms/items/ToggleFloorItemEvent.java | 4 +++ ...MonsterPlantSeedOwnershipContractTest.java | 29 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 Emulator/src/test/java/com/eu/habbo/messages/incoming/rooms/items/MonsterPlantSeedOwnershipContractTest.java diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleFloorItemEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleFloorItemEvent.java index 45098095..8bf4942b 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleFloorItemEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleFloorItemEvent.java @@ -103,6 +103,10 @@ public class ToggleFloorItemEvent extends MessageHandler { // Do not move to onClick(). Wired could trigger it. if (item instanceof InteractionMonsterPlantSeed) { + if (item.getUserId() != this.client.getHabbo().getHabboInfo().getId()) { + return; + } + Emulator.getThreading().run(new QueryDeleteHabboItem(item.getId())); boolean isRare = item.getBaseItem().getName().contains("rare"); diff --git a/Emulator/src/test/java/com/eu/habbo/messages/incoming/rooms/items/MonsterPlantSeedOwnershipContractTest.java b/Emulator/src/test/java/com/eu/habbo/messages/incoming/rooms/items/MonsterPlantSeedOwnershipContractTest.java new file mode 100644 index 00000000..e8516c19 --- /dev/null +++ b/Emulator/src/test/java/com/eu/habbo/messages/incoming/rooms/items/MonsterPlantSeedOwnershipContractTest.java @@ -0,0 +1,29 @@ +package com.eu.habbo.messages.incoming.rooms.items; + +import org.junit.jupiter.api.Test; + +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +class MonsterPlantSeedOwnershipContractTest { + @Test + void monsterPlantSeedsCanOnlyBeRedeemedByTheirOwner() throws Exception { + String source = Files.readString(Path.of("src/main/java/com/eu/habbo/messages/incoming/rooms/items/ToggleFloorItemEvent.java")); + int seedBranch = source.indexOf("item instanceof InteractionMonsterPlantSeed"); + + assertTrue(seedBranch >= 0, "ToggleFloorItemEvent must keep monsterplant seed handling explicit"); + + String seedHandling = source.substring(seedBranch, Math.min(source.length(), seedBranch + 1400)); + + String ownershipGuard = "if (item.getUserId() != this.client.getHabbo().getHabboInfo().getId())"; + + assertTrue(seedHandling.contains(ownershipGuard), + "Monsterplant seed redemption must reject callers who do not own the seed"); + assertTrue(seedHandling.contains("createMonsterplant"), + "Monsterplant seed handling must create the pet inside the guarded branch"); + assertTrue(seedHandling.indexOf(ownershipGuard) < seedHandling.indexOf("createMonsterplant"), + "Ownership rejection must happen before creating the pet"); + } +}