Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,32 @@
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.message.Message;

/**
* A Log4J implementation of the {@link LogFilter} that filters out sensitive authentication commands.
* <p>
* This class extends {@link LogFilter} and implements Log4J's {@link Filter} interface to provide filtering capabilities for Log4J logging system.
* <p>
* The filter is designed to prevent logging of sensitive information such as passwords and authentication tokens that might appear in certain commands. It integrates with Log4J's filtering chain and can be easily added to the root logger.
* <p>
* This implementation handles various Log4J filter method overloads to ensure comprehensive coverage of all possible logging scenarios, including parameterized messages and raw objects.
*/
public class Log4JFilter extends LogFilter implements Filter {

@Override
public void inject() {
((Logger) LogManager.getRootLogger()).addFilter(new Log4JFilter());
}

private Result checkMessageResult(String message) {
return checkMessage(message) ? Result.NEUTRAL : Result.DENY;
/**
* Converts the result of message checking into a Log4J {@link Result}.
*
* @param message The message pattern to be checked
* @param parameters The parameters associated with the message.
* @return {@link Result#NEUTRAL} if the message should be logged, {@link Result#DENY} if it should be filtered out
* @see LogFilter#checkMessage
*/
private Result checkMessageResult(String message, Object[] parameters) {
return checkMessage(message, parameters) ? Result.NEUTRAL : Result.DENY;
}

@Override
Expand All @@ -37,72 +54,73 @@ public Result getOnMismatch() {

@Override
public Result filter(Logger logger, Level level, Marker marker, String message, Object... params) {
return checkMessageResult(message);
return checkMessageResult(message, params);
}

@Override
public Result filter(Logger logger, Level level, Marker marker, String message, Object p0) {
return checkMessageResult(message);
return checkMessageResult(message, new Object[]{p0});
}

@Override
public Result filter(Logger logger, Level level, Marker marker, String message, Object p0, Object p1) {
return checkMessageResult(message);
return checkMessageResult(message, new Object[]{p0, p1});
}

@Override
public Result filter(Logger logger, Level level, Marker marker, String message, Object p0, Object p1, Object p2) {
return checkMessageResult(message);
return checkMessageResult(message, new Object[]{p0, p1, p2});
}

@Override
public Result filter(Logger logger, Level level, Marker marker, String message, Object p0, Object p1, Object p2, Object p3) {
return checkMessageResult(message);
return checkMessageResult(message, new Object[]{p0, p1, p2, p3});
}

@Override
public Result filter(Logger logger, Level level, Marker marker, String message, Object p0, Object p1, Object p2, Object p3, Object p4) {
return checkMessageResult(message);
return checkMessageResult(message, new Object[]{p0, p1, p2, p3, p4});
}

@Override
public Result filter(Logger logger, Level level, Marker marker, String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5) {
return checkMessageResult(message);
return checkMessageResult(message, new Object[]{p0, p1, p2, p3, p4, p5});
}

@Override
public Result filter(Logger logger, Level level, Marker marker, String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6) {
return checkMessageResult(message);
return checkMessageResult(message, new Object[]{p0, p1, p2, p3, p4, p5, p6});
}

@Override
public Result filter(Logger logger, Level level, Marker marker, String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7) {
return checkMessageResult(message);
return checkMessageResult(message, new Object[]{p0, p1, p2, p3, p4, p5, p6, p7});
}

@Override
public Result filter(Logger logger, Level level, Marker marker, String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7, Object p8) {
return checkMessageResult(message);
return checkMessageResult(message, new Object[]{p0, p1, p2, p3, p4, p5, p6, p7, p8});
}

@Override
public Result filter(Logger logger, Level level, Marker marker, String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7, Object p8, Object p9) {
return checkMessageResult(message);
return checkMessageResult(message, new Object[]{p0, p1, p2, p3, p4, p5, p6, p7, p8, p9});
}

@Override
public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) {
return checkMessageResult(msg.toString());
return checkMessageResult(msg.toString(), null);
}

@Override
public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) {
return checkMessageResult(msg.getFormattedMessage());
return checkMessageResult(msg.getFormat(), msg.getParameters());
}

@Override
public Result filter(LogEvent event) {
return checkMessageResult(event.getMessage().getFormattedMessage());
var message = event.getMessage();
return checkMessageResult(message.getFormat(), message.getParameters());
}

@Override
Expand All @@ -126,4 +144,4 @@ public void start() {

public void stop() {
}
}
}
78 changes: 49 additions & 29 deletions Plugin/src/main/java/xyz/kyngs/librelogin/common/log/LogFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,47 +6,67 @@

package xyz.kyngs.librelogin.common.log;

import java.util.HashSet;
import java.util.Set;

/**
* A filter that prevents sensitive authentication commands from being logged.
* <p>
* This abstract class provides functionality to filter out commands that might contain sensitive information such as passwords or authentication tokens.
*/
public abstract class LogFilter {

private static final Set<String> PROTECTED_COMMANDS;

static {
PROTECTED_COMMANDS = new HashSet<>();

PROTECTED_COMMANDS.add("login");
PROTECTED_COMMANDS.add("l");
PROTECTED_COMMANDS.add("log");
PROTECTED_COMMANDS.add("register");
PROTECTED_COMMANDS.add("reg");
PROTECTED_COMMANDS.add("premium");
PROTECTED_COMMANDS.add("autologin");
PROTECTED_COMMANDS.add("2faconfirm");
PROTECTED_COMMANDS.add("changepassword");
PROTECTED_COMMANDS.add("changepass");
PROTECTED_COMMANDS.add("passch");
PROTECTED_COMMANDS.add("passwd");
PROTECTED_COMMANDS.add("confirmpasswordreset");
PROTECTED_COMMANDS.add("setemail");
PROTECTED_COMMANDS.add("librelogin user register");
PROTECTED_COMMANDS.add("librelogin user pass-change");
}
/**
* A set of command prefixes that should be protected from logging.
* <p>
* These commands typically contain sensitive information like passwords.
*/
private static final Set<String> PROTECTED_COMMANDS = Set.of(
"/login ",
"/l ",
"/log ",
"/register ",
"/reg ",
"/premium ",
"/autologin ",
"/2faconfirm ",
"/changepassword ",
"/changepass ",
"/passch ",
"/passwd ",
"/confirmpasswordreset ",
"/setemail ",
"/librelogin user register ",
"/librelogin user pass-change "
);

/**
* Checks if a log message containing a command should be filtered out.
*
* @param message The message pattern being logged
* @param parameters The parameters being used in the message.
* @return {@code true} if the message should be logged, {@code false} if it should be filtered out
*/
protected boolean checkMessage(String message, Object[] parameters) {
if (parameters == null || parameters.length <= 1 || !(parameters[1] instanceof String parameter)) return true;

parameter = switch (message) {
case "{} issued server command: {}" -> parameter;
case "{0} executed command: /{1}", "{} -> executed command /{}" -> '/' + parameter;
default -> "";
};

protected boolean checkMessage(String message) {
// This sucks, but it's the only way to filter out the spam from the plugin
if (message.contains("Plugin listener xyz.kyngs.librelogin.bungeecord.BungeeCordListener took")) return false;
if (!message.contains("issued server command: /") && !message.contains("executed command /") && !message.contains("executed command: /") && !message.contains("Duplicate key name"))
return true;
if (parameter.isEmpty()) return true;

for (String command : PROTECTED_COMMANDS) {
if (message.contains(command)) return false;
if (parameter.startsWith(command)) return false;
}

return true;
}

/**
* Injects this filter into the logging system.
*/
public abstract void inject();

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,26 @@
import java.util.logging.LogRecord;
import java.util.logging.Logger;

/**
* A simple implementation of the LogFilter for Java's built-in logging system.
* <p>
* This class extends {@link LogFilter} and implements {@link Filter} interface to provide filtering capabilities for the standard Java logging framework.
* <p>
* This implementation preserves any existing filter that was already set on the logger by chaining it with this filter's functionality.</p>
*/
public class SimpleLogFilter extends LogFilter implements Filter {

/**
* The original filter that was set on the logger before this filter was applied.
* <p>
* This is preserved to maintain the existing filtering chain.
*/
private final Filter filter;
/**
* The logger instance that this filter is attached to.
* <p>
* Used for filter injection and management.
*/
private final Logger logger;

public SimpleLogFilter(Logger logger) {
Expand All @@ -24,7 +41,7 @@ public SimpleLogFilter(Logger logger) {
public boolean isLoggable(LogRecord record) {
if (filter != null && !filter.isLoggable(record)) return false;

return checkMessage(record.getMessage());
return checkMessage(record.getMessage(), record.getParameters());
}

@Override
Expand Down