Merge pull request #229 from simoleo89/fix/commands-inputs

fix(commands): enforce staff target ceilings
This commit is contained in:
DuckieTM
2026-06-17 10:01:06 +02:00
committed by GitHub
13 changed files with 144 additions and 9 deletions
@@ -32,6 +32,11 @@ public class AlertCommand extends Command {
Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(targetUsername); Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(targetUsername);
if (habbo != null) { if (habbo != null) {
if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), habbo)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT);
return true;
}
habbo.alert(message + "\r\n -" + gameClient.getHabbo().getHabboInfo().getUsername()); habbo.alert(message + "\r\n -" + gameClient.getHabbo().getHabboInfo().getUsername());
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.succes.cmd_alert.message_send").replace("%user%", targetUsername), RoomChatMessageBubbles.ALERT); gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.succes.cmd_alert.message_send").replace("%user%", targetUsername), RoomChatMessageBubbles.ALERT);
} else { } else {
@@ -60,7 +60,7 @@ public class BanCommand extends Command {
return true; return true;
} }
if (target.getRank().getId() >= gameClient.getHabbo().getHabboInfo().getRank().getId()) { if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), target)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT); gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT);
return true; return true;
} }
@@ -0,0 +1,46 @@
package com.eu.habbo.habbohotel.commands;
import com.eu.habbo.Emulator;
import com.eu.habbo.habbohotel.permissions.Rank;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.habbohotel.users.HabboInfo;
final class CommandTargetGuard {
private CommandTargetGuard() {
}
static boolean canTarget(Habbo moderator, Habbo target) {
return target != null && canTarget(moderator, target.getHabboInfo());
}
static boolean canTarget(Habbo moderator, HabboInfo target) {
if (moderator == null || target == null || moderator.getHabboInfo().getId() == target.getId()) {
return false;
}
int moderatorRankId = moderator.getHabboInfo().getRank().getId();
int targetRankId = target.getRank().getId();
return targetRankId < moderatorRankId || isCoreRank(moderatorRankId) && targetRankId <= moderatorRankId;
}
static boolean canAssignRank(Habbo moderator, Rank rank) {
if (moderator == null || rank == null) {
return false;
}
int moderatorRankId = moderator.getHabboInfo().getRank().getId();
int targetRankId = rank.getId();
return targetRankId < moderatorRankId || isCoreRank(moderatorRankId) && targetRankId <= moderatorRankId;
}
private static boolean isCoreRank(int rankId) {
int highestRankId = Emulator.getGameEnvironment().getPermissionsManager().getAllRanks().stream()
.mapToInt(Rank::getId)
.max()
.orElse(0);
return highestRankId > 0 && rankId >= highestRankId;
}
}
@@ -29,7 +29,7 @@ public class DisconnectCommand extends Command {
return true; return true;
} }
if (target.getHabboInfo().getRank().getId() > gameClient.getHabbo().getHabboInfo().getRank().getId()) { if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), target)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_disconnect.higher_rank"), RoomChatMessageBubbles.ALERT); gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_disconnect.higher_rank"), RoomChatMessageBubbles.ALERT);
return true; return true;
} }
@@ -47,6 +47,11 @@ public class GivePrefixCommand extends Command {
return true; return true;
} }
if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), target)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT);
return true;
}
UserPrefix prefix = new UserPrefix(target.getHabboInfo().getId(), text, color, icon, effect); UserPrefix prefix = new UserPrefix(target.getHabboInfo().getId(), text, color, icon, effect);
prefix.run(); prefix.run();
target.getInventory().getPrefixesComponent().addPrefix(prefix); target.getInventory().getPrefixesComponent().addPrefix(prefix);
@@ -36,7 +36,7 @@ public class GiveRankCommand extends Command {
} }
if (rank != null) { if (rank != null) {
if (rank.getId() > gameClient.getHabbo().getHabboInfo().getRank().getId()) { if (!CommandTargetGuard.canAssignRank(gameClient.getHabbo(), rank)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_give_rank.higher").replace("%username%", params[1]).replace("%id%", rank.getName()), RoomChatMessageBubbles.ALERT); gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_give_rank.higher").replace("%username%", params[1]).replace("%id%", rank.getName()), RoomChatMessageBubbles.ALERT);
return true; return true;
} }
@@ -44,7 +44,7 @@ public class GiveRankCommand extends Command {
HabboInfo habbo = HabboManager.getOfflineHabboInfo(params[1]); HabboInfo habbo = HabboManager.getOfflineHabboInfo(params[1]);
if (habbo != null) { if (habbo != null) {
if (habbo.getRank().getId() > gameClient.getHabbo().getHabboInfo().getRank().getId()) { if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), habbo)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_give_rank.higher.other").replace("%username%", params[1]).replace("%id%", rank.getName()), RoomChatMessageBubbles.ALERT); gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_give_rank.higher.other").replace("%username%", params[1]).replace("%id%", rank.getName()), RoomChatMessageBubbles.ALERT);
return true; return true;
} }
@@ -63,4 +63,4 @@ public class GiveRankCommand extends Command {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.errors.cmd_give_rank.not_found").replace("%id%", params[2]).replace("%username%", params[1]), RoomChatMessageBubbles.ALERT); gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.errors.cmd_give_rank.not_found").replace("%id%", params[2]).replace("%username%", params[1]), RoomChatMessageBubbles.ALERT);
return true; return true;
} }
} }
@@ -47,7 +47,7 @@ public class IPBanCommand extends Command {
return true; return true;
} }
if (habbo.getRank().getId() >= gameClient.getHabbo().getHabboInfo().getRank().getId()) { if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), habbo)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT); gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT);
return true; return true;
} }
@@ -43,7 +43,7 @@ public class MachineBanCommand extends Command {
return true; return true;
} }
if (habbo.getRank().getId() >= gameClient.getHabbo().getHabboInfo().getRank().getId()) { if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), habbo)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT); gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT);
return true; return true;
} }
@@ -29,6 +29,11 @@ public class MuteCommand extends Command {
return true; return true;
} }
if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), habbo)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT);
return true;
}
int duration = Integer.MAX_VALUE; int duration = Integer.MAX_VALUE;
if (params.length == 3) { if (params.length == 3) {
@@ -31,6 +31,11 @@ public class RemovePrefixCommand extends Command {
return true; return true;
} }
if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), target)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT);
return true;
}
if (prefixIdStr.equalsIgnoreCase("all")) { if (prefixIdStr.equalsIgnoreCase("all")) {
List<UserPrefix> prefixes = target.getInventory().getPrefixesComponent().getPrefixes(); List<UserPrefix> prefixes = target.getInventory().getPrefixesComponent().getPrefixes();
for (UserPrefix prefix : prefixes) { for (UserPrefix prefix : prefixes) {
@@ -41,7 +41,7 @@ public class SuperbanCommand extends Command {
return true; return true;
} }
if (habbo.getRank().getId() >= gameClient.getHabbo().getHabboInfo().getRank().getId()) { if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), habbo)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT); gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT);
return true; return true;
} }
@@ -56,4 +56,4 @@ public class SuperbanCommand extends Command {
return true; return true;
} }
} }
@@ -23,6 +23,11 @@ public class UnmuteCommand extends Command {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_unmute.not_found").replace("%user%", params[1]), RoomChatMessageBubbles.ALERT); gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_unmute.not_found").replace("%user%", params[1]), RoomChatMessageBubbles.ALERT);
return true; return true;
} else { } else {
if (!CommandTargetGuard.canTarget(gameClient.getHabbo(), habbo)) {
gameClient.getHabbo().whisper(Emulator.getTexts().getValue("commands.error.cmd_ban.target_rank_higher"), RoomChatMessageBubbles.ALERT);
return true;
}
if (!habbo.getHabboStats().allowTalk() || (habbo.getHabboInfo().getCurrentRoom() != null && habbo.getHabboInfo().getCurrentRoom().isMuted(habbo))) { if (!habbo.getHabboStats().allowTalk() || (habbo.getHabboInfo().getCurrentRoom() != null && habbo.getHabboInfo().getCurrentRoom().isMuted(habbo))) {
if (!habbo.getHabboStats().allowTalk()) { if (!habbo.getHabboStats().allowTalk()) {
habbo.unMute(); habbo.unMute();
@@ -0,0 +1,64 @@
package com.eu.habbo.habbohotel.commands;
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 CommandTargetGuardContractTest {
@Test
void highRiskUserCommandsUseCentralTargetGuard() throws Exception {
Path base = Path.of("src/main/java/com/eu/habbo/habbohotel/commands");
for (String command : List.of(
"AlertCommand.java",
"BanCommand.java",
"DisconnectCommand.java",
"GivePrefixCommand.java",
"GiveRankCommand.java",
"IPBanCommand.java",
"MachineBanCommand.java",
"MuteCommand.java",
"RemovePrefixCommand.java",
"SuperbanCommand.java",
"UnmuteCommand.java"
)) {
String source = Files.readString(base.resolve(command));
assertTrue(source.contains("CommandTargetGuard.canTarget"),
command + " must use the central command target guard for staff/core rank handling");
}
}
@Test
void rankGrantingUsesCentralAssignmentGuard() throws Exception {
String source = Files.readString(Path.of("src/main/java/com/eu/habbo/habbohotel/commands/GiveRankCommand.java"));
assertTrue(source.contains("CommandTargetGuard.canAssignRank"),
"GiveRankCommand must guard the assigned rank with the same core-rank semantics");
}
@Test
void targetGuardKeepsCorePeerOverrideCentralized() throws Exception {
String source = Files.readString(Path.of("src/main/java/com/eu/habbo/habbohotel/commands/CommandTargetGuard.java"));
String rule = "targetRankId < moderatorRankId || isCoreRank(moderatorRankId) && targetRankId <= moderatorRankId";
assertTrue(countOccurrences(source, rule) >= 2,
"non-core command users must only target lower ranks while the highest/core rank may target peer ranks");
}
private static int countOccurrences(String source, String needle) {
int count = 0;
int index = 0;
while ((index = source.indexOf(needle, index)) >= 0) {
count++;
index += needle.length();
}
return count;
}
}