From d75b74f9f95600a38de725592f8fccf3d51512a4 Mon Sep 17 00:00:00 2001 From: NotEvil Date: Thu, 16 Apr 2026 10:49:04 +0200 Subject: [PATCH] fix(P0): 3 exploit fixes from swarm review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RISK-006: Mittens bypass lock/unlock — add HANDS check in PacketV2SelfLock/Unlock - RISK-002: Struggle re-roll exploit — reject-if-active in startContinuous*Session() - RISK-003: Non-V2 locked items bypass conflict resolution — check ILockable before swap --- .../remake/minigame/StruggleSessionManager.java | 14 +++++++++----- .../remake/v2/bondage/V2EquipmentManager.java | 10 +++++++++- .../v2/bondage/network/PacketV2SelfLock.java | 5 +++-- .../v2/bondage/network/PacketV2SelfUnlock.java | 5 +++-- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/tiedup/remake/minigame/StruggleSessionManager.java b/src/main/java/com/tiedup/remake/minigame/StruggleSessionManager.java index 9ecdb06..654ed9a 100644 --- a/src/main/java/com/tiedup/remake/minigame/StruggleSessionManager.java +++ b/src/main/java/com/tiedup/remake/minigame/StruggleSessionManager.java @@ -83,16 +83,16 @@ public class StruggleSessionManager { ) { UUID playerId = player.getUUID(); - // Remove any existing continuous session + // RISK-002 fix: reject if active session exists (prevents direction re-roll exploit) ContinuousStruggleMiniGameState existing = continuousSessions.get( playerId ); if (existing != null) { TiedUpMod.LOGGER.debug( - "[StruggleSessionManager] Replacing existing continuous struggle session for {}", + "[StruggleSessionManager] Rejected continuous session: active session already exists for {}", player.getName().getString() ); - continuousSessions.remove(playerId); + return null; } // Create new session with configurable rate @@ -146,12 +146,16 @@ public class StruggleSessionManager { ) { UUID playerId = player.getUUID(); - // Remove any existing session + // RISK-002 fix: reject if active session exists (prevents direction re-roll exploit) ContinuousStruggleMiniGameState existing = continuousSessions.get( playerId ); if (existing != null) { - continuousSessions.remove(playerId); + TiedUpMod.LOGGER.debug( + "[StruggleSessionManager] Rejected accessory session: active session already exists for {}", + player.getName().getString() + ); + return null; } // Create new session with target slot and configurable rate diff --git a/src/main/java/com/tiedup/remake/v2/bondage/V2EquipmentManager.java b/src/main/java/com/tiedup/remake/v2/bondage/V2EquipmentManager.java index 8567a47..1f98cb6 100644 --- a/src/main/java/com/tiedup/remake/v2/bondage/V2EquipmentManager.java +++ b/src/main/java/com/tiedup/remake/v2/bondage/V2EquipmentManager.java @@ -1,6 +1,7 @@ package com.tiedup.remake.v2.bondage; import com.tiedup.remake.core.TiedUpMod; +import com.tiedup.remake.items.base.ILockable; import com.tiedup.remake.v2.BodyRegionV2; import java.util.ArrayList; import java.util.IdentityHashMap; @@ -149,7 +150,14 @@ public final class V2EquipmentManager { return V2EquipResult.swapped(conflictStack); } } else { - // Non-V2 item in region — log warning and remove + // Non-V2 item in region — RISK-003 fix: check if locked before removing + if (conflictStack.getItem() instanceof ILockable lockable && lockable.isLocked(conflictStack)) { + TiedUpMod.LOGGER.warn( + "V2EquipmentManager: blocked swap of locked non-V2 item {} from equipment", + conflictStack + ); + return V2EquipResult.BLOCKED; + } TiedUpMod.LOGGER.warn( "V2EquipmentManager: swapping out non-V2 item {} from equipment", conflictStack diff --git a/src/main/java/com/tiedup/remake/v2/bondage/network/PacketV2SelfLock.java b/src/main/java/com/tiedup/remake/v2/bondage/network/PacketV2SelfLock.java index c59ee11..b9cdfd7 100644 --- a/src/main/java/com/tiedup/remake/v2/bondage/network/PacketV2SelfLock.java +++ b/src/main/java/com/tiedup/remake/v2/bondage/network/PacketV2SelfLock.java @@ -41,9 +41,10 @@ public class PacketV2SelfLock { if (player == null) return; if (!PacketRateLimiter.allowPacket(player, "action")) return; - // Arms must be free to self-lock + // Arms and hands must be free to self-lock (RISK-006: mittens bypass) if ( - V2EquipmentHelper.isRegionOccupied(player, BodyRegionV2.ARMS) + V2EquipmentHelper.isRegionOccupied(player, BodyRegionV2.ARMS) || + V2EquipmentHelper.isRegionOccupied(player, BodyRegionV2.HANDS) ) return; ItemStack equipped = V2EquipmentHelper.getInRegion( diff --git a/src/main/java/com/tiedup/remake/v2/bondage/network/PacketV2SelfUnlock.java b/src/main/java/com/tiedup/remake/v2/bondage/network/PacketV2SelfUnlock.java index 79c5a0c..607ca69 100644 --- a/src/main/java/com/tiedup/remake/v2/bondage/network/PacketV2SelfUnlock.java +++ b/src/main/java/com/tiedup/remake/v2/bondage/network/PacketV2SelfUnlock.java @@ -42,9 +42,10 @@ public class PacketV2SelfUnlock { if (player == null) return; if (!PacketRateLimiter.allowPacket(player, "action")) return; - // Arms must be free to self-unlock + // Arms and hands must be free to self-unlock (RISK-006: mittens bypass) if ( - V2EquipmentHelper.isRegionOccupied(player, BodyRegionV2.ARMS) + V2EquipmentHelper.isRegionOccupied(player, BodyRegionV2.ARMS) || + V2EquipmentHelper.isRegionOccupied(player, BodyRegionV2.HANDS) ) return; ItemStack equipped = V2EquipmentHelper.getInRegion(