Skip to content

Commit ba2b3f6

Browse files
committed
new detection
1 parent 4fd47d2 commit ba2b3f6

14 files changed

Lines changed: 600 additions & 96 deletions

File tree

paper/src/main/java/io/wdsj/asw/bukkit/AdvancedSensitiveWords.java

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import io.wdsj.asw.bukkit.command.AswCommandRegistrar;
1818
import io.wdsj.asw.bukkit.ai.LlmChatDetectionService;
1919
import io.wdsj.asw.bukkit.core.condition.WordResultConditionNumMatch;
20+
import io.wdsj.asw.bukkit.core.network.ObfuscatedUrlDetector;
2021
import io.wdsj.asw.bukkit.integration.placeholder.ASWExpansion;
2122
import io.wdsj.asw.bukkit.service.chat.antispam.ChatAntiSpamService;
2223
import io.wdsj.asw.bukkit.manage.punish.PlayerAltController;
@@ -48,8 +49,10 @@
4849
import org.slf4j.Logger;
4950

5051
import java.util.ArrayList;
52+
import java.util.Comparator;
5153
import java.util.LinkedHashSet;
5254
import java.util.List;
55+
import java.util.stream.Collectors;
5356

5457
import static io.wdsj.asw.bukkit.util.LoggingUtils.purgeLog;
5558
import static io.wdsj.asw.bukkit.util.TimingUtils.resetStatistics;
@@ -60,6 +63,7 @@ public final class AdvancedSensitiveWords extends JavaPlugin {
6063
public static volatile boolean isInitialized = false;
6164
public static SensitiveWordBs sensitiveWordBs;
6265
public static SensitiveWordBs networkSensitiveWordBs;
66+
public static ObfuscatedUrlDetector obfuscatedUrlDetector;
6367
public static boolean isAuthMeAvailable;
6468
public static final String PLUGIN_VERSION = PluginBuildInfo.VERSION;
6569
private static AdvancedSensitiveWords instance;
@@ -149,6 +153,7 @@ public void doInitTasks() {
149153
isInitialized = false;
150154
sensitiveWordBs = null;
151155
networkSensitiveWordBs = null;
156+
obfuscatedUrlDetector = null;
152157
IWordResultCondition condition = createWordResultCondition();
153158
getScheduler().runTaskAsynchronously(() -> {
154159
IWordDeny wordDeny = createWordDeny();
@@ -174,6 +179,12 @@ public void doInitTasks() {
174179
.wordFailFast(configurationService.get(PluginSettings.FAIL_FAST))
175180
.init();
176181
networkSensitiveWordBs = createNetworkSensitiveWordBs();
182+
if (configurationService.get(PluginSettings.ENABLE_URL_CHECK)
183+
&& configurationService.get(PluginSettings.ENABLE_OBFUSCATED_URL_CHECK)) {
184+
obfuscatedUrlDetector = new ObfuscatedUrlDetector(
185+
configurationService.get(PluginSettings.URL_CHECK_NO_PREFIX)
186+
);
187+
}
177188
isInitialized = true;
178189
});
179190
}
@@ -199,6 +210,9 @@ public void onDisable() {
199210
if (networkSensitiveWordBs != null) {
200211
networkSensitiveWordBs.destroy();
201212
}
213+
if (obfuscatedUrlDetector != null) {
214+
obfuscatedUrlDetector.close();
215+
}
202216
}
203217
commandRegistrar = null;
204218
LOGGER.info("AdvancedSensitiveWords is disabled.");
@@ -377,6 +391,12 @@ public static List<String> findAllSensitive(String text) {
377391
if (networkWordBs != null) {
378392
results.addAll(networkWordBs.findAll(text));
379393
}
394+
ObfuscatedUrlDetector urlDetector = obfuscatedUrlDetector;
395+
if (urlDetector != null) {
396+
urlDetector.findAll(text).stream()
397+
.map(IWordResult::word)
398+
.forEach(results::add);
399+
}
380400
return List.copyOf(results);
381401
}
382402

@@ -390,14 +410,68 @@ public static List<IWordResult> findAllSensitiveRaw(String text) {
390410
if (networkWordBs != null) {
391411
results.addAll(networkWordBs.findAll(text, WordResultHandlers.raw()));
392412
}
413+
ObfuscatedUrlDetector urlDetector = obfuscatedUrlDetector;
414+
if (urlDetector != null) {
415+
results.addAll(urlDetector.findAll(text));
416+
}
393417
return results;
394418
}
395419

396420
public static String replaceSensitive(String text) {
421+
ObfuscatedUrlDetector urlDetector = obfuscatedUrlDetector;
422+
String obfuscatedUrlReplaced = urlDetector == null
423+
? text
424+
: replaceMappedResults(text, urlDetector.findAll(text));
397425
SensitiveWordBs wordBs = sensitiveWordBs;
398-
String result = wordBs == null ? text : wordBs.replace(text);
426+
String result = wordBs == null ? obfuscatedUrlReplaced : wordBs.replace(obfuscatedUrlReplaced);
399427
SensitiveWordBs networkWordBs = networkSensitiveWordBs;
400428
return networkWordBs == null ? result : networkWordBs.replace(result);
401429
}
402430

431+
private static String replaceMappedResults(String text, List<IWordResult> results) {
432+
if (results.isEmpty()) {
433+
return text;
434+
}
435+
436+
List<IWordResult> selected = results.stream()
437+
.sorted(Comparator.comparingInt(IWordResult::startIndex)
438+
.thenComparing(Comparator.comparingInt(IWordResult::endIndex).reversed()))
439+
.collect(Collectors.toCollection(ArrayList::new));
440+
int cursor = 0;
441+
for (int index = 0; index < selected.size(); ) {
442+
IWordResult result = selected.get(index);
443+
if (result.startIndex() < cursor || result.endIndex() <= result.startIndex()) {
444+
selected.remove(index);
445+
continue;
446+
}
447+
cursor = result.endIndex();
448+
index++;
449+
}
450+
451+
StringBuilder replaced = new StringBuilder(text);
452+
for (int index = selected.size() - 1; index >= 0; index--) {
453+
IWordResult result = selected.get(index);
454+
int start = result.startIndex();
455+
int end = result.endIndex();
456+
if (start < 0 || end > text.length()) {
457+
continue;
458+
}
459+
replaced.replace(start, end, replacementFor(text.substring(start, end)));
460+
}
461+
return replaced.toString();
462+
}
463+
464+
private static String replacementFor(String sensitiveText) {
465+
for (String definition : setting(PluginSettings.DEFINED_REPLACEMENT)) {
466+
int separator = definition.indexOf('|');
467+
if (separator <= 0 || definition.indexOf('|', separator + 1) >= 0) {
468+
continue;
469+
}
470+
if (definition.substring(0, separator).equals(sensitiveText)) {
471+
return definition.substring(separator + 1);
472+
}
473+
}
474+
return setting(PluginSettings.REPLACEMENT).repeat(sensitiveText.length());
475+
}
476+
403477
}

paper/src/main/java/io/wdsj/asw/bukkit/command/AswCommandService.java

Lines changed: 56 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import java.util.ArrayList;
2626
import java.util.Arrays;
27+
import java.util.LinkedHashMap;
2728
import java.util.List;
2829
import java.util.Locale;
2930
import java.util.Map;
@@ -51,6 +52,9 @@ public void reloadAll(CommandSender sender) {
5152
if (AdvancedSensitiveWords.networkSensitiveWordBs != null) {
5253
AdvancedSensitiveWords.networkSensitiveWordBs.destroy();
5354
}
55+
if (AdvancedSensitiveWords.obfuscatedUrlDetector != null) {
56+
AdvancedSensitiveWords.obfuscatedUrlDetector.close();
57+
}
5458
plugin.doInitTasks();
5559
if (configuration.get(PluginSettings.BOOK_CACHE_CLEAR_ON_RELOAD)
5660
&& configuration.get(PluginSettings.BOOK_CACHE)) {
@@ -69,36 +73,36 @@ public void showStatus(CommandSender sender) {
6973
? "Windows"
7074
: OsUtil.isMac() ? "Mac" : OsUtil.isUnix() ? "Linux" : "Unknown";
7175
String bitness = OsUtil.is64() ? "64bit" : "32bit";
72-
String message = MessageUtils.retrieveMessage(PluginMessages.MESSAGE_ON_COMMAND_STATUS)
73-
.replace("%num%", String.valueOf(Utils.messagesFilteredNum.get()))
74-
.replace("%mode%", "Event")
75-
.replace("%init%", String.valueOf(AdvancedSensitiveWords.isInitialized))
76-
.replace("%ms%", TimingUtils.getProcessAverage() + "ms")
77-
.replace("%version%", AdvancedSensitiveWords.PLUGIN_VERSION)
78-
.replace("%mc_version%", Utils.getMinecraftVersion())
79-
.replace("%platform%", platform)
80-
.replace("%bit%", bitness)
81-
.replace("%java_version%", TimingUtils.getJvmVersion())
82-
.replace("%java_vendor%", TimingUtils.getJvmVendor());
83-
MessageUtils.sendMessage(sender, message);
76+
sendTemplate(sender, PluginMessages.MESSAGE_ON_COMMAND_STATUS, Map.of(
77+
"num", String.valueOf(Utils.messagesFilteredNum.get()),
78+
"mode", "Event",
79+
"init", String.valueOf(AdvancedSensitiveWords.isInitialized),
80+
"ms", TimingUtils.getProcessAverage() + "ms",
81+
"version", AdvancedSensitiveWords.PLUGIN_VERSION,
82+
"mc_version", Utils.getMinecraftVersion(),
83+
"platform", platform,
84+
"bit", bitness,
85+
"java_version", TimingUtils.getJvmVersion(),
86+
"java_vendor", TimingUtils.getJvmVendor()
87+
));
8488
}
8589

8690
public void showAiStatus(CommandSender sender) {
8791
LlmChatDetectionService.LlmRuntimeStatus status = plugin.getLlmChatDetectionService().runtimeStatus();
88-
String message = MessageUtils.retrieveMessage(PluginMessages.MESSAGE_ON_AI_STATUS)
89-
.replace("%enabled%", String.valueOf(status.enabled()))
90-
.replace("%submitted%", String.valueOf(status.submittedRequests()))
91-
.replace("%dropped%", String.valueOf(status.droppedRequests()))
92-
.replace("%failed%", String.valueOf(status.failedRequests()))
93-
.replace("%invalid%", String.valueOf(status.invalidResponses()))
94-
.replace("%enforced%", String.valueOf(status.enforcedResponses()))
95-
.replace("%active%", String.valueOf(status.activeRequests()))
96-
.replace("%queued%", String.valueOf(status.queuedRequests()))
97-
.replace("%pool_size%", String.valueOf(status.poolSize()))
98-
.replace("%model%", status.modelName())
99-
.replace("%api_mode%", status.apiMode().name())
100-
.replace("%thresholds%", formatCategoryPolicies(status.categoryPolicy()));
101-
MessageUtils.sendMessage(sender, message);
92+
Map<String, String> placeholders = new LinkedHashMap<>();
93+
placeholders.put("enabled", String.valueOf(status.enabled()));
94+
placeholders.put("submitted", String.valueOf(status.submittedRequests()));
95+
placeholders.put("dropped", String.valueOf(status.droppedRequests()));
96+
placeholders.put("failed", String.valueOf(status.failedRequests()));
97+
placeholders.put("invalid", String.valueOf(status.invalidResponses()));
98+
placeholders.put("enforced", String.valueOf(status.enforcedResponses()));
99+
placeholders.put("active", String.valueOf(status.activeRequests()));
100+
placeholders.put("queued", String.valueOf(status.queuedRequests()));
101+
placeholders.put("pool_size", String.valueOf(status.poolSize()));
102+
placeholders.put("model", status.modelName());
103+
placeholders.put("api_mode", status.apiMode().name());
104+
placeholders.put("thresholds", formatCategoryPolicies(status.categoryPolicy()));
105+
sendTemplate(sender, PluginMessages.MESSAGE_ON_AI_STATUS, placeholders);
102106
}
103107

104108
public void test(CommandSender sender, String text) {
@@ -112,11 +116,11 @@ public void test(CommandSender sender, String text) {
112116
return;
113117
}
114118

115-
String message = MessageUtils.retrieveMessage(PluginMessages.MESSAGE_ON_COMMAND_TEST)
116-
.replace("%original_msg%", text)
117-
.replace("%processed_msg%", AdvancedSensitiveWords.replaceSensitive(text))
118-
.replace("%censored_list%", censoredWords.toString());
119-
MessageUtils.sendMessage(sender, message);
119+
sendTemplate(sender, PluginMessages.MESSAGE_ON_COMMAND_TEST, Map.of(
120+
"original_msg", text,
121+
"processed_msg", AdvancedSensitiveWords.replaceSensitive(text),
122+
"censored_list", censoredWords.toString()
123+
));
120124
}
121125

122126
public void addBlockedWords(CommandSender sender, String[] words) {
@@ -152,14 +156,14 @@ public void removeAllowedWords(CommandSender sender, String[] words) {
152156
}
153157

154158
public void showPlayerInfo(CommandSender sender, Player player) {
155-
String message = MessageUtils.retrieveMessage(PluginMessages.MESSAGE_ON_PLAYER_INFO)
156-
.replace("%player%", player.getName())
157-
.replace("%violation%", String.valueOf(ViolationCounter.INSTANCE.getTotalViolationCount(player)));
159+
Map<String, String> placeholders = new LinkedHashMap<>();
160+
placeholders.put("player", player.getName());
161+
placeholders.put("violation", String.valueOf(ViolationCounter.INSTANCE.getTotalViolationCount(player)));
158162
for (ModuleType moduleType : ModuleType.violationModules()) {
159-
String placeholder = "%" + moduleType.name().toLowerCase(Locale.ROOT) + "_violation%";
160-
message = message.replace(placeholder, String.valueOf(ViolationCounter.INSTANCE.getViolationCount(player, moduleType)));
163+
String placeholder = moduleType.name().toLowerCase(Locale.ROOT) + "_violation";
164+
placeholders.put(placeholder, String.valueOf(ViolationCounter.INSTANCE.getViolationCount(player, moduleType)));
161165
}
162-
MessageUtils.sendMessage(sender, message);
166+
sendTemplate(sender, PluginMessages.MESSAGE_ON_PLAYER_INFO, placeholders);
163167
}
164168

165169
public void resetPlayerViolations(CommandSender sender, Player player, ModuleType moduleType) {
@@ -172,10 +176,10 @@ public void resetPlayerViolations(CommandSender sender, Player player, ModuleTyp
172176
ViolationCounter.INSTANCE.resetViolationCount(player, moduleType);
173177
}
174178
}
175-
String message = MessageUtils.retrieveMessage(PluginMessages.MESSAGE_ON_COMMAND_RESET)
176-
.replace("%player%", player.getName())
177-
.replace("%module%", moduleType == null ? "ALL" : moduleType.name());
178-
MessageUtils.sendMessage(sender, message);
179+
sendTemplate(sender, PluginMessages.MESSAGE_ON_COMMAND_RESET, Map.of(
180+
"player", player.getName(),
181+
"module", moduleType == null ? "ALL" : moduleType.name()
182+
));
179183
}
180184

181185
public void teleportToReportedLocation(CommandSender sender, UUID worldId, double x, double y, double z) {
@@ -204,9 +208,9 @@ public void punishPlayer(CommandSender sender, Player player, String method) {
204208
return;
205209
}
206210

207-
String message = MessageUtils.retrieveMessage(PluginMessages.MESSAGE_ON_COMMAND_PUNISH_SUCCESS)
208-
.replace("%player%", player.getName());
209-
MessageUtils.sendMessage(sender, message);
211+
sendTemplate(sender, PluginMessages.MESSAGE_ON_COMMAND_PUNISH_SUCCESS, Map.of(
212+
"player", player.getName()
213+
));
210214
}
211215

212216
private boolean isInitialized(CommandSender sender) {
@@ -222,6 +226,14 @@ private void sendTemporaryMutationMessage(CommandSender sender, PluginMessages s
222226
MessageUtils.sendMessage(sender, PluginMessages.MESSAGE_ON_COMMAND_RUNTIME_ONLY);
223227
}
224228

229+
private static void sendTemplate(
230+
CommandSender sender,
231+
PluginMessages template,
232+
Map<String, String> placeholders
233+
) {
234+
MessageUtils.sendTemplate(sender, MessageUtils.retrieveMessage(template), placeholders);
235+
}
236+
225237
private List<String> toWordList(String[] words) {
226238
return new ArrayList<>(Arrays.asList(words));
227239
}

0 commit comments

Comments
 (0)