From 62454671d2e031f7c00daa5ace753ca5786b44d4 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Tue, 16 Jun 2026 21:36:06 +0200 Subject: [PATCH] fix(navigator): bound search inputs --- .../navigator/AddSavedSearchEvent.java | 7 +-- .../navigator/NavigatorInputGuard.java | 26 +++++++++++ .../navigator/SearchRoomsByTagEvent.java | 2 +- .../incoming/navigator/SearchRoomsEvent.java | 45 ++++++++++++------- .../navigator/NavigatorInputGuardTest.java | 21 +++++++++ .../NavigatorSearchInputContractTest.java | 37 +++++++++++++++ 6 files changed, 115 insertions(+), 23 deletions(-) create mode 100644 Emulator/src/main/java/com/eu/habbo/messages/incoming/navigator/NavigatorInputGuard.java create mode 100644 Emulator/src/test/java/com/eu/habbo/messages/incoming/navigator/NavigatorInputGuardTest.java create mode 100644 Emulator/src/test/java/com/eu/habbo/messages/incoming/navigator/NavigatorSearchInputContractTest.java diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/navigator/AddSavedSearchEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/navigator/AddSavedSearchEvent.java index 53cf4e3d..b397f3fd 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/navigator/AddSavedSearchEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/navigator/AddSavedSearchEvent.java @@ -14,11 +14,8 @@ public class AddSavedSearchEvent extends MessageHandler { @Override public void handle() throws Exception { - String searchCode = this.packet.readString(); - String filter = this.packet.readString(); - - if (searchCode.length() > 255) searchCode = searchCode.substring(0, 255); - if (filter.length() > 255) filter = filter.substring(0, 255); + String searchCode = NavigatorInputGuard.normalizeSavedSearchValue(this.packet.readString()); + String filter = NavigatorInputGuard.normalizeSavedSearchValue(this.packet.readString()); if (this.client.getHabbo().getHabboInfo().getSavedSearches().size() >= MAX_SAVED_SEARCHES) { this.client.sendResponse(new NewNavigatorSavedSearchesComposer(this.client.getHabbo().getHabboInfo().getSavedSearches())); diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/navigator/NavigatorInputGuard.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/navigator/NavigatorInputGuard.java new file mode 100644 index 00000000..434f4733 --- /dev/null +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/navigator/NavigatorInputGuard.java @@ -0,0 +1,26 @@ +package com.eu.habbo.messages.incoming.navigator; + +final class NavigatorInputGuard { + static final int MAX_SEARCH_LENGTH = 64; + static final int MAX_SAVED_SEARCH_LENGTH = 255; + + private NavigatorInputGuard() { + } + + static String normalizeSearch(String value) { + return normalize(value, MAX_SEARCH_LENGTH); + } + + static String normalizeSavedSearchValue(String value) { + return normalize(value, MAX_SAVED_SEARCH_LENGTH); + } + + private static String normalize(String value, int maxLength) { + if (value == null) { + return ""; + } + + String normalized = value.trim(); + return normalized.length() > maxLength ? normalized.substring(0, maxLength) : normalized; + } +} diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/navigator/SearchRoomsByTagEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/navigator/SearchRoomsByTagEvent.java index 0d572edf..cf18a821 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/navigator/SearchRoomsByTagEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/navigator/SearchRoomsByTagEvent.java @@ -7,7 +7,7 @@ import com.eu.habbo.messages.outgoing.navigator.PrivateRoomsComposer; public class SearchRoomsByTagEvent extends MessageHandler { @Override public void handle() throws Exception { - String tag = this.packet.readString(); + String tag = NavigatorInputGuard.normalizeSearch(this.packet.readString()); this.client.sendResponse(new PrivateRoomsComposer(Emulator.getGameEnvironment().getRoomManager().getRoomsWithTag(tag))); } diff --git a/Emulator/src/main/java/com/eu/habbo/messages/incoming/navigator/SearchRoomsEvent.java b/Emulator/src/main/java/com/eu/habbo/messages/incoming/navigator/SearchRoomsEvent.java index 98d824e7..3fc13ffc 100644 --- a/Emulator/src/main/java/com/eu/habbo/messages/incoming/navigator/SearchRoomsEvent.java +++ b/Emulator/src/main/java/com/eu/habbo/messages/incoming/navigator/SearchRoomsEvent.java @@ -34,36 +34,43 @@ public class SearchRoomsEvent extends MessageHandler { @Override public void handle() throws Exception { - String name = this.packet.readString(); + String name = NavigatorInputGuard.normalizeSearch(this.packet.readString()); String prefix = ""; String query = name; ArrayList rooms; + if (name.startsWith("owner:")) { + query = NavigatorInputGuard.normalizeSearch(name.substring("owner:".length())); + prefix = "owner:"; + } else if (name.startsWith("tag:")) { + query = NavigatorInputGuard.normalizeSearch(name.substring("tag:".length())); + prefix = "tag:"; + } else if (name.startsWith("group:")) { + query = NavigatorInputGuard.normalizeSearch(name.substring("group:".length())); + prefix = "group:"; + } + + String cacheKey = buildCacheKey(prefix, query); + ServerMessage message = null; Map rankCache = cachedResults.get(this.client.getHabbo().getHabboInfo().getRank()); if (rankCache != null) { - message = rankCache.get((name + "\t" + query).toLowerCase()); + message = rankCache.get(cacheKey); } else { rankCache = createLRUCache(); cachedResults.put(this.client.getHabbo().getHabboInfo().getRank(), rankCache); } if (message == null) { - if (name.startsWith("owner:")) { - query = name.split("owner:")[1]; - prefix = "owner:"; - rooms = (ArrayList) Emulator.getGameEnvironment().getRoomManager().getRoomsForHabbo(name); - } else if (name.startsWith("tag:")) { - query = name.split("tag:")[1]; - prefix = "tag:"; - rooms = Emulator.getGameEnvironment().getRoomManager().getRoomsWithTag(name); - } else if (name.startsWith("group:")) { - query = name.split("group:")[1]; - prefix = "group:"; - rooms = Emulator.getGameEnvironment().getRoomManager().getGroupRoomsWithName(name); + if (prefix.equals("owner:")) { + rooms = (ArrayList) Emulator.getGameEnvironment().getRoomManager().getRoomsForHabbo(query); + } else if (prefix.equals("tag:")) { + rooms = Emulator.getGameEnvironment().getRoomManager().getRoomsWithTag(query); + } else if (prefix.equals("group:")) { + rooms = Emulator.getGameEnvironment().getRoomManager().getGroupRoomsWithName(query); } else { - rooms = Emulator.getGameEnvironment().getRoomManager().getRoomsWithName(name); + rooms = Emulator.getGameEnvironment().getRoomManager().getRoomsWithName(query); } message = new PrivateRoomsComposer(rooms).compose(); @@ -73,7 +80,7 @@ public class SearchRoomsEvent extends MessageHandler { map = createLRUCache(); } - map.put((name + "\t" + query).toLowerCase(), message); + map.put(cacheKey, message); cachedResults.put(this.client.getHabbo().getHabboInfo().getRank(), map); NavigatorSearchResultEvent event = new NavigatorSearchResultEvent(this.client.getHabbo(), prefix, query, rooms); @@ -84,4 +91,8 @@ public class SearchRoomsEvent extends MessageHandler { this.client.sendResponse(message); } -} \ No newline at end of file + + private static String buildCacheKey(String prefix, String query) { + return (prefix + "\t" + query).toLowerCase(); + } +} diff --git a/Emulator/src/test/java/com/eu/habbo/messages/incoming/navigator/NavigatorInputGuardTest.java b/Emulator/src/test/java/com/eu/habbo/messages/incoming/navigator/NavigatorInputGuardTest.java new file mode 100644 index 00000000..4bb3b32b --- /dev/null +++ b/Emulator/src/test/java/com/eu/habbo/messages/incoming/navigator/NavigatorInputGuardTest.java @@ -0,0 +1,21 @@ +package com.eu.habbo.messages.incoming.navigator; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class NavigatorInputGuardTest { + @Test + void searchValuesAreTrimmedAndBounded() { + assertEquals("", NavigatorInputGuard.normalizeSearch(null)); + assertEquals("rare", NavigatorInputGuard.normalizeSearch(" rare ")); + assertEquals(NavigatorInputGuard.MAX_SEARCH_LENGTH, NavigatorInputGuard.normalizeSearch("a".repeat(100)).length()); + } + + @Test + void savedSearchValuesUseLargerBound() { + assertEquals("", NavigatorInputGuard.normalizeSavedSearchValue(null)); + assertEquals("owner:duckie", NavigatorInputGuard.normalizeSavedSearchValue(" owner:duckie ")); + assertEquals(NavigatorInputGuard.MAX_SAVED_SEARCH_LENGTH, NavigatorInputGuard.normalizeSavedSearchValue("a".repeat(400)).length()); + } +} diff --git a/Emulator/src/test/java/com/eu/habbo/messages/incoming/navigator/NavigatorSearchInputContractTest.java b/Emulator/src/test/java/com/eu/habbo/messages/incoming/navigator/NavigatorSearchInputContractTest.java new file mode 100644 index 00000000..db44b6a7 --- /dev/null +++ b/Emulator/src/test/java/com/eu/habbo/messages/incoming/navigator/NavigatorSearchInputContractTest.java @@ -0,0 +1,37 @@ +package com.eu.habbo.messages.incoming.navigator; + +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 NavigatorSearchInputContractTest { + @Test + void classicSearchNormalizesInputAndPassesUnprefixedQueriesToManagers() throws Exception { + String source = Files.readString(Path.of("src/main/java/com/eu/habbo/messages/incoming/navigator/SearchRoomsEvent.java")); + + assertTrue(source.contains("NavigatorInputGuard.normalizeSearch(this.packet.readString())"), + "classic room search must normalize raw client text before cache or manager lookups"); + assertTrue(source.contains("getRoomsForHabbo(query)"), + "owner search must pass only the unprefixed owner query"); + assertTrue(source.contains("getRoomsWithTag(query)"), + "tag search must pass only the unprefixed tag query"); + assertTrue(source.contains("getGroupRoomsWithName(query)"), + "group search must pass only the unprefixed group query"); + assertTrue(source.contains("buildCacheKey(prefix, query)"), + "classic room search must cache using normalized prefix/query pairs"); + } + + @Test + void savedAndTagSearchesNormalizeText() throws Exception { + String saved = Files.readString(Path.of("src/main/java/com/eu/habbo/messages/incoming/navigator/AddSavedSearchEvent.java")); + String tag = Files.readString(Path.of("src/main/java/com/eu/habbo/messages/incoming/navigator/SearchRoomsByTagEvent.java")); + + assertTrue(saved.contains("NavigatorInputGuard.normalizeSavedSearchValue"), + "saved searches must trim and bound search code/filter values"); + assertTrue(tag.contains("NavigatorInputGuard.normalizeSearch"), + "tag search must trim and bound tag values"); + } +}