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.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;

View File

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

View File

@@ -1,5 +1,7 @@
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.executor.PluginExecutor;
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 java.io.File;
import java.util.Collections;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public abstract class Analyser {
private final PluginExecutor pluginExecutor;
private final Set<Plugin> plugins;
private final FileProvider fileProvider;
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.plugins = plugins;
this.fileProvider = fileProvider;
this.contextConstructor = contextConstructor;
}
public Set<Issue> analyse(String source, Set<String> pluginCodes, Set<String> excludeCodes, boolean debug) {
String newSource = prepareSources(source, debug);
File manifest = getManifest(newSource);
Set<File> files = getFiles(newSource);
Set<Plugin> selectedPlugins = getPlugins(pluginCodes, excludeCodes);
Set<File> javaSources = getJavaSources(newSource);
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);
return issues;
}
@@ -60,14 +65,12 @@ public abstract class Analyser {
return (File) (manifests.toArray())[0];
}
private Set<File> getFiles(String source) {
Set<File> javaFiles = fileProvider.getGlobMatchedFiles(source, getJavaGlob());
Set<File> layoutFiles = fileProvider.getGlobMatchedFiles(source, getLayoutGlob());
Set<File> androidManifest = Collections.singleton(getManifest(source));
private Set<File> getJavaSources(String source) {
return fileProvider.getGlobMatchedFiles(source, getJavaGlob());
}
return Stream.of(javaFiles, androidManifest, layoutFiles)
.flatMap(Set::stream)
.collect(Collectors.toSet());
private Set<File> getLayoutFiles(String source) {
return fileProvider.getGlobMatchedFiles(source, getLayoutGlob());
}
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.source.SourceAnalyser;
import com.bartek.esa.context.constructor.ContextConstructor;
import com.bartek.esa.core.archetype.Plugin;
import com.bartek.esa.core.executor.PluginExecutor;
import com.bartek.esa.decompiler.archetype.Decompiler;
@@ -17,12 +18,12 @@ import java.util.Set;
public class AnalyserModule {
@Provides
public SourceAnalyser sourceAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider) {
return new SourceAnalyser(pluginExecutor, plugins, fileProvider);
public SourceAnalyser sourceAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider, ContextConstructor contextConstructor) {
return new SourceAnalyser(pluginExecutor, plugins, fileProvider, contextConstructor);
}
@Provides
public ApkAnalyser apkAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider, Decompiler decompiler, FileCleaner fileCleaner, GlobMatcher globMatcher) {
return new ApkAnalyser(pluginExecutor, plugins, fileProvider, decompiler, fileCleaner, 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, contextConstructor);
}
}

View File

@@ -1,6 +1,7 @@
package com.bartek.esa.analyser.source;
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.executor.PluginExecutor;
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 LAYOUT_GLOB = "**/res/layout*/*.xml";
public SourceAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider) {
super(pluginExecutor, plugins, fileProvider);
public SourceAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider, ContextConstructor contextConstructor) {
super(pluginExecutor, plugins, fileProvider, contextConstructor);
}
@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;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import com.bartek.esa.context.model.Context;
import com.bartek.esa.context.model.Source;
import org.w3c.dom.Document;
import java.io.File;
public abstract class AndroidManifestPlugin extends XmlPlugin {
private final GlobMatcher globMatcher;
public AndroidManifestPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
this.globMatcher = globMatcher;
}
public abstract class AndroidManifestPlugin extends BasePlugin {
@Override
public boolean supports(File file) {
return globMatcher.fileMatchesGlobPattern(file, "**/AndroidManifest.xml");
protected void run(Context context) {
run(context.getManifest());
}
protected abstract void run(Source<Document> manifest);
}

View File

@@ -1,49 +1,92 @@
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.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.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
import static java.lang.String.format;
public abstract class BasePlugin implements Plugin {
private Set<Issue> issues = new HashSet<>();
private Document manifest;
private File file;
private Set<Issue> issues;
@Override
public void update(File file, Document manifest) {
this.file = file;
this.manifest = manifest;
this.issues.clear();
}
@Override
public Set<Issue> runForIssues() {
run(file);
public Set<Issue> runForIssues(Context context) {
issues = new HashSet<>();
run(context);
return issues;
}
protected abstract void run(File file);
protected abstract void run(Context context);
protected void addIssue(Severity severity, Integer lineNumber, String line) {
addIssue(severity, "", lineNumber, line);
protected void addJavaIssue(Severity severity, File file, Expression expression) {
addIssue(severity, file, getLineNumberFromExpression(expression), expression.toString());
}
protected void addIssue(Severity severity, Map<String, String> descriptionModel, Integer lineNumber, String line) {
addIssue(severity, "", descriptionModel, lineNumber, line);
private Integer getLineNumberFromExpression(Expression expression) {
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) {
addIssue(severity, descriptionCode, new HashMap<>(), lineNumber, line);
protected void addJavaIssue(Severity severity, String descriptionCode, File file, Expression expression) {
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()
.severity(severity)
.issuer(this.getClass())
@@ -54,18 +97,10 @@ public abstract class BasePlugin implements Plugin {
.line(line)
.build();
issues.add(issue);
addIssue(issue);
}
protected void addIssue(Issue 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;
import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.model.object.Issue;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.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.bartek.esa.context.model.Context;
import com.bartek.esa.context.model.Source;
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 {
private final GlobMatcher globMatcher;
private final XmlHelper xmlHelper;
public JavaPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
this.globMatcher = globMatcher;
this.xmlHelper = xmlHelper;
}
@Override
public boolean supports(File file) {
return globMatcher.fileMatchesGlobPattern(file, "**/*.java");
protected void run(Context context) {
context.getJavaSources().forEach(this::run);
}
@Override
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);
protected abstract void run(Source<CompilationUnit> compilationUnit);
}

View File

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

View File

@@ -1,20 +1,15 @@
package com.bartek.esa.core.archetype;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import com.bartek.esa.context.model.Context;
import com.bartek.esa.context.model.Source;
import org.w3c.dom.Document;
import java.io.File;
public abstract class ResourceLayoutPlugin extends XmlPlugin {
private final GlobMatcher globMatcher;
public ResourceLayoutPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
this.globMatcher = globMatcher;
}
public abstract class ResourceLayoutPlugin extends BasePlugin {
@Override
public boolean supports(File file) {
return globMatcher.fileMatchesGlobPattern(file, "**/res/layout*/*.xml");
protected void run(Context context) {
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.helper.ParentNodeFinder;
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.xml.XmlHelper;
import dagger.Module;
@@ -13,8 +14,8 @@ import dagger.Provides;
public class CoreModule {
@Provides
public PluginExecutor pluginExecutor(XmlHelper xmlHelper) {
return new PluginExecutor(xmlHelper);
public PluginExecutor pluginExecutor() {
return new PluginExecutor();
}
@Provides
@@ -37,6 +38,11 @@ public class CoreModule {
return new StaticScopeHelper();
}
@Provides
public StringConcatenationChecker stringConcatenationChecker(StaticScopeHelper staticScopeHelper) {
return new StringConcatenationChecker(staticScopeHelper);
}
@Provides
public ParentNodeFinder 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.helper.ParentNodeFinder;
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.plugin.*;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.ElementsIntoSet;
@@ -26,127 +26,127 @@ public class PluginModule {
@Provides
@IntoSet
public Plugin loggingPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, StaticScopeHelper staticScopeHelper) {
return new LoggingPlugin(globMatcher, xmlHelper, staticScopeHelper);
public Plugin loggingPlugin(StaticScopeHelper staticScopeHelper, StringConcatenationChecker stringConcatenationChecker) {
return new LoggingPlugin(staticScopeHelper, stringConcatenationChecker);
}
@Provides
@IntoSet
public Plugin debuggablePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
return new DebuggablePlugin(globMatcher, xmlHelper);
public Plugin debuggablePlugin(XmlHelper xmlHelper) {
return new DebuggablePlugin(xmlHelper);
}
@Provides
@IntoSet
public Plugin allowBackupPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
return new AllowBackupPlugin(globMatcher, xmlHelper);
public Plugin allowBackupPlugin(XmlHelper xmlHelper) {
return new AllowBackupPlugin(xmlHelper);
}
@Provides
@IntoSet
public Plugin permissionRaceConditionPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
return new PermissionsRaceConditionPlugin(globMatcher, xmlHelper);
public Plugin permissionRaceConditionPlugin(XmlHelper xmlHelper) {
return new PermissionsRaceConditionPlugin(xmlHelper);
}
@Provides
@IntoSet
public Plugin secureRandomPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
return new SecureRandomPlugin(globMatcher, xmlHelper);
public Plugin secureRandomPlugin() {
return new SecureRandomPlugin();
}
@Provides
@IntoSet
public Plugin implicitIntentsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, JavaSyntaxRegexProvider javaSyntaxRegexProvider) {
return new ImplicitIntentsPlugin(globMatcher, xmlHelper, javaSyntaxRegexProvider);
public Plugin implicitIntentsPlugin(JavaSyntaxRegexProvider javaSyntaxRegexProvider) {
return new ImplicitIntentsPlugin(javaSyntaxRegexProvider);
}
@Provides
@IntoSet
public Plugin sharedUidPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
return new SharedUidPlugin(globMatcher, xmlHelper);
public Plugin sharedUidPlugin(XmlHelper xmlHelper) {
return new SharedUidPlugin(xmlHelper);
}
@Provides
@IntoSet
public Plugin usesSdkPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
return new UsesSdkPlugin(globMatcher, xmlHelper);
public Plugin usesSdkPlugin(XmlHelper xmlHelper) {
return new UsesSdkPlugin(xmlHelper);
}
@Provides
@IntoSet
public Plugin cipherInstancePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, StaticScopeHelper staticScopeHelper) {
return new CipherInstancePlugin(globMatcher, xmlHelper, staticScopeHelper);
public Plugin cipherInstancePlugin(StaticScopeHelper staticScopeHelper) {
return new CipherInstancePlugin(staticScopeHelper);
}
@Provides
@IntoSet
public Plugin strictModePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, StaticScopeHelper staticScopeHelper) {
return new StrictModePlugin(globMatcher, xmlHelper, staticScopeHelper);
public Plugin strictModePlugin(StaticScopeHelper staticScopeHelper) {
return new StrictModePlugin(staticScopeHelper);
}
@Provides
@IntoSet
public Plugin externalStoragePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, ParentNodeFinder parentNodeFinder) {
return new ExternalStoragePlugin(globMatcher, xmlHelper, parentNodeFinder);
public Plugin externalStoragePlugin(ParentNodeFinder parentNodeFinder) {
return new ExternalStoragePlugin(parentNodeFinder);
}
@Provides
@IntoSet
public Plugin suppressWarningsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
return new SuppressWarningsPlugin(globMatcher, xmlHelper);
public Plugin suppressWarningsPlugin() {
return new SuppressWarningsPlugin();
}
@Provides
@IntoSet
public Plugin exportedComponentsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
return new ExportedComponentsPlugin(globMatcher, xmlHelper);
public Plugin exportedComponentsPlugin(XmlHelper xmlHelper) {
return new ExportedComponentsPlugin(xmlHelper);
}
@Provides
@IntoSet
public Plugin dangerousPermissionPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
return new DangerousPermissionPlugin(globMatcher, xmlHelper);
public Plugin dangerousPermissionPlugin(XmlHelper xmlHelper) {
return new DangerousPermissionPlugin(xmlHelper);
}
@Provides
@IntoSet
public Plugin textInputValidationPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
return new TextInputValidationPlugin(globMatcher, xmlHelper);
public Plugin textInputValidationPlugin(XmlHelper xmlHelper) {
return new TextInputValidationPlugin(xmlHelper);
}
@Provides
@IntoSet
public Plugin intentFilterPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
return new IntentFilterPlugin(globMatcher, xmlHelper);
public Plugin intentFilterPlugin(XmlHelper xmlHelper) {
return new IntentFilterPlugin(xmlHelper);
}
@Provides
@IntoSet
public Plugin sqlInjectionPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
return new SqlInjectionPlugin(globMatcher, xmlHelper);
public Plugin sqlInjectionPlugin(StringConcatenationChecker stringConcatenationChecker) {
return new SqlInjectionPlugin( stringConcatenationChecker);
}
@Provides
@IntoSet
public Plugin worldAccessPermissionsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
return new WorldAccessPermissionsPlugin(globMatcher, xmlHelper);
public Plugin worldAccessPermissionsPlugin() {
return new WorldAccessPermissionsPlugin();
}
@Provides
@IntoSet
public Plugin orderedAndStickyBroadcastPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
return new OrderedBroadcastPlugin(globMatcher, xmlHelper);
public Plugin orderedAndStickyBroadcastPlugin() {
return new OrderedBroadcastPlugin();
}
@Provides
@IntoSet
public Plugin webViewPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
return new WebViewPlugin(globMatcher, xmlHelper);
public Plugin webViewPlugin() {
return new WebViewPlugin();
}
@Provides
@IntoSet
public Plugin telephonyManagerPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
return new TelephonyManagerPlugin(globMatcher, xmlHelper);
public Plugin telephonyManagerPlugin() {
return new TelephonyManagerPlugin();
}
}

View File

@@ -1,40 +1,30 @@
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.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.function.Consumer;
import static java.util.stream.Collectors.toSet;
public class PluginExecutor {
private final XmlHelper xmlHelper;
@Inject
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);
public Set<Issue> executeForContext(Context context, Set<Plugin> plugins, boolean debug) {
return plugins.stream()
.peek(plugin -> { if(debug) System.out.printf(" Plugin: %s\n", plugin.getClass().getCanonicalName()); })
.peek(plugin -> plugin.update(file, xmlManifest))
.filter(plugin -> plugin.supports(file))
.map(Plugin::runForIssues)
.peek(logPlugin(debug))
.map(plugin -> plugin.runForIssues(context))
.flatMap(Set::stream)
.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) {
Issue another = (Issue) o;
int compByFile = file.compareTo(another.file);
if(compByFile != 0) {
return compByFile;
}

View File

@@ -1,9 +1,9 @@
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.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
@@ -12,19 +12,20 @@ import javax.xml.xpath.XPathConstants;
import java.util.Optional;
public class AllowBackupPlugin extends AndroidManifestPlugin {
private final XmlHelper xmlHelper;
@Inject
public AllowBackupPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
public AllowBackupPlugin(XmlHelper xmlHelper) {
this.xmlHelper = xmlHelper;
}
@Override
protected void run(Document xml) {
Node applicationNode = (Node) xPath(xml, "/manifest/application", XPathConstants.NODE);
protected void run(Source<Document> manifest) {
Node applicationNode = (Node) xmlHelper.xPath(manifest.getModel(), "/manifest/application", XPathConstants.NODE);
Optional.ofNullable(applicationNode.getAttributes().getNamedItem("android:allowBackup")).ifPresentOrElse(n -> {
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;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.JavaPlugin;
import com.bartek.esa.core.helper.StaticScopeHelper;
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.expr.MethodCallExpr;
@@ -16,19 +15,18 @@ public class CipherInstancePlugin extends JavaPlugin {
private final StaticScopeHelper staticScopeHelper;
@Inject
public CipherInstancePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, StaticScopeHelper staticScopeHelper) {
super(globMatcher, xmlHelper);
public CipherInstancePlugin(StaticScopeHelper staticScopeHelper) {
this.staticScopeHelper = staticScopeHelper;
}
@Override
public void run(CompilationUnit compilationUnit) {
compilationUnit.findAll(MethodCallExpr.class).stream()
public void run(Source<CompilationUnit> java) {
java.getModel().findAll(MethodCallExpr.class).stream()
.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 -> !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) {

View File

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

View File

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

View File

@@ -1,44 +1,45 @@
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.helper.ParentNodeFinder;
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.body.MethodDeclaration;
import com.github.javaparser.ast.expr.MethodCallExpr;
import javax.inject.Inject;
import java.util.function.Consumer;
public class ExternalStoragePlugin extends JavaPlugin {
private final ParentNodeFinder parentNodeFinder;
@Inject
public ExternalStoragePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, ParentNodeFinder parentNodeFinder) {
super(globMatcher, xmlHelper);
public ExternalStoragePlugin(ParentNodeFinder parentNodeFinder) {
this.parentNodeFinder = parentNodeFinder;
}
@Override
public void run(CompilationUnit compilationUnit) {
compilationUnit.findAll(MethodCallExpr.class).stream()
public void run(Source<CompilationUnit> java) {
java.getModel().findAll(MethodCallExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().matches("getExternalStorageDirectory|getExternalStoragePublicDirectory"))
.forEach(this::findCheckingStorageStateForAccessingExternalStorage);
.forEach(findCheckingStorageStateForAccessingExternalStorage(java));
}
private void findCheckingStorageStateForAccessingExternalStorage(MethodCallExpr accessingToExternalStorage) {
parentNodeFinder.findParentNode(accessingToExternalStorage, MethodDeclaration.class).ifPresent(methodDeclaration ->
findCheckingStorageStateInMethodDeclaration(accessingToExternalStorage, methodDeclaration)
);
private Consumer<MethodCallExpr> findCheckingStorageStateForAccessingExternalStorage(Source<CompilationUnit> java) {
return accessingToExternalStorage -> parentNodeFinder
.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()
.anyMatch(e -> e.getName().getIdentifier().equals("getExternalStorageState"));
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;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.JavaPlugin;
import com.bartek.esa.core.java.JavaSyntaxRegexProvider;
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.Node;
import com.github.javaparser.ast.NodeList;
@@ -17,27 +16,26 @@ import java.util.Optional;
import java.util.stream.Collectors;
public class ImplicitIntentsPlugin extends JavaPlugin {
private final JavaSyntaxRegexProvider java;
private final JavaSyntaxRegexProvider javaSyntax;
@Inject
public ImplicitIntentsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, JavaSyntaxRegexProvider javaSyntaxRegexProvider) {
super(globMatcher, xmlHelper);
this.java = javaSyntaxRegexProvider;
public ImplicitIntentsPlugin(JavaSyntaxRegexProvider javaSyntaxRegexProvider) {
this.javaSyntax = javaSyntaxRegexProvider;
}
@Override
public void run(CompilationUnit compilationUnit) {
checkCreatingImplicitIntents(compilationUnit);
checkCreatingPendingIntentsWithoutIntentVariable(compilationUnit);
checkCreatingPendingIntentsWithIntentVariables(compilationUnit);
checkCreatingPendingIntentsWithIntentsArraysVariables(compilationUnit);
public void run(Source<CompilationUnit> java) {
checkCreatingImplicitIntents(java);
checkCreatingPendingIntentsWithoutIntentVariable(java);
checkCreatingPendingIntentsWithIntentVariables(java);
checkCreatingPendingIntentsWithIntentsArraysVariables(java);
}
// Works for:
// Intent[] myIntents = { new Intent(...), ... }
// getActivities(this, 0, myIntents, 0);
private void checkCreatingPendingIntentsWithIntentsArraysVariables(CompilationUnit compilationUnit) {
List<String> implicitIntentsArraysVariables = compilationUnit.findAll(ObjectCreationExpr.class).stream()
private void checkCreatingPendingIntentsWithIntentsArraysVariables(Source<CompilationUnit> java) {
List<String> implicitIntentsArraysVariables = java.getModel().findAll(ObjectCreationExpr.class).stream()
.filter(expr -> expr.getType().getName().getIdentifier().equals("Intent"))
.filter(this::isCreatingImplicitIntent)
.map(Node::getParentNode)
@@ -49,31 +47,31 @@ public class ImplicitIntentsPlugin extends JavaPlugin {
.map(VariableDeclarator::getName)
.map(SimpleName::getIdentifier)
.collect(Collectors.toList());
compilationUnit.findAll(MethodCallExpr.class).stream()
java.getModel().findAll(MethodCallExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().matches("getActivities"))
.filter(expr -> expr.getArguments().size() >= 4)
.filter(expr -> expr.getArguments().get(2).isNameExpr())
.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) {
List<String> intentVariables = getIntentVariables(compilationUnit);
checkAllSetActionMethodInvocations(compilationUnit, intentVariables);
checkCreatingImplicitIntentsUsingConstructor(compilationUnit);
private void checkCreatingImplicitIntents(Source<CompilationUnit> java) {
List<String> intentVariables = getIntentVariables(java);
checkAllSetActionMethodInvocations(java, intentVariables);
checkCreatingImplicitIntentsUsingConstructor(java);
}
// Works for: new Intent(Intent.ABC), new Intent(ABC)
private void checkCreatingImplicitIntentsUsingConstructor(CompilationUnit compilationUnit) {
compilationUnit.findAll(ObjectCreationExpr.class).stream()
private void checkCreatingImplicitIntentsUsingConstructor(Source<CompilationUnit> java) {
java.getModel().findAll(ObjectCreationExpr.class).stream()
.filter(expr -> expr.getType().getName().getIdentifier().equals("Intent"))
.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(...)
private List<String> getIntentVariables(CompilationUnit compilationUnit) {
return compilationUnit.findAll(VariableDeclarationExpr.class).stream()
private List<String> getIntentVariables(Source<CompilationUnit> java) {
return java.getModel().findAll(VariableDeclarationExpr.class).stream()
.filter(expr -> expr.getElementType().toString().equals("Intent"))
.map(VariableDeclarationExpr::getVariables)
.flatMap(NodeList::stream)
@@ -90,7 +88,7 @@ public class ImplicitIntentsPlugin extends JavaPlugin {
// Works for: new Intent(CONSTANT, ...)
if (arguments.size() == 1) {
Expression argument = arguments.get(0);
isImplicit = java.isConstant(argument);
isImplicit = javaSyntax.isConstant(argument);
}
// Not works for: new Intent(this, ...)
@@ -107,14 +105,14 @@ public class ImplicitIntentsPlugin extends JavaPlugin {
}
// Works for: i.setAction(...)
private void checkAllSetActionMethodInvocations(CompilationUnit compilationUnit, List<String> intentVariables) {
compilationUnit.findAll(MethodCallExpr.class).forEach(methodCall -> {
private void checkAllSetActionMethodInvocations(Source<CompilationUnit> java, List<String> intentVariables) {
java.getModel().findAll(MethodCallExpr.class).forEach(methodCall -> {
boolean isCalledOnIntentObject = methodCall.getScope()
.map(Expression::toString)
.filter(intentVariables::contains)
.isPresent();
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:
// Intent myIntent = new Intent(...)
// getActivity(this, 0, myIntent, 0);
private void checkCreatingPendingIntentsWithIntentVariables(CompilationUnit compilationUnit) {
List<String> implicitIntentsVariables = compilationUnit.findAll(ObjectCreationExpr.class).stream()
private void checkCreatingPendingIntentsWithIntentVariables(Source<CompilationUnit> java) {
List<String> implicitIntentsVariables = java.getModel().findAll(ObjectCreationExpr.class).stream()
.filter(expr -> expr.getType().getName().getIdentifier().equals("Intent"))
.filter(this::isCreatingImplicitIntent)
.map(Node::getParentNode)
@@ -133,30 +131,30 @@ public class ImplicitIntentsPlugin extends JavaPlugin {
.map(VariableDeclarator::getName)
.map(SimpleName::getIdentifier)
.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.getArguments().size() >= 4)
.filter(expr -> expr.getArguments().get(2).isNameExpr())
.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)
compilationUnit.findAll(MethodCallExpr.class).stream()
java.getModel().findAll(MethodCallExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().matches("getActivity|getBroadcast|getService"))
.filter(expr -> expr.getArguments().size() >= 4)
.filter(expr -> expr.getArguments().get(2).isObjectCreationExpr())
.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)
compilationUnit.findAll(MethodCallExpr.class).stream()
java.getModel().findAll(MethodCallExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().matches("getActivities"))
.filter(expr -> expr.getArguments().size() >= 4)
.filter(expr -> expr.getArguments().get(2).isArrayCreationExpr())
.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) {

View File

@@ -1,9 +1,9 @@
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.model.enumeration.Severity;
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;
@@ -12,19 +12,20 @@ import javax.inject.Inject;
import java.util.Map;
public class IntentFilterPlugin extends AndroidManifestPlugin {
private final XmlHelper xmlHelper;
@Inject
public IntentFilterPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
public IntentFilterPlugin(XmlHelper xmlHelper) {
this.xmlHelper = xmlHelper;
}
@Override
protected void run(Document xml) {
NodeList filters = xml.getElementsByTagName("intent-filter");
stream(filters)
protected void run(Source<Document> manifest) {
NodeList filters = manifest.getModel().getElementsByTagName("intent-filter");
xmlHelper.stream(filters)
.filter(this::isNotMainActivity)
.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) {
@@ -35,14 +36,14 @@ public class IntentFilterPlugin extends AndroidManifestPlugin {
}
private boolean isNotMainActivity(Node filter) {
long mainActivityIntentFilters = stream(filter.getChildNodes())
long mainActivityIntentFilters = xmlHelper.stream(filter.getChildNodes())
.filter(n -> n.getNodeName().matches("action|category"))
.map(n -> n.getAttributes().getNamedItem("android:name"))
.map(Node::getNodeValue)
.filter(v -> v.equals("android.intent.action.MAIN") || v.equals("android.intent.category.LAUNCHER"))
.count();
long currentIntentFilters = stream(filter.getChildNodes())
long currentIntentFilters = xmlHelper.stream(filter.getChildNodes())
.filter(n -> n.getNodeName().matches("action|category"))
.count();

View File

@@ -1,10 +1,10 @@
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.helper.StaticScopeHelper;
import com.bartek.esa.core.helper.StringConcatenationChecker;
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.expr.MethodCallExpr;
@@ -12,18 +12,21 @@ import javax.inject.Inject;
public class LoggingPlugin extends JavaPlugin {
private final StaticScopeHelper staticScopeHelper;
private final StringConcatenationChecker stringConcatenationChecker;
@Inject
public LoggingPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, StaticScopeHelper staticScopeHelper) {
super(globMatcher, xmlHelper);
public LoggingPlugin(StaticScopeHelper staticScopeHelper, StringConcatenationChecker stringConcatenationChecker) {
this.staticScopeHelper = staticScopeHelper;
this.stringConcatenationChecker = stringConcatenationChecker;
}
@Override
public void run(CompilationUnit compilationUnit) {
compilationUnit.findAll(MethodCallExpr.class).stream()
public void run(Source<CompilationUnit> java) {
java.getModel().findAll(MethodCallExpr.class).stream()
.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"))
.forEach(expr -> addIssue(Severity.INFO, getLineNumberFromExpression(expr), expr.toString()));
.filter(staticScopeHelper.isFromScope(java.getModel(), "v|d|i|w|e|wtf", "Log", "android.util"))
.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;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.JavaPlugin;
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.expr.MethodCallExpr;
import javax.inject.Inject;
public class OrderedBroadcastPlugin extends JavaPlugin {
@Inject
public OrderedBroadcastPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
}
@Override
public void run(CompilationUnit compilationUnit) {
compilationUnit.findAll(MethodCallExpr.class).stream()
public void run(Source<CompilationUnit> java) {
java.getModel().findAll(MethodCallExpr.class).stream()
.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;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.AndroidManifestPlugin;
import com.bartek.esa.core.model.enumeration.Severity;
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;
@@ -15,21 +15,22 @@ import java.util.Map;
import static java.lang.Integer.parseInt;
public class PermissionsRaceConditionPlugin extends AndroidManifestPlugin {
private final XmlHelper xmlHelper;
@Inject
public PermissionsRaceConditionPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
public PermissionsRaceConditionPlugin(XmlHelper xmlHelper) {
this.xmlHelper = xmlHelper;
}
@Override
protected void run(Document xml) {
boolean isAnyPermissionDefined = ((NodeList) xPath(xml, "/manifest/permission", XPathConstants.NODESET)).getLength() > 0;
protected void run(Source<Document> manifest) {
boolean isAnyPermissionDefined = ((NodeList) xmlHelper.xPath(manifest.getModel(), "/manifest/permission", XPathConstants.NODESET)).getLength() > 0;
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");
int minSdkVersion = parseInt(minSdkVersionNode.getNodeValue());
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;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.JavaPlugin;
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.expr.ObjectCreationExpr;
import javax.inject.Inject;
public class SecureRandomPlugin extends JavaPlugin {
@Inject
public SecureRandomPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
}
@Override
public void run(CompilationUnit compilationUnit) {
compilationUnit.findAll(ObjectCreationExpr.class).stream()
public void run(Source<CompilationUnit> java) {
java.getModel().findAll(ObjectCreationExpr.class).stream()
.filter(expr -> expr.getType().getName().getIdentifier().equals("SecureRandom"))
.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;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.AndroidManifestPlugin;
import com.bartek.esa.core.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
@@ -12,17 +12,18 @@ import javax.xml.xpath.XPathConstants;
import java.util.Optional;
public class SharedUidPlugin extends AndroidManifestPlugin {
private final XmlHelper xmlHelper;
@Inject
public SharedUidPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
public SharedUidPlugin(XmlHelper xmlHelper) {
this.xmlHelper = xmlHelper;
}
@Override
protected void run(Document xml) {
Node manifestNode = (Node) xPath(xml, "/manifest", XPathConstants.NODE);
protected void run(Source<Document> manifest) {
Node manifestNode = (Node) xmlHelper.xPath(manifest.getModel(), "/manifest", XPathConstants.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;
import com.bartek.esa.context.model.Source;
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.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.Expression;
import com.github.javaparser.ast.expr.MethodCallExpr;
import javax.inject.Inject;
public class SqlInjectionPlugin extends JavaPlugin {
private final StringConcatenationChecker stringConcatenationChecker;
@Inject
public SqlInjectionPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
public SqlInjectionPlugin(StringConcatenationChecker stringConcatenationChecker) {
this.stringConcatenationChecker = stringConcatenationChecker;
}
@Override
public void run(CompilationUnit compilationUnit) {
compilationUnit.findAll(MethodCallExpr.class).stream()
public void run(Source<CompilationUnit> java) {
java.getModel().findAll(MethodCallExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().equals("rawQuery"))
.filter(expr -> expr.getArguments().size() >= 2)
.filter(this::isConcatenationOrMethodCall)
.filter(this::ifMethodIsStringFormat)
.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;
.filter(expr -> stringConcatenationChecker.isStringConcatenation(java.getModel(), expr.getArguments().get(0)))
.forEach(expr -> addJavaIssue(Severity.VULNERABILITY, java.getFile(), expr));
}
}

View File

@@ -1,10 +1,9 @@
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.helper.StaticScopeHelper;
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.expr.MethodCallExpr;
@@ -14,16 +13,15 @@ public class StrictModePlugin extends JavaPlugin {
private final StaticScopeHelper staticScopeHelper;
@Inject
public StrictModePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, StaticScopeHelper staticScopeHelper) {
super(globMatcher, xmlHelper);
public StrictModePlugin(StaticScopeHelper staticScopeHelper) {
this.staticScopeHelper = staticScopeHelper;
}
@Override
public void run(CompilationUnit compilationUnit) {
compilationUnit.findAll(MethodCallExpr.class).stream()
public void run(Source<CompilationUnit> java) {
java.getModel().findAll(MethodCallExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().equals("setThreadPolicy"))
.filter(staticScopeHelper.isFromScope(compilationUnit, "setThreadPolicy", "StrictMode", "android.os"))
.forEach(expr -> addIssue(Severity.WARNING, getLineNumberFromExpression(expr), expr.toString()));
.filter(staticScopeHelper.isFromScope(java.getModel(), "setThreadPolicy", "StrictMode", "android.os"))
.forEach(expr -> addJavaIssue(Severity.WARNING, java.getFile(), expr));
}
}

View File

@@ -1,25 +1,17 @@
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.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.expr.AnnotationExpr;
import javax.inject.Inject;
public class SuppressWarningsPlugin extends JavaPlugin {
@Inject
public SuppressWarningsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
}
@Override
public void run(CompilationUnit compilationUnit) {
compilationUnit.findAll(AnnotationExpr.class).stream()
public void run(Source<CompilationUnit> java) {
java.getModel().findAll(AnnotationExpr.class).stream()
.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;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.JavaPlugin;
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.expr.CastExpr;
import javax.inject.Inject;
public class TelephonyManagerPlugin extends JavaPlugin {
@Inject
public TelephonyManagerPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
}
@Override
public void run(CompilationUnit compilationUnit) {
compilationUnit.findAll(CastExpr.class).stream()
public void run(Source<CompilationUnit> java) {
java.getModel().findAll(CastExpr.class).stream()
.filter(expr -> expr.getType().isClassOrInterfaceType())
.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;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.ResourceLayoutPlugin;
import com.bartek.esa.core.model.enumeration.Severity;
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;
@@ -12,18 +12,19 @@ import javax.inject.Inject;
import java.util.Optional;
public class TextInputValidationPlugin extends ResourceLayoutPlugin {
private final XmlHelper xmlHelper;
@Inject
public TextInputValidationPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
public TextInputValidationPlugin(XmlHelper xmlHelper) {
this.xmlHelper = xmlHelper;
}
@Override
protected void run(Document xml) {
NodeList editTextNodes = xml.getElementsByTagName("EditText");
stream(editTextNodes)
protected void run(Source<Document> layout) {
NodeList editTextNodes = layout.getModel().getElementsByTagName("EditText");
xmlHelper.stream(editTextNodes)
.filter(this::doesNotHaveInputType)
.forEach(n -> addIssue(Severity.WARNING, null, tagString(n)));
.forEach(n -> addXmlIssue(Severity.WARNING, layout.getFile(), n));
}
private boolean doesNotHaveInputType(Node editText) {

View File

@@ -1,9 +1,9 @@
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.model.enumeration.Severity;
import com.bartek.esa.core.xml.XmlHelper;
import com.bartek.esa.file.matcher.GlobMatcher;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
@@ -13,22 +13,23 @@ import java.util.Map;
import java.util.Optional;
public class UsesSdkPlugin extends AndroidManifestPlugin {
private final XmlHelper xmlHelper;
@Inject
public UsesSdkPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
public UsesSdkPlugin(XmlHelper xmlHelper) {
this.xmlHelper = xmlHelper;
}
@Override
protected void run(Document xml) {
Optional.ofNullable((Node) xPath(xml, "/manifest/uses-sdk", XPathConstants.NODE)).ifPresentOrElse(usesSdkNode -> {
protected void run(Source<Document> manifest) {
Optional.ofNullable((Node) xmlHelper.xPath(manifest.getModel(), "/manifest/uses-sdk", XPathConstants.NODE)).ifPresentOrElse(usesSdkNode -> {
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 ->
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;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.JavaPlugin;
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.expr.Expression;
import com.github.javaparser.ast.expr.MethodCallExpr;
import javax.inject.Inject;
import java.util.function.Consumer;
public class WebViewPlugin extends JavaPlugin {
private static final String SETTINGS_METHODS = "addJavascriptInterface|setJavaScriptEnabled|setWebContentsDebuggingEnabled|setAllowFileAccess|setDomStorageEnabled";
@Inject
public WebViewPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
}
@Override
public void run(CompilationUnit compilationUnit) {
compilationUnit.findAll(MethodCallExpr.class).stream()
public void run(Source<CompilationUnit> java) {
java.getModel().findAll(MethodCallExpr.class).stream()
.filter(expr -> expr.getName().getIdentifier().matches(SETTINGS_METHODS))
.forEach(this::issueMethod);
.forEach(issueMethod(java));
}
private void issueMethod(MethodCallExpr methodCall) {
switch (methodCall.getName().getIdentifier()) {
case "addJavascriptInterface":
addIssue(Severity.VULNERABILITY, ".JS_INTERFACE", getLineNumberFromExpression(methodCall), methodCall.toString());
break;
case "setJavaScriptEnabled":
issueSettingsMethod(methodCall, ".JS_ENABLED");
break;
case "setWebContentsDebuggingEnabled":
issueSettingsMethod(methodCall, ".DEBUGGING_ENABLED");
break;
case "setAllowFileAccess":
issueSettingsMethod(methodCall, ".ALLOW_FILE_ACCESS");
break;
}
private Consumer<MethodCallExpr> issueMethod(Source<CompilationUnit> java) {
return methodCall -> {
switch (methodCall.getName().getIdentifier()) {
case "addJavascriptInterface":
addJavaIssue(Severity.VULNERABILITY, ".JS_INTERFACE", java.getFile(), methodCall);
break;
case "setJavaScriptEnabled":
issueSettingsMethod(java, methodCall, ".JS_ENABLED");
break;
case "setWebContentsDebuggingEnabled":
issueSettingsMethod(java, methodCall, ".DEBUGGING_ENABLED");
break;
case "setAllowFileAccess":
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);
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;
import com.bartek.esa.context.model.Source;
import com.bartek.esa.core.archetype.JavaPlugin;
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.expr.FieldAccessExpr;
import com.github.javaparser.ast.expr.NameExpr;
import javax.inject.Inject;
import java.util.Map;
public class WorldAccessPermissionsPlugin extends JavaPlugin {
@Inject
public WorldAccessPermissionsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
super(globMatcher, xmlHelper);
}
@Override
public void run(CompilationUnit compilationUnit) {
compilationUnit.findAll(NameExpr.class).stream()
public void run(Source<CompilationUnit> java) {
java.getModel().findAll(NameExpr.class).stream()
.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)"))
.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) {

View File

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