Merge pull request #231 from simoleo89/fix/navigator-inputs

fix(navigator): bound search inputs
This commit is contained in:
DuckieTM
2026-06-17 10:01:55 +02:00
committed by GitHub
6 changed files with 115 additions and 23 deletions
@@ -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()));
@@ -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;
}
}
@@ -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)));
}
@@ -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<Room> 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<String, ServerMessage> 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<Room>) 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<Room>) 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);
}
}
private static String buildCacheKey(String prefix, String query) {
return (prefix + "\t" + query).toLowerCase();
}
}
@@ -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());
}
}
@@ -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");
}
}