17: Reinvent architecture to add project context
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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("");
|
||||
}
|
||||
}
|
||||
15
src/main/java/com/bartek/esa/context/di/ContextModule.java
Normal file
15
src/main/java/com/bartek/esa/context/di/ContextModule.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
25
src/main/java/com/bartek/esa/context/model/Context.java
Normal file
25
src/main/java/com/bartek/esa/context/model/Context.java
Normal 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;
|
||||
}
|
||||
17
src/main/java/com/bartek/esa/context/model/Source.java
Normal file
17
src/main/java/com/bartek/esa/context/model/Source.java
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user