Merge branch '6-create-analyser-and-auditperformer' into 'master'

Resolve "Create Analyser and AuditPerformer"

Closes #6

See merge request bartlomiej.pluta/esa-tool!6
This commit is contained in:
Bartłomiej Pluta
2019-04-03 08:34:52 +00:00
13 changed files with 297 additions and 20 deletions

View File

@@ -1,32 +1,40 @@
package com.bartek.esa;
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.object.Issue;
import com.bartek.esa.di.DaggerDependencyInjector;
import com.bartek.esa.dispatcher.dispatcher.MethodDispatcher;
import com.bartek.esa.dispatcher.model.DispatcherActions;
import javax.inject.Inject;
import java.util.List;
public class EsaMain {
private final CliArgsParser cliArgsParser;
private final MethodDispatcher methodDispatcher;
private final SourceAnalyser sourceAnalyser;
private final ApkAnalyser apkAnalyser;
@Inject
EsaMain(CliArgsParser cliArgsParser, MethodDispatcher methodDispatcher) {
EsaMain(CliArgsParser cliArgsParser, MethodDispatcher methodDispatcher, SourceAnalyser sourceAnalyser, ApkAnalyser apkAnalyser) {
this.cliArgsParser = cliArgsParser;
this.methodDispatcher = methodDispatcher;
this.sourceAnalyser = sourceAnalyser;
this.apkAnalyser = apkAnalyser;
}
private void run(String[] args) {
DispatcherActions dispatcherActions = DispatcherActions.builder()
.sourceAnalysis(source -> {})
.apkAudit(source -> {})
.sourceAnalysis(sourceAnalyser::analyse)
.apkAudit(apkAnalyser::analyse)
.build();
CliArgsOptions options = cliArgsParser.parse(args);
methodDispatcher.dispatchMethod(options, dispatcherActions);
List<Issue> issues = methodDispatcher.dispatchMethod(options, dispatcherActions);
}
public static void main(String[] args) {

View File

@@ -0,0 +1,43 @@
package com.bartek.esa.analyser.apk;
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.file.provider.FileProvider;
import java.io.File;
import java.util.Set;
public class ApkAnalyser extends Analyser {
private static final String ANDROID_MANIFEST_GLOB = "**/" + Decompiler.XML_FILES_DIR + "/AndroidManifest.xml";
private static final String JAVA_GLOB = "**/" + Decompiler.JAVA_FILES_DIR + "/**/*.java";
private static final String LAYOUT_GLOB = "**/" + Decompiler.XML_FILES_DIR + "/**/layout*/*.xml";
private final Decompiler decompiler;
public ApkAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider, Decompiler decompiler) {
super(pluginExecutor, plugins, fileProvider);
this.decompiler = decompiler;
}
@Override
protected String prepareSources(String source) {
return decompiler.decompile(new File(source)).getAbsolutePath();
}
@Override
protected String getAndroidManifestGlob() {
return ANDROID_MANIFEST_GLOB;
}
@Override
protected String getJavaGlob() {
return JAVA_GLOB;
}
@Override
protected String getLayoutGlob() {
return LAYOUT_GLOB;
}
}

View File

@@ -0,0 +1,92 @@
package com.bartek.esa.analyser.core;
import com.bartek.esa.core.archetype.Plugin;
import com.bartek.esa.core.executor.PluginExecutor;
import com.bartek.esa.core.model.object.Issue;
import com.bartek.esa.error.EsaException;
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;
public abstract class Analyser {
private final PluginExecutor pluginExecutor;
private final Set<Plugin> plugins;
private final FileProvider fileProvider;
public Analyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider) {
this.pluginExecutor = pluginExecutor;
this.plugins = plugins;
this.fileProvider = fileProvider;
}
public List<Issue> analyse(String source, Set<String> pluginCodes, Set<String> excludeCodes) {
String newSource = prepareSources(source);
File manifest = getManifest(newSource);
Set<File> files = getFiles(newSource);
Set<Plugin> selectedPlugins = getPlugins(pluginCodes, excludeCodes);
return pluginExecutor.executeForFiles(manifest, files, selectedPlugins);
}
protected abstract String prepareSources(String source);
protected abstract String getAndroidManifestGlob();
protected abstract String getJavaGlob();
protected abstract String getLayoutGlob();
private File getManifest(String source) {
Set<File> manifests = fileProvider.getGlobMatchedFiles(source, getAndroidManifestGlob());
if (manifests.isEmpty()) {
throw new EsaException("No AndroidManifest.xml file found. Interrupting...");
}
if (manifests.size() > 1) {
throw new EsaException("Found multiple AndroidManifest.xml files. Interrupting...");
}
return (File) (manifests.toArray())[0];
}
private Set<File> getFiles(String source) {
Set<File> javaFiles = fileProvider.getGlobMatchedFiles(source, getJavaGlob());
Set<File> layoutFiles = fileProvider.getGlobMatchedFiles(source, getLayoutGlob());
Set<File> androidManifest = Collections.singleton(getManifest(source));
return Stream.of(javaFiles, androidManifest, layoutFiles)
.flatMap(Set::stream)
.collect(Collectors.toSet());
}
private Set<Plugin> getPlugins(Set<String> pluginCodes, Set<String> excludeCodes) {
Set<Plugin> outputPlugins = plugins;
if (!pluginCodes.isEmpty()) {
outputPlugins = plugins.stream()
.filter(plugin -> pluginCodes
.stream()
.anyMatch(pluginCode -> plugin.getClass().getCanonicalName().equals(pluginCode))
)
.collect(Collectors.toSet());
}
if(!excludeCodes.isEmpty()) {
outputPlugins = outputPlugins.stream()
.filter(plugin -> excludeCodes
.stream()
.noneMatch(pluginCode -> plugin.getClass().getCanonicalName().equals(pluginCode))
)
.collect(Collectors.toSet());
}
return outputPlugins;
}
}

View File

@@ -0,0 +1,26 @@
package com.bartek.esa.analyser.di;
import com.bartek.esa.analyser.apk.ApkAnalyser;
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.provider.FileProvider;
import dagger.Module;
import dagger.Provides;
import java.util.Set;
@Module
public class AnalyserModule {
@Provides
public SourceAnalyser sourceAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider) {
return new SourceAnalyser(pluginExecutor, plugins, fileProvider);
}
@Provides
public ApkAnalyser apkAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider, Decompiler decompiler) {
return new ApkAnalyser(pluginExecutor, plugins, fileProvider, decompiler);
}
}

View File

@@ -0,0 +1,38 @@
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.file.provider.FileProvider;
import java.util.Set;
public class SourceAnalyser extends Analyser {
private static final String ANDROID_MANIFEST_GLOB = "**/AndroidManifest.xml";
private static final String JAVA_GLOB = "**/*.java";
private static final String LAYOUT_GLOB = "**/res/layout*/*.xml";
public SourceAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider) {
super(pluginExecutor, plugins, fileProvider);
}
@Override
protected String prepareSources(String source) {
return source;
}
@Override
protected String getAndroidManifestGlob() {
return ANDROID_MANIFEST_GLOB;
}
@Override
protected String getJavaGlob() {
return JAVA_GLOB;
}
@Override
protected String getLayoutGlob() {
return LAYOUT_GLOB;
}
}

View File

@@ -1,18 +1,25 @@
package com.bartek.esa.core.archetype;
import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.error.EsaException;
import com.bartek.esa.file.matcher.GlobMatcher;
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import io.vavr.control.Try;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import javax.xml.xpath.XPathConstants;
import java.io.File;
public abstract class JavaPlugin extends BasePlugin {
private final GlobMatcher globMatcher;
private final XmlHelper xmlHelper;
public JavaPlugin(GlobMatcher globMatcher) {
public JavaPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
this.globMatcher = globMatcher;
this.xmlHelper = xmlHelper;
}
@Override
@@ -22,9 +29,27 @@ public abstract class JavaPlugin extends BasePlugin {
@Override
protected void run(File file) {
if(!isApplicationPackageFile(file)) {
return;
}
CompilationUnit compilationUnit = Try.of(() -> StaticJavaParser.parse(file)).getOrElseThrow(EsaException::new);
run(compilationUnit);
}
private boolean isApplicationPackageFile(File file) {
Document manifest = getManifest();
Node root = (Node) xmlHelper.xPath(manifest, "/manifest", XPathConstants.NODE);
Node packageValue = root.getAttributes().getNamedItem("package");
if(packageValue == null) {
addIssue(Severity.ERROR, ".PACKAGE_LACK", null, null);
return false;
}
String path = packageValue.getNodeValue().replaceAll("\\.", "/");
return globMatcher.fileMatchesGlobPattern(file, String.format("**/%s/**", path));
}
public abstract void run(CompilationUnit compilationUnit);
}

View File

@@ -0,0 +1,19 @@
package com.bartek.esa.core.di;
import com.bartek.esa.core.archetype.Plugin;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.ElementsIntoSet;
import java.util.HashSet;
import java.util.Set;
@Module
public class PluginModule {
@Provides
@ElementsIntoSet
public Set<Plugin> plugins() {
return new HashSet<>();
}
}

View File

@@ -8,6 +8,7 @@ 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;
@@ -19,21 +20,19 @@ public class PluginExecutor {
this.xmlHelper = xmlHelper;
}
public List<Issue> executeForFiles(File manifest, List<File> files, List<Plugin> plugins) {
public List<Issue> executeForFiles(File manifest, Set<File> files, Set<Plugin> plugins) {
return files.stream()
.map(file -> executeForFile(manifest, file, plugins))
.flatMap(List::stream)
.collect(toList());
}
private List<Issue> executeForFile(File manifest, File file, List<Plugin> plugins) {
private List<Issue> executeForFile(File manifest, File file, Set<Plugin> plugins) {
Document xmlManifest = xmlHelper.parseXml(manifest);
return plugins.stream()
.peek(plugin -> plugin.update(file, xmlManifest))
.filter(plugin -> plugin.supports(file))
.map(plugin -> {
plugin.update(file, xmlManifest);
return plugin.runForIssues();
})
.map(Plugin::runForIssues)
.flatMap(List::stream)
.collect(toList());
}

View File

@@ -1,8 +1,10 @@
package com.bartek.esa.di;
import com.bartek.esa.EsaMain;
import com.bartek.esa.analyser.di.AnalyserModule;
import com.bartek.esa.cli.di.CliModule;
import com.bartek.esa.core.di.CoreModule;
import com.bartek.esa.core.di.PluginModule;
import com.bartek.esa.decompiler.di.DecompilerModule;
import com.bartek.esa.dispatcher.di.DispatcherModule;
import com.bartek.esa.file.di.FileModule;
@@ -13,7 +15,9 @@ import dagger.Component;
DispatcherModule.class,
FileModule.class,
DecompilerModule.class,
CoreModule.class
CoreModule.class,
PluginModule.class,
AnalyserModule.class
})
public interface DependencyInjector {
EsaMain esa();

View File

@@ -1,9 +1,12 @@
package com.bartek.esa.dispatcher.dispatcher;
import com.bartek.esa.cli.model.CliArgsOptions;
import com.bartek.esa.core.model.object.Issue;
import com.bartek.esa.dispatcher.model.DispatcherActions;
import javax.inject.Inject;
import java.util.Collections;
import java.util.List;
public class MethodDispatcher {
@@ -12,14 +15,23 @@ public class MethodDispatcher {
}
public void dispatchMethod(CliArgsOptions options, DispatcherActions actions) {
public List<Issue> dispatchMethod(CliArgsOptions options, DispatcherActions actions) {
if(options.isSourceAnalysis()) {
actions.getSourceAnalysis().accept(options.getSourceAnalysisDirectory());
return;
return actions.getSourceAnalysis().perform(
options.getSourceAnalysisDirectory(),
options.getPlugins(),
options.getExcludes()
);
}
if(options.isApkAudit()) {
actions.getApkAudit().accept(options.getApkAuditFile());
return actions.getApkAudit().perform(
options.getApkAuditFile(),
options.getPlugins(),
options.getExcludes()
);
}
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,12 @@
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<Issue> perform(String source, Set<String> plugins, Set<String> excludes);
}

View File

@@ -3,11 +3,9 @@ package com.bartek.esa.dispatcher.model;
import lombok.Builder;
import lombok.Data;
import java.util.function.Consumer;
@Data
@Builder
public class DispatcherActions {
private Consumer<String> sourceAnalysis;
private Consumer<String> apkAudit;
private Action sourceAnalysis;
private Action apkAudit;
}

View File

@@ -0,0 +1 @@
com.bartek.esa.core.plugin.JavaPlugin.PACKAGE_LACK=There is no package defined in AndroidManifest.xml file. Please check fix to use this tool.