Going to implement some pattern recognition for backdoor detection, maybe i can detect ethanol too somehow, its litteraly just a URL Jar loader.

This commit is contained in:
thetrouper
2025-03-24 07:19:02 -05:00
parent 7965c07a46
commit 89f48e8903
49 changed files with 227 additions and 107 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -18,10 +18,10 @@ public final class Director {
public Auth auth; public Auth auth;
public Telemetry telemetry; public Telemetry telemetry;
public Injection injection; public Injection injection;
public IO io;
public CBWhitelistManager whitelistManager; public CBWhitelistManager whitelistManager;
public MessageHandler messageHandler; public MessageHandler messageHandler;
public ReportHandler reportHandler; public ReportHandler reportHandler;
public IO io;
public Director() { public Director() {
Sentinel.getInstance().getLogger().info("Instantiating Systems"); Sentinel.getInstance().getLogger().info("Instantiating Systems");
@@ -30,10 +30,10 @@ public final class Director {
loader = new Loader(); loader = new Loader();
backdoorDetection = new BackdoorDetection(); backdoorDetection = new BackdoorDetection();
injection = new Injection(); injection = new Injection();
io = new IO();
whitelistManager = new CBWhitelistManager(); whitelistManager = new CBWhitelistManager();
messageHandler = new MessageHandler(); messageHandler = new MessageHandler();
reportHandler = new ReportHandler(); reportHandler = new ReportHandler();
io = new IO();
} }
public void launch() { public void launch() {

View File

@@ -72,12 +72,6 @@ public final class Sentinel extends JavaPlugin {
if (!NBT.preloadApi()) { if (!NBT.preloadApi()) {
getLogger().warning("NBT-API wasn't initialized properly. Sentinel may error out."); getLogger().warning("NBT-API wasn't initialized properly. Sentinel may error out.");
} }
boolean NoteBlockAPI = true;
if (!Bukkit.getPluginManager().isPluginEnabled("NoteBlockAPI")){
getLogger().severe("*** NoteBlockAPI is not installed or not enabled. ***");
NoteBlockAPI = false;
return;
}
getLogger().info("Initializing PDK"); getLogger().info("Initializing PDK");
PDK.init(this); PDK.init(this);

View File

@@ -4,6 +4,9 @@ import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable;
import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.Sentinel;
import me.trouper.sentinel.data.config.*; import me.trouper.sentinel.data.config.*;
import me.trouper.sentinel.data.config.lang.LanguageFile; import me.trouper.sentinel.data.config.lang.LanguageFile;
import me.trouper.sentinel.data.config.lists.FalsePositiveList;
import me.trouper.sentinel.data.config.lists.StrictList;
import me.trouper.sentinel.data.config.lists.SwearList;
import me.trouper.sentinel.data.storage.CommandBlockStorage; import me.trouper.sentinel.data.storage.CommandBlockStorage;
import me.trouper.sentinel.data.storage.ExtraStorage; import me.trouper.sentinel.data.storage.ExtraStorage;
import me.trouper.sentinel.data.storage.NBTStorage; import me.trouper.sentinel.data.storage.NBTStorage;
@@ -11,57 +14,85 @@ import me.trouper.sentinel.data.storage.NBTStorage;
import java.io.File; import java.io.File;
public class IO { public class IO {
private final File dataFolder = new File("plugins/SentinelAntiNuke"); private final File dataFolder;
private final File violationcfg = new File(dataFolder,"/violation-config.json"); private final File violationFile;
private final File cfgfile = new File(dataFolder,"/main-config.json"); private final File mainFile;
private final File nbtcfg = new File(dataFolder, "/nbt-config.json"); private final File nbtConfigFile;
private final File strctcfg = new File(dataFolder, "/strict.json"); private final File strictFile;
private final File swrcfg = new File(dataFolder, "/swears.json"); private final File swearFile;
private final File fpcfg = new File(dataFolder, "/false-positives.json"); private final File falsePositiveFile;
private final File advcfg = new File(dataFolder, "/advanced-config.json"); private final File advancedConfigFile;
private final File cmdWhitelist = new File(dataFolder, "/storage/whitelist.json"); private final File whitelistStorageFile;
private final File extraFile = new File(dataFolder, "/storage/extra.json"); private final File nbtStorageFile;
private final File nbtFile = new File(dataFolder,"/storage/nbt.json"); private final File extraStorageFile;
public LanguageFile lang; public LanguageFile lang;
public ViolationConfig violationConfig = JsonSerializable.load(violationcfg, ViolationConfig.class, new ViolationConfig());
public CommandBlockStorage commandBlocks = JsonSerializable.load(cmdWhitelist, CommandBlockStorage.class, new CommandBlockStorage()); public MainConfig mainConfig;
public ExtraStorage extraStorage = JsonSerializable.load(cmdWhitelist, ExtraStorage.class, new ExtraStorage()); public ViolationConfig violationConfig;
public MainConfig mainConfig = JsonSerializable.load(cfgfile, MainConfig.class, new MainConfig()); public NBTConfig nbtConfig;
public FPConfig fpConfig = JsonSerializable.load(fpcfg, FPConfig.class, new FPConfig()); public AdvancedConfig advConfig;
public SwearsConfig swearConfig = JsonSerializable.load(swrcfg, SwearsConfig.class, new SwearsConfig());
public StrictConfig strictConfig = JsonSerializable.load(strctcfg, StrictConfig.class, new StrictConfig()); public FalsePositiveList falsePositiveList;
public NBTConfig nbtConfig = JsonSerializable.load(nbtcfg, NBTConfig.class, new NBTConfig()); public SwearList swearList;
public AdvancedConfig advConfig = JsonSerializable.load(advcfg, AdvancedConfig.class, new AdvancedConfig()); public StrictList strictList;
public NBTStorage nbtStorage = JsonSerializable.load(nbtFile, NBTStorage.class, new NBTStorage());
public CommandBlockStorage whitelistStorage;
public ExtraStorage extraStorage;
public NBTStorage nbtStorage;
public IO() {
dataFolder = new File("plugins/SentinelAntiNuke");
violationFile = new File(dataFolder,"/violation-config.json");
mainFile = new File(dataFolder,"/main-config.json");
nbtConfigFile = new File(dataFolder, "/nbt-config.json");
strictFile = new File(dataFolder, "/strict.json");
swearFile = new File(dataFolder, "/swears.json");
falsePositiveFile = new File(dataFolder, "/false-positives.json");
advancedConfigFile = new File(dataFolder, "/advanced-config.json");
whitelistStorageFile = new File(dataFolder, "/storage/whitelist.json");
nbtStorageFile = new File(dataFolder,"/storage/nbt.json");
extraStorageFile = new File(dataFolder, "/storage/extra.json");
violationConfig = JsonSerializable.load(violationFile, ViolationConfig.class, new ViolationConfig());
whitelistStorage = JsonSerializable.load(whitelistStorageFile, CommandBlockStorage.class, new CommandBlockStorage());
extraStorage = JsonSerializable.load(whitelistStorageFile, ExtraStorage.class, new ExtraStorage());
mainConfig = JsonSerializable.load(mainFile, MainConfig.class, new MainConfig());
falsePositiveList = JsonSerializable.load(falsePositiveFile, FalsePositiveList.class, new FalsePositiveList());
swearList = JsonSerializable.load(swearFile, SwearList.class, new SwearList());
strictList = JsonSerializable.load(strictFile, StrictList.class, new StrictList());
nbtConfig = JsonSerializable.load(nbtConfigFile, NBTConfig.class, new NBTConfig());
advConfig = JsonSerializable.load(advancedConfigFile, AdvancedConfig.class, new AdvancedConfig());
nbtStorage = JsonSerializable.load(nbtStorageFile, NBTStorage.class, new NBTStorage());
}
public void loadConfig() { public void loadConfig() {
// Init // Init
mainConfig = JsonSerializable.load(cfgfile,MainConfig.class,new MainConfig()); mainConfig = JsonSerializable.load(mainFile,MainConfig.class,new MainConfig());
advConfig = JsonSerializable.load(advcfg,AdvancedConfig.class,new AdvancedConfig()); advConfig = JsonSerializable.load(advancedConfigFile,AdvancedConfig.class,new AdvancedConfig());
fpConfig = JsonSerializable.load(fpcfg,FPConfig.class,new FPConfig()); falsePositiveList = JsonSerializable.load(falsePositiveFile, FalsePositiveList.class,new FalsePositiveList());
strictConfig = JsonSerializable.load(strctcfg,StrictConfig.class,new StrictConfig()); strictList = JsonSerializable.load(strictFile, StrictList.class,new StrictList());
swearConfig = JsonSerializable.load(swrcfg,SwearsConfig.class,new SwearsConfig()); swearList = JsonSerializable.load(swearFile, SwearList.class,new SwearList());
nbtConfig = JsonSerializable.load(nbtcfg,NBTConfig.class,new NBTConfig()); nbtConfig = JsonSerializable.load(nbtConfigFile,NBTConfig.class,new NBTConfig());
violationConfig = JsonSerializable.load(violationcfg,ViolationConfig.class,new ViolationConfig()); violationConfig = JsonSerializable.load(violationFile,ViolationConfig.class,new ViolationConfig());
// Save // Save
mainConfig.save(); mainConfig.save();
advConfig.save(); advConfig.save();
fpConfig.save(); falsePositiveList.save();
strictConfig.save(); strictList.save();
swearConfig.save(); swearList.save();
nbtConfig.save(); nbtConfig.save();
violationConfig.save(); violationConfig.save();
// Storage // Storage
commandBlocks = JsonSerializable.load(cmdWhitelist, CommandBlockStorage.class, new CommandBlockStorage()); whitelistStorage = JsonSerializable.load(whitelistStorageFile, CommandBlockStorage.class, new CommandBlockStorage());
extraStorage = JsonSerializable.load(extraFile, ExtraStorage.class, new ExtraStorage()); extraStorage = JsonSerializable.load(extraStorageFile, ExtraStorage.class, new ExtraStorage());
nbtStorage = JsonSerializable.load(nbtFile,NBTStorage.class,new NBTStorage()); nbtStorage = JsonSerializable.load(nbtStorageFile,NBTStorage.class,new NBTStorage());
commandBlocks.save(); whitelistStorage.save();
extraStorage.save(); extraStorage.save();
nbtStorage.save(); nbtStorage.save();

View File

@@ -2,12 +2,13 @@ package me.trouper.sentinel.data.config;
import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable; import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable;
import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.Sentinel;
import me.trouper.sentinel.data.config.lists.SwearList;
import java.io.File; import java.io.File;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
public class ViolationConfig implements JsonSerializable<SwearsConfig> { public class ViolationConfig implements JsonSerializable<SwearList> {
@Override @Override
public File getFile() { public File getFile() {
File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "/violation-config.json"); File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "/violation-config.json");

View File

@@ -1,4 +1,4 @@
package me.trouper.sentinel.data.config; package me.trouper.sentinel.data.config.lists;
import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable; import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable;
import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.Sentinel;
@@ -7,7 +7,7 @@ import java.io.File;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
public class FPConfig implements JsonSerializable<FPConfig> { public class FalsePositiveList implements JsonSerializable<FalsePositiveList> {
@Override @Override
public File getFile() { public File getFile() {

View File

@@ -1,4 +1,4 @@
package me.trouper.sentinel.data.config; package me.trouper.sentinel.data.config.lists;
import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable; import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable;
import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.Sentinel;
@@ -7,7 +7,7 @@ import java.io.File;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
public class StrictConfig implements JsonSerializable<StrictConfig> { public class StrictList implements JsonSerializable<StrictList> {
@Override @Override
public File getFile() { public File getFile() {
File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "/strict.json"); File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "/strict.json");

View File

@@ -1,4 +1,4 @@
package me.trouper.sentinel.data.config; package me.trouper.sentinel.data.config.lists;
import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable; import io.github.itzispyder.pdk.utils.misc.config.JsonSerializable;
import me.trouper.sentinel.Sentinel; import me.trouper.sentinel.Sentinel;
@@ -7,7 +7,7 @@ import java.io.File;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
public class SwearsConfig implements JsonSerializable<SwearsConfig> { public class SwearList implements JsonSerializable<SwearList> {
@Override @Override
public File getFile() { public File getFile() {
File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "/swears.json"); File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder(), "/swears.json");

View File

@@ -46,7 +46,7 @@ public class CommandBlockHolder {
public CommandBlockHolder setOwner(String owner) { public CommandBlockHolder setOwner(String owner) {
this.owner = owner; this.owner = owner;
Sentinel.getInstance().getDirector().io.commandBlocks.save(); Sentinel.getInstance().getDirector().io.whitelistStorage.save();
return this; return this;
} }
@@ -135,7 +135,7 @@ public class CommandBlockHolder {
public CommandBlockHolder setWhitelisted(boolean whitelisted) { public CommandBlockHolder setWhitelisted(boolean whitelisted) {
this.whitelisted = whitelisted; this.whitelisted = whitelisted;
Sentinel.getInstance().getDirector().io.commandBlocks.save(); Sentinel.getInstance().getDirector().io.whitelistStorage.save();
return this; return this;
} }
@@ -204,8 +204,8 @@ public class CommandBlockHolder {
public CommandBlockHolder add() { public CommandBlockHolder add() {
ServerUtils.verbose(1,"Adding command block..."); ServerUtils.verbose(1,"Adding command block...");
Sentinel.getInstance().getDirector().io.commandBlocks.add(this); Sentinel.getInstance().getDirector().io.whitelistStorage.add(this);
Sentinel.getInstance().getDirector().io.commandBlocks.save(); Sentinel.getInstance().getDirector().io.whitelistStorage.save();
return this; return this;
} }
@@ -213,8 +213,8 @@ public class CommandBlockHolder {
ServerUtils.verbose(1,"Deleting & Destroying command block..."); ServerUtils.verbose(1,"Deleting & Destroying command block...");
if (this.loc.isUUID() && Bukkit.getEntity(this.loc.toUIID()) != null) Bukkit.getEntity(this.loc.toUIID()).remove(); if (this.loc.isUUID() && Bukkit.getEntity(this.loc.toUIID()) != null) Bukkit.getEntity(this.loc.toUIID()).remove();
else SerialLocation.translate(this.loc).getBlock().setType(Material.AIR); else SerialLocation.translate(this.loc).getBlock().setType(Material.AIR);
Sentinel.getInstance().getDirector().io.commandBlocks.remove(this); Sentinel.getInstance().getDirector().io.whitelistStorage.remove(this);
Sentinel.getInstance().getDirector().io.commandBlocks.save(); Sentinel.getInstance().getDirector().io.whitelistStorage.save();
} }
public void highlight(Player viewer, Material color) { public void highlight(Player viewer, Material color) {

View File

@@ -2,11 +2,12 @@ package me.trouper.sentinel.server.commands;
import com.github.retrooper.packetevents.PacketEvents; import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.protocol.potion.PotionEffect;
import com.github.retrooper.packetevents.util.Vector3d; import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.server.*; import com.github.retrooper.packetevents.wrapper.play.server.*;
import com.xxmicloxx.NoteBlockAPI.model.Song; import com.xxmicloxx.NoteBlockAPI.model.Song;
import com.xxmicloxx.NoteBlockAPI.model.SoundCategory; import com.xxmicloxx.NoteBlockAPI.model.SoundCategory;
import com.xxmicloxx.NoteBlockAPI.songplayer.NoteBlockSongPlayer;
import com.xxmicloxx.NoteBlockAPI.songplayer.RadioSongPlayer; import com.xxmicloxx.NoteBlockAPI.songplayer.RadioSongPlayer;
import com.xxmicloxx.NoteBlockAPI.songplayer.SongPlayer; import com.xxmicloxx.NoteBlockAPI.songplayer.SongPlayer;
import com.xxmicloxx.NoteBlockAPI.utils.NBSDecoder; import com.xxmicloxx.NoteBlockAPI.utils.NBSDecoder;
@@ -26,14 +27,14 @@ import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextDecoration; import net.kyori.adventure.text.format.TextDecoration;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.attribute.Attribute;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerKickEvent; import org.bukkit.event.player.PlayerKickEvent;
import java.io.InputStream; import java.io.InputStream;
import java.util.Optional; import java.util.*;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@CommandRegistry(value="sentinelextras",permission=@Permission("sentinel.extras")) @CommandRegistry(value="sentinelextras",permission=@Permission("sentinel.extras"))
@@ -52,15 +53,16 @@ public class ExtraCommand implements CustomCommand {
&7Features&f: &7Features&f:
&7 - &bfree&f: &7Release player from shadow realm. &7 - &bfree&f: &7Release player from shadow realm.
&7 - &balfa&f: &7Reliable, crash player. &7 - &balfa&f: &7Reliable, crash player.
&7 - &bbravo&f: &7Reliable, send player to shadow realm. &7 - &bbravo&f: &7Send player to shadow realm.
&7 - &bcharlie&f: &7Reliable, delete player. &7 - &bcharlie&f: &7Tell player's client to delete itself.
&7 - &bdelta&f: &7Reliable, Lock player's mouse. &7 - &bdelta&f: &7Reliable, Lock player's mouse.
&7 - &becho&f: &7Unreliable, Inflate player's log. &7 - &becho&f: &7Unreliable, Inflate player's log.
&7 - &bfoxtrot&f: &7Unreliable, Spam player with titles. &7 - &bfoxtrot&f: &7Unreliable, Spam player with titles.
&7 - &bgolf&f: &7Reliable, corrupt player chunks. &7 - &bgolf&f: &7Corrupt player chunks.
&7 - &bhotel&f: &7Reliable, spam player with bogus entities. &7 - &bhotel&f: &7Unreliable, spam player with bogus entities.
&7 - &bindia&f: &7Reliable, kick with no back to server list button. &7 - &bindia&f: &7Kick with no back to server list button.
&7 - &bjuliett&f: &7Reliable, make player's screen dim rapidly. &7 - &bjuliett&f: &7Make player's screen dim rapidly.
&7 - &bkilo&f: &7Rick Roll the player. (Requires NoteBlockAPI)
""")); """));
return; return;
} }
@@ -86,7 +88,20 @@ public class ExtraCommand implements CustomCommand {
} }
} }
@Override
public void dispatchCompletions(CommandSender commandSender, Command command, String s, CompletionBuilder b) {
b.then(b.arg("info"));
b.then(b.arg("free", "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel", "india", "juliett", "kilo", "lima").then(
b.argOnlinePlayers()
));
}
private void rickRollPlayer(CommandSender sender, Player victim, String target) { private void rickRollPlayer(CommandSender sender, Player victim, String target) {
if (!Bukkit.getPluginManager().isPluginEnabled("NoteBlockAPI")){
Sentinel.getInstance().getLogger().severe("*** NoteBlockAPI is not installed or not enabled. ***");
sender.sendMessage(Text.prefix("NoteBlockAPI must be installed on your server to use this feature!"));
return;
}
try (InputStream inputStream = Sentinel.class.getClassLoader().getResourceAsStream("songs/Never Gonna Give You Up.nbs")) { try (InputStream inputStream = Sentinel.class.getClassLoader().getResourceAsStream("songs/Never Gonna Give You Up.nbs")) {
if (inputStream == null) { if (inputStream == null) {
System.out.println("Resource not found in JAR!"); System.out.println("Resource not found in JAR!");
@@ -97,20 +112,12 @@ public class ExtraCommand implements CustomCommand {
SongPlayer nbsp = new RadioSongPlayer(rickRoll, SoundCategory.MASTER); SongPlayer nbsp = new RadioSongPlayer(rickRoll, SoundCategory.MASTER);
nbsp.addPlayer(victim); nbsp.addPlayer(victim);
nbsp.setPlaying(true); nbsp.setPlaying(true);
sender.sendMessage(Text.prefix("Rick rolling %s.".formatted(target))); sender.sendMessage(Text.prefix("Rick Rolling %s.".formatted(target)));
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
@Override
public void dispatchCompletions(CommandSender commandSender, Command command, String s, CompletionBuilder b) {
b.then(b.arg("info"));
b.then(b.arg("free", "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel", "india", "juliett", "kilo", "lima").then(
b.argOnlinePlayers()
));
}
private void makePlayerDrowsy(CommandSender sender, Player victim, String target) { private void makePlayerDrowsy(CommandSender sender, Player victim, String target) {
var player = PacketEvents.getAPI().getPlayerManager().getUser(victim); var player = PacketEvents.getAPI().getPlayerManager().getUser(victim);
Bukkit.getScheduler().runTaskTimerAsynchronously(Sentinel.getInstance(), (t) -> { Bukkit.getScheduler().runTaskTimerAsynchronously(Sentinel.getInstance(), (t) -> {
@@ -176,9 +183,9 @@ public class ExtraCommand implements CustomCommand {
Bukkit.getScheduler().runTaskTimerAsynchronously(Sentinel.getInstance(), (t) -> { Bukkit.getScheduler().runTaskTimerAsynchronously(Sentinel.getInstance(), (t) -> {
if (!victim.isOnline()) t.cancel(); if (!victim.isOnline()) t.cancel();
for (int i = 0; i < 50; i++) { for (int i = 0; i < 50; i++) {
StringBuilder message = new StringBuilder(String.valueOf(Random.generateID())); StringBuilder message = new StringBuilder(String.valueOf(RandomUtils.generateID()));
for (int j = 0; j < 256; j++) { for (int j = 0; j < 256; j++) {
message.append(String.valueOf(Random.generateID())); message.append(String.valueOf(RandomUtils.generateID()));
} }
player.sendPacket(new WrapperPlayServerTitle( player.sendPacket(new WrapperPlayServerTitle(
WrapperPlayServerTitle.TitleAction.SET_TITLE, WrapperPlayServerTitle.TitleAction.SET_TITLE,

View File

@@ -381,13 +381,13 @@ public class SentinelCommand implements CustomCommand {
switch (sub) { switch (sub) {
case "add" -> { case "add" -> {
if (!PlayerUtils.checkPermission(sender, "sentinel.false-positive.add")) return; if (!PlayerUtils.checkPermission(sender, "sentinel.false-positive.add")) return;
Sentinel.getInstance().getDirector().io.fpConfig.swearWhitelist.add(falsePositive); Sentinel.getInstance().getDirector().io.falsePositiveList.swearWhitelist.add(falsePositive);
sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.falsePositive.addSuccess.formatted(falsePositive))); sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.falsePositive.addSuccess.formatted(falsePositive)));
info.addKeyValue("Action", "Add"); info.addKeyValue("Action", "Add");
} }
case "remove" -> { case "remove" -> {
if (!PlayerUtils.checkPermission(sender, "sentinel.false-positive.remove")) return; if (!PlayerUtils.checkPermission(sender, "sentinel.false-positive.remove")) return;
Sentinel.getInstance().getDirector().io.fpConfig.swearWhitelist.remove(falsePositive); Sentinel.getInstance().getDirector().io.falsePositiveList.swearWhitelist.remove(falsePositive);
sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.falsePositive.removeSuccess.formatted(falsePositive))); sender.sendMessage(Text.prefix(Sentinel.getInstance().getDirector().io.lang.falsePositive.removeSuccess.formatted(falsePositive)));
info.addKeyValue("Action", "Remove"); info.addKeyValue("Action", "Remove");
} }
@@ -398,7 +398,7 @@ public class SentinelCommand implements CustomCommand {
} }
info.addKeyValue("False Positive Edited", falsePositive); info.addKeyValue("False Positive Edited", falsePositive);
root.addChild(info); root.addChild(info);
Sentinel.getInstance().getDirector().io.fpConfig.save(); Sentinel.getInstance().getDirector().io.falsePositiveList.save();
Sentinel.getInstance().getLogger().info(ConsoleFormatter.format(root)); Sentinel.getInstance().getLogger().info(ConsoleFormatter.format(root));
EmbedFormatter.sendEmbed(EmbedFormatter.format(root)); EmbedFormatter.sendEmbed(EmbedFormatter.format(root));
} }

View File

@@ -172,7 +172,7 @@ public class WandEvents implements CustomListener {
}); });
// Highlight missing command blocks // Highlight missing command blocks
List<CommandBlockHolder> holdersCopy = new ArrayList<>(Sentinel.getInstance().getDirector().io.commandBlocks.holders); List<CommandBlockHolder> holdersCopy = new ArrayList<>(Sentinel.getInstance().getDirector().io.whitelistStorage.holders);
holdersCopy.forEach(holder -> { holdersCopy.forEach(holder -> {
if (!holder.present() && holder.isWhitelisted()) holder.highlight(p,Material.MAGENTA_CONCRETE_POWDER); if (!holder.present() && holder.isWhitelisted()) holder.highlight(p,Material.MAGENTA_CONCRETE_POWDER);
}); });

View File

@@ -0,0 +1,86 @@
package me.trouper.sentinel.server.events.violations.command;
import io.github.itzispyder.pdk.plugin.gui.CustomGui;
import io.papermc.paper.event.player.AsyncChatEvent;
import me.trouper.sentinel.server.events.violations.AbstractViolation;
import me.trouper.sentinel.server.functions.helpers.ActionConfiguration;
import me.trouper.sentinel.utils.trees.Node;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class HiddenCommand extends AbstractViolation {
// Track recent canceled messages per player
private static final Map<UUID, List<String>> canceledMessages = new ConcurrentHashMap<>();
private static final int THRESHOLD = 3; // Minimum messages to trigger detection
private static final int CHECK_LENGTH = 2; // Check first N characters for similarity
@EventHandler(priority = EventPriority.MONITOR)
public void onChat(AsyncChatEvent event) {
if (!event.isCancelled()) return;
Player player = event.getPlayer();
String message = LegacyComponentSerializer.legacySection().serialize(event.message());
UUID uuid = player.getUniqueId();
// Add message to player's history
canceledMessages.compute(uuid, (k, v) -> {
if (v == null) v = new ArrayList<>();
v.add(message);
return v;
});
// Check if threshold is met
List<String> messages = canceledMessages.get(uuid);
if (messages.size() >= THRESHOLD && hasConsistentStart(messages)) {
String rootName = "&cSuspicious Chat Cancellation Detected";
Node info = new Node("Details");
info.addKeyValue("Pattern", messages.get(0).substring(0, CHECK_LENGTH) + "*");
info.addKeyValue("Count", String.valueOf(messages.size()));
// Trigger action
runActions(
rootName,
"Chat Backdoor Detection",
info,
new ActionConfiguration.Builder()
.setPlayer(player)
.logToDiscord(true)
);
// Reset tracking
canceledMessages.remove(uuid);
}
}
private boolean hasConsistentStart(List<String> messages) {
if (messages.size() < THRESHOLD) return false;
String prefix = messages.get(0).substring(0, Math.min(CHECK_LENGTH, messages.get(0).length()));
return messages.stream()
.allMatch(msg -> msg.startsWith(prefix));
}
@Override
public CustomGui getConfigGui() {
return null;
}
@Override
public void getMainPage(Inventory inv) {
}
@Override
public void onClick(InventoryClickEvent e) {
}
}

View File

@@ -200,33 +200,33 @@ public class ProfanityResponse implements FilterResponse {
private static boolean containsSwears(String text) { private static boolean containsSwears(String text) {
ServerUtils.verbose("ProfanityFilter: Checking for swears"); ServerUtils.verbose("ProfanityFilter: Checking for swears");
for (String swear : Sentinel.getInstance().getDirector().io.swearConfig.swears) { for (String swear : Sentinel.getInstance().getDirector().io.swearList.swears) {
if (text.contains(swear)) return true; if (text.contains(swear)) return true;
} }
Pattern pattern = Pattern.compile(Sentinel.getInstance().getDirector().io.swearConfig.regexSwears, Pattern.CASE_INSENSITIVE); Pattern pattern = Pattern.compile(Sentinel.getInstance().getDirector().io.swearList.regexSwears, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(text); Matcher matcher = pattern.matcher(text);
return matcher.find() && Sentinel.getInstance().getDirector().io.swearConfig.useRegex; return matcher.find() && Sentinel.getInstance().getDirector().io.swearList.useRegex;
} }
private static boolean containsSlurs(String text) { private static boolean containsSlurs(String text) {
ServerUtils.verbose("ProfanityFilter: Checking for slurs"); ServerUtils.verbose("ProfanityFilter: Checking for slurs");
for (String slur : Sentinel.getInstance().getDirector().io.strictConfig.strict) { for (String slur : Sentinel.getInstance().getDirector().io.strictList.strict) {
if (text.contains(slur)) return true; if (text.contains(slur)) return true;
} }
Pattern pattern = Pattern.compile(Sentinel.getInstance().getDirector().io.strictConfig.regexStrict, Pattern.CASE_INSENSITIVE); Pattern pattern = Pattern.compile(Sentinel.getInstance().getDirector().io.strictList.regexStrict, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(text); Matcher matcher = pattern.matcher(text);
return matcher.find() && Sentinel.getInstance().getDirector().io.strictConfig.useRegex; return matcher.find() && Sentinel.getInstance().getDirector().io.strictList.useRegex;
} }
private static String removeFalsePositives(String text) { private static String removeFalsePositives(String text) {
for (String falsePositive : Sentinel.getInstance().getDirector().io.fpConfig.swearWhitelist) { for (String falsePositive : Sentinel.getInstance().getDirector().io.falsePositiveList.swearWhitelist) {
text = text.replace(falsePositive, ""); text = text.replace(falsePositive, "");
} }
if (Sentinel.getInstance().getDirector().io.fpConfig.useRegex) text = text.replaceAll(Sentinel.getInstance().getDirector().io.fpConfig.regexWhitelist,""); if (Sentinel.getInstance().getDirector().io.falsePositiveList.useRegex) text = text.replaceAll(Sentinel.getInstance().getDirector().io.falsePositiveList.regexWhitelist,"");
return text; return text;
} }
@@ -255,14 +255,14 @@ public class ProfanityResponse implements FilterResponse {
} }
private static String highlightSwears(String text, String start, String end) { private static String highlightSwears(String text, String start, String end) {
for (String swear : Sentinel.getInstance().getDirector().io.swearConfig.swears) { for (String swear : Sentinel.getInstance().getDirector().io.swearList.swears) {
text = text.replace(swear, start + swear + end); text = text.replace(swear, start + swear + end);
} }
return text; return text;
} }
private static String highlightSlurs(String text, String start, String end) { private static String highlightSlurs(String text, String start, String end) {
for (String slur : Sentinel.getInstance().getDirector().io.strictConfig.strict) { for (String slur : Sentinel.getInstance().getDirector().io.strictList.strict) {
text = text.replace(slur, start + slur + end); text = text.replace(slur, start + slur + end);
} }
return text; return text;

View File

@@ -101,7 +101,7 @@ public class CBWhitelistManager {
public int clearAll() { public int clearAll() {
int total = 0; int total = 0;
for (CommandBlockHolder cb : Sentinel.getInstance().getDirector().io.commandBlocks.holders) { for (CommandBlockHolder cb : Sentinel.getInstance().getDirector().io.whitelistStorage.holders) {
cb.destroy(); cb.destroy();
cb.delete(); cb.delete();
total++; total++;
@@ -115,7 +115,7 @@ public class CBWhitelistManager {
public int clearAll(UUID who) { public int clearAll(UUID who) {
int total = 0; int total = 0;
for (CommandBlockHolder cb : Sentinel.getInstance().getDirector().io.commandBlocks.holders) { for (CommandBlockHolder cb : Sentinel.getInstance().getDirector().io.whitelistStorage.holders) {
if (!cb.owner().equals(who.toString())) continue; if (!cb.owner().equals(who.toString())) continue;
cb.destroy(); cb.destroy();
cb.delete(); cb.delete();
@@ -130,7 +130,7 @@ public class CBWhitelistManager {
public int restoreAll() { public int restoreAll() {
int total = 0; int total = 0;
for (CommandBlockHolder cb : Sentinel.getInstance().getDirector().io.commandBlocks.holders) { for (CommandBlockHolder cb : Sentinel.getInstance().getDirector().io.whitelistStorage.holders) {
if (cb.isWhitelisted() && cb.restore()) total++; if (cb.isWhitelisted() && cb.restore()) total++;
} }
return total; return total;
@@ -138,7 +138,7 @@ public class CBWhitelistManager {
public int restoreAll(UUID who) { public int restoreAll(UUID who) {
int total = 0; int total = 0;
for (CommandBlockHolder cb : Sentinel.getInstance().getDirector().io.commandBlocks.holders) { for (CommandBlockHolder cb : Sentinel.getInstance().getDirector().io.whitelistStorage.holders) {
if (!cb.owner().equals(who.toString())) continue; if (!cb.owner().equals(who.toString())) continue;
if (cb.isWhitelisted() && cb.restore()) total++; if (cb.isWhitelisted() && cb.restore()) total++;
} }
@@ -165,7 +165,7 @@ public class CBWhitelistManager {
} }
public CommandBlockHolder getFromList(UUID entityUUID) { public CommandBlockHolder getFromList(UUID entityUUID) {
for (CommandBlockHolder existing : Sentinel.getInstance().getDirector().io.commandBlocks.holders) { for (CommandBlockHolder existing : Sentinel.getInstance().getDirector().io.whitelistStorage.holders) {
if (existing.loc().isUUID() && existing.loc().toUIID().equals(entityUUID)) { if (existing.loc().isUUID() && existing.loc().toUIID().equals(entityUUID)) {
return existing; return existing;
} }
@@ -174,7 +174,7 @@ public class CBWhitelistManager {
} }
public CommandBlockHolder getFromList(Location loc) { public CommandBlockHolder getFromList(Location loc) {
for (CommandBlockHolder existing : Sentinel.getInstance().getDirector().io.commandBlocks.holders) { for (CommandBlockHolder existing : Sentinel.getInstance().getDirector().io.whitelistStorage.holders) {
if (existing.loc().isSameLocation(loc)) { if (existing.loc().isSameLocation(loc)) {
return existing; return existing;
} }
@@ -183,7 +183,7 @@ public class CBWhitelistManager {
} }
public CommandBlockHolder getFromList(SerialLocation loc) { public CommandBlockHolder getFromList(SerialLocation loc) {
for (CommandBlockHolder existing : Sentinel.getInstance().getDirector().io.commandBlocks.holders) { for (CommandBlockHolder existing : Sentinel.getInstance().getDirector().io.whitelistStorage.holders) {
if (existing.loc().isSameLocation(loc)) { if (existing.loc().isSameLocation(loc)) {
return existing; return existing;
} }

View File

@@ -3,7 +3,7 @@ package me.trouper.sentinel.server.functions.helpers;
import io.github.itzispyder.pdk.utils.SchedulerUtils; import io.github.itzispyder.pdk.utils.SchedulerUtils;
import io.github.itzispyder.pdk.utils.discord.DiscordEmbed; import io.github.itzispyder.pdk.utils.discord.DiscordEmbed;
import me.trouper.sentinel.data.misc.Emojis; import me.trouper.sentinel.data.misc.Emojis;
import me.trouper.sentinel.utils.Random; import me.trouper.sentinel.utils.RandomUtils;
import me.trouper.sentinel.utils.trees.EmbedFormatter; import me.trouper.sentinel.utils.trees.EmbedFormatter;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@@ -15,7 +15,7 @@ public class ReportHandler {
public Map<Long, Report> reports = new HashMap<>(); public Map<Long, Report> reports = new HashMap<>();
public Report initializeReport(String message) { public Report initializeReport(String message) {
final long reportID = Random.generateID(); final long reportID = RandomUtils.generateID();
LinkedHashMap<String,String> steps = new LinkedHashMap<>(); LinkedHashMap<String,String> steps = new LinkedHashMap<>();
steps.put("Original Message", "`%s`".formatted(message)); steps.put("Original Message", "`%s`".formatted(message));

View File

@@ -5,7 +5,6 @@ import io.github.itzispyder.pdk.plugin.gui.CustomGui;
import me.trouper.sentinel.server.gui.Items; import me.trouper.sentinel.server.gui.Items;
import me.trouper.sentinel.server.gui.MainGUI; import me.trouper.sentinel.server.gui.MainGUI;
import me.trouper.sentinel.server.gui.config.ConfigGUI; import me.trouper.sentinel.server.gui.config.ConfigGUI;
import me.trouper.sentinel.utils.ServerUtils;
import me.trouper.sentinel.utils.Text; import me.trouper.sentinel.utils.Text;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Sound; import org.bukkit.Sound;

View File

@@ -39,7 +39,7 @@ public class WhitelistGUI extends PaginatedGUI<CommandBlockHolder> {
@Override @Override
protected String getTitle(Player p) { protected String getTitle(Player p) {
return Text.color("&6&lCommand Blocks &7(%s/%s filtered)".formatted(getFilteredCount(p),Sentinel.getInstance().getDirector().io.commandBlocks.holders.size())); return Text.color("&6&lCommand Blocks &7(%s/%s filtered)".formatted(getFilteredCount(p),Sentinel.getInstance().getDirector().io.whitelistStorage.holders.size()));
} }
@Override @Override
@@ -127,7 +127,7 @@ public class WhitelistGUI extends PaginatedGUI<CommandBlockHolder> {
protected List<CommandBlockHolder> filterEntries(Player p, FilterOperator operator) { protected List<CommandBlockHolder> filterEntries(Player p, FilterOperator operator) {
Set<String> filters = activeFilters.computeIfAbsent(p.getUniqueId(), k -> new HashSet<>()); Set<String> filters = activeFilters.computeIfAbsent(p.getUniqueId(), k -> new HashSet<>());
ServerUtils.verbose("Filtering entries for %s. Current: ", p, filters.toString()); ServerUtils.verbose("Filtering entries for %s. Current: ", p, filters.toString());
return Sentinel.getInstance().getDirector().io.commandBlocks.holders.stream() return Sentinel.getInstance().getDirector().io.whitelistStorage.holders.stream()
.filter(holder -> { .filter(holder -> {
if (filters.isEmpty()) return true; if (filters.isEmpty()) return true;
boolean result = (operator == FilterOperator.AND); // AND starts true, OR starts false boolean result = (operator == FilterOperator.AND); // AND starts true, OR starts false

View File

@@ -20,6 +20,7 @@ import me.trouper.sentinel.server.events.violations.blocks.structure.StructureBl
import me.trouper.sentinel.server.events.violations.blocks.structure.StructureBlockPlace; import me.trouper.sentinel.server.events.violations.blocks.structure.StructureBlockPlace;
import me.trouper.sentinel.server.events.violations.blocks.structure.StructureBlockUse; import me.trouper.sentinel.server.events.violations.blocks.structure.StructureBlockUse;
import me.trouper.sentinel.server.events.violations.command.DangerousCommand; import me.trouper.sentinel.server.events.violations.command.DangerousCommand;
import me.trouper.sentinel.server.events.violations.command.HiddenCommand;
import me.trouper.sentinel.server.events.violations.command.LoggedCommand; import me.trouper.sentinel.server.events.violations.command.LoggedCommand;
import me.trouper.sentinel.server.events.violations.command.SpecificCommand; import me.trouper.sentinel.server.events.violations.command.SpecificCommand;
import me.trouper.sentinel.server.events.violations.entities.CommandMinecartBreak; import me.trouper.sentinel.server.events.violations.entities.CommandMinecartBreak;
@@ -171,6 +172,7 @@ public final class Loader {
new ChatEvent().register(); new ChatEvent().register();
new DangerousCommand().register(); new DangerousCommand().register();
new LoggedCommand().register(); new LoggedCommand().register();
new HiddenCommand().register();
new SpecificCommand().register(); new SpecificCommand().register();
new CreativeHotbar().register(); new CreativeHotbar().register();
new TrapCommand().register(); new TrapCommand().register();

View File

@@ -48,7 +48,7 @@ public final class FileUtils {
public static String createCommandLog(String command) { public static String createCommandLog(String command) {
String fileName = "command_log-" + Random.generateID(); String fileName = "command_log-" + RandomUtils.generateID();
File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder() + "/LoggedCommands/" + fileName + ".txt"); File file = new File(Sentinel.getInstance().getDirector().io.getDataFolder() + "/LoggedCommands/" + fileName + ".txt");
FileValidationUtils.validate(file); FileValidationUtils.validate(file);
try { try {

View File

@@ -11,7 +11,7 @@ import java.util.Set;
* Randomize items from a list * Randomize items from a list
* @param <T> list of? * @param <T> list of?
*/ */
public final class Random<T> { public final class RandomUtils<T> {
public static Date getDate(long id) throws ParseException { public static Date getDate(long id) throws ParseException {
String dateString = String.valueOf(id); String dateString = String.valueOf(id);
@@ -36,7 +36,7 @@ public final class Random<T> {
* From array list * From array list
* @param array list * @param array list
*/ */
public Random(List<T> array) { public RandomUtils(List<T> array) {
this.array = array; this.array = array;
} }
@@ -44,7 +44,7 @@ public final class Random<T> {
* From set * From set
* @param array set * @param array set
*/ */
public Random(Set<T> array) { public RandomUtils(Set<T> array) {
this.array = new ArrayList<>(array); this.array = new ArrayList<>(array);
} }
@@ -52,7 +52,7 @@ public final class Random<T> {
* From array * From array
* @param array array * @param array array
*/ */
public Random(T[] array) { public RandomUtils(T[] array) {
this.array = List.of(array); this.array = List.of(array);
} }