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.BufferedWriter;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.bartek.esa.analyser.apk;
|
package com.bartek.esa.analyser.apk;
|
||||||
|
|
||||||
import com.bartek.esa.analyser.core.Analyser;
|
import com.bartek.esa.analyser.core.Analyser;
|
||||||
|
import com.bartek.esa.context.constructor.ContextConstructor;
|
||||||
import com.bartek.esa.core.archetype.Plugin;
|
import com.bartek.esa.core.archetype.Plugin;
|
||||||
import com.bartek.esa.core.executor.PluginExecutor;
|
import com.bartek.esa.core.executor.PluginExecutor;
|
||||||
import com.bartek.esa.decompiler.archetype.Decompiler;
|
import com.bartek.esa.decompiler.archetype.Decompiler;
|
||||||
@@ -19,8 +20,8 @@ public class ApkAnalyser extends Analyser {
|
|||||||
private final FileCleaner fileCleaner;
|
private final FileCleaner fileCleaner;
|
||||||
private final GlobMatcher globMatcher;
|
private final GlobMatcher globMatcher;
|
||||||
|
|
||||||
public ApkAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider, Decompiler decompiler, FileCleaner fileCleaner, GlobMatcher globMatcher) {
|
public ApkAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider, Decompiler decompiler, FileCleaner fileCleaner, GlobMatcher globMatcher, ContextConstructor contextConstructor) {
|
||||||
super(pluginExecutor, plugins, fileProvider);
|
super(pluginExecutor, plugins, fileProvider, contextConstructor);
|
||||||
this.decompiler = decompiler;
|
this.decompiler = decompiler;
|
||||||
this.fileCleaner = fileCleaner;
|
this.fileCleaner = fileCleaner;
|
||||||
this.globMatcher = globMatcher;
|
this.globMatcher = globMatcher;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.bartek.esa.analyser.core;
|
package com.bartek.esa.analyser.core;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.constructor.ContextConstructor;
|
||||||
|
import com.bartek.esa.context.model.Context;
|
||||||
import com.bartek.esa.core.archetype.Plugin;
|
import com.bartek.esa.core.archetype.Plugin;
|
||||||
import com.bartek.esa.core.executor.PluginExecutor;
|
import com.bartek.esa.core.executor.PluginExecutor;
|
||||||
import com.bartek.esa.core.model.object.Issue;
|
import com.bartek.esa.core.model.object.Issue;
|
||||||
@@ -7,31 +9,34 @@ import com.bartek.esa.error.EsaException;
|
|||||||
import com.bartek.esa.file.provider.FileProvider;
|
import com.bartek.esa.file.provider.FileProvider;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public abstract class Analyser {
|
public abstract class Analyser {
|
||||||
private final PluginExecutor pluginExecutor;
|
private final PluginExecutor pluginExecutor;
|
||||||
private final Set<Plugin> plugins;
|
private final Set<Plugin> plugins;
|
||||||
private final FileProvider fileProvider;
|
private final FileProvider fileProvider;
|
||||||
|
private final ContextConstructor contextConstructor;
|
||||||
|
|
||||||
public Analyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider) {
|
public Analyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider, ContextConstructor contextConstructor) {
|
||||||
|
|
||||||
this.pluginExecutor = pluginExecutor;
|
this.pluginExecutor = pluginExecutor;
|
||||||
this.plugins = plugins;
|
this.plugins = plugins;
|
||||||
this.fileProvider = fileProvider;
|
this.fileProvider = fileProvider;
|
||||||
|
this.contextConstructor = contextConstructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Issue> analyse(String source, Set<String> pluginCodes, Set<String> excludeCodes, boolean debug) {
|
public Set<Issue> analyse(String source, Set<String> pluginCodes, Set<String> excludeCodes, boolean debug) {
|
||||||
String newSource = prepareSources(source, debug);
|
String newSource = prepareSources(source, debug);
|
||||||
File manifest = getManifest(newSource);
|
File manifest = getManifest(newSource);
|
||||||
Set<File> files = getFiles(newSource);
|
Set<File> javaSources = getJavaSources(newSource);
|
||||||
Set<Plugin> selectedPlugins = getPlugins(pluginCodes, excludeCodes);
|
Set<File> layoutFiles = getLayoutFiles(newSource);
|
||||||
|
Context context = contextConstructor.construct(manifest, javaSources, layoutFiles);
|
||||||
|
|
||||||
|
Set<Plugin> selectedPlugins = getPlugins(pluginCodes, excludeCodes);
|
||||||
|
Set<Issue> issues = pluginExecutor.executeForContext(context, selectedPlugins, debug);
|
||||||
|
|
||||||
Set<Issue> issues = pluginExecutor.executeForFiles(manifest, files, selectedPlugins, debug);
|
|
||||||
performCleaning(newSource, debug);
|
performCleaning(newSource, debug);
|
||||||
return issues;
|
return issues;
|
||||||
}
|
}
|
||||||
@@ -60,14 +65,12 @@ public abstract class Analyser {
|
|||||||
return (File) (manifests.toArray())[0];
|
return (File) (manifests.toArray())[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<File> getFiles(String source) {
|
private Set<File> getJavaSources(String source) {
|
||||||
Set<File> javaFiles = fileProvider.getGlobMatchedFiles(source, getJavaGlob());
|
return fileProvider.getGlobMatchedFiles(source, getJavaGlob());
|
||||||
Set<File> layoutFiles = fileProvider.getGlobMatchedFiles(source, getLayoutGlob());
|
}
|
||||||
Set<File> androidManifest = Collections.singleton(getManifest(source));
|
|
||||||
|
|
||||||
return Stream.of(javaFiles, androidManifest, layoutFiles)
|
private Set<File> getLayoutFiles(String source) {
|
||||||
.flatMap(Set::stream)
|
return fileProvider.getGlobMatchedFiles(source, getLayoutGlob());
|
||||||
.collect(Collectors.toSet());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<Plugin> getPlugins(Set<String> pluginCodes, Set<String> excludeCodes) {
|
private Set<Plugin> getPlugins(Set<String> pluginCodes, Set<String> excludeCodes) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.bartek.esa.analyser.di;
|
|||||||
|
|
||||||
import com.bartek.esa.analyser.apk.ApkAnalyser;
|
import com.bartek.esa.analyser.apk.ApkAnalyser;
|
||||||
import com.bartek.esa.analyser.source.SourceAnalyser;
|
import com.bartek.esa.analyser.source.SourceAnalyser;
|
||||||
|
import com.bartek.esa.context.constructor.ContextConstructor;
|
||||||
import com.bartek.esa.core.archetype.Plugin;
|
import com.bartek.esa.core.archetype.Plugin;
|
||||||
import com.bartek.esa.core.executor.PluginExecutor;
|
import com.bartek.esa.core.executor.PluginExecutor;
|
||||||
import com.bartek.esa.decompiler.archetype.Decompiler;
|
import com.bartek.esa.decompiler.archetype.Decompiler;
|
||||||
@@ -17,12 +18,12 @@ import java.util.Set;
|
|||||||
public class AnalyserModule {
|
public class AnalyserModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
public SourceAnalyser sourceAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider) {
|
public SourceAnalyser sourceAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider, ContextConstructor contextConstructor) {
|
||||||
return new SourceAnalyser(pluginExecutor, plugins, fileProvider);
|
return new SourceAnalyser(pluginExecutor, plugins, fileProvider, contextConstructor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
public ApkAnalyser apkAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider, Decompiler decompiler, FileCleaner fileCleaner, GlobMatcher globMatcher) {
|
public ApkAnalyser apkAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider, Decompiler decompiler, FileCleaner fileCleaner, GlobMatcher globMatcher, ContextConstructor contextConstructor) {
|
||||||
return new ApkAnalyser(pluginExecutor, plugins, fileProvider, decompiler, fileCleaner, globMatcher);
|
return new ApkAnalyser(pluginExecutor, plugins, fileProvider, decompiler, fileCleaner, globMatcher, contextConstructor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.bartek.esa.analyser.source;
|
package com.bartek.esa.analyser.source;
|
||||||
|
|
||||||
import com.bartek.esa.analyser.core.Analyser;
|
import com.bartek.esa.analyser.core.Analyser;
|
||||||
|
import com.bartek.esa.context.constructor.ContextConstructor;
|
||||||
import com.bartek.esa.core.archetype.Plugin;
|
import com.bartek.esa.core.archetype.Plugin;
|
||||||
import com.bartek.esa.core.executor.PluginExecutor;
|
import com.bartek.esa.core.executor.PluginExecutor;
|
||||||
import com.bartek.esa.error.EsaException;
|
import com.bartek.esa.error.EsaException;
|
||||||
@@ -14,8 +15,8 @@ public class SourceAnalyser extends Analyser {
|
|||||||
private static final String JAVA_GLOB = "**/*.java";
|
private static final String JAVA_GLOB = "**/*.java";
|
||||||
private static final String LAYOUT_GLOB = "**/res/layout*/*.xml";
|
private static final String LAYOUT_GLOB = "**/res/layout*/*.xml";
|
||||||
|
|
||||||
public SourceAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider) {
|
public SourceAnalyser(PluginExecutor pluginExecutor, Set<Plugin> plugins, FileProvider fileProvider, ContextConstructor contextConstructor) {
|
||||||
super(pluginExecutor, plugins, fileProvider);
|
super(pluginExecutor, plugins, fileProvider, contextConstructor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -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;
|
package com.bartek.esa.core.archetype;
|
||||||
|
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
import com.bartek.esa.context.model.Context;
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
import com.bartek.esa.context.model.Source;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
|
||||||
import java.io.File;
|
public abstract class AndroidManifestPlugin extends BasePlugin {
|
||||||
|
|
||||||
public abstract class AndroidManifestPlugin extends XmlPlugin {
|
|
||||||
private final GlobMatcher globMatcher;
|
|
||||||
|
|
||||||
public AndroidManifestPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
|
||||||
super(globMatcher, xmlHelper);
|
|
||||||
this.globMatcher = globMatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supports(File file) {
|
protected void run(Context context) {
|
||||||
return globMatcher.fileMatchesGlobPattern(file, "**/AndroidManifest.xml");
|
run(context.getManifest());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract void run(Source<Document> manifest);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +1,92 @@
|
|||||||
package com.bartek.esa.core.archetype;
|
package com.bartek.esa.core.archetype;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Context;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.model.object.Issue;
|
import com.bartek.esa.core.model.object.Issue;
|
||||||
import org.w3c.dom.Document;
|
import com.github.javaparser.ast.expr.Expression;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.HashSet;
|
import java.util.stream.Collectors;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import static java.lang.String.format;
|
||||||
|
|
||||||
public abstract class BasePlugin implements Plugin {
|
public abstract class BasePlugin implements Plugin {
|
||||||
private Set<Issue> issues = new HashSet<>();
|
private Set<Issue> issues;
|
||||||
private Document manifest;
|
|
||||||
private File file;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update(File file, Document manifest) {
|
public Set<Issue> runForIssues(Context context) {
|
||||||
this.file = file;
|
issues = new HashSet<>();
|
||||||
this.manifest = manifest;
|
run(context);
|
||||||
this.issues.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<Issue> runForIssues() {
|
|
||||||
run(file);
|
|
||||||
return issues;
|
return issues;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void run(File file);
|
protected abstract void run(Context context);
|
||||||
|
|
||||||
protected void addIssue(Severity severity, Integer lineNumber, String line) {
|
protected void addJavaIssue(Severity severity, File file, Expression expression) {
|
||||||
addIssue(severity, "", lineNumber, line);
|
addIssue(severity, file, getLineNumberFromExpression(expression), expression.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addIssue(Severity severity, Map<String, String> descriptionModel, Integer lineNumber, String line) {
|
private Integer getLineNumberFromExpression(Expression expression) {
|
||||||
addIssue(severity, "", descriptionModel, lineNumber, line);
|
return expression.getRange().map(r -> r.begin.line).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addJavaIssue(Severity severity, Map<String, String> descriptionModel, File file, Expression expression) {
|
||||||
|
addIssue(severity, "", descriptionModel, file, getLineNumberFromExpression(expression), expression.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void addIssue(Severity severity, String descriptionCode, Integer lineNumber, String line) {
|
protected void addJavaIssue(Severity severity, String descriptionCode, File file, Expression expression) {
|
||||||
addIssue(severity, descriptionCode, new HashMap<>(), lineNumber, line);
|
addIssue(severity, descriptionCode, new HashMap<>(), file, getLineNumberFromExpression(expression), expression.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addIssue(Severity severity, String descriptionCode, Map<String, String> descriptionModel, Integer lineNumber, String line) {
|
protected void addJavaIssue(Severity severity, String descriptionCode, Map<String, String> descriptionModel, File file, Expression expression) {
|
||||||
|
addIssue(severity, descriptionCode, descriptionModel, file, getLineNumberFromExpression(expression), expression.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addXmlIssue(Severity severity, File file, Node node) {
|
||||||
|
addIssue(severity, file, null, tagString(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addXmlIssue(Severity severity, Map<String, String> descriptionModel, File file, Node node) {
|
||||||
|
addIssue(severity, descriptionModel, file, null, tagString(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addXmlIssue(Severity severity, String descriptionCode, File file, Node node) {
|
||||||
|
addIssue(severity, descriptionCode, file, null, tagString(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addXmlIssue(Severity severity, String descriptionCode, Map<String, String> descriptionModel, File file, Node node) {
|
||||||
|
addIssue(severity, descriptionCode, descriptionModel, file, null, tagString(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String tagString(Node node) {
|
||||||
|
Node[] attributes = new Node[node.getAttributes().getLength()];
|
||||||
|
for (int i = 0; i < attributes.length; ++i) {
|
||||||
|
attributes[i] = node.getAttributes().item(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
String attributesString = Arrays.stream(attributes)
|
||||||
|
.map(n -> format("%s=\"%s\"", n.getNodeName(), n.getNodeValue()))
|
||||||
|
.collect(Collectors.joining(" "));
|
||||||
|
|
||||||
|
return format("<%s %s ...", node.getNodeName(), attributesString);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addIssue(Severity severity, File file, Integer lineNumber, String line) {
|
||||||
|
addIssue(severity, "", file, lineNumber, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addIssue(Severity severity, Map<String, String> descriptionModel, File file, Integer lineNumber, String line) {
|
||||||
|
addIssue(severity, "", descriptionModel, file, lineNumber, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addIssue(Severity severity, String descriptionCode, File file, Integer lineNumber, String line) {
|
||||||
|
addIssue(severity, descriptionCode, new HashMap<>(), file, lineNumber, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addIssue(Severity severity, String descriptionCode, Map<String, String> descriptionModel, File file, Integer lineNumber, String line) {
|
||||||
Issue issue = Issue.builder()
|
Issue issue = Issue.builder()
|
||||||
.severity(severity)
|
.severity(severity)
|
||||||
.issuer(this.getClass())
|
.issuer(this.getClass())
|
||||||
@@ -54,18 +97,10 @@ public abstract class BasePlugin implements Plugin {
|
|||||||
.line(line)
|
.line(line)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
issues.add(issue);
|
addIssue(issue);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addIssue(Issue issue) {
|
protected void addIssue(Issue issue) {
|
||||||
issues.add(issue);
|
issues.add(issue);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected File getOriginalFile() {
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Document getManifest() {
|
|
||||||
return manifest;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,92 +1,15 @@
|
|||||||
package com.bartek.esa.core.archetype;
|
package com.bartek.esa.core.archetype;
|
||||||
|
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.context.model.Context;
|
||||||
import com.bartek.esa.core.model.object.Issue;
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import com.github.javaparser.ParseProblemException;
|
|
||||||
import com.github.javaparser.Problem;
|
|
||||||
import com.github.javaparser.StaticJavaParser;
|
|
||||||
import com.github.javaparser.TokenRange;
|
|
||||||
import com.github.javaparser.ast.CompilationUnit;
|
import com.github.javaparser.ast.CompilationUnit;
|
||||||
import com.github.javaparser.ast.expr.Expression;
|
|
||||||
import org.w3c.dom.Document;
|
|
||||||
import org.w3c.dom.Node;
|
|
||||||
|
|
||||||
import javax.xml.xpath.XPathConstants;
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import static java.lang.String.format;
|
|
||||||
|
|
||||||
public abstract class JavaPlugin extends BasePlugin {
|
public abstract class JavaPlugin extends BasePlugin {
|
||||||
private final GlobMatcher globMatcher;
|
|
||||||
private final XmlHelper xmlHelper;
|
|
||||||
|
|
||||||
public JavaPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
|
||||||
this.globMatcher = globMatcher;
|
|
||||||
this.xmlHelper = xmlHelper;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supports(File file) {
|
protected void run(Context context) {
|
||||||
return globMatcher.fileMatchesGlobPattern(file, "**/*.java");
|
context.getJavaSources().forEach(this::run);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected abstract void run(Source<CompilationUnit> compilationUnit);
|
||||||
protected void run(File file) {
|
|
||||||
if(!isApplicationPackageFile(file)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
CompilationUnit compilationUnit = StaticJavaParser.parse(file);
|
|
||||||
run(compilationUnit);
|
|
||||||
} catch (ParseProblemException e) {
|
|
||||||
printParsingErrorToStderr(e, file);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void printParsingErrorToStderr(ParseProblemException e, File file) {
|
|
||||||
e.getProblems().stream()
|
|
||||||
.map(p -> format("%s%s:\n%s\nIgnoring file...\n", file.getAbsolutePath(), getRange(p), p.getMessage()))
|
|
||||||
.forEach(System.err::println);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getRange(Problem problem) {
|
|
||||||
return problem.getLocation()
|
|
||||||
.flatMap(TokenRange::toRange)
|
|
||||||
.map(range -> format(" (line %d, col %d)", range.begin.line, range.begin.column))
|
|
||||||
.orElse("");
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isApplicationPackageFile(File file) {
|
|
||||||
Document manifest = getManifest();
|
|
||||||
Node root = (Node) xmlHelper.xPath(manifest, "/manifest", XPathConstants.NODE);
|
|
||||||
Node packageValue = root.getAttributes().getNamedItem("package");
|
|
||||||
|
|
||||||
if(packageValue == null) {
|
|
||||||
Issue issue = Issue.builder()
|
|
||||||
.issuer(JavaPlugin.class)
|
|
||||||
.descriptionCode(".NO_PACKAGE")
|
|
||||||
.descriptionModel(new HashMap<>())
|
|
||||||
.severity(Severity.ERROR)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
addIssue(issue);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
String path = packageValue.getNodeValue().replaceAll("\\.", "/");
|
|
||||||
return globMatcher.fileMatchesGlobPattern(file, format("**/%s/**", path));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Integer getLineNumberFromExpression(Expression expression) {
|
|
||||||
return expression.getRange().map(r -> r.begin.line).orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void run(CompilationUnit compilationUnit);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
package com.bartek.esa.core.archetype;
|
package com.bartek.esa.core.archetype;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Context;
|
||||||
import com.bartek.esa.core.model.object.Issue;
|
import com.bartek.esa.core.model.object.Issue;
|
||||||
import org.w3c.dom.Document;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public interface Plugin {
|
public interface Plugin {
|
||||||
boolean supports(File file);
|
Set<Issue> runForIssues(Context context);
|
||||||
void update(File file, Document manifest);
|
|
||||||
Set<Issue> runForIssues();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,15 @@
|
|||||||
package com.bartek.esa.core.archetype;
|
package com.bartek.esa.core.archetype;
|
||||||
|
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
import com.bartek.esa.context.model.Context;
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
import com.bartek.esa.context.model.Source;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
|
||||||
import java.io.File;
|
public abstract class ResourceLayoutPlugin extends BasePlugin {
|
||||||
|
|
||||||
public abstract class ResourceLayoutPlugin extends XmlPlugin {
|
|
||||||
private final GlobMatcher globMatcher;
|
|
||||||
|
|
||||||
public ResourceLayoutPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
|
||||||
super(globMatcher, xmlHelper);
|
|
||||||
this.globMatcher = globMatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supports(File file) {
|
protected void run(Context context) {
|
||||||
return globMatcher.fileMatchesGlobPattern(file, "**/res/layout*/*.xml");
|
context.getLayouts().forEach(this::run);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract void run(Source<Document> layout);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.executor.PluginExecutor;
|
||||||
import com.bartek.esa.core.helper.ParentNodeFinder;
|
import com.bartek.esa.core.helper.ParentNodeFinder;
|
||||||
import com.bartek.esa.core.helper.StaticScopeHelper;
|
import com.bartek.esa.core.helper.StaticScopeHelper;
|
||||||
|
import com.bartek.esa.core.helper.StringConcatenationChecker;
|
||||||
import com.bartek.esa.core.java.JavaSyntaxRegexProvider;
|
import com.bartek.esa.core.java.JavaSyntaxRegexProvider;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
import com.bartek.esa.core.xml.XmlHelper;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
@@ -13,8 +14,8 @@ import dagger.Provides;
|
|||||||
public class CoreModule {
|
public class CoreModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
public PluginExecutor pluginExecutor(XmlHelper xmlHelper) {
|
public PluginExecutor pluginExecutor() {
|
||||||
return new PluginExecutor(xmlHelper);
|
return new PluginExecutor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@@ -37,6 +38,11 @@ public class CoreModule {
|
|||||||
return new StaticScopeHelper();
|
return new StaticScopeHelper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
public StringConcatenationChecker stringConcatenationChecker(StaticScopeHelper staticScopeHelper) {
|
||||||
|
return new StringConcatenationChecker(staticScopeHelper);
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
public ParentNodeFinder parentNodeFinder() {
|
public ParentNodeFinder parentNodeFinder() {
|
||||||
return new ParentNodeFinder();
|
return new ParentNodeFinder();
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ package com.bartek.esa.core.di;
|
|||||||
import com.bartek.esa.core.archetype.Plugin;
|
import com.bartek.esa.core.archetype.Plugin;
|
||||||
import com.bartek.esa.core.helper.ParentNodeFinder;
|
import com.bartek.esa.core.helper.ParentNodeFinder;
|
||||||
import com.bartek.esa.core.helper.StaticScopeHelper;
|
import com.bartek.esa.core.helper.StaticScopeHelper;
|
||||||
|
import com.bartek.esa.core.helper.StringConcatenationChecker;
|
||||||
import com.bartek.esa.core.java.JavaSyntaxRegexProvider;
|
import com.bartek.esa.core.java.JavaSyntaxRegexProvider;
|
||||||
import com.bartek.esa.core.plugin.*;
|
import com.bartek.esa.core.plugin.*;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
import com.bartek.esa.core.xml.XmlHelper;
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import dagger.multibindings.ElementsIntoSet;
|
import dagger.multibindings.ElementsIntoSet;
|
||||||
@@ -26,127 +26,127 @@ public class PluginModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin loggingPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, StaticScopeHelper staticScopeHelper) {
|
public Plugin loggingPlugin(StaticScopeHelper staticScopeHelper, StringConcatenationChecker stringConcatenationChecker) {
|
||||||
return new LoggingPlugin(globMatcher, xmlHelper, staticScopeHelper);
|
return new LoggingPlugin(staticScopeHelper, stringConcatenationChecker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin debuggablePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public Plugin debuggablePlugin(XmlHelper xmlHelper) {
|
||||||
return new DebuggablePlugin(globMatcher, xmlHelper);
|
return new DebuggablePlugin(xmlHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin allowBackupPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public Plugin allowBackupPlugin(XmlHelper xmlHelper) {
|
||||||
return new AllowBackupPlugin(globMatcher, xmlHelper);
|
return new AllowBackupPlugin(xmlHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin permissionRaceConditionPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public Plugin permissionRaceConditionPlugin(XmlHelper xmlHelper) {
|
||||||
return new PermissionsRaceConditionPlugin(globMatcher, xmlHelper);
|
return new PermissionsRaceConditionPlugin(xmlHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin secureRandomPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public Plugin secureRandomPlugin() {
|
||||||
return new SecureRandomPlugin(globMatcher, xmlHelper);
|
return new SecureRandomPlugin();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin implicitIntentsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, JavaSyntaxRegexProvider javaSyntaxRegexProvider) {
|
public Plugin implicitIntentsPlugin(JavaSyntaxRegexProvider javaSyntaxRegexProvider) {
|
||||||
return new ImplicitIntentsPlugin(globMatcher, xmlHelper, javaSyntaxRegexProvider);
|
return new ImplicitIntentsPlugin(javaSyntaxRegexProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin sharedUidPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public Plugin sharedUidPlugin(XmlHelper xmlHelper) {
|
||||||
return new SharedUidPlugin(globMatcher, xmlHelper);
|
return new SharedUidPlugin(xmlHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin usesSdkPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public Plugin usesSdkPlugin(XmlHelper xmlHelper) {
|
||||||
return new UsesSdkPlugin(globMatcher, xmlHelper);
|
return new UsesSdkPlugin(xmlHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin cipherInstancePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, StaticScopeHelper staticScopeHelper) {
|
public Plugin cipherInstancePlugin(StaticScopeHelper staticScopeHelper) {
|
||||||
return new CipherInstancePlugin(globMatcher, xmlHelper, staticScopeHelper);
|
return new CipherInstancePlugin(staticScopeHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin strictModePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, StaticScopeHelper staticScopeHelper) {
|
public Plugin strictModePlugin(StaticScopeHelper staticScopeHelper) {
|
||||||
return new StrictModePlugin(globMatcher, xmlHelper, staticScopeHelper);
|
return new StrictModePlugin(staticScopeHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin externalStoragePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, ParentNodeFinder parentNodeFinder) {
|
public Plugin externalStoragePlugin(ParentNodeFinder parentNodeFinder) {
|
||||||
return new ExternalStoragePlugin(globMatcher, xmlHelper, parentNodeFinder);
|
return new ExternalStoragePlugin(parentNodeFinder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin suppressWarningsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public Plugin suppressWarningsPlugin() {
|
||||||
return new SuppressWarningsPlugin(globMatcher, xmlHelper);
|
return new SuppressWarningsPlugin();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin exportedComponentsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public Plugin exportedComponentsPlugin(XmlHelper xmlHelper) {
|
||||||
return new ExportedComponentsPlugin(globMatcher, xmlHelper);
|
return new ExportedComponentsPlugin(xmlHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin dangerousPermissionPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public Plugin dangerousPermissionPlugin(XmlHelper xmlHelper) {
|
||||||
return new DangerousPermissionPlugin(globMatcher, xmlHelper);
|
return new DangerousPermissionPlugin(xmlHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin textInputValidationPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public Plugin textInputValidationPlugin(XmlHelper xmlHelper) {
|
||||||
return new TextInputValidationPlugin(globMatcher, xmlHelper);
|
return new TextInputValidationPlugin(xmlHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin intentFilterPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public Plugin intentFilterPlugin(XmlHelper xmlHelper) {
|
||||||
return new IntentFilterPlugin(globMatcher, xmlHelper);
|
return new IntentFilterPlugin(xmlHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin sqlInjectionPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public Plugin sqlInjectionPlugin(StringConcatenationChecker stringConcatenationChecker) {
|
||||||
return new SqlInjectionPlugin(globMatcher, xmlHelper);
|
return new SqlInjectionPlugin( stringConcatenationChecker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin worldAccessPermissionsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public Plugin worldAccessPermissionsPlugin() {
|
||||||
return new WorldAccessPermissionsPlugin(globMatcher, xmlHelper);
|
return new WorldAccessPermissionsPlugin();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin orderedAndStickyBroadcastPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public Plugin orderedAndStickyBroadcastPlugin() {
|
||||||
return new OrderedBroadcastPlugin(globMatcher, xmlHelper);
|
return new OrderedBroadcastPlugin();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin webViewPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public Plugin webViewPlugin() {
|
||||||
return new WebViewPlugin(globMatcher, xmlHelper);
|
return new WebViewPlugin();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@IntoSet
|
@IntoSet
|
||||||
public Plugin telephonyManagerPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public Plugin telephonyManagerPlugin() {
|
||||||
return new TelephonyManagerPlugin(globMatcher, xmlHelper);
|
return new TelephonyManagerPlugin();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +1,30 @@
|
|||||||
package com.bartek.esa.core.executor;
|
package com.bartek.esa.core.executor;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Context;
|
||||||
import com.bartek.esa.core.archetype.Plugin;
|
import com.bartek.esa.core.archetype.Plugin;
|
||||||
import com.bartek.esa.core.model.object.Issue;
|
import com.bartek.esa.core.model.object.Issue;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
|
||||||
import org.w3c.dom.Document;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static java.util.stream.Collectors.toSet;
|
import static java.util.stream.Collectors.toSet;
|
||||||
|
|
||||||
public class PluginExecutor {
|
public class PluginExecutor {
|
||||||
private final XmlHelper xmlHelper;
|
|
||||||
|
|
||||||
@Inject
|
public Set<Issue> executeForContext(Context context, Set<Plugin> plugins, boolean debug) {
|
||||||
public PluginExecutor(XmlHelper xmlHelper) {
|
|
||||||
this.xmlHelper = xmlHelper;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<Issue> executeForFiles(File manifest, Set<File> files, Set<Plugin> plugins, boolean debug) {
|
|
||||||
return files.stream()
|
|
||||||
.peek(file -> { if(debug) System.out.printf("File: %s\n", file.getAbsolutePath()); })
|
|
||||||
.map(file -> executeForFile(manifest, file, plugins, debug))
|
|
||||||
.flatMap(Set::stream)
|
|
||||||
.collect(toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<Issue> executeForFile(File manifest, File file, Set<Plugin> plugins, boolean debug) {
|
|
||||||
Document xmlManifest = xmlHelper.parseXml(manifest);
|
|
||||||
return plugins.stream()
|
return plugins.stream()
|
||||||
.peek(plugin -> { if(debug) System.out.printf(" Plugin: %s\n", plugin.getClass().getCanonicalName()); })
|
.peek(logPlugin(debug))
|
||||||
.peek(plugin -> plugin.update(file, xmlManifest))
|
.map(plugin -> plugin.runForIssues(context))
|
||||||
.filter(plugin -> plugin.supports(file))
|
|
||||||
.map(Plugin::runForIssues)
|
|
||||||
.flatMap(Set::stream)
|
.flatMap(Set::stream)
|
||||||
.collect(toSet());
|
.collect(toSet());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Consumer<Plugin> logPlugin(boolean debug) {
|
||||||
|
return plugin -> {
|
||||||
|
if(debug) {
|
||||||
|
System.out.printf(" Plugin: %s\n", plugin.getClass().getCanonicalName());
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
public int compareTo(Object o) {
|
||||||
Issue another = (Issue) o;
|
Issue another = (Issue) o;
|
||||||
int compByFile = file.compareTo(another.file);
|
int compByFile = file.compareTo(another.file);
|
||||||
|
|
||||||
if(compByFile != 0) {
|
if(compByFile != 0) {
|
||||||
return compByFile;
|
return compByFile;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.AndroidManifestPlugin;
|
import com.bartek.esa.core.archetype.AndroidManifestPlugin;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
import com.bartek.esa.core.xml.XmlHelper;
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
|
|
||||||
@@ -12,19 +12,20 @@ import javax.xml.xpath.XPathConstants;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class AllowBackupPlugin extends AndroidManifestPlugin {
|
public class AllowBackupPlugin extends AndroidManifestPlugin {
|
||||||
|
private final XmlHelper xmlHelper;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public AllowBackupPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public AllowBackupPlugin(XmlHelper xmlHelper) {
|
||||||
super(globMatcher, xmlHelper);
|
this.xmlHelper = xmlHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run(Document xml) {
|
protected void run(Source<Document> manifest) {
|
||||||
Node applicationNode = (Node) xPath(xml, "/manifest/application", XPathConstants.NODE);
|
Node applicationNode = (Node) xmlHelper.xPath(manifest.getModel(), "/manifest/application", XPathConstants.NODE);
|
||||||
Optional.ofNullable(applicationNode.getAttributes().getNamedItem("android:allowBackup")).ifPresentOrElse(n -> {
|
Optional.ofNullable(applicationNode.getAttributes().getNamedItem("android:allowBackup")).ifPresentOrElse(n -> {
|
||||||
if (!n.getNodeValue().equals("false")) {
|
if (!n.getNodeValue().equals("false")) {
|
||||||
addIssue(Severity.WARNING, ".NO_FALSE", null, n.toString());
|
addIssue(Severity.WARNING, ".NO_FALSE", manifest.getFile(), null, n.toString());
|
||||||
}
|
}
|
||||||
}, () -> addIssue(Severity.ERROR, ".NO_ATTR", null, null));
|
}, () -> addIssue(Severity.ERROR, ".NO_ATTR", manifest.getFile(), null, null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.JavaPlugin;
|
import com.bartek.esa.core.archetype.JavaPlugin;
|
||||||
import com.bartek.esa.core.helper.StaticScopeHelper;
|
import com.bartek.esa.core.helper.StaticScopeHelper;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import com.github.javaparser.ast.CompilationUnit;
|
import com.github.javaparser.ast.CompilationUnit;
|
||||||
import com.github.javaparser.ast.expr.MethodCallExpr;
|
import com.github.javaparser.ast.expr.MethodCallExpr;
|
||||||
|
|
||||||
@@ -16,19 +15,18 @@ public class CipherInstancePlugin extends JavaPlugin {
|
|||||||
private final StaticScopeHelper staticScopeHelper;
|
private final StaticScopeHelper staticScopeHelper;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public CipherInstancePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, StaticScopeHelper staticScopeHelper) {
|
public CipherInstancePlugin(StaticScopeHelper staticScopeHelper) {
|
||||||
super(globMatcher, xmlHelper);
|
|
||||||
this.staticScopeHelper = staticScopeHelper;
|
this.staticScopeHelper = staticScopeHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(CompilationUnit compilationUnit) {
|
public void run(Source<CompilationUnit> java) {
|
||||||
compilationUnit.findAll(MethodCallExpr.class).stream()
|
java.getModel().findAll(MethodCallExpr.class).stream()
|
||||||
.filter(expr -> expr.getName().getIdentifier().equals("getInstance"))
|
.filter(expr -> expr.getName().getIdentifier().equals("getInstance"))
|
||||||
.filter(staticScopeHelper.isFromScope(compilationUnit, "getInstance", "Cipher", "javax.crypto"))
|
.filter(staticScopeHelper.isFromScope(java.getModel(), "getInstance", "Cipher", "javax.crypto"))
|
||||||
.filter(expr -> expr.getArguments().isNonEmpty())
|
.filter(expr -> expr.getArguments().isNonEmpty())
|
||||||
.filter(expr -> !isFullCipherQualifier(expr.getArguments().get(0).toString()))
|
.filter(expr -> !isFullCipherQualifier(expr.getArguments().get(0).toString()))
|
||||||
.forEach(expr -> addIssue(Severity.ERROR, getLineNumberFromExpression(expr), expr.toString()));
|
.forEach(expr -> addJavaIssue(Severity.ERROR, java.getFile(), expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isFullCipherQualifier(String qualifier) {
|
private boolean isFullCipherQualifier(String qualifier) {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.AndroidManifestPlugin;
|
import com.bartek.esa.core.archetype.AndroidManifestPlugin;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
import com.bartek.esa.core.xml.XmlHelper;
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
@@ -13,19 +13,20 @@ import javax.xml.xpath.XPathConstants;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class DangerousPermissionPlugin extends AndroidManifestPlugin {
|
public class DangerousPermissionPlugin extends AndroidManifestPlugin {
|
||||||
|
private final XmlHelper xmlHelper;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public DangerousPermissionPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public DangerousPermissionPlugin(XmlHelper xmlHelper) {
|
||||||
super(globMatcher, xmlHelper);
|
this.xmlHelper = xmlHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run(Document xml) {
|
protected void run(Source<Document> manifest) {
|
||||||
NodeList customPermissions = (NodeList) xPath(xml, "/manifest/permission", XPathConstants.NODESET);
|
NodeList customPermissions = (NodeList) xmlHelper.xPath(manifest.getModel(), "/manifest/permission", XPathConstants.NODESET);
|
||||||
stream(customPermissions)
|
xmlHelper.stream(customPermissions)
|
||||||
.filter(this::isDangerousPermission)
|
.filter(this::isDangerousPermission)
|
||||||
.filter(this::doesNotHaveDescription)
|
.filter(this::doesNotHaveDescription)
|
||||||
.forEach(permission -> addIssue(Severity.WARNING, null, tagString(permission)));
|
.forEach(permission -> addXmlIssue(Severity.WARNING, manifest.getFile(), permission));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isDangerousPermission(Node permission) {
|
private boolean isDangerousPermission(Node permission) {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.AndroidManifestPlugin;
|
import com.bartek.esa.core.archetype.AndroidManifestPlugin;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
import com.bartek.esa.core.xml.XmlHelper;
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
|
|
||||||
@@ -12,19 +12,20 @@ import javax.xml.xpath.XPathConstants;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class DebuggablePlugin extends AndroidManifestPlugin {
|
public class DebuggablePlugin extends AndroidManifestPlugin {
|
||||||
|
private final XmlHelper xmlHelper;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public DebuggablePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public DebuggablePlugin(XmlHelper xmlHelper) {
|
||||||
super(globMatcher, xmlHelper);
|
this.xmlHelper = xmlHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run(Document xml) {
|
protected void run(Source<Document> manifest) {
|
||||||
Node applicationNode = (Node) xPath(xml, "/manifest/application", XPathConstants.NODE);
|
Node applicationNode = (Node) xmlHelper.xPath(manifest.getModel(), "/manifest/application", XPathConstants.NODE);
|
||||||
Optional.ofNullable(applicationNode.getAttributes().getNamedItem("android:debuggable")).ifPresentOrElse(n -> {
|
Optional.ofNullable(applicationNode.getAttributes().getNamedItem("android:debuggable")).ifPresentOrElse(n -> {
|
||||||
if(!n.getNodeValue().equals("false")) {
|
if(!n.getNodeValue().equals("false")) {
|
||||||
addIssue(Severity.WARNING, ".NO_FALSE", null, n.toString());
|
addIssue(Severity.WARNING, ".NO_FALSE", manifest.getFile(),null, n.toString());
|
||||||
}
|
}
|
||||||
}, () -> addIssue(Severity.ERROR, ".NO_ATTR",null, null));
|
}, () -> addIssue(Severity.ERROR, ".NO_ATTR", manifest.getFile(), null, null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.AndroidManifestPlugin;
|
import com.bartek.esa.core.archetype.AndroidManifestPlugin;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
import com.bartek.esa.core.xml.XmlHelper;
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
@@ -16,26 +16,27 @@ import java.util.Optional;
|
|||||||
import static java.lang.String.format;
|
import static java.lang.String.format;
|
||||||
|
|
||||||
public class ExportedComponentsPlugin extends AndroidManifestPlugin {
|
public class ExportedComponentsPlugin extends AndroidManifestPlugin {
|
||||||
|
private final XmlHelper xmlHelper;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ExportedComponentsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public ExportedComponentsPlugin(XmlHelper xmlHelper) {
|
||||||
super(globMatcher, xmlHelper);
|
this.xmlHelper = xmlHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run(Document xml) {
|
protected void run(Source<Document> manifest) {
|
||||||
findExportedComponents(xml, "activity");
|
findExportedComponents(manifest, "activity");
|
||||||
findExportedComponents(xml, "service");
|
findExportedComponents(manifest, "service");
|
||||||
findExportedComponents(xml, "receiver");
|
findExportedComponents(manifest, "receiver");
|
||||||
findExportedProviders(xml);
|
findExportedProviders(manifest);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findExportedComponents(Document xml, String component) {
|
private void findExportedComponents(Source<Document> manifest, String component) {
|
||||||
NodeList exportedActivities = (NodeList) xPath(xml, format("/manifest/application/%s", component), XPathConstants.NODESET);
|
NodeList exportedActivities = (NodeList) xmlHelper.xPath(manifest.getModel(), format("/manifest/application/%s", component), XPathConstants.NODESET);
|
||||||
stream(exportedActivities)
|
xmlHelper.stream(exportedActivities)
|
||||||
.filter(this::isExported)
|
.filter(this::isExported)
|
||||||
.filter(node -> doesNotHavePermission(node, "android:permission"))
|
.filter(node -> doesNotHavePermission(node, "android:permission"))
|
||||||
.forEach(node -> addIssue(Severity.WARNING, ".NO_PERMISSION", getModel(node), null, null));
|
.forEach(node -> addIssue(Severity.WARNING, ".NO_PERMISSION", getModel(node), manifest.getFile(), null, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, String> getModel(Node node) {
|
private Map<String, String> getModel(Node node) {
|
||||||
@@ -45,13 +46,13 @@ public class ExportedComponentsPlugin extends AndroidManifestPlugin {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findExportedProviders(Document xml) {
|
private void findExportedProviders(Source<Document> manifest) {
|
||||||
NodeList exportedProviders = (NodeList) xPath(xml, "/manifest/application/provider", XPathConstants.NODESET);
|
NodeList exportedProviders = (NodeList) xmlHelper.xPath(manifest.getModel(), "/manifest/application/provider", XPathConstants.NODESET);
|
||||||
stream(exportedProviders)
|
xmlHelper.stream(exportedProviders)
|
||||||
.filter(this::isExported)
|
.filter(this::isExported)
|
||||||
.filter(node -> doesNotHavePermission(node, "android:writePermission")
|
.filter(node -> doesNotHavePermission(node, "android:writePermission")
|
||||||
|| doesNotHavePermission(node, "android:readPermission"))
|
|| doesNotHavePermission(node, "android:readPermission"))
|
||||||
.forEach(node -> addIssue(Severity.WARNING, ".NO_PERMISSION", getModel(node), null, null));
|
.forEach(node -> addIssue(Severity.WARNING, ".NO_PERMISSION", getModel(node), manifest.getFile(), null, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean doesNotHavePermission(Node node, String permissionAttribute) {
|
private boolean doesNotHavePermission(Node node, String permissionAttribute) {
|
||||||
@@ -69,6 +70,7 @@ public class ExportedComponentsPlugin extends AndroidManifestPlugin {
|
|||||||
.orElse(false);
|
.orElse(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo remove it!
|
||||||
private String nodeToString(Node node) {
|
private String nodeToString(Node node) {
|
||||||
String nodeName = Optional.ofNullable(node.getAttributes().getNamedItem("android:name"))
|
String nodeName = Optional.ofNullable(node.getAttributes().getNamedItem("android:name"))
|
||||||
.map(Node::getNodeValue)
|
.map(Node::getNodeValue)
|
||||||
|
|||||||
@@ -1,44 +1,45 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.JavaPlugin;
|
import com.bartek.esa.core.archetype.JavaPlugin;
|
||||||
import com.bartek.esa.core.helper.ParentNodeFinder;
|
import com.bartek.esa.core.helper.ParentNodeFinder;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import com.github.javaparser.ast.CompilationUnit;
|
import com.github.javaparser.ast.CompilationUnit;
|
||||||
import com.github.javaparser.ast.body.MethodDeclaration;
|
import com.github.javaparser.ast.body.MethodDeclaration;
|
||||||
import com.github.javaparser.ast.expr.MethodCallExpr;
|
import com.github.javaparser.ast.expr.MethodCallExpr;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class ExternalStoragePlugin extends JavaPlugin {
|
public class ExternalStoragePlugin extends JavaPlugin {
|
||||||
private final ParentNodeFinder parentNodeFinder;
|
private final ParentNodeFinder parentNodeFinder;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ExternalStoragePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, ParentNodeFinder parentNodeFinder) {
|
public ExternalStoragePlugin(ParentNodeFinder parentNodeFinder) {
|
||||||
super(globMatcher, xmlHelper);
|
|
||||||
this.parentNodeFinder = parentNodeFinder;
|
this.parentNodeFinder = parentNodeFinder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(CompilationUnit compilationUnit) {
|
public void run(Source<CompilationUnit> java) {
|
||||||
compilationUnit.findAll(MethodCallExpr.class).stream()
|
java.getModel().findAll(MethodCallExpr.class).stream()
|
||||||
.filter(expr -> expr.getName().getIdentifier().matches("getExternalStorageDirectory|getExternalStoragePublicDirectory"))
|
.filter(expr -> expr.getName().getIdentifier().matches("getExternalStorageDirectory|getExternalStoragePublicDirectory"))
|
||||||
.forEach(this::findCheckingStorageStateForAccessingExternalStorage);
|
.forEach(findCheckingStorageStateForAccessingExternalStorage(java));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findCheckingStorageStateForAccessingExternalStorage(MethodCallExpr accessingToExternalStorage) {
|
private Consumer<MethodCallExpr> findCheckingStorageStateForAccessingExternalStorage(Source<CompilationUnit> java) {
|
||||||
parentNodeFinder.findParentNode(accessingToExternalStorage, MethodDeclaration.class).ifPresent(methodDeclaration ->
|
return accessingToExternalStorage -> parentNodeFinder
|
||||||
findCheckingStorageStateInMethodDeclaration(accessingToExternalStorage, methodDeclaration)
|
.findParentNode(accessingToExternalStorage, MethodDeclaration.class)
|
||||||
);
|
.ifPresent(methodDeclaration ->
|
||||||
|
findCheckingStorageStateInMethodDeclaration(java, accessingToExternalStorage, methodDeclaration)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void findCheckingStorageStateInMethodDeclaration(MethodCallExpr accessingToExternalStorage, MethodDeclaration methodDeclaration) {
|
private void findCheckingStorageStateInMethodDeclaration(Source<CompilationUnit> java, MethodCallExpr accessingToExternalStorage, MethodDeclaration methodDeclaration) {
|
||||||
boolean isStateBeingChecked = methodDeclaration.findAll(MethodCallExpr.class).stream()
|
boolean isStateBeingChecked = methodDeclaration.findAll(MethodCallExpr.class).stream()
|
||||||
.anyMatch(e -> e.getName().getIdentifier().equals("getExternalStorageState"));
|
.anyMatch(e -> e.getName().getIdentifier().equals("getExternalStorageState"));
|
||||||
|
|
||||||
if (!isStateBeingChecked) {
|
if (!isStateBeingChecked) {
|
||||||
addIssue(Severity.WARNING, getLineNumberFromExpression(accessingToExternalStorage), accessingToExternalStorage.toString());
|
addJavaIssue(Severity.WARNING, java.getFile(), accessingToExternalStorage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.JavaPlugin;
|
import com.bartek.esa.core.archetype.JavaPlugin;
|
||||||
import com.bartek.esa.core.java.JavaSyntaxRegexProvider;
|
import com.bartek.esa.core.java.JavaSyntaxRegexProvider;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import com.github.javaparser.ast.CompilationUnit;
|
import com.github.javaparser.ast.CompilationUnit;
|
||||||
import com.github.javaparser.ast.Node;
|
import com.github.javaparser.ast.Node;
|
||||||
import com.github.javaparser.ast.NodeList;
|
import com.github.javaparser.ast.NodeList;
|
||||||
@@ -17,27 +16,26 @@ import java.util.Optional;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class ImplicitIntentsPlugin extends JavaPlugin {
|
public class ImplicitIntentsPlugin extends JavaPlugin {
|
||||||
private final JavaSyntaxRegexProvider java;
|
private final JavaSyntaxRegexProvider javaSyntax;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ImplicitIntentsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, JavaSyntaxRegexProvider javaSyntaxRegexProvider) {
|
public ImplicitIntentsPlugin(JavaSyntaxRegexProvider javaSyntaxRegexProvider) {
|
||||||
super(globMatcher, xmlHelper);
|
this.javaSyntax = javaSyntaxRegexProvider;
|
||||||
this.java = javaSyntaxRegexProvider;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(CompilationUnit compilationUnit) {
|
public void run(Source<CompilationUnit> java) {
|
||||||
checkCreatingImplicitIntents(compilationUnit);
|
checkCreatingImplicitIntents(java);
|
||||||
checkCreatingPendingIntentsWithoutIntentVariable(compilationUnit);
|
checkCreatingPendingIntentsWithoutIntentVariable(java);
|
||||||
checkCreatingPendingIntentsWithIntentVariables(compilationUnit);
|
checkCreatingPendingIntentsWithIntentVariables(java);
|
||||||
checkCreatingPendingIntentsWithIntentsArraysVariables(compilationUnit);
|
checkCreatingPendingIntentsWithIntentsArraysVariables(java);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Works for:
|
// Works for:
|
||||||
// Intent[] myIntents = { new Intent(...), ... }
|
// Intent[] myIntents = { new Intent(...), ... }
|
||||||
// getActivities(this, 0, myIntents, 0);
|
// getActivities(this, 0, myIntents, 0);
|
||||||
private void checkCreatingPendingIntentsWithIntentsArraysVariables(CompilationUnit compilationUnit) {
|
private void checkCreatingPendingIntentsWithIntentsArraysVariables(Source<CompilationUnit> java) {
|
||||||
List<String> implicitIntentsArraysVariables = compilationUnit.findAll(ObjectCreationExpr.class).stream()
|
List<String> implicitIntentsArraysVariables = java.getModel().findAll(ObjectCreationExpr.class).stream()
|
||||||
.filter(expr -> expr.getType().getName().getIdentifier().equals("Intent"))
|
.filter(expr -> expr.getType().getName().getIdentifier().equals("Intent"))
|
||||||
.filter(this::isCreatingImplicitIntent)
|
.filter(this::isCreatingImplicitIntent)
|
||||||
.map(Node::getParentNode)
|
.map(Node::getParentNode)
|
||||||
@@ -49,31 +47,31 @@ public class ImplicitIntentsPlugin extends JavaPlugin {
|
|||||||
.map(VariableDeclarator::getName)
|
.map(VariableDeclarator::getName)
|
||||||
.map(SimpleName::getIdentifier)
|
.map(SimpleName::getIdentifier)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
compilationUnit.findAll(MethodCallExpr.class).stream()
|
java.getModel().findAll(MethodCallExpr.class).stream()
|
||||||
.filter(expr -> expr.getName().getIdentifier().matches("getActivities"))
|
.filter(expr -> expr.getName().getIdentifier().matches("getActivities"))
|
||||||
.filter(expr -> expr.getArguments().size() >= 4)
|
.filter(expr -> expr.getArguments().size() >= 4)
|
||||||
.filter(expr -> expr.getArguments().get(2).isNameExpr())
|
.filter(expr -> expr.getArguments().get(2).isNameExpr())
|
||||||
.filter(expr -> implicitIntentsArraysVariables.contains(expr.getArguments().get(2).asNameExpr().getName().getIdentifier()))
|
.filter(expr -> implicitIntentsArraysVariables.contains(expr.getArguments().get(2).asNameExpr().getName().getIdentifier()))
|
||||||
.forEach(expr -> addIssue(Severity.VULNERABILITY, ".PENDING_INTENT", getLineNumberFromExpression(expr), expr.toString()));
|
.forEach(expr -> addJavaIssue(Severity.VULNERABILITY, ".PENDING_INTENT", java.getFile(), expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkCreatingImplicitIntents(CompilationUnit compilationUnit) {
|
private void checkCreatingImplicitIntents(Source<CompilationUnit> java) {
|
||||||
List<String> intentVariables = getIntentVariables(compilationUnit);
|
List<String> intentVariables = getIntentVariables(java);
|
||||||
checkAllSetActionMethodInvocations(compilationUnit, intentVariables);
|
checkAllSetActionMethodInvocations(java, intentVariables);
|
||||||
checkCreatingImplicitIntentsUsingConstructor(compilationUnit);
|
checkCreatingImplicitIntentsUsingConstructor(java);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Works for: new Intent(Intent.ABC), new Intent(ABC)
|
// Works for: new Intent(Intent.ABC), new Intent(ABC)
|
||||||
private void checkCreatingImplicitIntentsUsingConstructor(CompilationUnit compilationUnit) {
|
private void checkCreatingImplicitIntentsUsingConstructor(Source<CompilationUnit> java) {
|
||||||
compilationUnit.findAll(ObjectCreationExpr.class).stream()
|
java.getModel().findAll(ObjectCreationExpr.class).stream()
|
||||||
.filter(expr -> expr.getType().getName().getIdentifier().equals("Intent"))
|
.filter(expr -> expr.getType().getName().getIdentifier().equals("Intent"))
|
||||||
.filter(this::isCreatingImplicitIntent)
|
.filter(this::isCreatingImplicitIntent)
|
||||||
.forEach(objectCreation -> addIssue(Severity.INFO, ".IMPLICIT_INTENT", getLineNumberFromExpression(objectCreation), objectCreation.toString()));
|
.forEach(objectCreation -> addJavaIssue(Severity.INFO, ".IMPLICIT_INTENT", java.getFile(), objectCreation));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns: i for: Intent i = new Intent(...)
|
// Returns: i for: Intent i = new Intent(...)
|
||||||
private List<String> getIntentVariables(CompilationUnit compilationUnit) {
|
private List<String> getIntentVariables(Source<CompilationUnit> java) {
|
||||||
return compilationUnit.findAll(VariableDeclarationExpr.class).stream()
|
return java.getModel().findAll(VariableDeclarationExpr.class).stream()
|
||||||
.filter(expr -> expr.getElementType().toString().equals("Intent"))
|
.filter(expr -> expr.getElementType().toString().equals("Intent"))
|
||||||
.map(VariableDeclarationExpr::getVariables)
|
.map(VariableDeclarationExpr::getVariables)
|
||||||
.flatMap(NodeList::stream)
|
.flatMap(NodeList::stream)
|
||||||
@@ -90,7 +88,7 @@ public class ImplicitIntentsPlugin extends JavaPlugin {
|
|||||||
// Works for: new Intent(CONSTANT, ...)
|
// Works for: new Intent(CONSTANT, ...)
|
||||||
if (arguments.size() == 1) {
|
if (arguments.size() == 1) {
|
||||||
Expression argument = arguments.get(0);
|
Expression argument = arguments.get(0);
|
||||||
isImplicit = java.isConstant(argument);
|
isImplicit = javaSyntax.isConstant(argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not works for: new Intent(this, ...)
|
// Not works for: new Intent(this, ...)
|
||||||
@@ -107,14 +105,14 @@ public class ImplicitIntentsPlugin extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Works for: i.setAction(...)
|
// Works for: i.setAction(...)
|
||||||
private void checkAllSetActionMethodInvocations(CompilationUnit compilationUnit, List<String> intentVariables) {
|
private void checkAllSetActionMethodInvocations(Source<CompilationUnit> java, List<String> intentVariables) {
|
||||||
compilationUnit.findAll(MethodCallExpr.class).forEach(methodCall -> {
|
java.getModel().findAll(MethodCallExpr.class).forEach(methodCall -> {
|
||||||
boolean isCalledOnIntentObject = methodCall.getScope()
|
boolean isCalledOnIntentObject = methodCall.getScope()
|
||||||
.map(Expression::toString)
|
.map(Expression::toString)
|
||||||
.filter(intentVariables::contains)
|
.filter(intentVariables::contains)
|
||||||
.isPresent();
|
.isPresent();
|
||||||
if(isCalledOnIntentObject && methodCall.getName().getIdentifier().equals("setAction")) {
|
if(isCalledOnIntentObject && methodCall.getName().getIdentifier().equals("setAction")) {
|
||||||
addIssue(Severity.INFO, ".IMPLICIT_INTENT", getLineNumberFromExpression(methodCall), methodCall.toString());
|
addJavaIssue(Severity.INFO, ".IMPLICIT_INTENT", java.getFile(), methodCall);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -122,8 +120,8 @@ public class ImplicitIntentsPlugin extends JavaPlugin {
|
|||||||
// Works for:
|
// Works for:
|
||||||
// Intent myIntent = new Intent(...)
|
// Intent myIntent = new Intent(...)
|
||||||
// getActivity(this, 0, myIntent, 0);
|
// getActivity(this, 0, myIntent, 0);
|
||||||
private void checkCreatingPendingIntentsWithIntentVariables(CompilationUnit compilationUnit) {
|
private void checkCreatingPendingIntentsWithIntentVariables(Source<CompilationUnit> java) {
|
||||||
List<String> implicitIntentsVariables = compilationUnit.findAll(ObjectCreationExpr.class).stream()
|
List<String> implicitIntentsVariables = java.getModel().findAll(ObjectCreationExpr.class).stream()
|
||||||
.filter(expr -> expr.getType().getName().getIdentifier().equals("Intent"))
|
.filter(expr -> expr.getType().getName().getIdentifier().equals("Intent"))
|
||||||
.filter(this::isCreatingImplicitIntent)
|
.filter(this::isCreatingImplicitIntent)
|
||||||
.map(Node::getParentNode)
|
.map(Node::getParentNode)
|
||||||
@@ -133,30 +131,30 @@ public class ImplicitIntentsPlugin extends JavaPlugin {
|
|||||||
.map(VariableDeclarator::getName)
|
.map(VariableDeclarator::getName)
|
||||||
.map(SimpleName::getIdentifier)
|
.map(SimpleName::getIdentifier)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
compilationUnit.findAll(MethodCallExpr.class).stream()
|
java.getModel().findAll(MethodCallExpr.class).stream()
|
||||||
.filter(expr -> expr.getName().getIdentifier().matches("getActivity|getBroadcast|getService"))
|
.filter(expr -> expr.getName().getIdentifier().matches("getActivity|getBroadcast|getService"))
|
||||||
.filter(expr -> expr.getArguments().size() >= 4)
|
.filter(expr -> expr.getArguments().size() >= 4)
|
||||||
.filter(expr -> expr.getArguments().get(2).isNameExpr())
|
.filter(expr -> expr.getArguments().get(2).isNameExpr())
|
||||||
.filter(expr -> implicitIntentsVariables.contains(expr.getArguments().get(2).asNameExpr().getName().getIdentifier()))
|
.filter(expr -> implicitIntentsVariables.contains(expr.getArguments().get(2).asNameExpr().getName().getIdentifier()))
|
||||||
.forEach(expr -> addIssue(Severity.VULNERABILITY, ".PENDING_INTENT", getLineNumberFromExpression(expr), expr.toString()));
|
.forEach(expr -> addJavaIssue(Severity.VULNERABILITY, ".PENDING_INTENT", java.getFile(), expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkCreatingPendingIntentsWithoutIntentVariable(CompilationUnit compilationUnit) {
|
private void checkCreatingPendingIntentsWithoutIntentVariable(Source<CompilationUnit> java) {
|
||||||
// Works for: getActivity(this, 0, new Intent(...), 0)
|
// Works for: getActivity(this, 0, new Intent(...), 0)
|
||||||
compilationUnit.findAll(MethodCallExpr.class).stream()
|
java.getModel().findAll(MethodCallExpr.class).stream()
|
||||||
.filter(expr -> expr.getName().getIdentifier().matches("getActivity|getBroadcast|getService"))
|
.filter(expr -> expr.getName().getIdentifier().matches("getActivity|getBroadcast|getService"))
|
||||||
.filter(expr -> expr.getArguments().size() >= 4)
|
.filter(expr -> expr.getArguments().size() >= 4)
|
||||||
.filter(expr -> expr.getArguments().get(2).isObjectCreationExpr())
|
.filter(expr -> expr.getArguments().get(2).isObjectCreationExpr())
|
||||||
.filter(expr -> isCreatingImplicitIntent(expr.getArguments().get(2).asObjectCreationExpr()))
|
.filter(expr -> isCreatingImplicitIntent(expr.getArguments().get(2).asObjectCreationExpr()))
|
||||||
.forEach(expr -> addIssue(Severity.VULNERABILITY, ".PENDING_INTENT", getLineNumberFromExpression(expr), expr.toString()));
|
.forEach(expr -> addJavaIssue(Severity.VULNERABILITY, ".PENDING_INTENT", java.getFile(), expr));
|
||||||
|
|
||||||
// Works for: getActivities(this, 0, new Intent[] { new Intent(...), ...}, 0)
|
// Works for: getActivities(this, 0, new Intent[] { new Intent(...), ...}, 0)
|
||||||
compilationUnit.findAll(MethodCallExpr.class).stream()
|
java.getModel().findAll(MethodCallExpr.class).stream()
|
||||||
.filter(expr -> expr.getName().getIdentifier().matches("getActivities"))
|
.filter(expr -> expr.getName().getIdentifier().matches("getActivities"))
|
||||||
.filter(expr -> expr.getArguments().size() >= 4)
|
.filter(expr -> expr.getArguments().size() >= 4)
|
||||||
.filter(expr -> expr.getArguments().get(2).isArrayCreationExpr())
|
.filter(expr -> expr.getArguments().get(2).isArrayCreationExpr())
|
||||||
.filter(expr -> isCreatingImplicitIntentsArray(expr.getArguments().get(2).asArrayCreationExpr()))
|
.filter(expr -> isCreatingImplicitIntentsArray(expr.getArguments().get(2).asArrayCreationExpr()))
|
||||||
.forEach(expr -> addIssue(Severity.VULNERABILITY, ".PENDING_INTENT", getLineNumberFromExpression(expr), expr.toString()));
|
.forEach(expr -> addJavaIssue(Severity.VULNERABILITY, ".PENDING_INTENT", java.getFile(), expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isCreatingImplicitIntentsArray(ArrayCreationExpr arrayCreationExpr) {
|
private boolean isCreatingImplicitIntentsArray(ArrayCreationExpr arrayCreationExpr) {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.AndroidManifestPlugin;
|
import com.bartek.esa.core.archetype.AndroidManifestPlugin;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
import com.bartek.esa.core.xml.XmlHelper;
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
@@ -12,19 +12,20 @@ import javax.inject.Inject;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class IntentFilterPlugin extends AndroidManifestPlugin {
|
public class IntentFilterPlugin extends AndroidManifestPlugin {
|
||||||
|
private final XmlHelper xmlHelper;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public IntentFilterPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public IntentFilterPlugin(XmlHelper xmlHelper) {
|
||||||
super(globMatcher, xmlHelper);
|
this.xmlHelper = xmlHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run(Document xml) {
|
protected void run(Source<Document> manifest) {
|
||||||
NodeList filters = xml.getElementsByTagName("intent-filter");
|
NodeList filters = manifest.getModel().getElementsByTagName("intent-filter");
|
||||||
stream(filters)
|
xmlHelper.stream(filters)
|
||||||
.filter(this::isNotMainActivity)
|
.filter(this::isNotMainActivity)
|
||||||
.map(Node::getParentNode)
|
.map(Node::getParentNode)
|
||||||
.forEach(n -> addIssue(Severity.INFO, getModel(n), null, null));
|
.forEach(n -> addIssue(Severity.INFO, getModel(n), manifest.getFile(), null, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, String> getModel(Node node) {
|
private Map<String, String> getModel(Node node) {
|
||||||
@@ -35,14 +36,14 @@ public class IntentFilterPlugin extends AndroidManifestPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isNotMainActivity(Node filter) {
|
private boolean isNotMainActivity(Node filter) {
|
||||||
long mainActivityIntentFilters = stream(filter.getChildNodes())
|
long mainActivityIntentFilters = xmlHelper.stream(filter.getChildNodes())
|
||||||
.filter(n -> n.getNodeName().matches("action|category"))
|
.filter(n -> n.getNodeName().matches("action|category"))
|
||||||
.map(n -> n.getAttributes().getNamedItem("android:name"))
|
.map(n -> n.getAttributes().getNamedItem("android:name"))
|
||||||
.map(Node::getNodeValue)
|
.map(Node::getNodeValue)
|
||||||
.filter(v -> v.equals("android.intent.action.MAIN") || v.equals("android.intent.category.LAUNCHER"))
|
.filter(v -> v.equals("android.intent.action.MAIN") || v.equals("android.intent.category.LAUNCHER"))
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
long currentIntentFilters = stream(filter.getChildNodes())
|
long currentIntentFilters = xmlHelper.stream(filter.getChildNodes())
|
||||||
.filter(n -> n.getNodeName().matches("action|category"))
|
.filter(n -> n.getNodeName().matches("action|category"))
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.JavaPlugin;
|
import com.bartek.esa.core.archetype.JavaPlugin;
|
||||||
import com.bartek.esa.core.helper.StaticScopeHelper;
|
import com.bartek.esa.core.helper.StaticScopeHelper;
|
||||||
|
import com.bartek.esa.core.helper.StringConcatenationChecker;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import com.github.javaparser.ast.CompilationUnit;
|
import com.github.javaparser.ast.CompilationUnit;
|
||||||
import com.github.javaparser.ast.expr.MethodCallExpr;
|
import com.github.javaparser.ast.expr.MethodCallExpr;
|
||||||
|
|
||||||
@@ -12,18 +12,21 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
public class LoggingPlugin extends JavaPlugin {
|
public class LoggingPlugin extends JavaPlugin {
|
||||||
private final StaticScopeHelper staticScopeHelper;
|
private final StaticScopeHelper staticScopeHelper;
|
||||||
|
private final StringConcatenationChecker stringConcatenationChecker;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public LoggingPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, StaticScopeHelper staticScopeHelper) {
|
public LoggingPlugin(StaticScopeHelper staticScopeHelper, StringConcatenationChecker stringConcatenationChecker) {
|
||||||
super(globMatcher, xmlHelper);
|
|
||||||
this.staticScopeHelper = staticScopeHelper;
|
this.staticScopeHelper = staticScopeHelper;
|
||||||
|
this.stringConcatenationChecker = stringConcatenationChecker;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(CompilationUnit compilationUnit) {
|
public void run(Source<CompilationUnit> java) {
|
||||||
compilationUnit.findAll(MethodCallExpr.class).stream()
|
java.getModel().findAll(MethodCallExpr.class).stream()
|
||||||
.filter(expr -> expr.getName().getIdentifier().matches("v|d|i|w|e|wtf"))
|
.filter(expr -> expr.getName().getIdentifier().matches("v|d|i|w|e|wtf"))
|
||||||
.filter(staticScopeHelper.isFromScope(compilationUnit, "v|d|i|w|e|wtf", "Log", "android.util"))
|
.filter(staticScopeHelper.isFromScope(java.getModel(), "v|d|i|w|e|wtf", "Log", "android.util"))
|
||||||
.forEach(expr -> addIssue(Severity.INFO, getLineNumberFromExpression(expr), expr.toString()));
|
.filter(expr -> expr.getArguments().size() >= 2)
|
||||||
|
.filter(expr -> stringConcatenationChecker.isStringConcatenation(java.getModel(), expr.getArguments().get(1)))
|
||||||
|
.forEach(expr -> addJavaIssue(Severity.INFO, java.getFile(), expr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,17 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.JavaPlugin;
|
import com.bartek.esa.core.archetype.JavaPlugin;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import com.github.javaparser.ast.CompilationUnit;
|
import com.github.javaparser.ast.CompilationUnit;
|
||||||
import com.github.javaparser.ast.expr.MethodCallExpr;
|
import com.github.javaparser.ast.expr.MethodCallExpr;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class OrderedBroadcastPlugin extends JavaPlugin {
|
public class OrderedBroadcastPlugin extends JavaPlugin {
|
||||||
|
|
||||||
@Inject
|
|
||||||
public OrderedBroadcastPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
|
||||||
super(globMatcher, xmlHelper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(CompilationUnit compilationUnit) {
|
public void run(Source<CompilationUnit> java) {
|
||||||
compilationUnit.findAll(MethodCallExpr.class).stream()
|
java.getModel().findAll(MethodCallExpr.class).stream()
|
||||||
.filter(expr -> expr.getName().getIdentifier().matches("sendOrderedBroadcast|sendOrderedBroadcastAsUser|sendStickyOrderedBroadcast|sendStickyOrderedBroadcastAsUser"))
|
.filter(expr -> expr.getName().getIdentifier().matches("sendOrderedBroadcast|sendOrderedBroadcastAsUser|sendStickyOrderedBroadcast|sendStickyOrderedBroadcastAsUser"))
|
||||||
.forEach(expr -> addIssue(Severity.WARNING, getLineNumberFromExpression(expr), expr.toString()));
|
.forEach(expr -> addJavaIssue(Severity.WARNING, java.getFile(), expr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.AndroidManifestPlugin;
|
import com.bartek.esa.core.archetype.AndroidManifestPlugin;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
import com.bartek.esa.core.xml.XmlHelper;
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
@@ -15,21 +15,22 @@ import java.util.Map;
|
|||||||
import static java.lang.Integer.parseInt;
|
import static java.lang.Integer.parseInt;
|
||||||
|
|
||||||
public class PermissionsRaceConditionPlugin extends AndroidManifestPlugin {
|
public class PermissionsRaceConditionPlugin extends AndroidManifestPlugin {
|
||||||
|
private final XmlHelper xmlHelper;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public PermissionsRaceConditionPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public PermissionsRaceConditionPlugin(XmlHelper xmlHelper) {
|
||||||
super(globMatcher, xmlHelper);
|
this.xmlHelper = xmlHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run(Document xml) {
|
protected void run(Source<Document> manifest) {
|
||||||
boolean isAnyPermissionDefined = ((NodeList) xPath(xml, "/manifest/permission", XPathConstants.NODESET)).getLength() > 0;
|
boolean isAnyPermissionDefined = ((NodeList) xmlHelper.xPath(manifest.getModel(), "/manifest/permission", XPathConstants.NODESET)).getLength() > 0;
|
||||||
if(isAnyPermissionDefined) {
|
if(isAnyPermissionDefined) {
|
||||||
Node usesSdkNode = (Node) xPath(xml, "/manifest/uses-sdk", XPathConstants.NODE);
|
Node usesSdkNode = (Node) xmlHelper.xPath(manifest.getModel(), "/manifest/uses-sdk", XPathConstants.NODE);
|
||||||
Node minSdkVersionNode = usesSdkNode.getAttributes().getNamedItem("android:minSdkVersion");
|
Node minSdkVersionNode = usesSdkNode.getAttributes().getNamedItem("android:minSdkVersion");
|
||||||
int minSdkVersion = parseInt(minSdkVersionNode.getNodeValue());
|
int minSdkVersion = parseInt(minSdkVersionNode.getNodeValue());
|
||||||
if(minSdkVersion < 21) {
|
if(minSdkVersion < 21) {
|
||||||
addIssue(Severity.VULNERABILITY, getModel(minSdkVersion), null, minSdkVersionNode.toString());
|
addIssue(Severity.VULNERABILITY, getModel(minSdkVersion), manifest.getFile(), null, minSdkVersionNode.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,18 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.JavaPlugin;
|
import com.bartek.esa.core.archetype.JavaPlugin;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import com.github.javaparser.ast.CompilationUnit;
|
import com.github.javaparser.ast.CompilationUnit;
|
||||||
import com.github.javaparser.ast.expr.ObjectCreationExpr;
|
import com.github.javaparser.ast.expr.ObjectCreationExpr;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class SecureRandomPlugin extends JavaPlugin {
|
public class SecureRandomPlugin extends JavaPlugin {
|
||||||
|
|
||||||
@Inject
|
|
||||||
public SecureRandomPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
|
||||||
super(globMatcher, xmlHelper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(CompilationUnit compilationUnit) {
|
public void run(Source<CompilationUnit> java) {
|
||||||
compilationUnit.findAll(ObjectCreationExpr.class).stream()
|
java.getModel().findAll(ObjectCreationExpr.class).stream()
|
||||||
.filter(expr -> expr.getType().getName().getIdentifier().equals("SecureRandom"))
|
.filter(expr -> expr.getType().getName().getIdentifier().equals("SecureRandom"))
|
||||||
.filter(expr -> expr.getArguments().isNonEmpty())
|
.filter(expr -> expr.getArguments().isNonEmpty())
|
||||||
.forEach(expr -> addIssue(Severity.VULNERABILITY, getLineNumberFromExpression(expr), expr.toString()));
|
.forEach(expr -> addJavaIssue(Severity.VULNERABILITY, java.getFile(), expr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.AndroidManifestPlugin;
|
import com.bartek.esa.core.archetype.AndroidManifestPlugin;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
import com.bartek.esa.core.xml.XmlHelper;
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
|
|
||||||
@@ -12,17 +12,18 @@ import javax.xml.xpath.XPathConstants;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class SharedUidPlugin extends AndroidManifestPlugin {
|
public class SharedUidPlugin extends AndroidManifestPlugin {
|
||||||
|
private final XmlHelper xmlHelper;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public SharedUidPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public SharedUidPlugin(XmlHelper xmlHelper) {
|
||||||
super(globMatcher, xmlHelper);
|
this.xmlHelper = xmlHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run(Document xml) {
|
protected void run(Source<Document> manifest) {
|
||||||
Node manifestNode = (Node) xPath(xml, "/manifest", XPathConstants.NODE);
|
Node manifestNode = (Node) xmlHelper.xPath(manifest.getModel(), "/manifest", XPathConstants.NODE);
|
||||||
Optional.ofNullable(manifestNode.getAttributes().getNamedItem("android:sharedUserId")).ifPresent(node -> {
|
Optional.ofNullable(manifestNode.getAttributes().getNamedItem("android:sharedUserId")).ifPresent(node -> {
|
||||||
addIssue(Severity.VULNERABILITY, null, node.toString());
|
addIssue(Severity.VULNERABILITY, manifest.getFile(), null, node.toString());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +1,28 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.JavaPlugin;
|
import com.bartek.esa.core.archetype.JavaPlugin;
|
||||||
|
import com.bartek.esa.core.helper.StringConcatenationChecker;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import com.github.javaparser.ast.CompilationUnit;
|
import com.github.javaparser.ast.CompilationUnit;
|
||||||
import com.github.javaparser.ast.expr.Expression;
|
|
||||||
import com.github.javaparser.ast.expr.MethodCallExpr;
|
import com.github.javaparser.ast.expr.MethodCallExpr;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
public class SqlInjectionPlugin extends JavaPlugin {
|
public class SqlInjectionPlugin extends JavaPlugin {
|
||||||
|
private final StringConcatenationChecker stringConcatenationChecker;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public SqlInjectionPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public SqlInjectionPlugin(StringConcatenationChecker stringConcatenationChecker) {
|
||||||
super(globMatcher, xmlHelper);
|
this.stringConcatenationChecker = stringConcatenationChecker;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(CompilationUnit compilationUnit) {
|
public void run(Source<CompilationUnit> java) {
|
||||||
compilationUnit.findAll(MethodCallExpr.class).stream()
|
java.getModel().findAll(MethodCallExpr.class).stream()
|
||||||
.filter(expr -> expr.getName().getIdentifier().equals("rawQuery"))
|
.filter(expr -> expr.getName().getIdentifier().equals("rawQuery"))
|
||||||
.filter(expr -> expr.getArguments().size() >= 2)
|
.filter(expr -> expr.getArguments().size() >= 2)
|
||||||
.filter(this::isConcatenationOrMethodCall)
|
.filter(expr -> stringConcatenationChecker.isStringConcatenation(java.getModel(), expr.getArguments().get(0)))
|
||||||
.filter(this::ifMethodIsStringFormat)
|
.forEach(expr -> addJavaIssue(Severity.VULNERABILITY, java.getFile(), expr));
|
||||||
.forEach(expr -> addIssue(Severity.VULNERABILITY, getLineNumberFromExpression(expr), expr.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isConcatenationOrMethodCall(MethodCallExpr expr) {
|
|
||||||
return expr.getArguments().get(0).isBinaryExpr() || expr.getArguments().get(0).isMethodCallExpr();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean ifMethodIsStringFormat(MethodCallExpr expr) {
|
|
||||||
if(expr.getArguments().get(0).isMethodCallExpr()) {
|
|
||||||
return expr.getArguments().get(0).asMethodCallExpr().getName().getIdentifier().equals("format");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.JavaPlugin;
|
import com.bartek.esa.core.archetype.JavaPlugin;
|
||||||
import com.bartek.esa.core.helper.StaticScopeHelper;
|
import com.bartek.esa.core.helper.StaticScopeHelper;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import com.github.javaparser.ast.CompilationUnit;
|
import com.github.javaparser.ast.CompilationUnit;
|
||||||
import com.github.javaparser.ast.expr.MethodCallExpr;
|
import com.github.javaparser.ast.expr.MethodCallExpr;
|
||||||
|
|
||||||
@@ -14,16 +13,15 @@ public class StrictModePlugin extends JavaPlugin {
|
|||||||
private final StaticScopeHelper staticScopeHelper;
|
private final StaticScopeHelper staticScopeHelper;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public StrictModePlugin(GlobMatcher globMatcher, XmlHelper xmlHelper, StaticScopeHelper staticScopeHelper) {
|
public StrictModePlugin(StaticScopeHelper staticScopeHelper) {
|
||||||
super(globMatcher, xmlHelper);
|
|
||||||
this.staticScopeHelper = staticScopeHelper;
|
this.staticScopeHelper = staticScopeHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(CompilationUnit compilationUnit) {
|
public void run(Source<CompilationUnit> java) {
|
||||||
compilationUnit.findAll(MethodCallExpr.class).stream()
|
java.getModel().findAll(MethodCallExpr.class).stream()
|
||||||
.filter(expr -> expr.getName().getIdentifier().equals("setThreadPolicy"))
|
.filter(expr -> expr.getName().getIdentifier().equals("setThreadPolicy"))
|
||||||
.filter(staticScopeHelper.isFromScope(compilationUnit, "setThreadPolicy", "StrictMode", "android.os"))
|
.filter(staticScopeHelper.isFromScope(java.getModel(), "setThreadPolicy", "StrictMode", "android.os"))
|
||||||
.forEach(expr -> addIssue(Severity.WARNING, getLineNumberFromExpression(expr), expr.toString()));
|
.forEach(expr -> addJavaIssue(Severity.WARNING, java.getFile(), expr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,17 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.JavaPlugin;
|
import com.bartek.esa.core.archetype.JavaPlugin;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import com.github.javaparser.ast.CompilationUnit;
|
import com.github.javaparser.ast.CompilationUnit;
|
||||||
import com.github.javaparser.ast.expr.AnnotationExpr;
|
import com.github.javaparser.ast.expr.AnnotationExpr;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class SuppressWarningsPlugin extends JavaPlugin {
|
public class SuppressWarningsPlugin extends JavaPlugin {
|
||||||
|
|
||||||
@Inject
|
|
||||||
public SuppressWarningsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
|
||||||
super(globMatcher, xmlHelper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(CompilationUnit compilationUnit) {
|
public void run(Source<CompilationUnit> java) {
|
||||||
compilationUnit.findAll(AnnotationExpr.class).stream()
|
java.getModel().findAll(AnnotationExpr.class).stream()
|
||||||
.filter(expr -> expr.getName().getIdentifier().equals("SuppressWarnings"))
|
.filter(expr -> expr.getName().getIdentifier().equals("SuppressWarnings"))
|
||||||
.forEach(expr -> addIssue(Severity.WARNING, getLineNumberFromExpression(expr), expr.toString()));
|
.forEach(expr -> addJavaIssue(Severity.WARNING, java.getFile(), expr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,18 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.JavaPlugin;
|
import com.bartek.esa.core.archetype.JavaPlugin;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import com.github.javaparser.ast.CompilationUnit;
|
import com.github.javaparser.ast.CompilationUnit;
|
||||||
import com.github.javaparser.ast.expr.CastExpr;
|
import com.github.javaparser.ast.expr.CastExpr;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class TelephonyManagerPlugin extends JavaPlugin {
|
public class TelephonyManagerPlugin extends JavaPlugin {
|
||||||
|
|
||||||
@Inject
|
|
||||||
public TelephonyManagerPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
|
||||||
super(globMatcher, xmlHelper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(CompilationUnit compilationUnit) {
|
public void run(Source<CompilationUnit> java) {
|
||||||
compilationUnit.findAll(CastExpr.class).stream()
|
java.getModel().findAll(CastExpr.class).stream()
|
||||||
.filter(expr -> expr.getType().isClassOrInterfaceType())
|
.filter(expr -> expr.getType().isClassOrInterfaceType())
|
||||||
.filter(expr -> expr.getType().asClassOrInterfaceType().getName().getIdentifier().equals("TelephonyManager"))
|
.filter(expr -> expr.getType().asClassOrInterfaceType().getName().getIdentifier().equals("TelephonyManager"))
|
||||||
.forEach(expr -> addIssue(Severity.INFO, getLineNumberFromExpression(expr), expr.toString()));
|
.forEach(expr -> addJavaIssue(Severity.INFO, java.getFile(), expr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.ResourceLayoutPlugin;
|
import com.bartek.esa.core.archetype.ResourceLayoutPlugin;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
import com.bartek.esa.core.xml.XmlHelper;
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
@@ -12,18 +12,19 @@ import javax.inject.Inject;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class TextInputValidationPlugin extends ResourceLayoutPlugin {
|
public class TextInputValidationPlugin extends ResourceLayoutPlugin {
|
||||||
|
private final XmlHelper xmlHelper;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public TextInputValidationPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public TextInputValidationPlugin(XmlHelper xmlHelper) {
|
||||||
super(globMatcher, xmlHelper);
|
this.xmlHelper = xmlHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run(Document xml) {
|
protected void run(Source<Document> layout) {
|
||||||
NodeList editTextNodes = xml.getElementsByTagName("EditText");
|
NodeList editTextNodes = layout.getModel().getElementsByTagName("EditText");
|
||||||
stream(editTextNodes)
|
xmlHelper.stream(editTextNodes)
|
||||||
.filter(this::doesNotHaveInputType)
|
.filter(this::doesNotHaveInputType)
|
||||||
.forEach(n -> addIssue(Severity.WARNING, null, tagString(n)));
|
.forEach(n -> addXmlIssue(Severity.WARNING, layout.getFile(), n));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean doesNotHaveInputType(Node editText) {
|
private boolean doesNotHaveInputType(Node editText) {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.AndroidManifestPlugin;
|
import com.bartek.esa.core.archetype.AndroidManifestPlugin;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
import com.bartek.esa.core.xml.XmlHelper;
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
|
|
||||||
@@ -13,22 +13,23 @@ import java.util.Map;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class UsesSdkPlugin extends AndroidManifestPlugin {
|
public class UsesSdkPlugin extends AndroidManifestPlugin {
|
||||||
|
private final XmlHelper xmlHelper;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public UsesSdkPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
public UsesSdkPlugin(XmlHelper xmlHelper) {
|
||||||
super(globMatcher, xmlHelper);
|
this.xmlHelper = xmlHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run(Document xml) {
|
protected void run(Source<Document> manifest) {
|
||||||
Optional.ofNullable((Node) xPath(xml, "/manifest/uses-sdk", XPathConstants.NODE)).ifPresentOrElse(usesSdkNode -> {
|
Optional.ofNullable((Node) xmlHelper.xPath(manifest.getModel(), "/manifest/uses-sdk", XPathConstants.NODE)).ifPresentOrElse(usesSdkNode -> {
|
||||||
if(usesSdkNode.getAttributes().getNamedItem("android:minSdkVersion") == null) {
|
if(usesSdkNode.getAttributes().getNamedItem("android:minSdkVersion") == null) {
|
||||||
addIssue(Severity.ERROR, ".USES_SDK.NO_MIN_SDK_VERSION", null, null);
|
addIssue(Severity.ERROR, ".USES_SDK.NO_MIN_SDK_VERSION", manifest.getFile(), null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional.ofNullable(usesSdkNode.getAttributes().getNamedItem("android:maxSdkVersion")).ifPresent(maxSdkVersion ->
|
Optional.ofNullable(usesSdkNode.getAttributes().getNamedItem("android:maxSdkVersion")).ifPresent(maxSdkVersion ->
|
||||||
addIssue(Severity.ERROR, ".USES_SDK.MAX_SDK_VERSION", Map.of("maxSdkVersion", maxSdkVersion.getNodeValue()),null, maxSdkVersion.toString())
|
addIssue(Severity.ERROR, ".USES_SDK.MAX_SDK_VERSION", Map.of("maxSdkVersion", maxSdkVersion.getNodeValue()), manifest.getFile(), null, maxSdkVersion.toString())
|
||||||
);
|
);
|
||||||
}, () -> addIssue(Severity.ERROR, ".NO_USES_SDK", null, null));
|
}, () -> addIssue(Severity.ERROR, ".NO_USES_SDK", manifest.getFile(), null, null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,51 +1,47 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.JavaPlugin;
|
import com.bartek.esa.core.archetype.JavaPlugin;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import com.github.javaparser.ast.CompilationUnit;
|
import com.github.javaparser.ast.CompilationUnit;
|
||||||
import com.github.javaparser.ast.expr.Expression;
|
import com.github.javaparser.ast.expr.Expression;
|
||||||
import com.github.javaparser.ast.expr.MethodCallExpr;
|
import com.github.javaparser.ast.expr.MethodCallExpr;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class WebViewPlugin extends JavaPlugin {
|
public class WebViewPlugin extends JavaPlugin {
|
||||||
private static final String SETTINGS_METHODS = "addJavascriptInterface|setJavaScriptEnabled|setWebContentsDebuggingEnabled|setAllowFileAccess|setDomStorageEnabled";
|
private static final String SETTINGS_METHODS = "addJavascriptInterface|setJavaScriptEnabled|setWebContentsDebuggingEnabled|setAllowFileAccess|setDomStorageEnabled";
|
||||||
|
|
||||||
@Inject
|
|
||||||
public WebViewPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
|
||||||
super(globMatcher, xmlHelper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(CompilationUnit compilationUnit) {
|
public void run(Source<CompilationUnit> java) {
|
||||||
compilationUnit.findAll(MethodCallExpr.class).stream()
|
java.getModel().findAll(MethodCallExpr.class).stream()
|
||||||
.filter(expr -> expr.getName().getIdentifier().matches(SETTINGS_METHODS))
|
.filter(expr -> expr.getName().getIdentifier().matches(SETTINGS_METHODS))
|
||||||
.forEach(this::issueMethod);
|
.forEach(issueMethod(java));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void issueMethod(MethodCallExpr methodCall) {
|
private Consumer<MethodCallExpr> issueMethod(Source<CompilationUnit> java) {
|
||||||
switch (methodCall.getName().getIdentifier()) {
|
return methodCall -> {
|
||||||
case "addJavascriptInterface":
|
switch (methodCall.getName().getIdentifier()) {
|
||||||
addIssue(Severity.VULNERABILITY, ".JS_INTERFACE", getLineNumberFromExpression(methodCall), methodCall.toString());
|
case "addJavascriptInterface":
|
||||||
break;
|
addJavaIssue(Severity.VULNERABILITY, ".JS_INTERFACE", java.getFile(), methodCall);
|
||||||
case "setJavaScriptEnabled":
|
break;
|
||||||
issueSettingsMethod(methodCall, ".JS_ENABLED");
|
case "setJavaScriptEnabled":
|
||||||
break;
|
issueSettingsMethod(java, methodCall, ".JS_ENABLED");
|
||||||
case "setWebContentsDebuggingEnabled":
|
break;
|
||||||
issueSettingsMethod(methodCall, ".DEBUGGING_ENABLED");
|
case "setWebContentsDebuggingEnabled":
|
||||||
break;
|
issueSettingsMethod(java, methodCall, ".DEBUGGING_ENABLED");
|
||||||
case "setAllowFileAccess":
|
break;
|
||||||
issueSettingsMethod(methodCall, ".ALLOW_FILE_ACCESS");
|
case "setAllowFileAccess":
|
||||||
break;
|
issueSettingsMethod(java, methodCall, ".ALLOW_FILE_ACCESS");
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void issueSettingsMethod(MethodCallExpr methodCall, String descriptionCode) {
|
private void issueSettingsMethod(Source<CompilationUnit> java, MethodCallExpr methodCall, String descriptionCode) {
|
||||||
Expression firstArg = methodCall.getArguments().get(0);
|
Expression firstArg = methodCall.getArguments().get(0);
|
||||||
if (firstArg.isBooleanLiteralExpr() && firstArg.asBooleanLiteralExpr().getValue()) {
|
if (firstArg.isBooleanLiteralExpr() && firstArg.asBooleanLiteralExpr().getValue()) {
|
||||||
addIssue(Severity.WARNING, descriptionCode, getLineNumberFromExpression(methodCall), methodCall.toString());
|
addJavaIssue(Severity.WARNING, descriptionCode, java.getFile(), methodCall);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,25 @@
|
|||||||
package com.bartek.esa.core.plugin;
|
package com.bartek.esa.core.plugin;
|
||||||
|
|
||||||
|
import com.bartek.esa.context.model.Source;
|
||||||
import com.bartek.esa.core.archetype.JavaPlugin;
|
import com.bartek.esa.core.archetype.JavaPlugin;
|
||||||
import com.bartek.esa.core.model.enumeration.Severity;
|
import com.bartek.esa.core.model.enumeration.Severity;
|
||||||
import com.bartek.esa.core.xml.XmlHelper;
|
|
||||||
import com.bartek.esa.file.matcher.GlobMatcher;
|
|
||||||
import com.github.javaparser.ast.CompilationUnit;
|
import com.github.javaparser.ast.CompilationUnit;
|
||||||
import com.github.javaparser.ast.expr.FieldAccessExpr;
|
import com.github.javaparser.ast.expr.FieldAccessExpr;
|
||||||
import com.github.javaparser.ast.expr.NameExpr;
|
import com.github.javaparser.ast.expr.NameExpr;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class WorldAccessPermissionsPlugin extends JavaPlugin {
|
public class WorldAccessPermissionsPlugin extends JavaPlugin {
|
||||||
|
|
||||||
@Inject
|
|
||||||
public WorldAccessPermissionsPlugin(GlobMatcher globMatcher, XmlHelper xmlHelper) {
|
|
||||||
super(globMatcher, xmlHelper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(CompilationUnit compilationUnit) {
|
public void run(Source<CompilationUnit> java) {
|
||||||
compilationUnit.findAll(NameExpr.class).stream()
|
java.getModel().findAll(NameExpr.class).stream()
|
||||||
.filter(expr -> expr.getName().getIdentifier().matches("MODE_WORLD_(READABLE|WRITEABLE)"))
|
.filter(expr -> expr.getName().getIdentifier().matches("MODE_WORLD_(READABLE|WRITEABLE)"))
|
||||||
.forEach(expr -> addIssue(Severity.ERROR, getModel(expr), getLineNumberFromExpression(expr), expr.toString()));
|
.forEach(expr -> addJavaIssue(Severity.ERROR, getModel(expr), java.getFile(), expr));
|
||||||
|
|
||||||
compilationUnit.findAll(FieldAccessExpr.class).stream()
|
java.getModel().findAll(FieldAccessExpr.class).stream()
|
||||||
.filter(expr -> expr.getName().getIdentifier().matches("MODE_WORLD_(READABLE|WRITEABLE)"))
|
.filter(expr -> expr.getName().getIdentifier().matches("MODE_WORLD_(READABLE|WRITEABLE)"))
|
||||||
.forEach(expr -> addIssue(Severity.ERROR, getModel(expr), getLineNumberFromExpression(expr), expr.toString()));
|
.forEach(expr -> addJavaIssue(Severity.ERROR, getModel(expr), java.getFile(), expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, String> getModel(NameExpr expression) {
|
private Map<String, String> getModel(NameExpr expression) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.bartek.esa.di;
|
|||||||
import com.bartek.esa.EsaMain;
|
import com.bartek.esa.EsaMain;
|
||||||
import com.bartek.esa.analyser.di.AnalyserModule;
|
import com.bartek.esa.analyser.di.AnalyserModule;
|
||||||
import com.bartek.esa.cli.di.CliModule;
|
import com.bartek.esa.cli.di.CliModule;
|
||||||
|
import com.bartek.esa.context.di.ContextModule;
|
||||||
import com.bartek.esa.core.di.CoreModule;
|
import com.bartek.esa.core.di.CoreModule;
|
||||||
import com.bartek.esa.core.di.PluginModule;
|
import com.bartek.esa.core.di.PluginModule;
|
||||||
import com.bartek.esa.decompiler.di.DecompilerModule;
|
import com.bartek.esa.decompiler.di.DecompilerModule;
|
||||||
@@ -19,7 +20,8 @@ import dagger.Component;
|
|||||||
CoreModule.class,
|
CoreModule.class,
|
||||||
PluginModule.class,
|
PluginModule.class,
|
||||||
AnalyserModule.class,
|
AnalyserModule.class,
|
||||||
FormatterModule.class
|
FormatterModule.class,
|
||||||
|
ContextModule.class
|
||||||
})
|
})
|
||||||
public interface DependencyInjector {
|
public interface DependencyInjector {
|
||||||
EsaMain esa();
|
EsaMain esa();
|
||||||
|
|||||||
Reference in New Issue
Block a user