Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make these translatable #546

Open
github-actions bot opened this issue Mar 30, 2022 · 0 comments
Open

Make these translatable #546

github-actions bot opened this issue Mar 30, 2022 · 0 comments
Labels

Comments

@github-actions
Copy link

Make these translatable

public class Scanner extends Thread {

    private static final Queue<Path> SCANNABLE_FILES = new ConcurrentLinkedQueue<>();
    private final ThreadPoolExecutor executorService;
    protected final AntiMalware antiMalware;
    protected final File scanDirectory;
    protected final CheckManager checkManager;
    protected final CommandLineParser commandLineParser;
    private final NotificationHandler notificationHandler;
    private final Database database;
    protected File unzipDirectory;
    private Path zippedFilePath;
    private final CacheContainer cache;

    public Scanner(AntiMalware antiMalware, File scanDirectory) {
        setName("AntiMalware/Scanner");
        this.antiMalware = antiMalware;
        database = antiMalware.getDatabase();
        cache = antiMalware.getCache();
        checkManager = antiMalware.getCheckManager();
        commandLineParser = antiMalware.getCommandLineParser();
        if (commandLineParser.shouldScanZippedFiles()) {
            unzipDirectory = new File("AntiMalware", "unzipped");
            if (!unzipDirectory.exists()) {
                unzipDirectory.mkdirs();
            }
        }
        this.scanDirectory = scanDirectory;
        notificationHandler = antiMalware.getNotificationHandler();

        // Moved to constructor - Attempt at fixing memory issues
        executorService = new ThreadPoolExecutor(4, Math.max(Runtime.getRuntime().availableProcessors(), 4), 10,
                TimeUnit.SECONDS, new ArrayBlockingQueue<>(50000));
        executorService.allowCoreThreadTimeOut(true);
    }

    @Override
    public void run() {
        try {
            Path file = SCANNABLE_FILES.remove();
            if (file == null) {
                return;
            }
            scanFile(file);
        } catch (Exception e) {
            LOGGER.exception(e);
        }
        if (SCANNABLE_FILES.size() % 100 == 0) {
            System.out.println(I18n.tl("scanner_remaining_files", SCANNABLE_FILES.size()));
        }
    }

    public void scanFiles() {
        LOGGER.info(I18n.tl("scanner_scan_start"));
        try {
            Files.walk(scanDirectory.toPath()).filter(Files::isRegularFile)
                    .forEachOrdered(this::addFileToQueue);
        } catch (IOException e) {
            LOGGER.exception(e);
        }
    }

    private void scanZip(Path zippedFile) {
        LOGGER.info("Unzipping " + zippedFile);
        File destination = new File(unzipDirectory, UUID.randomUUID().toString());
        zippedFilePath = zippedFile;
        if (!destination.exists()) {
            destination.mkdirs();
        }
        try {
            Utils.unzip(new ZipFile(zippedFile.toFile()), destination);
            File file = new File(destination, ".info");
            file.createNewFile();
            try ( PrintWriter pw = new PrintWriter(file)) {
                pw.write(zippedFile.toString());
            }
            for (File foundDir : destination.listFiles()) {
                if (foundDir.isDirectory()) {
                    scanDirectory(foundDir.toPath());
                    continue;
                }
                scanFile(foundDir.toPath());
            }
        } catch (IOException ex) {
            Logger.getLogger(Scanner.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void scanDirectory(Path directory) {
        if (!Files.isDirectory(directory)) {
            scanFile(directory);
            return;
        }
        try {
            Files.list(directory).forEach((path) -> {
                if (path.getFileSystem().isOpen()) {
                    if (Files.isDirectory(path)) {
                        scanDirectory(path);
                    } else {
                        scanFile(path);
                    }
                }
            });
        } catch (IOException e) {
            LOGGER.exception(e);
        }
    }

    private void scanFile(Path file) {
        if (Files.isDirectory(file)) {
            scanDirectory(file);
            return;
        }
        String fileName = file.getFileName().toString();
        if (cache.containsBlacklistedFileName(fileName)) {
            try {
                CheckResult result = database.getCheckResultForFileName(fileName);
                if (result != null) {
                    sendNotification(file.toAbsolutePath(), result);
                }
                return;
            } catch (SQLException ex) {
                LOGGER.exception(ex);
            }
        }
        if (fileName.equals("VaultLib.jar")) {
            sendNotification(file.toAbsolutePath(), new CheckResult("Spigot", "MALWARE", "Qlutch", "C"));
            return;
        }
        if (cache.containsBlacklistedFilePath(file.toString())) {
            try {
                CheckResult result = database.getCheckResultForFilePath(file.toString());
                if (result != null) {
                    sendNotification(file.toAbsolutePath(), result);
                }
                return;
            } catch (SQLException ex) {
                LOGGER.exception(ex);
            }
        }
        if (!fileName.endsWith(".jar") && !fileName.endsWith(".zip") && !fileName.endsWith(".rar")) {
            return;
        }
        try {
            if (Files.size(file) == 0) {
                return;
            }
        } catch (IOException e) {
            LOGGER.exception(e);
            return;
        }
        String checksum = cache.fetchSHA1(file, file).toUpperCase();
        if (cache.containsBlacklistedChecksum(checksum)) {
            try {
                CheckResult checkResult = database.getCheckResultForChecksum(checksum);
                if (checkResult != null) {
                    if (commandLineParser.dontLogINFOCR() && checkResult.getType().equals("INFO")) {
                        return;
                    }
                    sendNotification(file, checkResult);
                    return;
                }
                System.out.println(I18n.tl("scanner_blacklisted_not_in_database", file));
            } catch (SQLException ex) {
                LOGGER.exception(ex);
            }
        }
        try ( FileSystem fs = Utils.fileSystemForZip(file)) {
            if (fs == null) {
                return;
            }
            Path rootFolder = fs.getRootDirectories().iterator().next();
            if (commandLineParser.shouldScanZippedFiles()) {
                if (fileName.endsWith(".zip") || fileName.endsWith(".rar")) {
                    scanZip(file);
                    return;
                }
            }
            WhitelistResult result = isFileWhitelisted(file);
            if (result == WhitelistResult.INVALID_FILE || result == WhitelistResult.WHITELISTED) {
                return;
            }
            if (Files.exists(rootFolder.resolve("dev/jnic/lib/"))) {
                sendNotification(file, new CheckResult("Spigot", "JNIC", "A"));
                return;
            }
            boolean possiblyMalicious = false;
            Stream<Path> validClasses = walkThroughFiles(rootFolder);
            Iterator<Path> validClassIterator = validClasses.iterator();
            while (validClassIterator.hasNext()) {
                Path classPath = validClassIterator.next();
                if (!validClassPath(classPath)) {
                    continue;
                }
                ClassNode classNode = cache.fetchClass(file, classPath);
                if (classNode == null) {
                    continue;
                }
                for (BaseCheck check : checkManager.getChecks()) {
                    List<CheckResult> results = check.process(classNode, rootFolder, file, cache);
                    if (results != null && !results.isEmpty()) {
                        possiblyMalicious = true;
                        for (CheckResult checkResult : results) {
                            if (commandLineParser.dontLogINFOCR() && checkResult.getType().equals("INFO")) {
                                return;
                            }
                            if (file.startsWith("AntiMalware\\unzipped")) {
                                File infoFile = new File(file.toFile().getAbsoluteFile().getParent(), ".info");
                                if (infoFile.exists()) {
                                    try {
                                        String originalFile = FileUtils.readLines(infoFile, StandardCharsets.UTF_8).get(0);
                                        sendNotification(file, checkResult);
                                        sendNotification(new File(originalFile).toPath(), checkResult);
                                    } catch (IOException ex) {
                                        LOGGER.exception(ex);
                                    }
                                }
                            } else {
                                if (checkResult != null) {
                                    sendNotification(file, checkResult);
                                }
                            }
                        }
                    }
                    check.reset();
                }
            }
            cache.clearCache(file); // Attempt at fixing memory issues
            if (commandLineParser.shouldPrintNotInfectedMessages() && !possiblyMalicious) {
                LOGGER.info(I18n.tl("scanner_probably_safe", file));
            }
            if (zippedFilePath != null) {
                zippedFilePath = null;
            }
        } catch (IOException ex) {
            LOGGER.exception(ex);
        }
    }

    public enum WhitelistResult {
        INVALID_FILE, NOT_WHITELISTED, WHITELISTED
    }

    private WhitelistResult isFileWhitelisted(Path file) {
        try {
            if (file == null || Files.size(file) == 0) {
                return WhitelistResult.INVALID_FILE;
            }
            try {
                Path avPath = new File(AntiMalware.class.getProtectionDomain().getCodeSource().getLocation().toURI()).toPath();
                if (Files.isSameFile(file, avPath)) {
                    return WhitelistResult.WHITELISTED;
                }
            } catch (URISyntaxException ex) {
                return WhitelistResult.INVALID_FILE;
            }
            String fileChecksum = DigestUtils.sha1Hex(Files.newInputStream(file));
            WhitelistResult result = isChecksumWhitelisted(fileChecksum);
            if (result == WhitelistResult.WHITELISTED && commandLineParser.shouldPrintNotInfectedMessages()) {
                LOGGER.info(I18n.tl("scanner_probably_safe_whitelisted", file.getFileName().toString()));
            }
            return result;
        } catch (IOException e) {
            LOGGER.exception(e);
        }
        return WhitelistResult.NOT_WHITELISTED;
    }

    public void addFileToQueue(Path file) {
        if (Files.isDirectory(file)) {
            addDirectoryToQueue(file);
            return;
        }
        SCANNABLE_FILES.add(file);
        executorService.execute(this);
    }

    public void addDirectoryToQueue(Path dir) {
        if (!Files.isDirectory(dir)) {
            return;
        }
        try {
            Files.list(dir).forEach(this::addFileToQueue);
        } catch (IOException e) {
            LOGGER.exception(e);
        }
    }

    public boolean awaitTermination(int time, TimeUnit unit) {
        try {
            return executorService.awaitTermination(time, unit);
        } catch (InterruptedException e) {
        }
        return false;
    }

    private WhitelistResult isChecksumWhitelisted(String checksum) {
        return antiMalware.getCache().containsWhitelistedChecksum(checksum) ? WhitelistResult.WHITELISTED
                : WhitelistResult.NOT_WHITELISTED;
    }

    public enum Status {
        SCANNING, WAITING, NOT_RUNNING
    }

    public Status getStatus() {
        if (executorService == null) {
            return Status.NOT_RUNNING;
        } else if (executorService.getTaskCount() == 0) {
            return Status.WAITING;
        } else {
            return Status.SCANNING;
        }
    }

    public File getScanDirectory() {
        return scanDirectory;
    }

    public AntiMalware getAntiMalware() {
        return antiMalware;
    }

    public void sendNotification(Path path, CheckResult result) {
        // TODO: Make these translatable
        Objects.requireNonNull(path, "Path cannot be null");
        Objects.requireNonNull(result, "CheckResult cannot be null");
        notificationHandler.sendNotification(path, result);
    }

    // Should be in "Utils" but i can't think of a good way of implementing this as is.
    protected Stream<Path> walkThroughFiles(Path dir) {
        try {
            if (dir.getFileName() != null && dir.getFileName().toString().equals(".")) {
                return Stream.of();
            } else if (Files.isDirectory(dir, LinkOption.NOFOLLOW_LINKS)) {
                try {
                    return Files.list(dir).filter(path -> !Files.isSymbolicLink(path)).flatMap(this::walkThroughFiles);
                } catch (IOException e) {
                    LOGGER.exception(e);
                    return Stream.of();
                }
            } else if (Files.isSymbolicLink(dir)) {
                return Stream.of();
            } else {
                return Stream.of(dir);
            }
        } catch (StackOverflowError ex) {
            LOGGER.severe(I18n.tl("scanner_stackoverflow", dir));
        }
        return Stream.of();
    }

    public boolean validClassPath(Path classPath) {
        return classPath.toString().endsWith(".class") && !classPath.toString().contains("__MACOSX");
    }

}

36e5a0cc6c1fbc09bff25773d8af71863ab484de

@github-actions github-actions bot added the todo label Mar 30, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

0 participants