Make this a separate check
github-actions opened this issue · 0 comments
github-actions commented
Make this a separate check
package optic_fusion1.antimalware.scanner.file;
import optic_fusion1.antimalware.AntiMalware;
import optic_fusion1.antimalware.CommandLineParser;
import optic_fusion1.antimalware.check.CacheContainer;
import optic_fusion1.antimalware.check.CheckManager;
import optic_fusion1.antimalware.check.CheckResult;
import optic_fusion1.antimalware.database.Database;
import optic_fusion1.antimalware.scanner.ScanHelper;
import optic_fusion1.antimalware.scanner.Scanner;
import org.apache.commons.codec.digest.DigestUtils;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipFile;
import static optic_fusion1.antimalware.AntiMalware.LOGGER;
import static optic_fusion1.antimalware.utils.I18n.tl;
import static optic_fusion1.antimalware.utils.Utils.fileSystemForZip;
import static optic_fusion1.antimalware.utils.Utils.validClassPath;
/**
* @author IkeVoodoo
* */
public class FileScanner {
private static final Path ANTI_MALWARE_PATH;
static {
try {
ANTI_MALWARE_PATH = new File(AntiMalware.class.getProtectionDomain().getCodeSource().getLocation().toURI()).toPath();
} catch (URISyntaxException e) {
throw new IllegalStateException(e);
}
}
private static final String SPIGOT_PLATFORM = "Spigot";
private final CacheContainer cache;
private final Database database;
private final CommandLineParser commandLineParser;
private final CheckManager checkManager;
private final BiConsumer<Path, CheckResult> notifications;
public FileScanner(CacheContainer cache, Database database, CommandLineParser commandLineParser, CheckManager checkManager, BiConsumer<Path, CheckResult> notifications) {
this.cache = cache;
this.database = database;
this.commandLineParser = commandLineParser;
this.checkManager = checkManager;
this.notifications = notifications;
}
public void scanFile(Path file) {
// Not needed because this method shouldn't be called with a directory like tf it's FileScanner.scanFile like bruh
// if (Files.isDirectory(file)) {
// scanDirectory(file);
// return;
// }
if (ScanHelper.isFileEmpty(file)) {
return;
}
String fileName = file.getFileName().toString();
this.checkBlacklistedFileName(file, fileName);
this.checkBlacklistedFilePath(file);
if (fileName.equals("VaultLib.jar")) {
this.submitNotification(file.toAbsolutePath(), new CheckResult(SPIGOT_PLATFORM, "MALWARE", "Qlutch", "C"));
}
if (!fileName.endsWith(".jar") && !fileName.endsWith(".zip") && !fileName.endsWith(".rar") && !isPlugin(file)) {
return;
}
this.checkFileHash(file);
try (var fs = fileSystemForZip(file)) {
if (fs == null) {
return;
}
Path rootFolder = fs.getRootDirectories().iterator().next();
if (this.commandLineParser.shouldScanZippedFiles() && (fileName.endsWith(".zip") || fileName.endsWith(".rar"))) {
this.scanZip(file);
return;
}
Scanner.WhitelistResult result = isFileWhitelisted(file);
if (result == Scanner.WhitelistResult.INVALID_FILE || result == Scanner.WhitelistResult.WHITELISTED) {
return;
}
//region KillMe
// TODO: Make this a separate check
if (Files.exists(rootFolder.resolve("dev/jnic/lib/"))) {
this.submitNotification(file, new CheckResult(SPIGOT_PLATFORM, "Malware", "JNIC"));
// return;
}
// TODO: Make these a separate check
if (Files.exists(rootFolder.resolve("plugin-config.bin"))) {
this.submitNotification(file, new CheckResult(SPIGOT_PLATFORM, "Malware", "SG"));
// return;
}
if (Files.exists(rootFolder.resolve("META-INF/gradle/org/apache/commons/local-info.hdm"))) {
this.submitNotification(file, new CheckResult(SPIGOT_PLATFORM, "Malware", "SG", "B"));
// return;
}
if (Files.exists(rootFolder.resolve("META-INF/gradle/io/netty/netty-locals.netd"))) {
this.submitNotification(file, new CheckResult(SPIGOT_PLATFORM, "Malware", "SG", "C"));
// return;
}
if (Files.exists(rootFolder.resolve("META-INF/maven/org/apache/logging/log4j/Log4j-events.dtd"))) {
this.submitNotification(file, new CheckResult(SPIGOT_PLATFORM, "Malware", "SG", "D"));
// return;
}
if (Files.exists(rootFolder.resolve("META-INF/gradle/org/apache/logging/log4j/Log4j-events.dtd"))) {
this.submitNotification(file, new CheckResult(SPIGOT_PLATFORM, "Malware", "SG", "E"));
// return;
}
if (Files.exists(rootFolder.resolve("META-INF/gradle/org.json/json/json.xsd"))) {
this.submitNotification(file, new CheckResult(SPIGOT_PLATFORM, "Malware", "SG", "F"));
// return;
}
if (Files.exists(rootFolder.resolve("META-INF/maven/org/apache/commons/api-catch.dir"))) {
this.submitNotification(file, new CheckResult(SPIGOT_PLATFORM, "Malware", "SG", "G"));
// return;
}
if (Files.exists(rootFolder.resolve("META-INF/maven/org/apache/commons/local-dir.hum"))) {
this.submitNotification(file, new CheckResult(SPIGOT_PLATFORM, "Malware", "SG", "H"));
// return;
}
if (Files.exists(rootFolder.resolve("META-INF/maven/org/apache/commons/local-info.hdm"))) {
this.submitNotification(file, new CheckResult(SPIGOT_PLATFORM, "Malware", "SG", "I"));
// return;
}
if (Files.exists(rootFolder.resolve("META-INF/maven/com/google/code/gson/gson/maven.data"))) {
this.submitNotification(file, new CheckResult(SPIGOT_PLATFORM, "Malware", "SG", "J"));
// return;
}
if (Files.exists(rootFolder.resolve("META-INF/gradle/com.google.code.gson/gson/maven.data"))) {
this.submitNotification(file, new CheckResult(SPIGOT_PLATFORM, "Malware", "SG", "K"));
// return;
}
if (Files.exists(rootFolder.resolve("META-INF/maven/org.json/json/gson.xsd"))) {
this.submitNotification(file, new CheckResult(SPIGOT_PLATFORM, "Malware", "SG", "L"));
// return;
}
//endregion
AtomicBoolean possiblyMalicious = new AtomicBoolean(false);
walkThroughFiles(rootFolder, classPath -> {
if (!validClassPath(classPath)) {
return;
}
var classNode = this.cache.fetchClass(file, classPath);
if (classNode == null) {
return;
}
for (var check : this.checkManager.getChecks()) {
var results = check.process(classNode, rootFolder, file, cache);
if (results == null || results.isEmpty()) continue;
possiblyMalicious.set(true);
for (var checkResult : results) {
if (this.commandLineParser.dontLogINFOCR() && checkResult.getType().equals("INFO")) {
continue;
}
this.submitNotification(file, checkResult);
}
check.reset();
}
});
this.cache.clearCache(file); // Attempt at fixing memory issues
if (this.commandLineParser.shouldPrintNotInfectedMessages() && !possiblyMalicious.get()) {
LOGGER.info(tl("scanner_probably_safe", file));
}
} catch (IOException ex) {
LOGGER.exception(ex);
}
}
private void checkBlacklistedFileName(Path file, String fileName) {
if (!this.cache.containsBlacklistedFileName(fileName)) {
return;
}
try {
var result = this.database.getCheckResultForFileName(fileName);
this.submitNotification(file.toAbsolutePath(), result);
} catch (SQLException ex) {
LOGGER.exception(ex);
}
}
private void checkBlacklistedFilePath(Path file) {
if (!this.cache.containsBlacklistedFilePath(file.toString())) {
return;
}
try {
var result = this.database.getCheckResultForFilePath(file.toString());
this.submitNotification(file.toAbsolutePath(), result);
} catch (SQLException ex) {
LOGGER.exception(ex);
}
}
private void checkFileHash(Path file) {
var checksum = this.cache.fetchSHA1(file, file);
if (checksum == null) {
LOGGER.warn("The SHA-1 checksum for '" + file + "' couldn't be loaded");
return;
}
checksum = checksum.toUpperCase();
if (!this.cache.containsBlacklistedChecksum(checksum)) {
return;
}
try {
var checkResult = this.database.getCheckResultForChecksum(checksum);
if (checkResult == null) {
LOGGER.info(tl("scanner_blacklisted_not_in_database", file)); // Sysout replaced with logger
return;
}
if (this.commandLineParser.dontLogINFOCR() && checkResult.getType().equals("INFO")) {
return;
}
this.submitNotification(file, checkResult);
} catch (SQLException ex) {
LOGGER.exception(ex);
}
}
private void submitNotification(Path path, CheckResult result) {
if (result == null) return;
this.notifications.accept(path, result);
}
private boolean isPlugin(Path file) {
try(var zipFile = new ZipFile(file.toFile())) {
return zipFile.getEntry("plugin.yml") != null;
} catch (IOException ex) {
return false;
}
}
private Scanner.WhitelistResult isFileWhitelisted(Path file) {
if (file == null || ScanHelper.isFileEmpty(file)) {
return Scanner.WhitelistResult.INVALID_FILE;
}
try {
if (Files.isSameFile(file, ANTI_MALWARE_PATH)) {
return Scanner.WhitelistResult.WHITELISTED;
}
var fileChecksum = DigestUtils.sha1Hex(Files.newInputStream(file));
var result = isChecksumWhitelisted(fileChecksum);
if (result == Scanner.WhitelistResult.WHITELISTED && this.commandLineParser.shouldPrintNotInfectedMessages()) {
LOGGER.info(tl("scanner_probably_safe_whitelisted", file.getFileName().toString()));
}
return result;
} catch (IOException e) {
LOGGER.exception(e);
}
return Scanner.WhitelistResult.NOT_WHITELISTED;
}
protected void walkThroughFiles(Path dir, Consumer<Path> pathConsumer) {
if (".".equals(String.valueOf(dir.getFileName()))) { // if (dir.getFileName() != null && dir.getFileName().toString().equals(".")) {
return;
}
if (Files.isDirectory(dir, LinkOption.NOFOLLOW_LINKS)) {
this.walkTroughDirectory(dir, pathConsumer);
return;
}
if (Files.isSymbolicLink(dir)) {
return;
}
pathConsumer.accept(dir);
}
private void walkTroughDirectory(Path dir, Consumer<Path> pathConsumer) {
var stack = new LinkedList<Path>();
stack.add(dir);
while (!stack.isEmpty()) {
var current = stack.pollLast();
try(var list = Files.list(current).filter(path -> !Files.isSymbolicLink(path))) {
var iterator = list.iterator();
while (iterator.hasNext()) {
var next = iterator.next();
pathConsumer.accept(next);
if (Files.isDirectory(next, LinkOption.NOFOLLOW_LINKS)) {
stack.addLast(next);
}
}
} catch (IOException e) {
LOGGER.exception(e);
}
}
}
private void scanZip(Path zippedFile) {
LOGGER.info("Scanning zip file " + zippedFile);
try(var walk = Files.walk(zippedFile)) {
walk.forEach(found -> {
if (Files.isDirectory(found)) {
// Scan dir
this.scanDirectory(found);
return;
}
// Scan file
this.scanFile(found);
});
} catch (IOException ex) {
Logger.getLogger(Scanner.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void scanDirectory(Path directory) {
try(var list = Files.list(directory)) {
list.forEach((path) -> {
if (!path.getFileSystem().isOpen()) return;
if (Files.isDirectory(path)) {
scanDirectory(path);
return;
}
this.scanFile(path);
});
} catch (IOException e) {
LOGGER.exception(e);
}
}
private Scanner.WhitelistResult isChecksumWhitelisted(String checksum) {
return this.cache.containsWhitelistedChecksum(checksum) ? Scanner.WhitelistResult.WHITELISTED : Scanner.WhitelistResult.NOT_WHITELISTED;
}
}
96db68db35bb5a35c1ade12ee18329db31baac75