diff --git a/src/main/java/com/bartek/esa/EsaMain.java b/src/main/java/com/bartek/esa/EsaMain.java index c9ea10c..aab71b6 100644 --- a/src/main/java/com/bartek/esa/EsaMain.java +++ b/src/main/java/com/bartek/esa/EsaMain.java @@ -4,7 +4,6 @@ import com.bartek.esa.analyser.apk.ApkAnalyser; import com.bartek.esa.analyser.source.SourceAnalyser; import com.bartek.esa.cli.model.CliArgsOptions; import com.bartek.esa.cli.parser.CliArgsParser; -import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.object.Issue; import com.bartek.esa.di.DaggerDependencyInjector; import com.bartek.esa.dispatcher.dispatcher.MethodDispatcher; @@ -12,7 +11,7 @@ import com.bartek.esa.dispatcher.model.DispatcherActions; import com.bartek.esa.formatter.provider.FormatterProvider; import javax.inject.Inject; -import java.util.List; +import java.util.Set; public class EsaMain { private final CliArgsParser cliArgsParser; @@ -37,14 +36,14 @@ public class EsaMain { .build(); CliArgsOptions options = cliArgsParser.parse(args); - List issues = methodDispatcher.dispatchMethod(options, dispatcherActions); + Set issues = methodDispatcher.dispatchMethod(options, dispatcherActions); formatterProvider.provide(options).format(issues); exitWithErrorIfAnyIssueIsAnError(issues); } - private void exitWithErrorIfAnyIssueIsAnError(List issues) { - if(issues.stream().anyMatch(i -> i.getSeverity() == Severity.ERROR)) { + private void exitWithErrorIfAnyIssueIsAnError(Set issues) { + if(issues.stream().anyMatch(i -> i.getSeverity().isExitWithError())) { System.exit(1); } } diff --git a/src/main/java/com/bartek/esa/analyser/apk/ApkAnalyser.java b/src/main/java/com/bartek/esa/analyser/apk/ApkAnalyser.java index 2dae5f3..67dca4a 100644 --- a/src/main/java/com/bartek/esa/analyser/apk/ApkAnalyser.java +++ b/src/main/java/com/bartek/esa/analyser/apk/ApkAnalyser.java @@ -4,6 +4,9 @@ import com.bartek.esa.analyser.core.Analyser; import com.bartek.esa.core.archetype.Plugin; import com.bartek.esa.core.executor.PluginExecutor; import com.bartek.esa.decompiler.decompiler.Decompiler; +import com.bartek.esa.error.EsaException; +import com.bartek.esa.file.cleaner.FileCleaner; +import com.bartek.esa.file.matcher.GlobMatcher; import com.bartek.esa.file.provider.FileProvider; import java.io.File; @@ -15,17 +18,29 @@ public class ApkAnalyser extends Analyser { private static final String LAYOUT_GLOB = "**/" + Decompiler.XML_FILES_DIR + "/**/layout*/*.xml"; private final Decompiler decompiler; + private final FileCleaner fileCleaner; + private final GlobMatcher globMatcher; - public ApkAnalyser(PluginExecutor pluginExecutor, Set plugins, FileProvider fileProvider, Decompiler decompiler) { + public ApkAnalyser(PluginExecutor pluginExecutor, Set plugins, FileProvider fileProvider, Decompiler decompiler, FileCleaner fileCleaner, GlobMatcher globMatcher) { super(pluginExecutor, plugins, fileProvider); this.decompiler = decompiler; + this.fileCleaner = fileCleaner; + this.globMatcher = globMatcher; } @Override protected String prepareSources(String source) { + checkIfSourceIsApkFile(source); + System.out.println("Decompiling APK..."); return decompiler.decompile(new File(source)).getAbsolutePath(); } + private void checkIfSourceIsApkFile(String source) { + if (!globMatcher.fileMatchesGlobPattern(new File(source), "**/*.apk")) { + throw new EsaException("Provided source is not *.apk file. Interrupting..."); + } + } + @Override protected String getAndroidManifestGlob() { return ANDROID_MANIFEST_GLOB; @@ -40,4 +55,9 @@ public class ApkAnalyser extends Analyser { protected String getLayoutGlob() { return LAYOUT_GLOB; } + + @Override + protected void performCleaning(String source) { + fileCleaner.deleteRecursively(new File(source)); + } } diff --git a/src/main/java/com/bartek/esa/analyser/core/Analyser.java b/src/main/java/com/bartek/esa/analyser/core/Analyser.java index 0f3eb4c..a4ba6b6 100644 --- a/src/main/java/com/bartek/esa/analyser/core/Analyser.java +++ b/src/main/java/com/bartek/esa/analyser/core/Analyser.java @@ -8,7 +8,6 @@ import com.bartek.esa.file.provider.FileProvider; import java.io.File; import java.util.Collections; -import java.util.List; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -25,13 +24,15 @@ public abstract class Analyser { this.fileProvider = fileProvider; } - public List analyse(String source, Set pluginCodes, Set excludeCodes) { + public Set analyse(String source, Set pluginCodes, Set excludeCodes) { String newSource = prepareSources(source); File manifest = getManifest(newSource); Set files = getFiles(newSource); Set selectedPlugins = getPlugins(pluginCodes, excludeCodes); - return pluginExecutor.executeForFiles(manifest, files, selectedPlugins); + Set issues = pluginExecutor.executeForFiles(manifest, files, selectedPlugins); + performCleaning(newSource); + return issues; } protected abstract String prepareSources(String source); @@ -42,6 +43,8 @@ public abstract class Analyser { protected abstract String getLayoutGlob(); + protected abstract void performCleaning(String source); + private File getManifest(String source) { Set manifests = fileProvider.getGlobMatchedFiles(source, getAndroidManifestGlob()); diff --git a/src/main/java/com/bartek/esa/analyser/di/AnalyserModule.java b/src/main/java/com/bartek/esa/analyser/di/AnalyserModule.java index 3087dd5..f977f89 100644 --- a/src/main/java/com/bartek/esa/analyser/di/AnalyserModule.java +++ b/src/main/java/com/bartek/esa/analyser/di/AnalyserModule.java @@ -5,6 +5,8 @@ import com.bartek.esa.analyser.source.SourceAnalyser; import com.bartek.esa.core.archetype.Plugin; import com.bartek.esa.core.executor.PluginExecutor; import com.bartek.esa.decompiler.decompiler.Decompiler; +import com.bartek.esa.file.cleaner.FileCleaner; +import com.bartek.esa.file.matcher.GlobMatcher; import com.bartek.esa.file.provider.FileProvider; import dagger.Module; import dagger.Provides; @@ -20,7 +22,7 @@ public class AnalyserModule { } @Provides - public ApkAnalyser apkAnalyser(PluginExecutor pluginExecutor, Set plugins, FileProvider fileProvider, Decompiler decompiler) { - return new ApkAnalyser(pluginExecutor, plugins, fileProvider, decompiler); + public ApkAnalyser apkAnalyser(PluginExecutor pluginExecutor, Set plugins, FileProvider fileProvider, Decompiler decompiler, FileCleaner fileCleaner, GlobMatcher globMatcher) { + return new ApkAnalyser(pluginExecutor, plugins, fileProvider, decompiler, fileCleaner, globMatcher); } } diff --git a/src/main/java/com/bartek/esa/analyser/source/SourceAnalyser.java b/src/main/java/com/bartek/esa/analyser/source/SourceAnalyser.java index 6374ee9..97a9fed 100644 --- a/src/main/java/com/bartek/esa/analyser/source/SourceAnalyser.java +++ b/src/main/java/com/bartek/esa/analyser/source/SourceAnalyser.java @@ -3,8 +3,10 @@ package com.bartek.esa.analyser.source; import com.bartek.esa.analyser.core.Analyser; import com.bartek.esa.core.archetype.Plugin; import com.bartek.esa.core.executor.PluginExecutor; +import com.bartek.esa.error.EsaException; import com.bartek.esa.file.provider.FileProvider; +import java.io.File; import java.util.Set; public class SourceAnalyser extends Analyser { @@ -18,9 +20,16 @@ public class SourceAnalyser extends Analyser { @Override protected String prepareSources(String source) { + checkIfSourceIsDirectory(source); return source; } + private void checkIfSourceIsDirectory(String source) { + if (!new File(source).isDirectory()) { + throw new EsaException("Provided source is not a directory. Interrupting..."); + } + } + @Override protected String getAndroidManifestGlob() { return ANDROID_MANIFEST_GLOB; @@ -35,4 +44,9 @@ public class SourceAnalyser extends Analyser { protected String getLayoutGlob() { return LAYOUT_GLOB; } + + @Override + protected void performCleaning(String source) { + // do nothing + } } diff --git a/src/main/java/com/bartek/esa/core/archetype/BasePlugin.java b/src/main/java/com/bartek/esa/core/archetype/BasePlugin.java index 391ac16..fbcd814 100644 --- a/src/main/java/com/bartek/esa/core/archetype/BasePlugin.java +++ b/src/main/java/com/bartek/esa/core/archetype/BasePlugin.java @@ -5,11 +5,11 @@ import com.bartek.esa.core.model.object.Issue; import org.w3c.dom.Document; import java.io.File; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; +import java.util.Set; public abstract class BasePlugin implements Plugin { - private List issues = new ArrayList<>(); + private Set issues = new HashSet<>(); private Document manifest; private File file; @@ -21,7 +21,7 @@ public abstract class BasePlugin implements Plugin { } @Override - public List runForIssues() { + public Set runForIssues() { run(file); return issues; } @@ -45,6 +45,10 @@ public abstract class BasePlugin implements Plugin { issues.add(issue); } + protected void addIssue(Issue issue) { + issues.add(issue); + } + protected File getOriginalFile() { return file; } diff --git a/src/main/java/com/bartek/esa/core/archetype/JavaPlugin.java b/src/main/java/com/bartek/esa/core/archetype/JavaPlugin.java index d617c13..b438f82 100644 --- a/src/main/java/com/bartek/esa/core/archetype/JavaPlugin.java +++ b/src/main/java/com/bartek/esa/core/archetype/JavaPlugin.java @@ -1,6 +1,7 @@ package com.bartek.esa.core.archetype; import com.bartek.esa.core.model.enumeration.Severity; +import com.bartek.esa.core.model.object.Issue; import com.bartek.esa.core.xml.XmlHelper; import com.bartek.esa.error.EsaException; import com.bartek.esa.file.matcher.GlobMatcher; @@ -43,7 +44,14 @@ public abstract class JavaPlugin extends BasePlugin { Node packageValue = root.getAttributes().getNamedItem("package"); if(packageValue == null) { - addIssue(Severity.ERROR, ".PACKAGE_LACK", null, null); + Issue issue = Issue.builder() + .issuer(JavaPlugin.class) + .descriptionCode(".NO_PACKAGE") + .severity(Severity.ERROR) + .build(); + + addIssue(issue); + return false; } diff --git a/src/main/java/com/bartek/esa/core/archetype/Plugin.java b/src/main/java/com/bartek/esa/core/archetype/Plugin.java index ddbf1d4..ffbd990 100644 --- a/src/main/java/com/bartek/esa/core/archetype/Plugin.java +++ b/src/main/java/com/bartek/esa/core/archetype/Plugin.java @@ -4,10 +4,10 @@ import com.bartek.esa.core.model.object.Issue; import org.w3c.dom.Document; import java.io.File; -import java.util.List; +import java.util.Set; public interface Plugin { boolean supports(File file); void update(File file, Document manifest); - List runForIssues(); + Set runForIssues(); } diff --git a/src/main/java/com/bartek/esa/core/executor/PluginExecutor.java b/src/main/java/com/bartek/esa/core/executor/PluginExecutor.java index 10d0041..3560e12 100644 --- a/src/main/java/com/bartek/esa/core/executor/PluginExecutor.java +++ b/src/main/java/com/bartek/esa/core/executor/PluginExecutor.java @@ -7,10 +7,9 @@ import org.w3c.dom.Document; import javax.inject.Inject; import java.io.File; -import java.util.List; import java.util.Set; -import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toSet; public class PluginExecutor { private final XmlHelper xmlHelper; @@ -20,20 +19,20 @@ public class PluginExecutor { this.xmlHelper = xmlHelper; } - public List executeForFiles(File manifest, Set files, Set plugins) { + public Set executeForFiles(File manifest, Set files, Set plugins) { return files.stream() .map(file -> executeForFile(manifest, file, plugins)) - .flatMap(List::stream) - .collect(toList()); + .flatMap(Set::stream) + .collect(toSet()); } - private List executeForFile(File manifest, File file, Set plugins) { + private Set executeForFile(File manifest, File file, Set plugins) { Document xmlManifest = xmlHelper.parseXml(manifest); return plugins.stream() .peek(plugin -> plugin.update(file, xmlManifest)) .filter(plugin -> plugin.supports(file)) .map(Plugin::runForIssues) - .flatMap(List::stream) - .collect(toList()); + .flatMap(Set::stream) + .collect(toSet()); } } diff --git a/src/main/java/com/bartek/esa/core/model/enumeration/Severity.java b/src/main/java/com/bartek/esa/core/model/enumeration/Severity.java index 78b81ea..81dfd5f 100644 --- a/src/main/java/com/bartek/esa/core/model/enumeration/Severity.java +++ b/src/main/java/com/bartek/esa/core/model/enumeration/Severity.java @@ -1,6 +1,17 @@ package com.bartek.esa.core.model.enumeration; +import lombok.Getter; + +@Getter public enum Severity { - WARNING, - ERROR + INFO(false), + WARNING(false), + ERROR(true), + VULNERABILITY(true); + + private final boolean exitWithError; + + Severity(boolean exitWithError) { + this.exitWithError = exitWithError; + } } diff --git a/src/main/java/com/bartek/esa/dispatcher/dispatcher/MethodDispatcher.java b/src/main/java/com/bartek/esa/dispatcher/dispatcher/MethodDispatcher.java index 3dc20b4..5b00209 100644 --- a/src/main/java/com/bartek/esa/dispatcher/dispatcher/MethodDispatcher.java +++ b/src/main/java/com/bartek/esa/dispatcher/dispatcher/MethodDispatcher.java @@ -6,7 +6,7 @@ import com.bartek.esa.dispatcher.model.DispatcherActions; import javax.inject.Inject; import java.util.Collections; -import java.util.List; +import java.util.Set; public class MethodDispatcher { @@ -15,7 +15,7 @@ public class MethodDispatcher { } - public List dispatchMethod(CliArgsOptions options, DispatcherActions actions) { + public Set dispatchMethod(CliArgsOptions options, DispatcherActions actions) { if(options.isSourceAnalysis()) { return actions.getSourceAnalysis().perform( options.getSourceAnalysisDirectory(), @@ -32,6 +32,6 @@ public class MethodDispatcher { ); } - return Collections.emptyList(); + return Collections.emptySet(); } } diff --git a/src/main/java/com/bartek/esa/dispatcher/model/Action.java b/src/main/java/com/bartek/esa/dispatcher/model/Action.java index b7ba664..cf5af89 100644 --- a/src/main/java/com/bartek/esa/dispatcher/model/Action.java +++ b/src/main/java/com/bartek/esa/dispatcher/model/Action.java @@ -2,11 +2,10 @@ package com.bartek.esa.dispatcher.model; import com.bartek.esa.core.model.object.Issue; -import java.util.List; import java.util.Set; @FunctionalInterface public interface Action { - List perform(String source, Set plugins, Set excludes); + Set perform(String source, Set plugins, Set excludes); } diff --git a/src/main/java/com/bartek/esa/formatter/archetype/Formatter.java b/src/main/java/com/bartek/esa/formatter/archetype/Formatter.java index cac7e43..a84e644 100644 --- a/src/main/java/com/bartek/esa/formatter/archetype/Formatter.java +++ b/src/main/java/com/bartek/esa/formatter/archetype/Formatter.java @@ -2,8 +2,8 @@ package com.bartek.esa.formatter.archetype; import com.bartek.esa.core.model.object.Issue; -import java.util.List; +import java.util.Set; public interface Formatter { - void format(List issues); + void format(Set issues); } diff --git a/src/main/java/com/bartek/esa/formatter/formatter/ColorFormatter.java b/src/main/java/com/bartek/esa/formatter/formatter/ColorFormatter.java index fdae927..34ee131 100644 --- a/src/main/java/com/bartek/esa/formatter/formatter/ColorFormatter.java +++ b/src/main/java/com/bartek/esa/formatter/formatter/ColorFormatter.java @@ -7,8 +7,9 @@ import org.fusesource.jansi.Ansi; import org.fusesource.jansi.AnsiConsole; import javax.inject.Inject; -import java.util.List; +import java.util.Arrays; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import static org.fusesource.jansi.Ansi.Color.*; @@ -24,9 +25,9 @@ public class ColorFormatter implements Formatter { } @Override - public void format(List issues) { + public void format(Set issues) { AnsiConsole.systemInstall(); - if(issues.isEmpty()) { + if (issues.isEmpty()) { Ansi noIssuesFound = ansi().fg(GREEN).a("No issues found.").reset(); System.out.println(noIssuesFound); return; @@ -52,29 +53,36 @@ public class ColorFormatter implements Formatter { private Ansi appendDescription(Issue issue, Ansi ansi) { String description = descriptionProvider.getDescriptionForIssue(issue); + String[] lines = description.split("\n"); + String firstLine = lines[0]; + String theRestOfLines = lines.length > 1 ? "\n" + String.join("\n", Arrays.copyOfRange(lines, 1, lines.length)) : ""; return ansi .fg(getColorForSeverity(issue)) .a(issue.getSeverity().name()) .reset() - .a(": ").a(description) + .a(": ").a(firstLine) + .fgBrightBlack().a(theRestOfLines) + .reset() .a("\n"); } private Ansi appendFile(Issue issue, Ansi ansi) { - return ansi - .fg(GREEN) - .a("File: ") - .reset() - .a(issue.getFile().getAbsolutePath()) - .a("\n"); + return Optional.ofNullable(issue.getFile()) + .map(file -> ansi + .fg(BLUE) + .a("File: ") + .reset() + .a(file.getAbsolutePath()) + .a("\n")) + .orElse(ansi); } private Ansi appendLine(Issue issue, Ansi ansi) { Optional.ofNullable(issue.getLine()) .ifPresent(line -> { ansi - .fg(BLUE) + .fg(CYAN) .a("Line"); Optional.ofNullable(issue.getLineNumber()).ifPresentOrElse( number -> ansi.a(" ").a(number).a(": "), @@ -86,9 +94,15 @@ public class ColorFormatter implements Formatter { } private Ansi.Color getColorForSeverity(Issue issue) { - switch(issue.getSeverity()) { - case WARNING: return YELLOW; - case ERROR: return RED; + switch (issue.getSeverity()) { + case INFO: + return GREEN; + case WARNING: + return YELLOW; + case ERROR: + return MAGENTA; + case VULNERABILITY: + return RED; } return RED; diff --git a/src/main/java/com/bartek/esa/formatter/formatter/SimpleFormatter.java b/src/main/java/com/bartek/esa/formatter/formatter/SimpleFormatter.java index 606da51..aae9181 100644 --- a/src/main/java/com/bartek/esa/formatter/formatter/SimpleFormatter.java +++ b/src/main/java/com/bartek/esa/formatter/formatter/SimpleFormatter.java @@ -5,8 +5,8 @@ import com.bartek.esa.core.model.object.Issue; import com.bartek.esa.formatter.archetype.Formatter; import javax.inject.Inject; -import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; public class SimpleFormatter implements Formatter { @@ -18,8 +18,8 @@ public class SimpleFormatter implements Formatter { } @Override - public void format(List issues) { - if(issues.isEmpty()) { + public void format(Set issues) { + if (issues.isEmpty()) { System.out.println("No issues found."); return; } @@ -50,10 +50,13 @@ public class SimpleFormatter implements Formatter { } private void appendFile(Issue issue, StringBuilder format) { - format - .append("File: ") - .append(issue.getFile().getAbsolutePath()) - .append("\n"); + Optional.ofNullable(issue.getFile()) + .ifPresent(file -> + format + .append("File: ") + .append(file.getAbsolutePath()) + .append("\n") + ); } private void appendLine(Issue issue, StringBuilder format) { diff --git a/src/main/resources/description.properties b/src/main/resources/description.properties index 39d333c..7efa949 100644 --- a/src/main/resources/description.properties +++ b/src/main/resources/description.properties @@ -1 +1,4 @@ -com.bartek.esa.core.plugin.JavaPlugin.PACKAGE_LACK=There is no package defined in AndroidManifest.xml file. Please check fix to use this tool. \ No newline at end of file +com.bartek.esa.core.archetype.JavaPlugin.NO_PACKAGE=There is no package defined in AndroidManifest.xml file. \n\ + Package should be defined as attribute of tag.\n\ + For example: \n\ + Please fix it to use this tool. \ No newline at end of file