17: Reinvent architecture to add project context

This commit is contained in:
Bartłomiej Pluta
2019-06-09 16:16:30 +02:00
parent e6265927f0
commit 5f790e4def
42 changed files with 616 additions and 558 deletions

View File

@@ -17,7 +17,6 @@ import javax.inject.Inject;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;

View File

@@ -1,6 +1,7 @@
package com.bartek.esa.analyser.apk; package com.bartek.esa.analyser.apk;
import com.bartek.esa.analyser.core.Analyser; import com.bartek.esa.analyser.core.Analyser;
import com.bartek.esa.context.constructor.ContextConstructor;
import com.bartek.esa.core.archetype.Plugin; import com.bartek.esa.core.archetype.Plugin;
import com.bartek.esa.core.executor.PluginExecutor; import com.bartek.esa.core.executor.PluginExecutor;
import com.bartek.esa.decompiler.archetype.Decompiler; import com.bartek.esa.decompiler.archetype.Decompiler;
@@ -19,8 +20,8 @@ public class ApkAnalyser extends Analyser {
private final FileCleaner fileCleaner; private final FileCleaner fileCleaner;
private final GlobMatcher globMatcher; private final GlobMatcher globMatcher;
public ApkAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider, Decompiler decompiler, FileCleaner fileCleaner, GlobMatcher globMatcher) { public ApkAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider, Decompiler decompiler, FileCleaner fileCleaner, GlobMatcher globMatcher, ContextConstructor contextConstructor) {
super(pluginExecutor, plugins, fileProvider); super(pluginExecutor, plugins, fileProvider, contextConstructor);
this.decompiler = decompiler; this.decompiler = decompiler;
this.fileCleaner = fileCleaner; this.fileCleaner = fileCleaner;
this.globMatcher = globMatcher; this.globMatcher = globMatcher;

View File

@@ -1,5 +1,7 @@
package com.bartek.esa.analyser.core; package com.bartek.esa.analyser.core;
import com.bartek.esa.context.constructor.ContextConstructor;
import com.bartek.esa.context.model.Context;
import com.bartek.esa.core.archetype.Plugin; import com.bartek.esa.core.archetype.Plugin;
import com.bartek.esa.core.executor.PluginExecutor; import com.bartek.esa.core.executor.PluginExecutor;
import com.bartek.esa.core.model.object.Issue; import com.bartek.esa.core.model.object.Issue;
@@ -7,31 +9,34 @@ import com.bartek.esa.error.EsaException;
import com.bartek.esa.file.provider.FileProvider; import com.bartek.esa.file.provider.FileProvider;
import java.io.File; import java.io.File;
import java.util.Collections;
import java.util.Set; import java.util.Set;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
public abstract class Analyser { public abstract class Analyser {
private final PluginExecutor pluginExecutor; private final PluginExecutor pluginExecutor;
private final Set<Plugin> plugins; private final Set<Plugin> plugins;
private final FileProvider fileProvider; private final FileProvider fileProvider;
private final ContextConstructor contextConstructor;
public Analyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider) { public Analyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider, ContextConstructor contextConstructor) {
this.pluginExecutor = pluginExecutor; this.pluginExecutor = pluginExecutor;
this.plugins = plugins; this.plugins = plugins;
this.fileProvider = fileProvider; this.fileProvider = fileProvider;
this.contextConstructor = contextConstructor;
} }
public Set<Issue> analyse(String source, Set<String> pluginCodes, Set<String> excludeCodes, boolean debug) { public Set<Issue> analyse(String source, Set<String> pluginCodes, Set<String> excludeCodes, boolean debug) {
String newSource = prepareSources(source, debug); String newSource = prepareSources(source, debug);
File manifest = getManifest(newSource); File manifest = getManifest(newSource);
Set<File> files = getFiles(newSource); Set<File> javaSources = getJavaSources(newSource);
Set<Plugin> selectedPlugins = getPlugins(pluginCodes, excludeCodes); Set<File> layoutFiles = getLayoutFiles(newSource);
Context context = contextConstructor.construct(manifest, javaSources, layoutFiles);
Set<Plugin> selectedPlugins = getPlugins(pluginCodes, excludeCodes);
Set<Issue> issues = pluginExecutor.executeForContext(context, selectedPlugins, debug);
Set<Issue> issues = pluginExecutor.executeForFiles(manifest, files, selectedPlugins, debug);
performCleaning(newSource, debug); performCleaning(newSource, debug);
return issues; return issues;
} }
@@ -60,14 +65,12 @@ public abstract class Analyser {
return (File) (manifests.toArray())[0]; return (File) (manifests.toArray())[0];
} }
private Set<File> getFiles(String source) { private Set<File> getJavaSources(String source) {
Set<File> javaFiles = fileProvider.getGlobMatchedFiles(source, getJavaGlob()); return fileProvider.getGlobMatchedFiles(source, getJavaGlob());
Set<File> layoutFiles = fileProvider.getGlobMatchedFiles(source, getLayoutGlob()); }
Set<File> androidManifest = Collections.singleton(getManifest(source));
return Stream.of(javaFiles, androidManifest, layoutFiles) private Set<File> getLayoutFiles(String source) {
.flatMap(Set::stream) return fileProvider.getGlobMatchedFiles(source, getLayoutGlob());
.collect(Collectors.toSet());
} }
private Set<Plugin> getPlugins(Set<String> pluginCodes, Set<String> excludeCodes) { private Set<Plugin> getPlugins(Set<String> pluginCodes, Set<String> excludeCodes) {

View File

@@ -2,6 +2,7 @@ package com.bartek.esa.analyser.di;
import com.bartek.esa.analyser.apk.ApkAnalyser; import com.bartek.esa.analyser.apk.ApkAnalyser;
import com.bartek.esa.analyser.source.SourceAnalyser; import com.bartek.esa.analyser.source.SourceAnalyser;
import com.bartek.esa.context.constructor.ContextConstructor;
import com.bartek.esa.core.archetype.Plugin; import com.bartek.esa.core.archetype.Plugin;
import com.bartek.esa.core.executor.PluginExecutor; import com.bartek.esa.core.executor.PluginExecutor;
import com.bartek.esa.decompiler.archetype.Decompiler; import com.bartek.esa.decompiler.archetype.Decompiler;
@@ -17,12 +18,12 @@ import java.util.Set;
public class AnalyserModule { public class AnalyserModule {
@Provides @Provides
public SourceAnalyser sourceAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider) { public SourceAnalyser sourceAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider, ContextConstructor contextConstructor) {
return new SourceAnalyser(pluginExecutor, plugins, fileProvider); return new SourceAnalyser(pluginExecutor, plugins, fileProvider, contextConstructor);
} }
@Provides @Provides
public ApkAnalyser apkAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider, Decompiler decompiler, FileCleaner fileCleaner, GlobMatcher globMatcher) { public ApkAnalyser apkAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider, Decompiler decompiler, FileCleaner fileCleaner, GlobMatcher globMatcher, ContextConstructor contextConstructor) {
return new ApkAnalyser(pluginExecutor, plugins, fileProvider, decompiler, fileCleaner, globMatcher); return new ApkAnalyser(pluginExecutor, plugins, fileProvider, decompiler, fileCleaner, globMatcher, contextConstructor);
} }
} }

View File

@@ -1,6 +1,7 @@
package com.bartek.esa.analyser.source; package com.bartek.esa.analyser.source;
import com.bartek.esa.analyser.core.Analyser; import com.bartek.esa.analyser.core.Analyser;
import com.bartek.esa.context.constructor.ContextConstructor;
import com.bartek.esa.core.archetype.Plugin; import com.bartek.esa.core.archetype.Plugin;
import com.bartek.esa.core.executor.PluginExecutor; import com.bartek.esa.core.executor.PluginExecutor;
import com.bartek.esa.error.EsaException; import com.bartek.esa.error.EsaException;
@@ -14,8 +15,8 @@ public class SourceAnalyser extends Analyser {
private static final String JAVA_GLOB = "**/*.java"; private static final String JAVA_GLOB = "**/*.java";
private static final String LAYOUT_GLOB = "**/res/layout*/*.xml"; private static final String LAYOUT_GLOB = "**/res/layout*/*.xml";
public SourceAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider) { public SourceAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider, ContextConstructor contextConstructor) {
super(pluginExecutor, plugins, fileProvider); super(pluginExecutor, plugins, fileProvider, contextConstructor);
} }
@Override @Override

View File

@@ -0,0 +1,116 @@
package com.bartek.esa.context.constructor;
import com.bartek.esa.context.model.Context;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.error.EsaException;
import com.bartek.esa.file.matcher.GlobMatcher;
import com.github.javaparser.ParseProblemException;
import com.github.javaparser.Problem;
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.TokenRange;
import com.github.javaparser.ast.CompilationUnit;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import javax.inject.Inject;
import javax.xml.xpath.XPathConstants;
import java.io.File;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import static java.lang.String.format;
import static java.util.stream.Collectors.toSet;
public class ContextConstructor {
private final XmlHelper xmlHelper;
private final GlobMatcher globMatcher;
@Inject
public ContextConstructor(XmlHelper xmlHelper, GlobMatcher globMatcher) {
this.xmlHelper = xmlHelper;
this.globMatcher = globMatcher;
}
public Context construct(File androidManifestFile, Set<File> javaFiles, Set<File> layoutFiles) {
Document manifest = xmlHelper.parseXml(androidManifestFile);
String packageName = getPackageName(manifest);
return Context.builder()
.packageName(packageName)
.minSdkVersion(getUsesSdkVersion(manifest, "android:minSdkVersion"))
.targetSdkVersion(getUsesSdkVersion(manifest, "android:targetSdkVersion"))
.maxSdkVersion(getUsesSdkVersion(manifest, "android:maxSdkVersion"))
.manifest(new Source<>(androidManifestFile, manifest))
.javaSources(parseJavaFiles(javaFiles, packageName))
.layouts(parseLayoutFiles(layoutFiles))
.build();
}
private String getPackageName(Document androidManifest) {
return Optional.ofNullable(xmlHelper.xPath(androidManifest, "/manifest", XPathConstants.NODE))
.map(n -> (Node) n)
.map(Node::getAttributes)
.map(attr -> attr.getNamedItem("package"))
.map(Node::getNodeValue)
.orElseThrow(() -> new EsaException("No 'package' attribute found in manifest file. Interrupting..."));
}
private Integer getUsesSdkVersion(Document manifest, String attribute) {
return Optional.ofNullable(xmlHelper.xPath(manifest, "/manifest/uses-sdk", XPathConstants.NODE))
.map(n -> (Node) n)
.map(Node::getAttributes)
.map(attr -> attr.getNamedItem(attribute))
.map(Node::getNodeValue)
.map(Integer::parseInt)
.orElse(null);
}
private Set<Source<Document>> parseLayoutFiles(Set<File> layoutFiles) {
return layoutFiles.stream()
.map(file -> new Source<>(file, xmlHelper.parseXml(file)))
.collect(toSet());
}
private Set<Source<CompilationUnit>> parseJavaFiles(Set<File> javaFiles, String packageName) {
return javaFiles.stream()
.filter(isApplicationPackageFile(packageName))
.map(file -> new Source<>(file, parseJava(file)))
.filter(s -> s.getModel() != null)
.collect(toSet());
}
private Predicate<File> isApplicationPackageFile(String packageName) {
return file -> {
String path = packageName.replaceAll("\\.", "/");
return globMatcher.fileMatchesGlobPattern(file, format("**/%s/**", path));
};
}
private CompilationUnit parseJava(File javaFile) {
try {
return StaticJavaParser.parse(javaFile);
} catch (ParseProblemException e) {
printParsingErrorToStderr(e, javaFile);
} catch (Exception e) {
throw new EsaException(e);
}
return null;
}
private void printParsingErrorToStderr(ParseProblemException e, File file) {
e.getProblems().stream()
.map(p -> format("%s%s:\n%s\nIgnoring file...\n", file.getAbsolutePath(), getProblemRange(p), p.getMessage()))
.forEach(System.err::println);
}
private String getProblemRange(Problem problem) {
return problem.getLocation()
.flatMap(TokenRange::toRange)
.map(range -> format(" (line %d, col %d)", range.begin.line, range.begin.column))
.orElse("");
}
}

View File

@@ -0,0 +1,15 @@
package com.bartek.esa.context.di;
import com.bartek.esa.context.constructor.ContextConstructor;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import dagger.Module;
@Module
public class ContextModule {
public ContextConstructor contextConstructor(XmlHelper xmlHelper, GlobMatcher globMatcher) {
return new ContextConstructor(xmlHelper, globMatcher);
}
}

View File

@@ -0,0 +1,25 @@
package com.bartek.esa.context.model;
import com.github.javaparser.ast.CompilationUnit;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import org.w3c.dom.Document;
import java.util.Set;
@Getter
@Builder
@ToString
@EqualsAndHashCode
public class Context {
private String packageName;
private Integer minSdkVersion;
private Integer targetSdkVersion;
private Integer maxSdkVersion;
private Source<Document> manifest;
private Set<Source<CompilationUnit>> javaSources;
private Set<Source<Document>> layouts;
}

View File

@@ -0,0 +1,17 @@
package com.bartek.esa.context.model;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import java.io.File;
@Getter
@ToString
@EqualsAndHashCode
@RequiredArgsConstructor
public class Source <M> {
private final File file;
private final M model;
}

View File

@@ -1,20 +1,15 @@
package com.bartek.esa.core.archetype; package com.bartek.esa.core.archetype;
import com.bartek.esa.core.xml.XmlHelper; import com.bartek.esa.context.model.Context;
import com.bartek.esa.file.matcher.GlobMatcher; import com.bartek.esa.context.model.Source;
import org.w3c.dom.Document;
import java.io.File; public abstract class AndroidManifestPlugin extends BasePlugin {
public abstract class AndroidManifestPlugin extends XmlPlugin {
private final GlobMatcher globMatcher;
public AndroidManifestPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
this.globMatcher = globMatcher;
}
@Override @Override
public boolean supports(File file) { protected void run(Context context) {
return globMatcher.fileMatchesGlobPattern(file, "**/AndroidManifest.xml"); run(context.getManifest());
} }
protected abstract void run(Source<Document> manifest);
} }

View File

@@ -1,49 +1,92 @@
package com.bartek.esa.core.archetype; package com.bartek.esa.core.archetype;
import com.bartek.esa.context.model.Context;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.model.object.Issue; import com.bartek.esa.core.model.object.Issue;
import org.w3c.dom.Document; import com.github.javaparser.ast.expr.Expression;
import org.w3c.dom.Node;
import java.io.File; import java.io.File;
import java.util.HashMap; import java.util.*;
import java.util.HashSet; import java.util.stream.Collectors;
import java.util.Map;
import java.util.Set; import static java.lang.String.format;
public abstract class BasePlugin implements Plugin { public abstract class BasePlugin implements Plugin {
private Set<Issue> issues = new HashSet<>(); private Set<Issue> issues;
private Document manifest;
private File file;
@Override @Override
public void update(File file, Document manifest) { public Set<Issue> runForIssues(Context context) {
this.file = file; issues = new HashSet<>();
this.manifest = manifest; run(context);
this.issues.clear();
}
@Override
public Set<Issue> runForIssues() {
run(file);
return issues; return issues;
} }
protected abstract void run(File file); protected abstract void run(Context context);
protected void addIssue(Severity severity, Integer lineNumber, String line) { protected void addJavaIssue(Severity severity, File file, Expression expression) {
addIssue(severity, "", lineNumber, line); addIssue(severity, file, getLineNumberFromExpression(expression), expression.toString());
} }
protected void addIssue(Severity severity, Map<String, String> descriptionModel, Integer lineNumber, String line) { private Integer getLineNumberFromExpression(Expression expression) {
addIssue(severity, "", descriptionModel, lineNumber, line); return expression.getRange().map(r -> r.begin.line).orElse(null);
}
protected void addJavaIssue(Severity severity, Map<String, String> descriptionModel, File file, Expression expression) {
addIssue(severity, "", descriptionModel, file, getLineNumberFromExpression(expression), expression.toString());
} }
protected void addIssue(Severity severity, String descriptionCode, Integer lineNumber, String line) { protected void addJavaIssue(Severity severity, String descriptionCode, File file, Expression expression) {
addIssue(severity, descriptionCode, new HashMap<>(), lineNumber, line); addIssue(severity, descriptionCode, new HashMap<>(), file, getLineNumberFromExpression(expression), expression.toString());
} }
protected void addIssue(Severity severity, String descriptionCode, Map<String, String> descriptionModel, Integer lineNumber, String line) { protected void addJavaIssue(Severity severity, String descriptionCode, Map<String, String> descriptionModel, File file, Expression expression) {
addIssue(severity, descriptionCode, descriptionModel, file, getLineNumberFromExpression(expression), expression.toString());
}
protected void addXmlIssue(Severity severity, File file, Node node) {
addIssue(severity, file, null, tagString(node));
}
protected void addXmlIssue(Severity severity, Map<String, String> descriptionModel, File file, Node node) {
addIssue(severity, descriptionModel, file, null, tagString(node));
}
protected void addXmlIssue(Severity severity, String descriptionCode, File file, Node node) {
addIssue(severity, descriptionCode, file, null, tagString(node));
}
protected void addXmlIssue(Severity severity, String descriptionCode, Map<String, String> descriptionModel, File file, Node node) {
addIssue(severity, descriptionCode, descriptionModel, file, null, tagString(node));
}
protected String tagString(Node node) {
Node[] attributes = new Node[node.getAttributes().getLength()];
for (int i = 0; i < attributes.length; ++i) {
attributes[i] = node.getAttributes().item(i);
}
String attributesString = Arrays.stream(attributes)
.map(n -> format("%s=\"%s\"", n.getNodeName(), n.getNodeValue()))
.collect(Collectors.joining(" "));
return format("<%s %s ...", node.getNodeName(), attributesString);
}
protected void addIssue(Severity severity, File file, Integer lineNumber, String line) {
addIssue(severity, "", file, lineNumber, line);
}
protected void addIssue(Severity severity, Map<String, String> descriptionModel, File file, Integer lineNumber, String line) {
addIssue(severity, "", descriptionModel, file, lineNumber, line);
}
protected void addIssue(Severity severity, String descriptionCode, File file, Integer lineNumber, String line) {
addIssue(severity, descriptionCode, new HashMap<>(), file, lineNumber, line);
}
protected void addIssue(Severity severity, String descriptionCode, Map<String, String> descriptionModel, File file, Integer lineNumber, String line) {
Issue issue = Issue.builder() Issue issue = Issue.builder()
.severity(severity) .severity(severity)
.issuer(this.getClass()) .issuer(this.getClass())
@@ -54,18 +97,10 @@ public abstract class BasePlugin implements Plugin {
.line(line) .line(line)
.build(); .build();
issues.add(issue); addIssue(issue);
} }
protected void addIssue(Issue issue) { protected void addIssue(Issue issue) {
issues.add(issue); issues.add(issue);
} }
protected File getOriginalFile() {
return file;
}
protected Document getManifest() {
return manifest;
}
} }

View File

@@ -1,92 +1,15 @@
package com.bartek.esa.core.archetype; package com.bartek.esa.core.archetype;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.context.model.Context;
import com.bartek.esa.core.model.object.Issue; import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import com.github.javaparser.ParseProblemException;
import com.github.javaparser.Problem;
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.TokenRange;
import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.Expression;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import javax.xml.xpath.XPathConstants;
import java.io.File;
import java.util.HashMap;
import static java.lang.String.format;
public abstract class JavaPlugin extends BasePlugin { public abstract class JavaPlugin extends BasePlugin {
private final GlobMatcher globMatcher;
private final XmlHelper xmlHelper;
public JavaPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
this.globMatcher = globMatcher;
this.xmlHelper = xmlHelper;
}
@Override @Override
public boolean supports(File file) { protected void run(Context context) {
return globMatcher.fileMatchesGlobPattern(file, "**/*.java"); context.getJavaSources().forEach(this::run);
} }
@Override protected abstract void run(Source<CompilationUnit> compilationUnit);
protected void run(File file) {
if(!isApplicationPackageFile(file)) {
return;
}
try {
CompilationUnit compilationUnit = StaticJavaParser.parse(file);
run(compilationUnit);
} catch (ParseProblemException e) {
printParsingErrorToStderr(e, file);
} catch (Exception e) {
e.printStackTrace();
}
}
private void printParsingErrorToStderr(ParseProblemException e, File file) {
e.getProblems().stream()
.map(p -> format("%s%s:\n%s\nIgnoring file...\n", file.getAbsolutePath(), getRange(p), p.getMessage()))
.forEach(System.err::println);
}
private String getRange(Problem problem) {
return problem.getLocation()
.flatMap(TokenRange::toRange)
.map(range -> format(" (line %d, col %d)", range.begin.line, range.begin.column))
.orElse("");
}
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) {
Issue issue = Issue.builder()
.issuer(JavaPlugin.class)
.descriptionCode(".NO_PACKAGE")
.descriptionModel(new HashMap<>())
.severity(Severity.ERROR)
.build();
addIssue(issue);
return false;
}
String path = packageValue.getNodeValue().replaceAll("\\.", "/");
return globMatcher.fileMatchesGlobPattern(file, format("**/%s/**", path));
}
protected Integer getLineNumberFromExpression(Expression expression) {
return expression.getRange().map(r -> r.begin.line).orElse(null);
}
public abstract void run(CompilationUnit compilationUnit);
} }

View File

@@ -1,13 +1,10 @@
package com.bartek.esa.core.archetype; package com.bartek.esa.core.archetype;
import com.bartek.esa.context.model.Context;
import com.bartek.esa.core.model.object.Issue; import com.bartek.esa.core.model.object.Issue;
import org.w3c.dom.Document;
import java.io.File;
import java.util.Set; import java.util.Set;
public interface Plugin { public interface Plugin {
boolean supports(File file); Set<Issue> runForIssues(Context context);
void update(File file, Document manifest);
Set<Issue> runForIssues();
} }

View File

@@ -1,20 +1,15 @@
package com.bartek.esa.core.archetype; package com.bartek.esa.core.archetype;
import com.bartek.esa.core.xml.XmlHelper; import com.bartek.esa.context.model.Context;
import com.bartek.esa.file.matcher.GlobMatcher; import com.bartek.esa.context.model.Source;
import org.w3c.dom.Document;
import java.io.File; public abstract class ResourceLayoutPlugin extends BasePlugin {
public abstract class ResourceLayoutPlugin extends XmlPlugin {
private final GlobMatcher globMatcher;
public ResourceLayoutPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
this.globMatcher = globMatcher;
}
@Override @Override
public boolean supports(File file) { protected void run(Context context) {
return globMatcher.fileMatchesGlobPattern(file, "**/res/layout*/*.xml"); context.getLayouts().forEach(this::run);
} }
protected abstract void run(Source<Document> layout);
} }

View File

@@ -1,59 +0,0 @@
package com.bartek.esa.core.archetype;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.namespace.QName;
import java.io.File;
import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.lang.String.format;
public abstract class XmlPlugin extends BasePlugin {
private final GlobMatcher globMatcher;
private final XmlHelper xmlHelper;
public XmlPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
this.globMatcher = globMatcher;
this.xmlHelper = xmlHelper;
}
@Override
public boolean supports(File file) {
return globMatcher.fileMatchesGlobPattern(file, "**/*.xml");
}
@Override
protected void run(File file) {
Document xml = xmlHelper.parseXml(file);
run(xml);
}
protected abstract void run(Document xml);
protected Object xPath(Document xml, String expression, QName returnType) {
return xmlHelper.xPath(xml, expression, returnType);
}
protected Stream<Node> stream(NodeList nodeList) {
return xmlHelper.stream(nodeList);
}
protected String tagString(Node node) {
Node[] attributes = new Node[node.getAttributes().getLength()];
for(int i=0; i<attributes.length; ++i) {
attributes[i] = node.getAttributes().item(i);
}
String attributesString = Arrays.stream(attributes)
.map(n -> format("%s=\"%s\"", n.getNodeName(), n.getNodeValue()))
.collect(Collectors.joining(" "));
return format("<%s %s ...", node.getNodeName(), attributesString);
}
}

View File

@@ -4,6 +4,7 @@ import com.bartek.esa.core.desc.provider.DescriptionProvider;
import com.bartek.esa.core.executor.PluginExecutor; import com.bartek.esa.core.executor.PluginExecutor;
import com.bartek.esa.core.helper.ParentNodeFinder; import com.bartek.esa.core.helper.ParentNodeFinder;
import com.bartek.esa.core.helper.StaticScopeHelper; import com.bartek.esa.core.helper.StaticScopeHelper;
import com.bartek.esa.core.helper.StringConcatenationChecker;
import com.bartek.esa.core.java.JavaSyntaxRegexProvider; import com.bartek.esa.core.java.JavaSyntaxRegexProvider;
import com.bartek.esa.core.xml.XmlHelper; import com.bartek.esa.core.xml.XmlHelper;
import dagger.Module; import dagger.Module;
@@ -13,8 +14,8 @@ import dagger.Provides;
public class CoreModule { public class CoreModule {
@Provides @Provides
public PluginExecutor pluginExecutor(XmlHelper xmlHelper) { public PluginExecutor pluginExecutor() {
return new PluginExecutor(xmlHelper); return new PluginExecutor();
} }
@Provides @Provides
@@ -37,6 +38,11 @@ public class CoreModule {
return new StaticScopeHelper(); return new StaticScopeHelper();
} }
@Provides
public StringConcatenationChecker stringConcatenationChecker(StaticScopeHelper staticScopeHelper) {
return new StringConcatenationChecker(staticScopeHelper);
}
@Provides @Provides
public ParentNodeFinder parentNodeFinder() { public ParentNodeFinder parentNodeFinder() {
return new ParentNodeFinder(); return new ParentNodeFinder();

View File

@@ -3,10 +3,10 @@ package com.bartek.esa.core.di;
import com.bartek.esa.core.archetype.Plugin; import com.bartek.esa.core.archetype.Plugin;
import com.bartek.esa.core.helper.ParentNodeFinder; import com.bartek.esa.core.helper.ParentNodeFinder;
import com.bartek.esa.core.helper.StaticScopeHelper; import com.bartek.esa.core.helper.StaticScopeHelper;
import com.bartek.esa.core.helper.StringConcatenationChecker;
import com.bartek.esa.core.java.JavaSyntaxRegexProvider; import com.bartek.esa.core.java.JavaSyntaxRegexProvider;
import com.bartek.esa.core.plugin.*; import com.bartek.esa.core.plugin.*;
import com.bartek.esa.core.xml.XmlHelper; import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import dagger.multibindings.ElementsIntoSet; import dagger.multibindings.ElementsIntoSet;
@@ -26,127 +26,127 @@ public class PluginModule {
@Provides @Provides
@IntoSet @IntoSet
public Plugin loggingPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, StaticScopeHelper staticScopeHelper) { public Plugin loggingPlugin(StaticScopeHelper staticScopeHelper, StringConcatenationChecker stringConcatenationChecker) {
return new LoggingPlugin(globMatcher, xmlHelper, staticScopeHelper); return new LoggingPlugin(staticScopeHelper, stringConcatenationChecker);
} }
@Provides @Provides
@IntoSet @IntoSet
public Plugin debuggablePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public Plugin debuggablePlugin(XmlHelper xmlHelper) {
return new DebuggablePlugin(globMatcher, xmlHelper); return new DebuggablePlugin(xmlHelper);
} }
@Provides @Provides
@IntoSet @IntoSet
public Plugin allowBackupPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public Plugin allowBackupPlugin(XmlHelper xmlHelper) {
return new AllowBackupPlugin(globMatcher, xmlHelper); return new AllowBackupPlugin(xmlHelper);
} }
@Provides @Provides
@IntoSet @IntoSet
public Plugin permissionRaceConditionPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public Plugin permissionRaceConditionPlugin(XmlHelper xmlHelper) {
return new PermissionsRaceConditionPlugin(globMatcher, xmlHelper); return new PermissionsRaceConditionPlugin(xmlHelper);
} }
@Provides @Provides
@IntoSet @IntoSet
public Plugin secureRandomPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public Plugin secureRandomPlugin() {
return new SecureRandomPlugin(globMatcher, xmlHelper); return new SecureRandomPlugin();
} }
@Provides @Provides
@IntoSet @IntoSet
public Plugin implicitIntentsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, JavaSyntaxRegexProvider javaSyntaxRegexProvider) { public Plugin implicitIntentsPlugin(JavaSyntaxRegexProvider javaSyntaxRegexProvider) {
return new ImplicitIntentsPlugin(globMatcher, xmlHelper, javaSyntaxRegexProvider); return new ImplicitIntentsPlugin(javaSyntaxRegexProvider);
} }
@Provides @Provides
@IntoSet @IntoSet
public Plugin sharedUidPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public Plugin sharedUidPlugin(XmlHelper xmlHelper) {
return new SharedUidPlugin(globMatcher, xmlHelper); return new SharedUidPlugin(xmlHelper);
} }
@Provides @Provides
@IntoSet @IntoSet
public Plugin usesSdkPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public Plugin usesSdkPlugin(XmlHelper xmlHelper) {
return new UsesSdkPlugin(globMatcher, xmlHelper); return new UsesSdkPlugin(xmlHelper);
} }
@Provides @Provides
@IntoSet @IntoSet
public Plugin cipherInstancePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, StaticScopeHelper staticScopeHelper) { public Plugin cipherInstancePlugin(StaticScopeHelper staticScopeHelper) {
return new CipherInstancePlugin(globMatcher, xmlHelper, staticScopeHelper); return new CipherInstancePlugin(staticScopeHelper);
} }
@Provides @Provides
@IntoSet @IntoSet
public Plugin strictModePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, StaticScopeHelper staticScopeHelper) { public Plugin strictModePlugin(StaticScopeHelper staticScopeHelper) {
return new StrictModePlugin(globMatcher, xmlHelper, staticScopeHelper); return new StrictModePlugin(staticScopeHelper);
} }
@Provides @Provides
@IntoSet @IntoSet
public Plugin externalStoragePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, ParentNodeFinder parentNodeFinder) { public Plugin externalStoragePlugin(ParentNodeFinder parentNodeFinder) {
return new ExternalStoragePlugin(globMatcher, xmlHelper, parentNodeFinder); return new ExternalStoragePlugin(parentNodeFinder);
} }
@Provides @Provides
@IntoSet @IntoSet
public Plugin suppressWarningsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public Plugin suppressWarningsPlugin() {
return new SuppressWarningsPlugin(globMatcher, xmlHelper); return new SuppressWarningsPlugin();
} }
@Provides @Provides
@IntoSet @IntoSet
public Plugin exportedComponentsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public Plugin exportedComponentsPlugin(XmlHelper xmlHelper) {
return new ExportedComponentsPlugin(globMatcher, xmlHelper); return new ExportedComponentsPlugin(xmlHelper);
} }
@Provides @Provides
@IntoSet @IntoSet
public Plugin dangerousPermissionPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public Plugin dangerousPermissionPlugin(XmlHelper xmlHelper) {
return new DangerousPermissionPlugin(globMatcher, xmlHelper); return new DangerousPermissionPlugin(xmlHelper);
} }
@Provides @Provides
@IntoSet @IntoSet
public Plugin textInputValidationPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public Plugin textInputValidationPlugin(XmlHelper xmlHelper) {
return new TextInputValidationPlugin(globMatcher, xmlHelper); return new TextInputValidationPlugin(xmlHelper);
} }
@Provides @Provides
@IntoSet @IntoSet
public Plugin intentFilterPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public Plugin intentFilterPlugin(XmlHelper xmlHelper) {
return new IntentFilterPlugin(globMatcher, xmlHelper); return new IntentFilterPlugin(xmlHelper);
} }
@Provides @Provides
@IntoSet @IntoSet
public Plugin sqlInjectionPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public Plugin sqlInjectionPlugin(StringConcatenationChecker stringConcatenationChecker) {
return new SqlInjectionPlugin(globMatcher, xmlHelper); return new SqlInjectionPlugin( stringConcatenationChecker);
} }
@Provides @Provides
@IntoSet @IntoSet
public Plugin worldAccessPermissionsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public Plugin worldAccessPermissionsPlugin() {
return new WorldAccessPermissionsPlugin(globMatcher, xmlHelper); return new WorldAccessPermissionsPlugin();
} }
@Provides @Provides
@IntoSet @IntoSet
public Plugin orderedAndStickyBroadcastPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public Plugin orderedAndStickyBroadcastPlugin() {
return new OrderedBroadcastPlugin(globMatcher, xmlHelper); return new OrderedBroadcastPlugin();
} }
@Provides @Provides
@IntoSet @IntoSet
public Plugin webViewPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public Plugin webViewPlugin() {
return new WebViewPlugin(globMatcher, xmlHelper); return new WebViewPlugin();
} }
@Provides @Provides
@IntoSet @IntoSet
public Plugin telephonyManagerPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public Plugin telephonyManagerPlugin() {
return new TelephonyManagerPlugin(globMatcher, xmlHelper); return new TelephonyManagerPlugin();
} }
} }

View File

@@ -1,40 +1,30 @@
package com.bartek.esa.core.executor; package com.bartek.esa.core.executor;
import com.bartek.esa.context.model.Context;
import com.bartek.esa.core.archetype.Plugin; import com.bartek.esa.core.archetype.Plugin;
import com.bartek.esa.core.model.object.Issue; import com.bartek.esa.core.model.object.Issue;
import com.bartek.esa.core.xml.XmlHelper;
import org.w3c.dom.Document;
import javax.inject.Inject;
import java.io.File;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer;
import static java.util.stream.Collectors.toSet; import static java.util.stream.Collectors.toSet;
public class PluginExecutor { public class PluginExecutor {
private final XmlHelper xmlHelper;
@Inject public Set<Issue> executeForContext(Context context, Set<Plugin> plugins, boolean debug) {
public PluginExecutor(XmlHelper xmlHelper) {
this.xmlHelper = xmlHelper;
}
public Set<Issue> executeForFiles(File manifest, Set<File> files, Set<Plugin> plugins, boolean debug) {
return files.stream()
.peek(file -> { if(debug) System.out.printf("File: %s\n", file.getAbsolutePath()); })
.map(file -> executeForFile(manifest, file, plugins, debug))
.flatMap(Set::stream)
.collect(toSet());
}
private Set<Issue> executeForFile(File manifest, File file, Set<Plugin> plugins, boolean debug) {
Document xmlManifest = xmlHelper.parseXml(manifest);
return plugins.stream() return plugins.stream()
.peek(plugin -> { if(debug) System.out.printf(" Plugin: %s\n", plugin.getClass().getCanonicalName()); }) .peek(logPlugin(debug))
.peek(plugin -> plugin.update(file, xmlManifest)) .map(plugin -> plugin.runForIssues(context))
.filter(plugin -> plugin.supports(file))
.map(Plugin::runForIssues)
.flatMap(Set::stream) .flatMap(Set::stream)
.collect(toSet()); .collect(toSet());
}
private Consumer<Plugin> logPlugin(boolean debug) {
return plugin -> {
if(debug) {
System.out.printf(" Plugin: %s\n", plugin.getClass().getCanonicalName());
}
};
} }
} }

View File

@@ -0,0 +1,45 @@
package com.bartek.esa.core.helper;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.MethodCallExpr;
import javax.inject.Inject;
import java.util.function.Predicate;
public class StringConcatenationChecker {
private final StaticScopeHelper staticScopeHelper;
@Inject
public StringConcatenationChecker(StaticScopeHelper staticScopeHelper) {
this.staticScopeHelper = staticScopeHelper;
}
public boolean isStringConcatenation(CompilationUnit unit, Expression expr) {
Predicate<MethodCallExpr> isStringFormatMethod = staticScopeHelper.isFromScope(unit, "format", "String", "java.lang");
if(expr.isMethodCallExpr() && isStringFormatMethod.test(expr.asMethodCallExpr())) {
return true;
}
return isStringConcatenation(expr);
}
private boolean isStringConcatenation(Expression expr) {
if(expr.isBinaryExpr() && expr.asBinaryExpr().getOperator().asString().equals("+")) {
return isLiteralStringOrConcatenation(expr);
}
return false;
}
private boolean isLiteralStringOrConcatenation(Expression expr) {
if(expr.isBinaryExpr() && expr.asBinaryExpr().getOperator().asString().equals("+")) {
boolean isLeftArgumentString = isLiteralStringOrConcatenation(expr.asBinaryExpr().getLeft());
boolean isRightArgumentString = isLiteralStringOrConcatenation(expr.asBinaryExpr().getRight());
return isLeftArgumentString || isRightArgumentString;
}
return expr.isStringLiteralExpr();
}
}

View File

@@ -23,7 +23,6 @@ public class Issue implements Comparable {
public int compareTo(Object o) { public int compareTo(Object o) {
Issue another = (Issue) o; Issue another = (Issue) o;
int compByFile = file.compareTo(another.file); int compByFile = file.compareTo(another.file);
if(compByFile != 0) { if(compByFile != 0) {
return compByFile; return compByFile;
} }

View File

@@ -1,9 +1,9 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.AndroidManifestPlugin; import com.bartek.esa.core.archetype.AndroidManifestPlugin;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper; import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Node; import org.w3c.dom.Node;
@@ -12,19 +12,20 @@ import javax.xml.xpath.XPathConstants;
import java.util.Optional; import java.util.Optional;
public class AllowBackupPlugin extends AndroidManifestPlugin { public class AllowBackupPlugin extends AndroidManifestPlugin {
private final XmlHelper xmlHelper;
@Inject @Inject
public AllowBackupPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public AllowBackupPlugin(XmlHelper xmlHelper) {
super(globMatcher, xmlHelper); this.xmlHelper = xmlHelper;
} }
@Override @Override
protected void run(Document xml) { protected void run(Source<Document> manifest) {
Node applicationNode = (Node) xPath(xml, "/manifest/application", XPathConstants.NODE); Node applicationNode = (Node) xmlHelper.xPath(manifest.getModel(), "/manifest/application", XPathConstants.NODE);
Optional.ofNullable(applicationNode.getAttributes().getNamedItem("android:allowBackup")).ifPresentOrElse(n -> { Optional.ofNullable(applicationNode.getAttributes().getNamedItem("android:allowBackup")).ifPresentOrElse(n -> {
if (!n.getNodeValue().equals("false")) { if (!n.getNodeValue().equals("false")) {
addIssue(Severity.WARNING, ".NO_FALSE", null, n.toString()); addIssue(Severity.WARNING, ".NO_FALSE", manifest.getFile(), null, n.toString());
} }
}, () -> addIssue(Severity.ERROR, ".NO_ATTR", null, null)); }, () -> addIssue(Severity.ERROR, ".NO_ATTR", manifest.getFile(), null, null));
} }
} }

View File

@@ -1,10 +1,9 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.JavaPlugin; import com.bartek.esa.core.archetype.JavaPlugin;
import com.bartek.esa.core.helper.StaticScopeHelper; import com.bartek.esa.core.helper.StaticScopeHelper;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.MethodCallExpr;
@@ -16,19 +15,18 @@ public class CipherInstancePlugin extends JavaPlugin {
private final StaticScopeHelper staticScopeHelper; private final StaticScopeHelper staticScopeHelper;
@Inject @Inject
public CipherInstancePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, StaticScopeHelper staticScopeHelper) { public CipherInstancePlugin(StaticScopeHelper staticScopeHelper) {
super(globMatcher, xmlHelper);
this.staticScopeHelper = staticScopeHelper; this.staticScopeHelper = staticScopeHelper;
} }
@Override @Override
public void run(CompilationUnit compilationUnit) { public void run(Source<CompilationUnit> java) {
compilationUnit.findAll(MethodCallExpr.class).stream() java.getModel().findAll(MethodCallExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().equals("getInstance")) .filter(expr -> expr.getName().getIdentifier().equals("getInstance"))
.filter(staticScopeHelper.isFromScope(compilationUnit, "getInstance", "Cipher", "javax.crypto")) .filter(staticScopeHelper.isFromScope(java.getModel(), "getInstance", "Cipher", "javax.crypto"))
.filter(expr -> expr.getArguments().isNonEmpty()) .filter(expr -> expr.getArguments().isNonEmpty())
.filter(expr -> !isFullCipherQualifier(expr.getArguments().get(0).toString())) .filter(expr -> !isFullCipherQualifier(expr.getArguments().get(0).toString()))
.forEach(expr -> addIssue(Severity.ERROR, getLineNumberFromExpression(expr), expr.toString())); .forEach(expr -> addJavaIssue(Severity.ERROR, java.getFile(), expr));
} }
private boolean isFullCipherQualifier(String qualifier) { private boolean isFullCipherQualifier(String qualifier) {

View File

@@ -1,9 +1,9 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.AndroidManifestPlugin; import com.bartek.esa.core.archetype.AndroidManifestPlugin;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper; import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
@@ -13,19 +13,20 @@ import javax.xml.xpath.XPathConstants;
import java.util.Optional; import java.util.Optional;
public class DangerousPermissionPlugin extends AndroidManifestPlugin { public class DangerousPermissionPlugin extends AndroidManifestPlugin {
private final XmlHelper xmlHelper;
@Inject @Inject
public DangerousPermissionPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public DangerousPermissionPlugin(XmlHelper xmlHelper) {
super(globMatcher, xmlHelper); this.xmlHelper = xmlHelper;
} }
@Override @Override
protected void run(Document xml) { protected void run(Source<Document> manifest) {
NodeList customPermissions = (NodeList) xPath(xml, "/manifest/permission", XPathConstants.NODESET); NodeList customPermissions = (NodeList) xmlHelper.xPath(manifest.getModel(), "/manifest/permission", XPathConstants.NODESET);
stream(customPermissions) xmlHelper.stream(customPermissions)
.filter(this::isDangerousPermission) .filter(this::isDangerousPermission)
.filter(this::doesNotHaveDescription) .filter(this::doesNotHaveDescription)
.forEach(permission -> addIssue(Severity.WARNING, null, tagString(permission))); .forEach(permission -> addXmlIssue(Severity.WARNING, manifest.getFile(), permission));
} }
private boolean isDangerousPermission(Node permission) { private boolean isDangerousPermission(Node permission) {

View File

@@ -1,9 +1,9 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.AndroidManifestPlugin; import com.bartek.esa.core.archetype.AndroidManifestPlugin;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper; import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Node; import org.w3c.dom.Node;
@@ -12,19 +12,20 @@ import javax.xml.xpath.XPathConstants;
import java.util.Optional; import java.util.Optional;
public class DebuggablePlugin extends AndroidManifestPlugin { public class DebuggablePlugin extends AndroidManifestPlugin {
private final XmlHelper xmlHelper;
@Inject @Inject
public DebuggablePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public DebuggablePlugin(XmlHelper xmlHelper) {
super(globMatcher, xmlHelper); this.xmlHelper = xmlHelper;
} }
@Override @Override
protected void run(Document xml) { protected void run(Source<Document> manifest) {
Node applicationNode = (Node) xPath(xml, "/manifest/application", XPathConstants.NODE); Node applicationNode = (Node) xmlHelper.xPath(manifest.getModel(), "/manifest/application", XPathConstants.NODE);
Optional.ofNullable(applicationNode.getAttributes().getNamedItem("android:debuggable")).ifPresentOrElse(n -> { Optional.ofNullable(applicationNode.getAttributes().getNamedItem("android:debuggable")).ifPresentOrElse(n -> {
if(!n.getNodeValue().equals("false")) { if(!n.getNodeValue().equals("false")) {
addIssue(Severity.WARNING, ".NO_FALSE", null, n.toString()); addIssue(Severity.WARNING, ".NO_FALSE", manifest.getFile(),null, n.toString());
} }
}, () -> addIssue(Severity.ERROR, ".NO_ATTR",null, null)); }, () -> addIssue(Severity.ERROR, ".NO_ATTR", manifest.getFile(), null, null));
} }
} }

View File

@@ -1,9 +1,9 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.AndroidManifestPlugin; import com.bartek.esa.core.archetype.AndroidManifestPlugin;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper; import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
@@ -16,26 +16,27 @@ import java.util.Optional;
import static java.lang.String.format; import static java.lang.String.format;
public class ExportedComponentsPlugin extends AndroidManifestPlugin { public class ExportedComponentsPlugin extends AndroidManifestPlugin {
private final XmlHelper xmlHelper;
@Inject @Inject
public ExportedComponentsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public ExportedComponentsPlugin(XmlHelper xmlHelper) {
super(globMatcher, xmlHelper); this.xmlHelper = xmlHelper;
} }
@Override @Override
protected void run(Document xml) { protected void run(Source<Document> manifest) {
findExportedComponents(xml, "activity"); findExportedComponents(manifest, "activity");
findExportedComponents(xml, "service"); findExportedComponents(manifest, "service");
findExportedComponents(xml, "receiver"); findExportedComponents(manifest, "receiver");
findExportedProviders(xml); findExportedProviders(manifest);
} }
private void findExportedComponents(Document xml, String component) { private void findExportedComponents(Source<Document> manifest, String component) {
NodeList exportedActivities = (NodeList) xPath(xml, format("/manifest/application/%s", component), XPathConstants.NODESET); NodeList exportedActivities = (NodeList) xmlHelper.xPath(manifest.getModel(), format("/manifest/application/%s", component), XPathConstants.NODESET);
stream(exportedActivities) xmlHelper.stream(exportedActivities)
.filter(this::isExported) .filter(this::isExported)
.filter(node -> doesNotHavePermission(node, "android:permission")) .filter(node -> doesNotHavePermission(node, "android:permission"))
.forEach(node -> addIssue(Severity.WARNING, ".NO_PERMISSION", getModel(node), null, null)); .forEach(node -> addIssue(Severity.WARNING, ".NO_PERMISSION", getModel(node), manifest.getFile(), null, null));
} }
private Map<String, String> getModel(Node node) { private Map<String, String> getModel(Node node) {
@@ -45,13 +46,13 @@ public class ExportedComponentsPlugin extends AndroidManifestPlugin {
); );
} }
private void findExportedProviders(Document xml) { private void findExportedProviders(Source<Document> manifest) {
NodeList exportedProviders = (NodeList) xPath(xml, "/manifest/application/provider", XPathConstants.NODESET); NodeList exportedProviders = (NodeList) xmlHelper.xPath(manifest.getModel(), "/manifest/application/provider", XPathConstants.NODESET);
stream(exportedProviders) xmlHelper.stream(exportedProviders)
.filter(this::isExported) .filter(this::isExported)
.filter(node -> doesNotHavePermission(node, "android:writePermission") .filter(node -> doesNotHavePermission(node, "android:writePermission")
|| doesNotHavePermission(node, "android:readPermission")) || doesNotHavePermission(node, "android:readPermission"))
.forEach(node -> addIssue(Severity.WARNING, ".NO_PERMISSION", getModel(node), null, null)); .forEach(node -> addIssue(Severity.WARNING, ".NO_PERMISSION", getModel(node), manifest.getFile(), null, null));
} }
private boolean doesNotHavePermission(Node node, String permissionAttribute) { private boolean doesNotHavePermission(Node node, String permissionAttribute) {
@@ -69,6 +70,7 @@ public class ExportedComponentsPlugin extends AndroidManifestPlugin {
.orElse(false); .orElse(false);
} }
// todo remove it!
private String nodeToString(Node node) { private String nodeToString(Node node) {
String nodeName = Optional.ofNullable(node.getAttributes().getNamedItem("android:name")) String nodeName = Optional.ofNullable(node.getAttributes().getNamedItem("android:name"))
.map(Node::getNodeValue) .map(Node::getNodeValue)

View File

@@ -1,44 +1,45 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.JavaPlugin; import com.bartek.esa.core.archetype.JavaPlugin;
import com.bartek.esa.core.helper.ParentNodeFinder; import com.bartek.esa.core.helper.ParentNodeFinder;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.MethodCallExpr;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.function.Consumer;
public class ExternalStoragePlugin extends JavaPlugin { public class ExternalStoragePlugin extends JavaPlugin {
private final ParentNodeFinder parentNodeFinder; private final ParentNodeFinder parentNodeFinder;
@Inject @Inject
public ExternalStoragePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, ParentNodeFinder parentNodeFinder) { public ExternalStoragePlugin(ParentNodeFinder parentNodeFinder) {
super(globMatcher, xmlHelper);
this.parentNodeFinder = parentNodeFinder; this.parentNodeFinder = parentNodeFinder;
} }
@Override @Override
public void run(CompilationUnit compilationUnit) { public void run(Source<CompilationUnit> java) {
compilationUnit.findAll(MethodCallExpr.class).stream() java.getModel().findAll(MethodCallExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().matches("getExternalStorageDirectory|getExternalStoragePublicDirectory")) .filter(expr -> expr.getName().getIdentifier().matches("getExternalStorageDirectory|getExternalStoragePublicDirectory"))
.forEach(this::findCheckingStorageStateForAccessingExternalStorage); .forEach(findCheckingStorageStateForAccessingExternalStorage(java));
} }
private void findCheckingStorageStateForAccessingExternalStorage(MethodCallExpr accessingToExternalStorage) { private Consumer<MethodCallExpr> findCheckingStorageStateForAccessingExternalStorage(Source<CompilationUnit> java) {
parentNodeFinder.findParentNode(accessingToExternalStorage, MethodDeclaration.class).ifPresent(methodDeclaration -> return accessingToExternalStorage -> parentNodeFinder
findCheckingStorageStateInMethodDeclaration(accessingToExternalStorage, methodDeclaration) .findParentNode(accessingToExternalStorage, MethodDeclaration.class)
); .ifPresent(methodDeclaration ->
findCheckingStorageStateInMethodDeclaration(java, accessingToExternalStorage, methodDeclaration)
);
} }
private void findCheckingStorageStateInMethodDeclaration(MethodCallExpr accessingToExternalStorage, MethodDeclaration methodDeclaration) { private void findCheckingStorageStateInMethodDeclaration(Source<CompilationUnit> java, MethodCallExpr accessingToExternalStorage, MethodDeclaration methodDeclaration) {
boolean isStateBeingChecked = methodDeclaration.findAll(MethodCallExpr.class).stream() boolean isStateBeingChecked = methodDeclaration.findAll(MethodCallExpr.class).stream()
.anyMatch(e -> e.getName().getIdentifier().equals("getExternalStorageState")); .anyMatch(e -> e.getName().getIdentifier().equals("getExternalStorageState"));
if (!isStateBeingChecked) { if (!isStateBeingChecked) {
addIssue(Severity.WARNING, getLineNumberFromExpression(accessingToExternalStorage), accessingToExternalStorage.toString()); addJavaIssue(Severity.WARNING, java.getFile(), accessingToExternalStorage);
} }
} }
} }

View File

@@ -1,10 +1,9 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.JavaPlugin; import com.bartek.esa.core.archetype.JavaPlugin;
import com.bartek.esa.core.java.JavaSyntaxRegexProvider; import com.bartek.esa.core.java.JavaSyntaxRegexProvider;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node; import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.NodeList;
@@ -17,27 +16,26 @@ import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class ImplicitIntentsPlugin extends JavaPlugin { public class ImplicitIntentsPlugin extends JavaPlugin {
private final JavaSyntaxRegexProvider java; private final JavaSyntaxRegexProvider javaSyntax;
@Inject @Inject
public ImplicitIntentsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, JavaSyntaxRegexProvider javaSyntaxRegexProvider) { public ImplicitIntentsPlugin(JavaSyntaxRegexProvider javaSyntaxRegexProvider) {
super(globMatcher, xmlHelper); this.javaSyntax = javaSyntaxRegexProvider;
this.java = javaSyntaxRegexProvider;
} }
@Override @Override
public void run(CompilationUnit compilationUnit) { public void run(Source<CompilationUnit> java) {
checkCreatingImplicitIntents(compilationUnit); checkCreatingImplicitIntents(java);
checkCreatingPendingIntentsWithoutIntentVariable(compilationUnit); checkCreatingPendingIntentsWithoutIntentVariable(java);
checkCreatingPendingIntentsWithIntentVariables(compilationUnit); checkCreatingPendingIntentsWithIntentVariables(java);
checkCreatingPendingIntentsWithIntentsArraysVariables(compilationUnit); checkCreatingPendingIntentsWithIntentsArraysVariables(java);
} }
// Works for: // Works for:
// Intent[] myIntents = { new Intent(...), ... } // Intent[] myIntents = { new Intent(...), ... }
// getActivities(this, 0, myIntents, 0); // getActivities(this, 0, myIntents, 0);
private void checkCreatingPendingIntentsWithIntentsArraysVariables(CompilationUnit compilationUnit) { private void checkCreatingPendingIntentsWithIntentsArraysVariables(Source<CompilationUnit> java) {
List<String> implicitIntentsArraysVariables = compilationUnit.findAll(ObjectCreationExpr.class).stream() List<String> implicitIntentsArraysVariables = java.getModel().findAll(ObjectCreationExpr.class).stream()
.filter(expr -> expr.getType().getName().getIdentifier().equals("Intent")) .filter(expr -> expr.getType().getName().getIdentifier().equals("Intent"))
.filter(this::isCreatingImplicitIntent) .filter(this::isCreatingImplicitIntent)
.map(Node::getParentNode) .map(Node::getParentNode)
@@ -49,31 +47,31 @@ public class ImplicitIntentsPlugin extends JavaPlugin {
.map(VariableDeclarator::getName) .map(VariableDeclarator::getName)
.map(SimpleName::getIdentifier) .map(SimpleName::getIdentifier)
.collect(Collectors.toList()); .collect(Collectors.toList());
compilationUnit.findAll(MethodCallExpr.class).stream() java.getModel().findAll(MethodCallExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().matches("getActivities")) .filter(expr -> expr.getName().getIdentifier().matches("getActivities"))
.filter(expr -> expr.getArguments().size() >= 4) .filter(expr -> expr.getArguments().size() >= 4)
.filter(expr -> expr.getArguments().get(2).isNameExpr()) .filter(expr -> expr.getArguments().get(2).isNameExpr())
.filter(expr -> implicitIntentsArraysVariables.contains(expr.getArguments().get(2).asNameExpr().getName().getIdentifier())) .filter(expr -> implicitIntentsArraysVariables.contains(expr.getArguments().get(2).asNameExpr().getName().getIdentifier()))
.forEach(expr -> addIssue(Severity.VULNERABILITY, ".PENDING_INTENT", getLineNumberFromExpression(expr), expr.toString())); .forEach(expr -> addJavaIssue(Severity.VULNERABILITY, ".PENDING_INTENT", java.getFile(), expr));
} }
private void checkCreatingImplicitIntents(CompilationUnit compilationUnit) { private void checkCreatingImplicitIntents(Source<CompilationUnit> java) {
List<String> intentVariables = getIntentVariables(compilationUnit); List<String> intentVariables = getIntentVariables(java);
checkAllSetActionMethodInvocations(compilationUnit, intentVariables); checkAllSetActionMethodInvocations(java, intentVariables);
checkCreatingImplicitIntentsUsingConstructor(compilationUnit); checkCreatingImplicitIntentsUsingConstructor(java);
} }
// Works for: new Intent(Intent.ABC), new Intent(ABC) // Works for: new Intent(Intent.ABC), new Intent(ABC)
private void checkCreatingImplicitIntentsUsingConstructor(CompilationUnit compilationUnit) { private void checkCreatingImplicitIntentsUsingConstructor(Source<CompilationUnit> java) {
compilationUnit.findAll(ObjectCreationExpr.class).stream() java.getModel().findAll(ObjectCreationExpr.class).stream()
.filter(expr -> expr.getType().getName().getIdentifier().equals("Intent")) .filter(expr -> expr.getType().getName().getIdentifier().equals("Intent"))
.filter(this::isCreatingImplicitIntent) .filter(this::isCreatingImplicitIntent)
.forEach(objectCreation -> addIssue(Severity.INFO, ".IMPLICIT_INTENT", getLineNumberFromExpression(objectCreation), objectCreation.toString())); .forEach(objectCreation -> addJavaIssue(Severity.INFO, ".IMPLICIT_INTENT", java.getFile(), objectCreation));
} }
// Returns: i for: Intent i = new Intent(...) // Returns: i for: Intent i = new Intent(...)
private List<String> getIntentVariables(CompilationUnit compilationUnit) { private List<String> getIntentVariables(Source<CompilationUnit> java) {
return compilationUnit.findAll(VariableDeclarationExpr.class).stream() return java.getModel().findAll(VariableDeclarationExpr.class).stream()
.filter(expr -> expr.getElementType().toString().equals("Intent")) .filter(expr -> expr.getElementType().toString().equals("Intent"))
.map(VariableDeclarationExpr::getVariables) .map(VariableDeclarationExpr::getVariables)
.flatMap(NodeList::stream) .flatMap(NodeList::stream)
@@ -90,7 +88,7 @@ public class ImplicitIntentsPlugin extends JavaPlugin {
// Works for: new Intent(CONSTANT, ...) // Works for: new Intent(CONSTANT, ...)
if (arguments.size() == 1) { if (arguments.size() == 1) {
Expression argument = arguments.get(0); Expression argument = arguments.get(0);
isImplicit = java.isConstant(argument); isImplicit = javaSyntax.isConstant(argument);
} }
// Not works for: new Intent(this, ...) // Not works for: new Intent(this, ...)
@@ -107,14 +105,14 @@ public class ImplicitIntentsPlugin extends JavaPlugin {
} }
// Works for: i.setAction(...) // Works for: i.setAction(...)
private void checkAllSetActionMethodInvocations(CompilationUnit compilationUnit, List<String> intentVariables) { private void checkAllSetActionMethodInvocations(Source<CompilationUnit> java, List<String> intentVariables) {
compilationUnit.findAll(MethodCallExpr.class).forEach(methodCall -> { java.getModel().findAll(MethodCallExpr.class).forEach(methodCall -> {
boolean isCalledOnIntentObject = methodCall.getScope() boolean isCalledOnIntentObject = methodCall.getScope()
.map(Expression::toString) .map(Expression::toString)
.filter(intentVariables::contains) .filter(intentVariables::contains)
.isPresent(); .isPresent();
if(isCalledOnIntentObject && methodCall.getName().getIdentifier().equals("setAction")) { if(isCalledOnIntentObject && methodCall.getName().getIdentifier().equals("setAction")) {
addIssue(Severity.INFO, ".IMPLICIT_INTENT", getLineNumberFromExpression(methodCall), methodCall.toString()); addJavaIssue(Severity.INFO, ".IMPLICIT_INTENT", java.getFile(), methodCall);
} }
}); });
} }
@@ -122,8 +120,8 @@ public class ImplicitIntentsPlugin extends JavaPlugin {
// Works for: // Works for:
// Intent myIntent = new Intent(...) // Intent myIntent = new Intent(...)
// getActivity(this, 0, myIntent, 0); // getActivity(this, 0, myIntent, 0);
private void checkCreatingPendingIntentsWithIntentVariables(CompilationUnit compilationUnit) { private void checkCreatingPendingIntentsWithIntentVariables(Source<CompilationUnit> java) {
List<String> implicitIntentsVariables = compilationUnit.findAll(ObjectCreationExpr.class).stream() List<String> implicitIntentsVariables = java.getModel().findAll(ObjectCreationExpr.class).stream()
.filter(expr -> expr.getType().getName().getIdentifier().equals("Intent")) .filter(expr -> expr.getType().getName().getIdentifier().equals("Intent"))
.filter(this::isCreatingImplicitIntent) .filter(this::isCreatingImplicitIntent)
.map(Node::getParentNode) .map(Node::getParentNode)
@@ -133,30 +131,30 @@ public class ImplicitIntentsPlugin extends JavaPlugin {
.map(VariableDeclarator::getName) .map(VariableDeclarator::getName)
.map(SimpleName::getIdentifier) .map(SimpleName::getIdentifier)
.collect(Collectors.toList()); .collect(Collectors.toList());
compilationUnit.findAll(MethodCallExpr.class).stream() java.getModel().findAll(MethodCallExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().matches("getActivity|getBroadcast|getService")) .filter(expr -> expr.getName().getIdentifier().matches("getActivity|getBroadcast|getService"))
.filter(expr -> expr.getArguments().size() >= 4) .filter(expr -> expr.getArguments().size() >= 4)
.filter(expr -> expr.getArguments().get(2).isNameExpr()) .filter(expr -> expr.getArguments().get(2).isNameExpr())
.filter(expr -> implicitIntentsVariables.contains(expr.getArguments().get(2).asNameExpr().getName().getIdentifier())) .filter(expr -> implicitIntentsVariables.contains(expr.getArguments().get(2).asNameExpr().getName().getIdentifier()))
.forEach(expr -> addIssue(Severity.VULNERABILITY, ".PENDING_INTENT", getLineNumberFromExpression(expr), expr.toString())); .forEach(expr -> addJavaIssue(Severity.VULNERABILITY, ".PENDING_INTENT", java.getFile(), expr));
} }
private void checkCreatingPendingIntentsWithoutIntentVariable(CompilationUnit compilationUnit) { private void checkCreatingPendingIntentsWithoutIntentVariable(Source<CompilationUnit> java) {
// Works for: getActivity(this, 0, new Intent(...), 0) // Works for: getActivity(this, 0, new Intent(...), 0)
compilationUnit.findAll(MethodCallExpr.class).stream() java.getModel().findAll(MethodCallExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().matches("getActivity|getBroadcast|getService")) .filter(expr -> expr.getName().getIdentifier().matches("getActivity|getBroadcast|getService"))
.filter(expr -> expr.getArguments().size() >= 4) .filter(expr -> expr.getArguments().size() >= 4)
.filter(expr -> expr.getArguments().get(2).isObjectCreationExpr()) .filter(expr -> expr.getArguments().get(2).isObjectCreationExpr())
.filter(expr -> isCreatingImplicitIntent(expr.getArguments().get(2).asObjectCreationExpr())) .filter(expr -> isCreatingImplicitIntent(expr.getArguments().get(2).asObjectCreationExpr()))
.forEach(expr -> addIssue(Severity.VULNERABILITY, ".PENDING_INTENT", getLineNumberFromExpression(expr), expr.toString())); .forEach(expr -> addJavaIssue(Severity.VULNERABILITY, ".PENDING_INTENT", java.getFile(), expr));
// Works for: getActivities(this, 0, new Intent[] { new Intent(...), ...}, 0) // Works for: getActivities(this, 0, new Intent[] { new Intent(...), ...}, 0)
compilationUnit.findAll(MethodCallExpr.class).stream() java.getModel().findAll(MethodCallExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().matches("getActivities")) .filter(expr -> expr.getName().getIdentifier().matches("getActivities"))
.filter(expr -> expr.getArguments().size() >= 4) .filter(expr -> expr.getArguments().size() >= 4)
.filter(expr -> expr.getArguments().get(2).isArrayCreationExpr()) .filter(expr -> expr.getArguments().get(2).isArrayCreationExpr())
.filter(expr -> isCreatingImplicitIntentsArray(expr.getArguments().get(2).asArrayCreationExpr())) .filter(expr -> isCreatingImplicitIntentsArray(expr.getArguments().get(2).asArrayCreationExpr()))
.forEach(expr -> addIssue(Severity.VULNERABILITY, ".PENDING_INTENT", getLineNumberFromExpression(expr), expr.toString())); .forEach(expr -> addJavaIssue(Severity.VULNERABILITY, ".PENDING_INTENT", java.getFile(), expr));
} }
private boolean isCreatingImplicitIntentsArray(ArrayCreationExpr arrayCreationExpr) { private boolean isCreatingImplicitIntentsArray(ArrayCreationExpr arrayCreationExpr) {

View File

@@ -1,9 +1,9 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.AndroidManifestPlugin; import com.bartek.esa.core.archetype.AndroidManifestPlugin;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper; import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
@@ -12,19 +12,20 @@ import javax.inject.Inject;
import java.util.Map; import java.util.Map;
public class IntentFilterPlugin extends AndroidManifestPlugin { public class IntentFilterPlugin extends AndroidManifestPlugin {
private final XmlHelper xmlHelper;
@Inject @Inject
public IntentFilterPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public IntentFilterPlugin(XmlHelper xmlHelper) {
super(globMatcher, xmlHelper); this.xmlHelper = xmlHelper;
} }
@Override @Override
protected void run(Document xml) { protected void run(Source<Document> manifest) {
NodeList filters = xml.getElementsByTagName("intent-filter"); NodeList filters = manifest.getModel().getElementsByTagName("intent-filter");
stream(filters) xmlHelper.stream(filters)
.filter(this::isNotMainActivity) .filter(this::isNotMainActivity)
.map(Node::getParentNode) .map(Node::getParentNode)
.forEach(n -> addIssue(Severity.INFO, getModel(n), null, null)); .forEach(n -> addIssue(Severity.INFO, getModel(n), manifest.getFile(), null, null));
} }
private Map<String, String> getModel(Node node) { private Map<String, String> getModel(Node node) {
@@ -35,14 +36,14 @@ public class IntentFilterPlugin extends AndroidManifestPlugin {
} }
private boolean isNotMainActivity(Node filter) { private boolean isNotMainActivity(Node filter) {
long mainActivityIntentFilters = stream(filter.getChildNodes()) long mainActivityIntentFilters = xmlHelper.stream(filter.getChildNodes())
.filter(n -> n.getNodeName().matches("action|category")) .filter(n -> n.getNodeName().matches("action|category"))
.map(n -> n.getAttributes().getNamedItem("android:name")) .map(n -> n.getAttributes().getNamedItem("android:name"))
.map(Node::getNodeValue) .map(Node::getNodeValue)
.filter(v -> v.equals("android.intent.action.MAIN") || v.equals("android.intent.category.LAUNCHER")) .filter(v -> v.equals("android.intent.action.MAIN") || v.equals("android.intent.category.LAUNCHER"))
.count(); .count();
long currentIntentFilters = stream(filter.getChildNodes()) long currentIntentFilters = xmlHelper.stream(filter.getChildNodes())
.filter(n -> n.getNodeName().matches("action|category")) .filter(n -> n.getNodeName().matches("action|category"))
.count(); .count();

View File

@@ -1,10 +1,10 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.JavaPlugin; import com.bartek.esa.core.archetype.JavaPlugin;
import com.bartek.esa.core.helper.StaticScopeHelper; import com.bartek.esa.core.helper.StaticScopeHelper;
import com.bartek.esa.core.helper.StringConcatenationChecker;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.MethodCallExpr;
@@ -12,18 +12,21 @@ import javax.inject.Inject;
public class LoggingPlugin extends JavaPlugin { public class LoggingPlugin extends JavaPlugin {
private final StaticScopeHelper staticScopeHelper; private final StaticScopeHelper staticScopeHelper;
private final StringConcatenationChecker stringConcatenationChecker;
@Inject @Inject
public LoggingPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, StaticScopeHelper staticScopeHelper) { public LoggingPlugin(StaticScopeHelper staticScopeHelper, StringConcatenationChecker stringConcatenationChecker) {
super(globMatcher, xmlHelper);
this.staticScopeHelper = staticScopeHelper; this.staticScopeHelper = staticScopeHelper;
this.stringConcatenationChecker = stringConcatenationChecker;
} }
@Override @Override
public void run(CompilationUnit compilationUnit) { public void run(Source<CompilationUnit> java) {
compilationUnit.findAll(MethodCallExpr.class).stream() java.getModel().findAll(MethodCallExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().matches("v|d|i|w|e|wtf")) .filter(expr -> expr.getName().getIdentifier().matches("v|d|i|w|e|wtf"))
.filter(staticScopeHelper.isFromScope(compilationUnit, "v|d|i|w|e|wtf", "Log", "android.util")) .filter(staticScopeHelper.isFromScope(java.getModel(), "v|d|i|w|e|wtf", "Log", "android.util"))
.forEach(expr -> addIssue(Severity.INFO, getLineNumberFromExpression(expr), expr.toString())); .filter(expr -> expr.getArguments().size() >= 2)
.filter(expr -> stringConcatenationChecker.isStringConcatenation(java.getModel(), expr.getArguments().get(1)))
.forEach(expr -> addJavaIssue(Severity.INFO, java.getFile(), expr));
} }
} }

View File

@@ -1,25 +1,17 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.JavaPlugin; import com.bartek.esa.core.archetype.JavaPlugin;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.MethodCallExpr;
import javax.inject.Inject;
public class OrderedBroadcastPlugin extends JavaPlugin { public class OrderedBroadcastPlugin extends JavaPlugin {
@Inject
public OrderedBroadcastPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
}
@Override @Override
public void run(CompilationUnit compilationUnit) { public void run(Source<CompilationUnit> java) {
compilationUnit.findAll(MethodCallExpr.class).stream() java.getModel().findAll(MethodCallExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().matches("sendOrderedBroadcast|sendOrderedBroadcastAsUser|sendStickyOrderedBroadcast|sendStickyOrderedBroadcastAsUser")) .filter(expr -> expr.getName().getIdentifier().matches("sendOrderedBroadcast|sendOrderedBroadcastAsUser|sendStickyOrderedBroadcast|sendStickyOrderedBroadcastAsUser"))
.forEach(expr -> addIssue(Severity.WARNING, getLineNumberFromExpression(expr), expr.toString())); .forEach(expr -> addJavaIssue(Severity.WARNING, java.getFile(), expr));
} }
} }

View File

@@ -1,9 +1,9 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.AndroidManifestPlugin; import com.bartek.esa.core.archetype.AndroidManifestPlugin;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper; import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
@@ -15,21 +15,22 @@ import java.util.Map;
import static java.lang.Integer.parseInt; import static java.lang.Integer.parseInt;
public class PermissionsRaceConditionPlugin extends AndroidManifestPlugin { public class PermissionsRaceConditionPlugin extends AndroidManifestPlugin {
private final XmlHelper xmlHelper;
@Inject @Inject
public PermissionsRaceConditionPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public PermissionsRaceConditionPlugin(XmlHelper xmlHelper) {
super(globMatcher, xmlHelper); this.xmlHelper = xmlHelper;
} }
@Override @Override
protected void run(Document xml) { protected void run(Source<Document> manifest) {
boolean isAnyPermissionDefined = ((NodeList) xPath(xml, "/manifest/permission", XPathConstants.NODESET)).getLength() > 0; boolean isAnyPermissionDefined = ((NodeList) xmlHelper.xPath(manifest.getModel(), "/manifest/permission", XPathConstants.NODESET)).getLength() > 0;
if(isAnyPermissionDefined) { if(isAnyPermissionDefined) {
Node usesSdkNode = (Node) xPath(xml, "/manifest/uses-sdk", XPathConstants.NODE); Node usesSdkNode = (Node) xmlHelper.xPath(manifest.getModel(), "/manifest/uses-sdk", XPathConstants.NODE);
Node minSdkVersionNode = usesSdkNode.getAttributes().getNamedItem("android:minSdkVersion"); Node minSdkVersionNode = usesSdkNode.getAttributes().getNamedItem("android:minSdkVersion");
int minSdkVersion = parseInt(minSdkVersionNode.getNodeValue()); int minSdkVersion = parseInt(minSdkVersionNode.getNodeValue());
if(minSdkVersion < 21) { if(minSdkVersion < 21) {
addIssue(Severity.VULNERABILITY, getModel(minSdkVersion), null, minSdkVersionNode.toString()); addIssue(Severity.VULNERABILITY, getModel(minSdkVersion), manifest.getFile(), null, minSdkVersionNode.toString());
} }
} }
} }

View File

@@ -1,26 +1,18 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.JavaPlugin; import com.bartek.esa.core.archetype.JavaPlugin;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.ObjectCreationExpr; import com.github.javaparser.ast.expr.ObjectCreationExpr;
import javax.inject.Inject;
public class SecureRandomPlugin extends JavaPlugin { public class SecureRandomPlugin extends JavaPlugin {
@Inject
public SecureRandomPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
}
@Override @Override
public void run(CompilationUnit compilationUnit) { public void run(Source<CompilationUnit> java) {
compilationUnit.findAll(ObjectCreationExpr.class).stream() java.getModel().findAll(ObjectCreationExpr.class).stream()
.filter(expr -> expr.getType().getName().getIdentifier().equals("SecureRandom")) .filter(expr -> expr.getType().getName().getIdentifier().equals("SecureRandom"))
.filter(expr -> expr.getArguments().isNonEmpty()) .filter(expr -> expr.getArguments().isNonEmpty())
.forEach(expr -> addIssue(Severity.VULNERABILITY, getLineNumberFromExpression(expr), expr.toString())); .forEach(expr -> addJavaIssue(Severity.VULNERABILITY, java.getFile(), expr));
} }
} }

View File

@@ -1,9 +1,9 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.AndroidManifestPlugin; import com.bartek.esa.core.archetype.AndroidManifestPlugin;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper; import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Node; import org.w3c.dom.Node;
@@ -12,17 +12,18 @@ import javax.xml.xpath.XPathConstants;
import java.util.Optional; import java.util.Optional;
public class SharedUidPlugin extends AndroidManifestPlugin { public class SharedUidPlugin extends AndroidManifestPlugin {
private final XmlHelper xmlHelper;
@Inject @Inject
public SharedUidPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public SharedUidPlugin(XmlHelper xmlHelper) {
super(globMatcher, xmlHelper); this.xmlHelper = xmlHelper;
} }
@Override @Override
protected void run(Document xml) { protected void run(Source<Document> manifest) {
Node manifestNode = (Node) xPath(xml, "/manifest", XPathConstants.NODE); Node manifestNode = (Node) xmlHelper.xPath(manifest.getModel(), "/manifest", XPathConstants.NODE);
Optional.ofNullable(manifestNode.getAttributes().getNamedItem("android:sharedUserId")).ifPresent(node -> { Optional.ofNullable(manifestNode.getAttributes().getNamedItem("android:sharedUserId")).ifPresent(node -> {
addIssue(Severity.VULNERABILITY, null, node.toString()); addIssue(Severity.VULNERABILITY, manifest.getFile(), null, node.toString());
}); });
} }
} }

View File

@@ -1,41 +1,28 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.JavaPlugin; import com.bartek.esa.core.archetype.JavaPlugin;
import com.bartek.esa.core.helper.StringConcatenationChecker;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.MethodCallExpr;
import javax.inject.Inject; import javax.inject.Inject;
public class SqlInjectionPlugin extends JavaPlugin { public class SqlInjectionPlugin extends JavaPlugin {
private final StringConcatenationChecker stringConcatenationChecker;
@Inject @Inject
public SqlInjectionPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public SqlInjectionPlugin(StringConcatenationChecker stringConcatenationChecker) {
super(globMatcher, xmlHelper); this.stringConcatenationChecker = stringConcatenationChecker;
} }
@Override @Override
public void run(CompilationUnit compilationUnit) { public void run(Source<CompilationUnit> java) {
compilationUnit.findAll(MethodCallExpr.class).stream() java.getModel().findAll(MethodCallExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().equals("rawQuery")) .filter(expr -> expr.getName().getIdentifier().equals("rawQuery"))
.filter(expr -> expr.getArguments().size() >= 2) .filter(expr -> expr.getArguments().size() >= 2)
.filter(this::isConcatenationOrMethodCall) .filter(expr -> stringConcatenationChecker.isStringConcatenation(java.getModel(), expr.getArguments().get(0)))
.filter(this::ifMethodIsStringFormat) .forEach(expr -> addJavaIssue(Severity.VULNERABILITY, java.getFile(), expr));
.forEach(expr -> addIssue(Severity.VULNERABILITY, getLineNumberFromExpression(expr), expr.toString()));
}
private boolean isConcatenationOrMethodCall(MethodCallExpr expr) {
return expr.getArguments().get(0).isBinaryExpr() || expr.getArguments().get(0).isMethodCallExpr();
}
private boolean ifMethodIsStringFormat(MethodCallExpr expr) {
if(expr.getArguments().get(0).isMethodCallExpr()) {
return expr.getArguments().get(0).asMethodCallExpr().getName().getIdentifier().equals("format");
}
return true;
} }
} }

View File

@@ -1,10 +1,9 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.JavaPlugin; import com.bartek.esa.core.archetype.JavaPlugin;
import com.bartek.esa.core.helper.StaticScopeHelper; import com.bartek.esa.core.helper.StaticScopeHelper;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.MethodCallExpr;
@@ -14,16 +13,15 @@ public class StrictModePlugin extends JavaPlugin {
private final StaticScopeHelper staticScopeHelper; private final StaticScopeHelper staticScopeHelper;
@Inject @Inject
public StrictModePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, StaticScopeHelper staticScopeHelper) { public StrictModePlugin(StaticScopeHelper staticScopeHelper) {
super(globMatcher, xmlHelper);
this.staticScopeHelper = staticScopeHelper; this.staticScopeHelper = staticScopeHelper;
} }
@Override @Override
public void run(CompilationUnit compilationUnit) { public void run(Source<CompilationUnit> java) {
compilationUnit.findAll(MethodCallExpr.class).stream() java.getModel().findAll(MethodCallExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().equals("setThreadPolicy")) .filter(expr -> expr.getName().getIdentifier().equals("setThreadPolicy"))
.filter(staticScopeHelper.isFromScope(compilationUnit, "setThreadPolicy", "StrictMode", "android.os")) .filter(staticScopeHelper.isFromScope(java.getModel(), "setThreadPolicy", "StrictMode", "android.os"))
.forEach(expr -> addIssue(Severity.WARNING, getLineNumberFromExpression(expr), expr.toString())); .forEach(expr -> addJavaIssue(Severity.WARNING, java.getFile(), expr));
} }
} }

View File

@@ -1,25 +1,17 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.JavaPlugin; import com.bartek.esa.core.archetype.JavaPlugin;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.AnnotationExpr; import com.github.javaparser.ast.expr.AnnotationExpr;
import javax.inject.Inject;
public class SuppressWarningsPlugin extends JavaPlugin { public class SuppressWarningsPlugin extends JavaPlugin {
@Inject
public SuppressWarningsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
}
@Override @Override
public void run(CompilationUnit compilationUnit) { public void run(Source<CompilationUnit> java) {
compilationUnit.findAll(AnnotationExpr.class).stream() java.getModel().findAll(AnnotationExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().equals("SuppressWarnings")) .filter(expr -> expr.getName().getIdentifier().equals("SuppressWarnings"))
.forEach(expr -> addIssue(Severity.WARNING, getLineNumberFromExpression(expr), expr.toString())); .forEach(expr -> addJavaIssue(Severity.WARNING, java.getFile(), expr));
} }
} }

View File

@@ -1,26 +1,18 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.JavaPlugin; import com.bartek.esa.core.archetype.JavaPlugin;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.CastExpr; import com.github.javaparser.ast.expr.CastExpr;
import javax.inject.Inject;
public class TelephonyManagerPlugin extends JavaPlugin { public class TelephonyManagerPlugin extends JavaPlugin {
@Inject
public TelephonyManagerPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
}
@Override @Override
public void run(CompilationUnit compilationUnit) { public void run(Source<CompilationUnit> java) {
compilationUnit.findAll(CastExpr.class).stream() java.getModel().findAll(CastExpr.class).stream()
.filter(expr -> expr.getType().isClassOrInterfaceType()) .filter(expr -> expr.getType().isClassOrInterfaceType())
.filter(expr -> expr.getType().asClassOrInterfaceType().getName().getIdentifier().equals("TelephonyManager")) .filter(expr -> expr.getType().asClassOrInterfaceType().getName().getIdentifier().equals("TelephonyManager"))
.forEach(expr -> addIssue(Severity.INFO, getLineNumberFromExpression(expr), expr.toString())); .forEach(expr -> addJavaIssue(Severity.INFO, java.getFile(), expr));
} }
} }

View File

@@ -1,9 +1,9 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.ResourceLayoutPlugin; import com.bartek.esa.core.archetype.ResourceLayoutPlugin;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper; import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
@@ -12,18 +12,19 @@ import javax.inject.Inject;
import java.util.Optional; import java.util.Optional;
public class TextInputValidationPlugin extends ResourceLayoutPlugin { public class TextInputValidationPlugin extends ResourceLayoutPlugin {
private final XmlHelper xmlHelper;
@Inject @Inject
public TextInputValidationPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public TextInputValidationPlugin(XmlHelper xmlHelper) {
super(globMatcher, xmlHelper); this.xmlHelper = xmlHelper;
} }
@Override @Override
protected void run(Document xml) { protected void run(Source<Document> layout) {
NodeList editTextNodes = xml.getElementsByTagName("EditText"); NodeList editTextNodes = layout.getModel().getElementsByTagName("EditText");
stream(editTextNodes) xmlHelper.stream(editTextNodes)
.filter(this::doesNotHaveInputType) .filter(this::doesNotHaveInputType)
.forEach(n -> addIssue(Severity.WARNING, null, tagString(n))); .forEach(n -> addXmlIssue(Severity.WARNING, layout.getFile(), n));
} }
private boolean doesNotHaveInputType(Node editText) { private boolean doesNotHaveInputType(Node editText) {

View File

@@ -1,9 +1,9 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.AndroidManifestPlugin; import com.bartek.esa.core.archetype.AndroidManifestPlugin;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper; import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Node; import org.w3c.dom.Node;
@@ -13,22 +13,23 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
public class UsesSdkPlugin extends AndroidManifestPlugin { public class UsesSdkPlugin extends AndroidManifestPlugin {
private final XmlHelper xmlHelper;
@Inject @Inject
public UsesSdkPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) { public UsesSdkPlugin(XmlHelper xmlHelper) {
super(globMatcher, xmlHelper); this.xmlHelper = xmlHelper;
} }
@Override @Override
protected void run(Document xml) { protected void run(Source<Document> manifest) {
Optional.ofNullable((Node) xPath(xml, "/manifest/uses-sdk", XPathConstants.NODE)).ifPresentOrElse(usesSdkNode -> { Optional.ofNullable((Node) xmlHelper.xPath(manifest.getModel(), "/manifest/uses-sdk", XPathConstants.NODE)).ifPresentOrElse(usesSdkNode -> {
if(usesSdkNode.getAttributes().getNamedItem("android:minSdkVersion") == null) { if(usesSdkNode.getAttributes().getNamedItem("android:minSdkVersion") == null) {
addIssue(Severity.ERROR, ".USES_SDK.NO_MIN_SDK_VERSION", null, null); addIssue(Severity.ERROR, ".USES_SDK.NO_MIN_SDK_VERSION", manifest.getFile(), null, null);
} }
Optional.ofNullable(usesSdkNode.getAttributes().getNamedItem("android:maxSdkVersion")).ifPresent(maxSdkVersion -> Optional.ofNullable(usesSdkNode.getAttributes().getNamedItem("android:maxSdkVersion")).ifPresent(maxSdkVersion ->
addIssue(Severity.ERROR, ".USES_SDK.MAX_SDK_VERSION", Map.of("maxSdkVersion", maxSdkVersion.getNodeValue()),null, maxSdkVersion.toString()) addIssue(Severity.ERROR, ".USES_SDK.MAX_SDK_VERSION", Map.of("maxSdkVersion", maxSdkVersion.getNodeValue()), manifest.getFile(), null, maxSdkVersion.toString())
); );
}, () -> addIssue(Severity.ERROR, ".NO_USES_SDK", null, null)); }, () -> addIssue(Severity.ERROR, ".NO_USES_SDK", manifest.getFile(), null, null));
} }
} }

View File

@@ -1,51 +1,47 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.JavaPlugin; import com.bartek.esa.core.archetype.JavaPlugin;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.MethodCallExpr;
import javax.inject.Inject; import java.util.function.Consumer;
public class WebViewPlugin extends JavaPlugin { public class WebViewPlugin extends JavaPlugin {
private static final String SETTINGS_METHODS = "addJavascriptInterface|setJavaScriptEnabled|setWebContentsDebuggingEnabled|setAllowFileAccess|setDomStorageEnabled"; private static final String SETTINGS_METHODS = "addJavascriptInterface|setJavaScriptEnabled|setWebContentsDebuggingEnabled|setAllowFileAccess|setDomStorageEnabled";
@Inject
public WebViewPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
}
@Override @Override
public void run(CompilationUnit compilationUnit) { public void run(Source<CompilationUnit> java) {
compilationUnit.findAll(MethodCallExpr.class).stream() java.getModel().findAll(MethodCallExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().matches(SETTINGS_METHODS)) .filter(expr -> expr.getName().getIdentifier().matches(SETTINGS_METHODS))
.forEach(this::issueMethod); .forEach(issueMethod(java));
} }
private void issueMethod(MethodCallExpr methodCall) { private Consumer<MethodCallExpr> issueMethod(Source<CompilationUnit> java) {
switch (methodCall.getName().getIdentifier()) { return methodCall -> {
case "addJavascriptInterface": switch (methodCall.getName().getIdentifier()) {
addIssue(Severity.VULNERABILITY, ".JS_INTERFACE", getLineNumberFromExpression(methodCall), methodCall.toString()); case "addJavascriptInterface":
break; addJavaIssue(Severity.VULNERABILITY, ".JS_INTERFACE", java.getFile(), methodCall);
case "setJavaScriptEnabled": break;
issueSettingsMethod(methodCall, ".JS_ENABLED"); case "setJavaScriptEnabled":
break; issueSettingsMethod(java, methodCall, ".JS_ENABLED");
case "setWebContentsDebuggingEnabled": break;
issueSettingsMethod(methodCall, ".DEBUGGING_ENABLED"); case "setWebContentsDebuggingEnabled":
break; issueSettingsMethod(java, methodCall, ".DEBUGGING_ENABLED");
case "setAllowFileAccess": break;
issueSettingsMethod(methodCall, ".ALLOW_FILE_ACCESS"); case "setAllowFileAccess":
break; issueSettingsMethod(java, methodCall, ".ALLOW_FILE_ACCESS");
} break;
}
};
} }
private void issueSettingsMethod(MethodCallExpr methodCall, String descriptionCode) { private void issueSettingsMethod(Source<CompilationUnit> java, MethodCallExpr methodCall, String descriptionCode) {
Expression firstArg = methodCall.getArguments().get(0); Expression firstArg = methodCall.getArguments().get(0);
if (firstArg.isBooleanLiteralExpr() && firstArg.asBooleanLiteralExpr().getValue()) { if (firstArg.isBooleanLiteralExpr() && firstArg.asBooleanLiteralExpr().getValue()) {
addIssue(Severity.WARNING, descriptionCode, getLineNumberFromExpression(methodCall), methodCall.toString()); addJavaIssue(Severity.WARNING, descriptionCode, java.getFile(), methodCall);
} }
} }
} }

View File

@@ -1,32 +1,25 @@
package com.bartek.esa.core.plugin; package com.bartek.esa.core.plugin;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.JavaPlugin; import com.bartek.esa.core.archetype.JavaPlugin;
import com.bartek.esa.core.model.enumeration.Severity; import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.FieldAccessExpr; import com.github.javaparser.ast.expr.FieldAccessExpr;
import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.ast.expr.NameExpr;
import javax.inject.Inject;
import java.util.Map; import java.util.Map;
public class WorldAccessPermissionsPlugin extends JavaPlugin { public class WorldAccessPermissionsPlugin extends JavaPlugin {
@Inject
public WorldAccessPermissionsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
}
@Override @Override
public void run(CompilationUnit compilationUnit) { public void run(Source<CompilationUnit> java) {
compilationUnit.findAll(NameExpr.class).stream() java.getModel().findAll(NameExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().matches("MODE_WORLD_(READABLE|WRITEABLE)")) .filter(expr -> expr.getName().getIdentifier().matches("MODE_WORLD_(READABLE|WRITEABLE)"))
.forEach(expr -> addIssue(Severity.ERROR, getModel(expr), getLineNumberFromExpression(expr), expr.toString())); .forEach(expr -> addJavaIssue(Severity.ERROR, getModel(expr), java.getFile(), expr));
compilationUnit.findAll(FieldAccessExpr.class).stream() java.getModel().findAll(FieldAccessExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().matches("MODE_WORLD_(READABLE|WRITEABLE)")) .filter(expr -> expr.getName().getIdentifier().matches("MODE_WORLD_(READABLE|WRITEABLE)"))
.forEach(expr -> addIssue(Severity.ERROR, getModel(expr), getLineNumberFromExpression(expr), expr.toString())); .forEach(expr -> addJavaIssue(Severity.ERROR, getModel(expr), java.getFile(), expr));
} }
private Map<String, String> getModel(NameExpr expression) { private Map<String, String> getModel(NameExpr expression) {

View File

@@ -3,6 +3,7 @@ package com.bartek.esa.di;
import com.bartek.esa.EsaMain; import com.bartek.esa.EsaMain;
import com.bartek.esa.analyser.di.AnalyserModule; import com.bartek.esa.analyser.di.AnalyserModule;
import com.bartek.esa.cli.di.CliModule; import com.bartek.esa.cli.di.CliModule;
import com.bartek.esa.context.di.ContextModule;
import com.bartek.esa.core.di.CoreModule; import com.bartek.esa.core.di.CoreModule;
import com.bartek.esa.core.di.PluginModule; import com.bartek.esa.core.di.PluginModule;
import com.bartek.esa.decompiler.di.DecompilerModule; import com.bartek.esa.decompiler.di.DecompilerModule;
@@ -19,7 +20,8 @@ import dagger.Component;
CoreModule.class, CoreModule.class,
PluginModule.class, PluginModule.class,
AnalyserModule.class, AnalyserModule.class,
FormatterModule.class FormatterModule.class,
ContextModule.class
}) })
public interface DependencyInjector { public interface DependencyInjector {
EsaMain esa(); EsaMain esa();