You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-19 15:06:19 +00:00
fix(rcon): bound mute and achievement mutations
This commit is contained in:
@@ -157,6 +157,8 @@ public final class Emulator {
|
||||
Emulator.config.register("rcon.rate_limit.limit_for_period", "60");
|
||||
Emulator.config.register("rcon.rate_limit.refresh_period_ms", "1000");
|
||||
Emulator.config.register("rcon.rate_limit.timeout_ms", "0");
|
||||
Emulator.config.register("rcon.mute.max_duration_seconds", "604800");
|
||||
Emulator.config.register("rcon.achievement.max_progress", "10000");
|
||||
String hotelTimezoneId = Emulator.getConfig().getValue("hotel.timezone", java.time.ZoneId.systemDefault().getId());
|
||||
System.out.println();
|
||||
LOGGER.info("https://github.com/duckietm/Arcturus-Morningstar-Extended, ");
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.eu.habbo.messages.rcon;
|
||||
import com.eu.habbo.Emulator;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.google.gson.Gson;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -12,6 +14,7 @@ import java.sql.SQLException;
|
||||
|
||||
public class MuteUser extends RCONMessage<MuteUser.JSON> {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MuteUser.class);
|
||||
static final int DEFAULT_MAX_DURATION_SECONDS = 604_800;
|
||||
|
||||
public MuteUser() {
|
||||
super(MuteUser.JSON.class);
|
||||
@@ -19,6 +22,13 @@ public class MuteUser extends RCONMessage<MuteUser.JSON> {
|
||||
|
||||
@Override
|
||||
public void handle(Gson gson, JSON json) {
|
||||
int maxDuration = parseMaxDuration(Emulator.getConfig().getValue("rcon.mute.max_duration_seconds", String.valueOf(DEFAULT_MAX_DURATION_SECONDS)));
|
||||
if (json.duration < 0 || json.duration > maxDuration) {
|
||||
this.status = RCONMessage.STATUS_ERROR;
|
||||
this.message = "duration must be between 0 and " + maxDuration + " seconds";
|
||||
return;
|
||||
}
|
||||
|
||||
Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(json.user_id);
|
||||
|
||||
if (habbo != null) {
|
||||
@@ -29,7 +39,7 @@ public class MuteUser extends RCONMessage<MuteUser.JSON> {
|
||||
}
|
||||
} else {
|
||||
try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("UPDATE users_settings SET mute_end_timestamp = ? WHERE user_id = ? LIMIT 1")) {
|
||||
statement.setInt(1, Emulator.getIntUnixTimestamp() + json.duration);
|
||||
statement.setInt(1, json.duration == 0 ? 0 : Emulator.getIntUnixTimestamp() + json.duration);
|
||||
statement.setInt(2, json.user_id);
|
||||
if (statement.executeUpdate() == 0) {
|
||||
this.status = HABBO_NOT_FOUND;
|
||||
@@ -40,11 +50,24 @@ public class MuteUser extends RCONMessage<MuteUser.JSON> {
|
||||
}
|
||||
}
|
||||
|
||||
static int parseMaxDuration(String configured) {
|
||||
try {
|
||||
int parsed = Integer.parseInt(configured);
|
||||
if (parsed >= 0) {
|
||||
return parsed;
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
|
||||
return DEFAULT_MAX_DURATION_SECONDS;
|
||||
}
|
||||
|
||||
static class JSON {
|
||||
|
||||
@Positive(message = "invalid user")
|
||||
public int user_id;
|
||||
|
||||
|
||||
@Min(value = 0, message = "invalid duration")
|
||||
public int duration;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@ import com.eu.habbo.habbohotel.achievements.Achievement;
|
||||
import com.eu.habbo.habbohotel.achievements.AchievementManager;
|
||||
import com.eu.habbo.habbohotel.users.Habbo;
|
||||
import com.google.gson.Gson;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
|
||||
public class ProgressAchievement extends RCONMessage<ProgressAchievement.ProgressAchievementJSON> {
|
||||
static final int DEFAULT_MAX_PROGRESS = 10_000;
|
||||
|
||||
public ProgressAchievement() {
|
||||
super(ProgressAchievementJSON.class);
|
||||
@@ -14,6 +16,13 @@ public class ProgressAchievement extends RCONMessage<ProgressAchievement.Progres
|
||||
|
||||
@Override
|
||||
public void handle(Gson gson, ProgressAchievementJSON json) {
|
||||
int maxProgress = parseMaxProgress(Emulator.getConfig().getValue("rcon.achievement.max_progress", String.valueOf(DEFAULT_MAX_PROGRESS)));
|
||||
if (json.progress > maxProgress) {
|
||||
this.status = RCONMessage.STATUS_ERROR;
|
||||
this.message = "progress must be between 1 and " + maxProgress;
|
||||
return;
|
||||
}
|
||||
|
||||
Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(json.user_id);
|
||||
|
||||
if (habbo != null) {
|
||||
@@ -28,14 +37,27 @@ public class ProgressAchievement extends RCONMessage<ProgressAchievement.Progres
|
||||
}
|
||||
}
|
||||
|
||||
static int parseMaxProgress(String configured) {
|
||||
try {
|
||||
int parsed = Integer.parseInt(configured);
|
||||
if (parsed > 0) {
|
||||
return parsed;
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
|
||||
return DEFAULT_MAX_PROGRESS;
|
||||
}
|
||||
|
||||
static class ProgressAchievementJSON {
|
||||
|
||||
@Positive(message = "invalid user")
|
||||
public int user_id;
|
||||
|
||||
|
||||
@Positive(message = "invalid achievement")
|
||||
public int achievement_id;
|
||||
|
||||
|
||||
@Positive(message = "invalid progress")
|
||||
public int progress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.eu.habbo.messages.rcon;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class MuteUserGuardTest {
|
||||
@Test
|
||||
void parsesInvalidDurationCeilingsAsDefault() {
|
||||
assertEquals(MuteUser.DEFAULT_MAX_DURATION_SECONDS, MuteUser.parseMaxDuration(null));
|
||||
assertEquals(MuteUser.DEFAULT_MAX_DURATION_SECONDS, MuteUser.parseMaxDuration("-1"));
|
||||
assertEquals(0, MuteUser.parseMaxDuration("0"));
|
||||
assertEquals(60, MuteUser.parseMaxDuration("60"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsNegativeAndOversizedMuteDurations() throws Exception {
|
||||
String source = Files.readString(Path.of("src/main/java/com/eu/habbo/messages/rcon/MuteUser.java"));
|
||||
|
||||
assertTrue(source.contains("json.duration < 0 || json.duration > maxDuration"),
|
||||
"RCON mute must reject negative durations and configured-duration overflows");
|
||||
assertTrue(source.contains("json.duration == 0 ? 0 : Emulator.getIntUnixTimestamp() + json.duration"),
|
||||
"Offline unmute must clear mute_end_timestamp instead of writing the current timestamp");
|
||||
assertTrue(source.contains("rcon.mute.max_duration_seconds"),
|
||||
"RCON mute duration ceiling must be configurable");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.eu.habbo.messages.rcon;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ProgressAchievementGuardTest {
|
||||
@Test
|
||||
void parsesInvalidProgressCeilingsAsDefault() {
|
||||
assertEquals(ProgressAchievement.DEFAULT_MAX_PROGRESS, ProgressAchievement.parseMaxProgress(null));
|
||||
assertEquals(ProgressAchievement.DEFAULT_MAX_PROGRESS, ProgressAchievement.parseMaxProgress("0"));
|
||||
assertEquals(50, ProgressAchievement.parseMaxProgress("50"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void validatesAchievementProgressPayload() throws Exception {
|
||||
String source = Files.readString(Path.of("src/main/java/com/eu/habbo/messages/rcon/ProgressAchievement.java"));
|
||||
|
||||
assertTrue(source.contains("@Positive(message = \"invalid user\")"),
|
||||
"RCON achievement progress must reject invalid target users before execution");
|
||||
assertTrue(source.contains("@Positive(message = \"invalid achievement\")"),
|
||||
"RCON achievement progress must reject invalid achievement ids before execution");
|
||||
assertTrue(source.contains("@Positive(message = \"invalid progress\")"),
|
||||
"RCON achievement progress must reject zero or negative progress before execution");
|
||||
assertTrue(source.contains("json.progress > maxProgress"),
|
||||
"RCON achievement progress must reject configured-progress overflows");
|
||||
assertTrue(source.contains("rcon.achievement.max_progress"),
|
||||
"RCON achievement progress ceiling must be configurable");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user