diff --git a/CHANGELOG.md b/CHANGELOG.md
index 85d31e64..9a3902b4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -29,6 +29,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin
* Correct rejection message in LandGolfBallInCupHandler to output the proper expected player id. (@drunderscore)
* Clarified the error mesage that the console is presented if a rate-limit is reached over REST to indicate that "tokens" actually refers to rate-limit tokens, and not auth tokens, and added a hint as to what config setting determines this. (@hakusaro, @patsore)
* Fixed an issue where, when the console was redirected, input was disabled and commands didn't work, in TSAPI. You can now pass `-disable-commands` to disable the input thread, but by default, it will be enabled. Fixes [#1450](https://github.com/Pryaxis/TShock/issues/1450). (@DeathCradle, @QuiCM)
+* Properly sanitize packet tile coordinates that coulbe used to DoS attack a server. This was assigned [GHSA-jq4j-v8pr-jv7j](https://github.com/Pryaxis/TShock/security/advisories/GHSA-jq4j-v8pr-jv7j). (@drunderscore)
## TShock 4.5.4
* Fixed ridiculous typo in `GetDataHandlers` which caused TShock to read the wrong field in the packet for `usingBiomeTorches`. (@hakusaro, @Arthri)
diff --git a/TShockAPI/Bouncer.cs b/TShockAPI/Bouncer.cs
index 42cc6cd1..60737a7f 100644
--- a/TShockAPI/Bouncer.cs
+++ b/TShockAPI/Bouncer.cs
@@ -260,6 +260,13 @@ namespace TShockAPI
try
{
+ if (!TShock.Utils.TilePlacementValid(tileX, tileY))
+ {
+ TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from (tile placement valid) {0} {1} {2}", args.Player.Name, action, editData);
+ args.Handled = true;
+ return;
+ }
+
if (editData < 0 ||
((action == EditAction.PlaceTile || action == EditAction.ReplaceTile) && editData >= Main.maxTileSets) ||
((action == EditAction.PlaceWall || action == EditAction.ReplaceWall) && editData >= Main.maxWallTypes))
@@ -270,14 +277,6 @@ namespace TShockAPI
return;
}
- if (!TShock.Utils.TilePlacementValid(tileX, tileY))
- {
- TShock.Log.ConsoleDebug("Bouncer / OnTileEdit rejected from (tile placement valid) {0} {1} {2}", args.Player.Name, action, editData);
- args.Player.SendTileSquare(tileX, tileY, 1);
- args.Handled = true;
- return;
- }
-
if (action == EditAction.KillTile && Main.tile[tileX, tileY].type == TileID.MagicalIceBlock)
{
TShock.Log.ConsoleDebug("Bouncer / OnTileEdit super accepted from (ice block) {0} {1} {2}", args.Player.Name, action, editData);
@@ -1654,6 +1653,13 @@ namespace TShockAPI
short type = args.Type;
short style = args.Style;
+ if (!TShock.Utils.TilePlacementValid(x, y))
+ {
+ TShock.Log.ConsoleDebug("Bouncer / OnPlaceObject rejected valid placements from {0}", args.Player.Name);
+ args.Handled = true;
+ return;
+ }
+
if (type < 0 || type >= Main.maxTileSets)
{
TShock.Log.ConsoleDebug("Bouncer / OnPlaceObject rejected out of bounds tile from {0}", args.Player.Name);
@@ -1702,14 +1708,6 @@ namespace TShockAPI
return;
}
- if (!TShock.Utils.TilePlacementValid(x, y))
- {
- TShock.Log.ConsoleDebug("Bouncer / OnPlaceObject rejected valid placements from {0}", args.Player.Name);
- args.Player.SendTileSquare(x, y, 1);
- args.Handled = true;
- return;
- }
-
if (args.Player.Dead && TShock.Config.Settings.PreventDeadModification)
{
TShock.Log.ConsoleDebug("Bouncer / OnPlaceObject rejected dead people don't do things from {0}", args.Player.Name);
@@ -1801,6 +1799,13 @@ namespace TShockAPI
/// The packet arguments that the event has.
internal void OnPlaceTileEntity(object sender, GetDataHandlers.PlaceTileEntityEventArgs args)
{
+ if (!TShock.Utils.TilePlacementValid(args.X, args.Y))
+ {
+ TShock.Log.ConsoleDebug("Bouncer / OnPlaceTileEntity rejected tile placement valid from {0}", args.Player.Name);
+ args.Handled = true;
+ return;
+ }
+
if (args.Player.IsBeingDisabled())
{
TShock.Log.ConsoleDebug("Bouncer / OnPlaceTileEntity rejected disabled from {0}", args.Player.Name);
@@ -1828,6 +1833,13 @@ namespace TShockAPI
/// The packet arguments that the event has.
internal void OnPlaceItemFrame(object sender, GetDataHandlers.PlaceItemFrameEventArgs args)
{
+ if (!TShock.Utils.TilePlacementValid(args.X, args.Y))
+ {
+ TShock.Log.ConsoleDebug("Bouncer / OnPlaceItemFrame rejected tile placement valid from {0}", args.Player.Name);
+ args.Handled = true;
+ return;
+ }
+
if (args.Player.IsBeingDisabled())
{
TShock.Log.ConsoleDebug("Bouncer / OnPlaceItemFrame rejected disabled from {0}", args.Player.Name);
@@ -2129,6 +2141,13 @@ namespace TShockAPI
///
internal void OnFoodPlatterTryPlacing(object sender, GetDataHandlers.FoodPlatterTryPlacingEventArgs args)
{
+ if (!TShock.Utils.TilePlacementValid(args.TileX, args.TileY))
+ {
+ TShock.Log.ConsoleDebug("Bouncer / OnFoodPlatterTryPlacing rejected tile placement valid from {0}", args.Player.Name);
+ args.Handled = true;
+ return;
+ }
+
if ((args.Player.SelectedItem.type != args.ItemID && args.Player.ItemInHand.type != args.ItemID))
{
TShock.Log.ConsoleDebug("Bouncer / OnFoodPlatterTryPlacing rejected item not placed by hand from {0}", args.Player.Name);