diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll index db1b5a61ef6..f518dbd2802 100644 --- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll @@ -84,6 +84,7 @@ private module Frameworks { private import internal.ContainerFlow private import semmle.code.java.frameworks.android.Android private import semmle.code.java.frameworks.android.ContentProviders + private import semmle.code.java.frameworks.android.ExternalStorage private import semmle.code.java.frameworks.android.Intent private import semmle.code.java.frameworks.android.Notifications private import semmle.code.java.frameworks.android.SharedPreferences diff --git a/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll b/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll index 2c44d7a15b6..fcd4fe90b6d 100644 --- a/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll +++ b/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll @@ -17,6 +17,7 @@ import semmle.code.java.frameworks.android.WebView import semmle.code.java.frameworks.JaxWS import semmle.code.java.frameworks.javase.WebSocket import semmle.code.java.frameworks.android.Android +import semmle.code.java.frameworks.android.ExternalStorage import semmle.code.java.frameworks.android.OnActivityResultSource import semmle.code.java.frameworks.android.Intent import semmle.code.java.frameworks.play.Play @@ -152,6 +153,12 @@ private class ThriftIfaceParameterSource extends RemoteFlowSource { override string getSourceType() { result = "Thrift Iface parameter" } } +private class AndroidExternalStorageSource extends RemoteFlowSource { + AndroidExternalStorageSource() { androidExternalStorageSource(this) } + + override string getSourceType() { result = "Android external storage" } +} + /** Class for `tainted` user input. */ abstract class UserInput extends DataFlow::Node { } diff --git a/java/ql/lib/semmle/code/java/frameworks/android/ExternalStorage.qll b/java/ql/lib/semmle/code/java/frameworks/android/ExternalStorage.qll new file mode 100644 index 00000000000..60214f8861a --- /dev/null +++ b/java/ql/lib/semmle/code/java/frameworks/android/ExternalStorage.qll @@ -0,0 +1,46 @@ +/** Provides definitions for working with uses of Android external storage */ + +import java +import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.dataflow.ExternalFlow + +private class ExternalStorageDirSourceModel extends SourceModelCsv { + override predicate row(string row) { + row = + [ + //"package;type;overrides;name;signature;ext;spec;kind" + "android.content;Context;true;getExternalFilesDir;(String);;ReturnValue;android-external-storage-dir", + "android.content;Context;true;getExternalFilesDirs;(String);;ReturnValue.ArrayElement;android-external-storage-dir", + "android.content;Context;true;getExternalCachesDir;(String);;ReturnValue;android-external-storage-dir", + "android.content;Context;true;getExternalCachesDirs;(String);;ReturnValue.ArrayElement;android-external-storage-dir", + "android.os;Environment;false;getExternalStorageDirectory;(String);;ReturnValue.ArrayElement;android-external-storage-dir", + "android.os;Environment;false;getExternalStoragePublicDirectory;(String);;ReturnValue.ArrayElement;android-external-storage-dir", + ] + } +} + +private predicate externalStorageFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + DataFlow::localFlowStep(node1, node2) + or + exists(ConstructorCall c | c.getConstructedType() instanceof TypeFile | + node1.asExpr() = c.getArgument(1) and + node2.asExpr() = c + ) +} + +private predicate externalStorageFlow(DataFlow::Node node1, DataFlow::Node node2) { + externalStorageFlowStep*(node1, node2) +} + +/** + * Holds if `n` is a node that reads the contents of an external file in Android. + * This may be controlable by third-party applications, so is treated as a remote flow source. + */ +predicate androidExternalStorageSource(DataFlow::Node n) { + exists(ConstructorCall fInp, DataFlow::Node externalDir | + fInp.getConstructedType().hasQualifiedName("java.io", "FileInputStream") and + n.asExpr() = fInp and + sourceNode(externalDir, "android-external-storage-dir") and + externalStorageFlow(externalDir, DataFlow::exprNode(fInp.getArgument(0))) + ) +}