From f5ca459795491f86a68bbf950f32ec944cdc9704 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Thu, 25 Jun 2020 20:20:18 +0000 Subject: [PATCH 01/15] Add remote source of Android intent extra --- .../semmle/code/java/dataflow/FlowSources.qll | 34 +++++++++++++++++++ .../code/java/frameworks/android/Android.qll | 21 ++++++++++++ .../src/semmle/code/xml/AndroidManifest.qll | 5 +++ 3 files changed, 60 insertions(+) diff --git a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll index fb2a0345b8b..253a39019a9 100644 --- a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll +++ b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll @@ -23,6 +23,7 @@ import semmle.code.java.frameworks.spring.SpringWebClient import semmle.code.java.frameworks.Guice import semmle.code.java.frameworks.struts.StrutsActions import semmle.code.java.frameworks.Thrift +import semmle.code.java.frameworks.android.Android /** A data flow source of remote user input. */ abstract class RemoteFlowSource extends DataFlow::Node { @@ -318,3 +319,36 @@ class AndroidIntentInput extends DataFlow::Node { ) } } + +/** + * Method access to external inputs of `android.content.Intent` object + */ +class IntentGetExtraMethodAccess extends MethodAccess { + IntentGetExtraMethodAccess() { + exists(AndroidComponent ac | + this.getEnclosingCallable().getDeclaringType() = ac and ac.isExported() + ) and + ( + this.getMethod().getName().regexpMatch("get\\w+Extra") and + this.getMethod().getDeclaringType() instanceof TypeIntent + or + this.getMethod().getName().regexpMatch("get\\w+") and + this.getQualifier().(MethodAccess).getMethod().hasName("getExtras") and + this.getQualifier().(MethodAccess).getMethod().getDeclaringType() instanceof TypeIntent + ) + } +} + +/** + * Android intent extra source + */ +private class AndroidIntentExtraSource extends RemoteFlowSource { + AndroidIntentExtraSource() { + exists(MethodAccess ma | + ma instanceof IntentGetExtraMethodAccess and + this.asExpr().(VarAccess).getVariable().getAnAssignedValue() = ma + ) + } + + override string getSourceType() { result = "Android intent extra" } +} diff --git a/java/ql/src/semmle/code/java/frameworks/android/Android.qll b/java/ql/src/semmle/code/java/frameworks/android/Android.qll index da500afbe6e..1415efedafd 100644 --- a/java/ql/src/semmle/code/java/frameworks/android/Android.qll +++ b/java/ql/src/semmle/code/java/frameworks/android/Android.qll @@ -33,11 +33,25 @@ class AndroidComponent extends Class { /** An Android activity. */ class AndroidActivity extends AndroidComponent { AndroidActivity() { this.getASupertype*().hasQualifiedName("android.app", "Activity") } + + /** Holds if this Android component is configured as `exported` or has intent filters configured without `exported` explicitly disabled in an `AndroidManifest.xml` file. */ + override predicate isExported() { + getAndroidComponentXmlElement().isExported() + or + not getAndroidComponentXmlElement().isNotExported() and hasIntentFilter() + } } /** An Android service. */ class AndroidService extends AndroidComponent { AndroidService() { this.getASupertype*().hasQualifiedName("android.app", "Service") } + + /** Holds if this Android component is configured as `exported` or has intent filters configured without `exported` explicitly disabled in an `AndroidManifest.xml` file. */ + override predicate isExported() { + getAndroidComponentXmlElement().isExported() + or + not getAndroidComponentXmlElement().isNotExported() and hasIntentFilter() + } } /** An Android broadcast receiver. */ @@ -45,6 +59,13 @@ class AndroidBroadcastReceiver extends AndroidComponent { AndroidBroadcastReceiver() { this.getASupertype*().hasQualifiedName("android.content", "BroadcastReceiver") } + + /** Holds if this Android component is configured as `exported` or has intent filters configured without `exported` explicitly disabled in an `AndroidManifest.xml` file. */ + override predicate isExported() { + getAndroidComponentXmlElement().isExported() + or + not getAndroidComponentXmlElement().isNotExported() and hasIntentFilter() + } } /** An Android content provider. */ diff --git a/java/ql/src/semmle/code/xml/AndroidManifest.qll b/java/ql/src/semmle/code/xml/AndroidManifest.qll index 70afabc8d3d..7c6b2f3e569 100644 --- a/java/ql/src/semmle/code/xml/AndroidManifest.qll +++ b/java/ql/src/semmle/code/xml/AndroidManifest.qll @@ -137,6 +137,11 @@ class AndroidComponentXmlElement extends XMLElement { * Holds if the `android:exported` attribute of this component element is `true`. */ predicate isExported() { getExportedAttributeValue() = "true" } + + /** + * Holds if the `android:exported` attribute of this component element is explicitly set to `false`. + */ + predicate isNotExported() { getExportedAttributeValue() = "false" } } /** From 2ddeb0b16977a05fb0d8061f429f36553451ff9d Mon Sep 17 00:00:00 2001 From: Bt2018 Date: Sat, 27 Jun 2020 18:00:52 -0400 Subject: [PATCH 02/15] Add method access qualifier as source --- java/ql/src/semmle/code/java/dataflow/FlowSources.qll | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll index 253a39019a9..49a2f8c8198 100644 --- a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll +++ b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll @@ -346,7 +346,10 @@ private class AndroidIntentExtraSource extends RemoteFlowSource { AndroidIntentExtraSource() { exists(MethodAccess ma | ma instanceof IntentGetExtraMethodAccess and - this.asExpr().(VarAccess).getVariable().getAnAssignedValue() = ma + ( + this.asExpr().(VarAccess).getVariable().getAnAssignedValue() = ma or + ma.getQualifier() = this.asExpr() + ) ) } From f86413a9b5523d1dc8f83616607a09930aed221e Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Sat, 27 Jun 2020 22:56:00 +0000 Subject: [PATCH 03/15] text changes --- java/ql/src/semmle/code/java/dataflow/FlowSources.qll | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll index 49a2f8c8198..3774525741d 100644 --- a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll +++ b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll @@ -16,6 +16,7 @@ import semmle.code.java.frameworks.android.XmlParsing 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.Intent import semmle.code.java.frameworks.spring.SpringWeb import semmle.code.java.frameworks.spring.SpringController @@ -23,7 +24,6 @@ import semmle.code.java.frameworks.spring.SpringWebClient import semmle.code.java.frameworks.Guice import semmle.code.java.frameworks.struts.StrutsActions import semmle.code.java.frameworks.Thrift -import semmle.code.java.frameworks.android.Android /** A data flow source of remote user input. */ abstract class RemoteFlowSource extends DataFlow::Node { @@ -320,9 +320,7 @@ class AndroidIntentInput extends DataFlow::Node { } } -/** - * Method access to external inputs of `android.content.Intent` object - */ +/** Method access to external inputs of `android.content.Intent` object. */ class IntentGetExtraMethodAccess extends MethodAccess { IntentGetExtraMethodAccess() { exists(AndroidComponent ac | @@ -339,9 +337,7 @@ class IntentGetExtraMethodAccess extends MethodAccess { } } -/** - * Android intent extra source - */ +/** Android intent extra source. */ private class AndroidIntentExtraSource extends RemoteFlowSource { AndroidIntentExtraSource() { exists(MethodAccess ma | From 3c5c8494b13a32013c3a22d2c5d308f286ce0579 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Wed, 21 Oct 2020 00:25:56 +0000 Subject: [PATCH 04/15] Refine the query to check intents coming from outside only --- .../semmle/code/java/dataflow/FlowSources.qll | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll index 3774525741d..ea37408d9fa 100644 --- a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll +++ b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll @@ -342,9 +342,23 @@ private class AndroidIntentExtraSource extends RemoteFlowSource { AndroidIntentExtraSource() { exists(MethodAccess ma | ma instanceof IntentGetExtraMethodAccess and - ( - this.asExpr().(VarAccess).getVariable().getAnAssignedValue() = ma or - ma.getQualifier() = this.asExpr() + this.asExpr() = ma and + exists(AndroidIntentInput inode | + ( + ma.getQualifier() = inode.asExpr() or // extra from intent + ma.getQualifier() = inode.asParameter().getAnAccess() + ) + or + exists( + MethodAccess ema // extra from extras bundle of intent + | + ema.getMethod().hasName("getExtras") and + ma.getQualifier() = ema and + ( + ema.getQualifier() = inode.asExpr() or + ema.getQualifier() = inode.asParameter().getAnAccess() + ) + ) ) ) } From f5f72599376c771c8b4507b55acacb0cea313520 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Thu, 22 Oct 2020 14:55:07 +0000 Subject: [PATCH 05/15] Revamp the query to implement AdditionalTaintStep --- .../semmle/code/java/dataflow/FlowSources.qll | 41 ++++++------------- .../semmle/code/java/dataflow/FlowSteps.qll | 29 +++++++++++++ 2 files changed, 42 insertions(+), 28 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll index ea37408d9fa..f3ae6dcbbdd 100644 --- a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll +++ b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll @@ -320,46 +320,31 @@ class AndroidIntentInput extends DataFlow::Node { } } -/** Method access to external inputs of `android.content.Intent` object. */ +/** Method access to external inputs of `android.content.Intent` or `android.os.BaseBundle` object. */ class IntentGetExtraMethodAccess extends MethodAccess { IntentGetExtraMethodAccess() { exists(AndroidComponent ac | - this.getEnclosingCallable().getDeclaringType() = ac and ac.isExported() - ) and - ( + this.getEnclosingCallable().getDeclaringType() = ac and + ac.isExported() and this.getMethod().getName().regexpMatch("get\\w+Extra") and this.getMethod().getDeclaringType() instanceof TypeIntent - or - this.getMethod().getName().regexpMatch("get\\w+") and - this.getQualifier().(MethodAccess).getMethod().hasName("getExtras") and - this.getQualifier().(MethodAccess).getMethod().getDeclaringType() instanceof TypeIntent ) + or + this.getMethod().getName().regexpMatch("get\\w+") and + this + .getMethod() + .getDeclaringType() + .getASupertype*() + .hasQualifiedName("android.os", "BaseBundle") } } /** Android intent extra source. */ private class AndroidIntentExtraSource extends RemoteFlowSource { AndroidIntentExtraSource() { - exists(MethodAccess ma | - ma instanceof IntentGetExtraMethodAccess and - this.asExpr() = ma and - exists(AndroidIntentInput inode | - ( - ma.getQualifier() = inode.asExpr() or // extra from intent - ma.getQualifier() = inode.asParameter().getAnAccess() - ) - or - exists( - MethodAccess ema // extra from extras bundle of intent - | - ema.getMethod().hasName("getExtras") and - ma.getQualifier() = ema and - ( - ema.getQualifier() = inode.asExpr() or - ema.getQualifier() = inode.asParameter().getAnAccess() - ) - ) - ) + exists(AndroidIntentInput inode | + this.asExpr() = inode.asExpr() or + this.asExpr() = inode.asParameter().getAnAccess() ) } diff --git a/java/ql/src/semmle/code/java/dataflow/FlowSteps.qll b/java/ql/src/semmle/code/java/dataflow/FlowSteps.qll index 9ed602d86d7..50c01ce3c5f 100644 --- a/java/ql/src/semmle/code/java/dataflow/FlowSteps.qll +++ b/java/ql/src/semmle/code/java/dataflow/FlowSteps.qll @@ -4,6 +4,7 @@ private import java private import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.dataflow.FlowSources /** * A module importing the frameworks that implement additional flow steps, @@ -139,3 +140,31 @@ private class StringBuilderTaintPreservingCallable extends TaintPreservingCallab sink = -1 } } + +/** + * Holds if `n1` to `n2` is a dataflow step between the extra getter method and its caller Android `Intent` or `Bundle`. + */ +private predicate intentExtraStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) { + exists(IntentGetExtraMethodAccess ma | + n1.asExpr() = ma.getQualifier() and + n2.asExpr() = ma + ) +} + +/** + * Holds if `n1` to `n2` is a dataflow step from Android `Intent` to its `getExtras` method. + */ +private predicate bundleExtraStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) { + exists(MethodAccess ma | ma.getMethod().hasName("getExtras") | + n1.asExpr() = ma.getQualifier() and + n2.asExpr() = ma + ) +} + +/** A set of additional taint steps to consider when taint tracking Android intent extra related data flows. */ +class AndroidExtraSourceAdditionalTaintStep extends AdditionalTaintStep { + override predicate step(DataFlow::Node node1, DataFlow::Node node2) { + intentExtraStep(node1, node2) or + bundleExtraStep(node1, node2) + } +} From 9ae5689af682cb9118f3ce67bb8074847e73984d Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Sat, 24 Oct 2020 11:55:00 +0000 Subject: [PATCH 06/15] Use AndroidIntentInput source --- .../semmle/code/java/dataflow/FlowSources.qll | 54 +++++-------------- .../semmle/code/java/dataflow/FlowSteps.qll | 25 ++++----- 2 files changed, 27 insertions(+), 52 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll index f3ae6dcbbdd..35cfa972b8b 100644 --- a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll +++ b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll @@ -305,48 +305,22 @@ class ReverseDNSMethod extends Method { } } -/** Android `Intent` that may have come from a hostile application. */ -class AndroidIntentInput extends DataFlow::Node { +/** Exported Android `Intent` that may have come from a hostile application. */ +class AndroidIntentInput extends RemoteFlowSource { AndroidIntentInput() { - exists(MethodAccess ma, AndroidGetIntentMethod m | - ma.getMethod().overrides*(m) and - this.asExpr() = ma - ) - or - exists(Method m, AndroidReceiveIntentMethod rI | - m.overrides*(rI) and - this.asParameter() = m.getParameter(1) - ) - } -} - -/** Method access to external inputs of `android.content.Intent` or `android.os.BaseBundle` object. */ -class IntentGetExtraMethodAccess extends MethodAccess { - IntentGetExtraMethodAccess() { - exists(AndroidComponent ac | - this.getEnclosingCallable().getDeclaringType() = ac and - ac.isExported() and - this.getMethod().getName().regexpMatch("get\\w+Extra") and - this.getMethod().getDeclaringType() instanceof TypeIntent - ) - or - this.getMethod().getName().regexpMatch("get\\w+") and - this - .getMethod() - .getDeclaringType() - .getASupertype*() - .hasQualifiedName("android.os", "BaseBundle") - } -} - -/** Android intent extra source. */ -private class AndroidIntentExtraSource extends RemoteFlowSource { - AndroidIntentExtraSource() { - exists(AndroidIntentInput inode | - this.asExpr() = inode.asExpr() or - this.asExpr() = inode.asParameter().getAnAccess() + this.getEnclosingCallable().getDeclaringType().(AndroidComponent).isExported() and + ( + exists(MethodAccess ma, AndroidGetIntentMethod m | + ma.getMethod().overrides*(m) and + this.asExpr() = ma + ) + or + exists(Method m, AndroidReceiveIntentMethod rI | + m.overrides*(rI) and + this.asParameter() = m.getParameter(1) + ) ) } - override string getSourceType() { result = "Android intent extra" } + override string getSourceType() { result = "Android intent source" } } diff --git a/java/ql/src/semmle/code/java/dataflow/FlowSteps.qll b/java/ql/src/semmle/code/java/dataflow/FlowSteps.qll index 50c01ce3c5f..eac6536b76a 100644 --- a/java/ql/src/semmle/code/java/dataflow/FlowSteps.qll +++ b/java/ql/src/semmle/code/java/dataflow/FlowSteps.qll @@ -141,30 +141,31 @@ private class StringBuilderTaintPreservingCallable extends TaintPreservingCallab } } -/** - * Holds if `n1` to `n2` is a dataflow step between the extra getter method and its caller Android `Intent` or `Bundle`. - */ -private predicate intentExtraStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) { - exists(IntentGetExtraMethodAccess ma | - n1.asExpr() = ma.getQualifier() and - n2.asExpr() = ma - ) +/** Method access to external inputs of `android.os.BaseBundle` object. */ +class GetBundleExtraMethodAccess extends MethodAccess { + GetBundleExtraMethodAccess() { + this.getMethod().getName().regexpMatch("get\\w+") and + this + .getMethod() + .getDeclaringType() + .getASupertype*() + .hasQualifiedName("android.os", "BaseBundle") + } } /** - * Holds if `n1` to `n2` is a dataflow step from Android `Intent` to its `getExtras` method. + * Holds if `n1` to `n2` is a dataflow step between the extra getter method and its caller `Bundle`. */ private predicate bundleExtraStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) { - exists(MethodAccess ma | ma.getMethod().hasName("getExtras") | + exists(GetBundleExtraMethodAccess ma | n1.asExpr() = ma.getQualifier() and n2.asExpr() = ma ) } -/** A set of additional taint steps to consider when taint tracking Android intent extra related data flows. */ +/** Additional taint step to consider when taint tracking Android intent extra related data flows. */ class AndroidExtraSourceAdditionalTaintStep extends AdditionalTaintStep { override predicate step(DataFlow::Node node1, DataFlow::Node node2) { - intentExtraStep(node1, node2) or bundleExtraStep(node1, node2) } } From 60e8910330f6a0c1f9c4ebf72bd41f124f8f7c54 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Tue, 27 Oct 2020 12:01:30 +0000 Subject: [PATCH 07/15] Follow taint across getExtras without qualifier --- java/ql/src/semmle/code/java/dataflow/FlowSources.qll | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll index 35cfa972b8b..9d0905d2107 100644 --- a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll +++ b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll @@ -308,16 +308,18 @@ class ReverseDNSMethod extends Method { /** Exported Android `Intent` that may have come from a hostile application. */ class AndroidIntentInput extends RemoteFlowSource { AndroidIntentInput() { - this.getEnclosingCallable().getDeclaringType().(AndroidComponent).isExported() and - ( + exists(AndroidComponent exportedType | + exportedType.isExported() | exists(MethodAccess ma, AndroidGetIntentMethod m | ma.getMethod().overrides*(m) and - this.asExpr() = ma + this.asExpr() = ma and + exportedType = ma.getReceiverType() ) or exists(Method m, AndroidReceiveIntentMethod rI | m.overrides*(rI) and - this.asParameter() = m.getParameter(1) + this.asParameter() = m.getParameter(1) and + exportedType = m.getDeclaringType() ) ) } From 54c1480fd62aef5078d0405f74ffd6ea3e89e771 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Tue, 27 Oct 2020 12:02:29 +0000 Subject: [PATCH 08/15] Replace explicit extra step with TaintPreservingCallable --- .../semmle/code/java/dataflow/FlowSteps.qll | 29 ------------------- .../code/java/frameworks/android/Intent.qll | 9 ++++++ 2 files changed, 9 insertions(+), 29 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/FlowSteps.qll b/java/ql/src/semmle/code/java/dataflow/FlowSteps.qll index eac6536b76a..d2abbefe9ac 100644 --- a/java/ql/src/semmle/code/java/dataflow/FlowSteps.qll +++ b/java/ql/src/semmle/code/java/dataflow/FlowSteps.qll @@ -140,32 +140,3 @@ private class StringBuilderTaintPreservingCallable extends TaintPreservingCallab sink = -1 } } - -/** Method access to external inputs of `android.os.BaseBundle` object. */ -class GetBundleExtraMethodAccess extends MethodAccess { - GetBundleExtraMethodAccess() { - this.getMethod().getName().regexpMatch("get\\w+") and - this - .getMethod() - .getDeclaringType() - .getASupertype*() - .hasQualifiedName("android.os", "BaseBundle") - } -} - -/** - * Holds if `n1` to `n2` is a dataflow step between the extra getter method and its caller `Bundle`. - */ -private predicate bundleExtraStep(DataFlow::ExprNode n1, DataFlow::ExprNode n2) { - exists(GetBundleExtraMethodAccess ma | - n1.asExpr() = ma.getQualifier() and - n2.asExpr() = ma - ) -} - -/** Additional taint step to consider when taint tracking Android intent extra related data flows. */ -class AndroidExtraSourceAdditionalTaintStep extends AdditionalTaintStep { - override predicate step(DataFlow::Node node1, DataFlow::Node node2) { - bundleExtraStep(node1, node2) - } -} diff --git a/java/ql/src/semmle/code/java/frameworks/android/Intent.qll b/java/ql/src/semmle/code/java/frameworks/android/Intent.qll index c4894a5976c..f9a04b870fe 100644 --- a/java/ql/src/semmle/code/java/frameworks/android/Intent.qll +++ b/java/ql/src/semmle/code/java/frameworks/android/Intent.qll @@ -42,3 +42,12 @@ class IntentGetExtraMethod extends Method, TaintPreservingCallable { override predicate returnsTaintFrom(int arg) { arg = -1 } } + +class BundleGetterMethods extends Method, TaintPreservingCallable { + BundleGetterMethods() { + getDeclaringType().hasQualifiedName("android.os", ["BaseBundle", "Bundle"]) and + getName().matches("get%") + } + + override predicate returnsTaintFrom(int arg) { arg = -1 } +} \ No newline at end of file From 3f298f3dc8ed8333e4bfdfbdec7d8a967bfc8be3 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Tue, 27 Oct 2020 12:03:05 +0000 Subject: [PATCH 09/15] Add basic tests for Android intents as flow sources --- .../security/CWE-927/AndroidManifest.xml | 48 +++++++++++++++++++ .../security/CWE-927/ExecTainted.qlref | 1 + .../security/CWE-927/IntentSources.java | 37 ++++++++++++++ .../query-tests/security/CWE-927/options | 1 + 4 files changed, 87 insertions(+) create mode 100644 java/ql/test/experimental/query-tests/security/CWE-927/AndroidManifest.xml create mode 100644 java/ql/test/experimental/query-tests/security/CWE-927/ExecTainted.qlref create mode 100644 java/ql/test/experimental/query-tests/security/CWE-927/IntentSources.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-927/options diff --git a/java/ql/test/experimental/query-tests/security/CWE-927/AndroidManifest.xml b/java/ql/test/experimental/query-tests/security/CWE-927/AndroidManifest.xml new file mode 100644 index 00000000000..f9fe7eff10a --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-927/AndroidManifest.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/java/ql/test/experimental/query-tests/security/CWE-927/ExecTainted.qlref b/java/ql/test/experimental/query-tests/security/CWE-927/ExecTainted.qlref new file mode 100644 index 00000000000..1de765a2fdf --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-927/ExecTainted.qlref @@ -0,0 +1 @@ +Security/CWE/CWE-078/ExecTainted.ql diff --git a/java/ql/test/experimental/query-tests/security/CWE-927/IntentSources.java b/java/ql/test/experimental/query-tests/security/CWE-927/IntentSources.java new file mode 100644 index 00000000000..960bbff0bce --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-927/IntentSources.java @@ -0,0 +1,37 @@ +package com.example.myapp; + +import android.app.Activity; + +public class IntentSources extends Activity { + + public void test() { + + String trouble = this.getIntent().getStringExtra("key"); + Runtime.getRuntime().exec(trouble); + + } + + public void test2() { + + String trouble = getIntent().getStringExtra("key"); + Runtime.getRuntime().exec(trouble); + + } + + public void test3() { + + String trouble = getIntent().getExtras().getString("key"); + Runtime.getRuntime().exec(trouble); + + } + +} + +class OtherClass { + + public void test(IntentSources is) { + String trouble = is.getIntent().getStringExtra("key"); + Runtime.getRuntime().exec(trouble); + } + +} \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-927/options b/java/ql/test/experimental/query-tests/security/CWE-927/options new file mode 100644 index 00000000000..43e25f608b6 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-927/options @@ -0,0 +1 @@ +// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/google-android-9.0.0 From 3cc3fe9d37d67dd3bf1de48d4548bb8f8c45485e Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Wed, 28 Oct 2020 00:33:07 +0000 Subject: [PATCH 10/15] Switch to TaintPreservingCallable and add test cases --- .../CWE/CWE-749/UnsafeAndroidAccess.ql | 23 +-------- .../semmle/code/java/dataflow/FlowSources.qll | 24 +++++++-- .../code/java/frameworks/android/Android.qll | 33 +++++------- .../security/CWE-749/AndroidManifest.xml | 51 +++++++++++++++++++ .../security/CWE-749/IntentUtils.java | 24 +++++++++ .../security/CWE-749/SafeActivity1.java | 34 +++++++++++++ .../security/CWE-749/SafeActivity2.java | 34 +++++++++++++ .../security/CWE-749/SafeActivity3.java | 34 +++++++++++++ .../security/CWE-749/UnsafeActivity1.java | 34 +++++++++++++ .../security/CWE-749/UnsafeActivity2.java | 34 +++++++++++++ .../security/CWE-749/UnsafeActivity3.java | 34 +++++++++++++ .../security/CWE-749/UnsafeActivity4.java | 35 +++++++++++++ .../CWE-749/UnsafeAndroidAccess.expected | 40 ++++++++++----- .../security/CWE-749/UnsafeAndroidAccess.java | 2 + .../UnsafeAndroidBroadcastReceiver.java | 34 +++++++++++++ 15 files changed, 410 insertions(+), 60 deletions(-) create mode 100755 java/ql/test/experimental/query-tests/security/CWE-749/AndroidManifest.xml create mode 100644 java/ql/test/experimental/query-tests/security/CWE-749/IntentUtils.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity1.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity2.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity3.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity1.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity2.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity3.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity4.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidBroadcastReceiver.java diff --git a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql index 23a6e0a7003..bd69eecf2c4 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql @@ -58,27 +58,6 @@ class FetchResourceMethodAccess extends MethodAccess { } } -/** - * Method access to external inputs of `android.content.Intent` object - */ -class IntentGetExtraMethodAccess extends MethodAccess { - IntentGetExtraMethodAccess() { - this.getMethod().getName().regexpMatch("get\\w+Extra") and - this.getMethod().getDeclaringType() instanceof TypeIntent - or - this.getMethod().getName().regexpMatch("get\\w+") and - this.getQualifier().(MethodAccess).getMethod().hasName("getExtras") and - this.getQualifier().(MethodAccess).getMethod().getDeclaringType() instanceof TypeIntent - } -} - -/** - * Source of fetching URLs from intent extras - */ -class UntrustedResourceSource extends DataFlow::ExprNode { - UntrustedResourceSource() { this.asExpr() instanceof IntentGetExtraMethodAccess } -} - /** * Holds if `ma` loads URL `sink` */ @@ -127,7 +106,7 @@ class UrlResourceSink extends DataFlow::ExprNode { class FetchUntrustedResourceConfiguration extends TaintTracking::Configuration { FetchUntrustedResourceConfiguration() { this = "FetchUntrustedResourceConfiguration" } - override predicate isSource(DataFlow::Node source) { source instanceof UntrustedResourceSource } + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } override predicate isSink(DataFlow::Node sink) { sink instanceof UrlResourceSink and diff --git a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll index 9d0905d2107..e51c455a124 100644 --- a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll +++ b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll @@ -305,15 +305,29 @@ class ReverseDNSMethod extends Method { } } -/** Exported Android `Intent` that may have come from a hostile application. */ -class AndroidIntentInput extends RemoteFlowSource { +/** Android `Intent` that may have come from a hostile application. */ +class AndroidIntentInput extends DataFlow::Node { AndroidIntentInput() { - exists(AndroidComponent exportedType | - exportedType.isExported() | + exists(MethodAccess ma, AndroidGetIntentMethod m | + ma.getMethod().overrides*(m) and + this.asExpr() = ma + ) + or + exists(Method m, AndroidReceiveIntentMethod rI | + m.overrides*(rI) and + this.asParameter() = m.getParameter(1) + ) + } +} + +/** Exported Android `Intent` that may have come from a hostile application. */ +class ExportedAndroidIntentInput extends RemoteFlowSource { + ExportedAndroidIntentInput() { + exists(ExportableAndroidComponent exportedType | exportedType.isExported() | exists(MethodAccess ma, AndroidGetIntentMethod m | ma.getMethod().overrides*(m) and this.asExpr() = ma and - exportedType = ma.getReceiverType() + exportedType = ma.getEnclosingCallable().getDeclaringType() ) or exists(Method m, AndroidReceiveIntentMethod rI | diff --git a/java/ql/src/semmle/code/java/frameworks/android/Android.qll b/java/ql/src/semmle/code/java/frameworks/android/Android.qll index 1415efedafd..af0ecf81b4b 100644 --- a/java/ql/src/semmle/code/java/frameworks/android/Android.qll +++ b/java/ql/src/semmle/code/java/frameworks/android/Android.qll @@ -30,10 +30,10 @@ class AndroidComponent extends Class { predicate hasIntentFilter() { exists(getAndroidComponentXmlElement().getAnIntentFilterElement()) } } -/** An Android activity. */ -class AndroidActivity extends AndroidComponent { - AndroidActivity() { this.getASupertype*().hasQualifiedName("android.app", "Activity") } - +/** + * An Android component that is explicitly or implicitly exported. + */ +class ExportableAndroidComponent extends AndroidComponent { /** Holds if this Android component is configured as `exported` or has intent filters configured without `exported` explicitly disabled in an `AndroidManifest.xml` file. */ override predicate isExported() { getAndroidComponentXmlElement().isExported() @@ -42,34 +42,25 @@ class AndroidActivity extends AndroidComponent { } } +/** An Android activity. */ +class AndroidActivity extends ExportableAndroidComponent { + AndroidActivity() { this.getASupertype*().hasQualifiedName("android.app", "Activity") } +} + /** An Android service. */ -class AndroidService extends AndroidComponent { +class AndroidService extends ExportableAndroidComponent { AndroidService() { this.getASupertype*().hasQualifiedName("android.app", "Service") } - - /** Holds if this Android component is configured as `exported` or has intent filters configured without `exported` explicitly disabled in an `AndroidManifest.xml` file. */ - override predicate isExported() { - getAndroidComponentXmlElement().isExported() - or - not getAndroidComponentXmlElement().isNotExported() and hasIntentFilter() - } } /** An Android broadcast receiver. */ -class AndroidBroadcastReceiver extends AndroidComponent { +class AndroidBroadcastReceiver extends ExportableAndroidComponent { AndroidBroadcastReceiver() { this.getASupertype*().hasQualifiedName("android.content", "BroadcastReceiver") } - - /** Holds if this Android component is configured as `exported` or has intent filters configured without `exported` explicitly disabled in an `AndroidManifest.xml` file. */ - override predicate isExported() { - getAndroidComponentXmlElement().isExported() - or - not getAndroidComponentXmlElement().isNotExported() and hasIntentFilter() - } } /** An Android content provider. */ -class AndroidContentProvider extends AndroidComponent { +class AndroidContentProvider extends ExportableAndroidComponent { AndroidContentProvider() { this.getASupertype*().hasQualifiedName("android.content", "ContentProvider") } diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/AndroidManifest.xml b/java/ql/test/experimental/query-tests/security/CWE-749/AndroidManifest.xml new file mode 100755 index 00000000000..b215e4d3466 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-749/AndroidManifest.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/IntentUtils.java b/java/ql/test/experimental/query-tests/security/CWE-749/IntentUtils.java new file mode 100644 index 00000000000..87ba8d5c429 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-749/IntentUtils.java @@ -0,0 +1,24 @@ +package com.example.app; + +import android.app.Activity; + +import android.os.Bundle; + +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +/** A utility program for getting intent extra information from Android activity */ +public class IntentUtils { + /** Get intent extra */ + public static String getIntentUrl(Activity a) { + String thisUrl = a.getIntent().getStringExtra("url"); + return thisUrl; + } + + /** Get bundle extra */ + public static String getBundleUrl(Activity a) { + String thisUrl = a.getIntent().getExtras().getString("url"); + return thisUrl; + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity1.java b/java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity1.java new file mode 100644 index 00000000000..9af651031cb --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity1.java @@ -0,0 +1,34 @@ +package com.example.app; + +import android.app.Activity; + +import android.os.Bundle; + +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +public class SafeActivity1 extends Activity { + //Test onCreate with both JavaScript and cross-origin resource access enabled while taking remote user inputs from bundle extras + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(-1); + + WebView wv = (WebView) findViewById(-1); + WebSettings webSettings = wv.getSettings(); + + webSettings.setJavaScriptEnabled(true); + webSettings.setAllowFileAccessFromFileURLs(true); + + wv.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + view.loadUrl(url); + return true; + } + }); + + String thisUrl = getIntent().getExtras().getString("url"); + wv.loadUrl(thisUrl); + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity2.java b/java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity2.java new file mode 100644 index 00000000000..b8959c657ca --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity2.java @@ -0,0 +1,34 @@ +package com.example.app; + +import android.app.Activity; + +import android.os.Bundle; + +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +public class SafeActivity2 extends Activity { + //Test onCreate with both JavaScript and cross-origin resource access enabled while taking remote user inputs from bundle extras + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(-1); + + WebView wv = (WebView) findViewById(-1); + WebSettings webSettings = wv.getSettings(); + + webSettings.setJavaScriptEnabled(true); + webSettings.setAllowFileAccessFromFileURLs(true); + + wv.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + view.loadUrl(url); + return true; + } + }); + + String thisUrl = getIntent().getExtras().getString("url"); + wv.loadUrl(thisUrl); + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity3.java b/java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity3.java new file mode 100644 index 00000000000..29ba013affb --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity3.java @@ -0,0 +1,34 @@ +package com.example.app; + +import android.app.Activity; + +import android.os.Bundle; + +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +public class SafeActivity3 extends Activity { + //Test onCreate with both JavaScript and cross-origin resource access enabled while taking remote user inputs from bundle extras + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(-1); + + WebView wv = (WebView) findViewById(-1); + WebSettings webSettings = wv.getSettings(); + + webSettings.setJavaScriptEnabled(true); + webSettings.setAllowFileAccessFromFileURLs(true); + + wv.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + view.loadUrl(url); + return true; + } + }); + + String thisUrl = getIntent().getExtras().getString("url"); + wv.loadUrl(thisUrl); + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity1.java b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity1.java new file mode 100644 index 00000000000..3f97d7501b2 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity1.java @@ -0,0 +1,34 @@ +package com.example.app; + +import android.app.Activity; + +import android.os.Bundle; + +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +public class UnsafeActivity1 extends Activity { + //Test onCreate with both JavaScript and cross-origin resource access enabled while taking remote user inputs from bundle extras + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(-1); + + WebView wv = (WebView) findViewById(-1); + WebSettings webSettings = wv.getSettings(); + + webSettings.setJavaScriptEnabled(true); + webSettings.setAllowFileAccessFromFileURLs(true); + + wv.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + view.loadUrl(url); + return true; + } + }); + + String thisUrl = getIntent().getExtras().getString("url"); + wv.loadUrl(thisUrl); + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity2.java b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity2.java new file mode 100644 index 00000000000..c5b15c2ebba --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity2.java @@ -0,0 +1,34 @@ +package com.example.app; + +import android.app.Activity; + +import android.os.Bundle; + +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +public class UnsafeActivity2 extends Activity { + //Test onCreate with both JavaScript and cross-origin resource access enabled while taking remote user inputs from bundle extras + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(-1); + + WebView wv = (WebView) findViewById(-1); + WebSettings webSettings = wv.getSettings(); + + webSettings.setJavaScriptEnabled(true); + webSettings.setAllowFileAccessFromFileURLs(true); + + wv.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + view.loadUrl(url); + return true; + } + }); + + String thisUrl = getIntent().getExtras().getString("url"); + wv.loadUrl(thisUrl); + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity3.java b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity3.java new file mode 100644 index 00000000000..0fe6c342b5a --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity3.java @@ -0,0 +1,34 @@ +package com.example.app; + +import android.app.Activity; + +import android.os.Bundle; + +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +public class UnsafeActivity3 extends Activity { + //Test onCreate with both JavaScript and cross-origin resource access enabled while taking remote user inputs from bundle extras + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(-1); + + WebView wv = (WebView) findViewById(-1); + WebSettings webSettings = wv.getSettings(); + + webSettings.setJavaScriptEnabled(true); + webSettings.setAllowFileAccessFromFileURLs(true); + + wv.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + view.loadUrl(url); + return true; + } + }); + + String thisUrl = getIntent().getExtras().getString("url"); + wv.loadUrl(thisUrl); + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity4.java b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity4.java new file mode 100644 index 00000000000..dc3df2957b8 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity4.java @@ -0,0 +1,35 @@ +package com.example.app; + +import android.app.Activity; + +import android.os.Bundle; + +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +public class UnsafeActivity4 extends Activity { + //Test onCreate with both JavaScript and cross-origin resource access enabled while taking remote user inputs from bundle extras + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(-1); + + WebView wv = (WebView) findViewById(-1); + WebSettings webSettings = wv.getSettings(); + + webSettings.setJavaScriptEnabled(true); + webSettings.setAllowFileAccessFromFileURLs(true); + + wv.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + view.loadUrl(url); + return true; + } + }); + + String thisUrl = IntentUtils.getIntentUrl(this); + thisUrl = IntentUtils.getBundleUrl(this); + wv.loadUrl(thisUrl); + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected index 660472cd7d8..9e2b153dd64 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected @@ -1,15 +1,31 @@ edges -| UnsafeAndroidAccess.java:29:20:29:59 | getString(...) : String | UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | -| UnsafeAndroidAccess.java:52:20:52:52 | getStringExtra(...) : String | UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | -| UnsafeAndroidAccess.java:94:20:94:52 | getStringExtra(...) : String | UnsafeAndroidAccess.java:95:14:95:20 | thisUrl | +| UnsafeActivity1.java:31:20:31:30 | getIntent(...) : Intent | UnsafeActivity1.java:32:14:32:20 | thisUrl | +| UnsafeActivity2.java:31:20:31:30 | getIntent(...) : Intent | UnsafeActivity2.java:32:14:32:20 | thisUrl | +| UnsafeActivity3.java:31:20:31:30 | getIntent(...) : Intent | UnsafeActivity3.java:32:14:32:20 | thisUrl | +| UnsafeAndroidAccess.java:31:20:31:30 | getIntent(...) : Intent | UnsafeAndroidAccess.java:32:14:32:20 | thisUrl | +| UnsafeAndroidAccess.java:54:20:54:30 | getIntent(...) : Intent | UnsafeAndroidAccess.java:55:14:55:20 | thisUrl | +| UnsafeAndroidAccess.java:96:20:96:30 | getIntent(...) : Intent | UnsafeAndroidAccess.java:97:14:97:20 | thisUrl | +| UnsafeAndroidBroadcastReceiver.java:16:41:16:53 | intent : Intent | UnsafeAndroidBroadcastReceiver.java:32:14:32:20 | thisUrl | nodes -| UnsafeAndroidAccess.java:29:20:29:59 | getString(...) : String | semmle.label | getString(...) : String | -| UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | semmle.label | thisUrl | -| UnsafeAndroidAccess.java:52:20:52:52 | getStringExtra(...) : String | semmle.label | getStringExtra(...) : String | -| UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | semmle.label | thisUrl | -| UnsafeAndroidAccess.java:94:20:94:52 | getStringExtra(...) : String | semmle.label | getStringExtra(...) : String | -| UnsafeAndroidAccess.java:95:14:95:20 | thisUrl | semmle.label | thisUrl | +| UnsafeActivity1.java:31:20:31:30 | getIntent(...) : Intent | semmle.label | getIntent(...) : Intent | +| UnsafeActivity1.java:32:14:32:20 | thisUrl | semmle.label | thisUrl | +| UnsafeActivity2.java:31:20:31:30 | getIntent(...) : Intent | semmle.label | getIntent(...) : Intent | +| UnsafeActivity2.java:32:14:32:20 | thisUrl | semmle.label | thisUrl | +| UnsafeActivity3.java:31:20:31:30 | getIntent(...) : Intent | semmle.label | getIntent(...) : Intent | +| UnsafeActivity3.java:32:14:32:20 | thisUrl | semmle.label | thisUrl | +| UnsafeAndroidAccess.java:31:20:31:30 | getIntent(...) : Intent | semmle.label | getIntent(...) : Intent | +| UnsafeAndroidAccess.java:32:14:32:20 | thisUrl | semmle.label | thisUrl | +| UnsafeAndroidAccess.java:54:20:54:30 | getIntent(...) : Intent | semmle.label | getIntent(...) : Intent | +| UnsafeAndroidAccess.java:55:14:55:20 | thisUrl | semmle.label | thisUrl | +| UnsafeAndroidAccess.java:96:20:96:30 | getIntent(...) : Intent | semmle.label | getIntent(...) : Intent | +| UnsafeAndroidAccess.java:97:14:97:20 | thisUrl | semmle.label | thisUrl | +| UnsafeAndroidBroadcastReceiver.java:16:41:16:53 | intent : Intent | semmle.label | intent : Intent | +| UnsafeAndroidBroadcastReceiver.java:32:14:32:20 | thisUrl | semmle.label | thisUrl | #select -| UnsafeAndroidAccess.java:30:3:30:21 | loadUrl(...) | UnsafeAndroidAccess.java:29:20:29:59 | getString(...) : String | UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:29:20:29:59 | getString(...) | user input vulnerable to cross-origin and sensitive resource disclosure attacks | -| UnsafeAndroidAccess.java:53:3:53:21 | loadUrl(...) | UnsafeAndroidAccess.java:52:20:52:52 | getStringExtra(...) : String | UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:52:20:52:52 | getStringExtra(...) | user input vulnerable to cross-origin and sensitive resource disclosure attacks | -| UnsafeAndroidAccess.java:95:3:95:21 | loadUrl(...) | UnsafeAndroidAccess.java:94:20:94:52 | getStringExtra(...) : String | UnsafeAndroidAccess.java:95:14:95:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:94:20:94:52 | getStringExtra(...) | user input vulnerable to XSS attacks | +| UnsafeActivity1.java:32:3:32:21 | loadUrl(...) | UnsafeActivity1.java:31:20:31:30 | getIntent(...) : Intent | UnsafeActivity1.java:32:14:32:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeActivity1.java:31:20:31:30 | getIntent(...) | user input vulnerable to cross-origin and sensitive resource disclosure attacks | +| UnsafeActivity2.java:32:3:32:21 | loadUrl(...) | UnsafeActivity2.java:31:20:31:30 | getIntent(...) : Intent | UnsafeActivity2.java:32:14:32:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeActivity2.java:31:20:31:30 | getIntent(...) | user input vulnerable to cross-origin and sensitive resource disclosure attacks | +| UnsafeActivity3.java:32:3:32:21 | loadUrl(...) | UnsafeActivity3.java:31:20:31:30 | getIntent(...) : Intent | UnsafeActivity3.java:32:14:32:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeActivity3.java:31:20:31:30 | getIntent(...) | user input vulnerable to cross-origin and sensitive resource disclosure attacks | +| UnsafeAndroidAccess.java:32:3:32:21 | loadUrl(...) | UnsafeAndroidAccess.java:31:20:31:30 | getIntent(...) : Intent | UnsafeAndroidAccess.java:32:14:32:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:31:20:31:30 | getIntent(...) | user input vulnerable to cross-origin and sensitive resource disclosure attacks | +| UnsafeAndroidAccess.java:55:3:55:21 | loadUrl(...) | UnsafeAndroidAccess.java:54:20:54:30 | getIntent(...) : Intent | UnsafeAndroidAccess.java:55:14:55:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:54:20:54:30 | getIntent(...) | user input vulnerable to cross-origin and sensitive resource disclosure attacks | +| UnsafeAndroidAccess.java:97:3:97:21 | loadUrl(...) | UnsafeAndroidAccess.java:96:20:96:30 | getIntent(...) : Intent | UnsafeAndroidAccess.java:97:14:97:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:96:20:96:30 | getIntent(...) | user input vulnerable to XSS attacks | +| UnsafeAndroidBroadcastReceiver.java:32:3:32:21 | loadUrl(...) | UnsafeAndroidBroadcastReceiver.java:16:41:16:53 | intent : Intent | UnsafeAndroidBroadcastReceiver.java:32:14:32:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidBroadcastReceiver.java:16:41:16:53 | intent | user input vulnerable to cross-origin and sensitive resource disclosure attacks | diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.java b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.java index 8a929120bf8..ddddafd95d1 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.java +++ b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.java @@ -1,3 +1,5 @@ +package com.example.app; + import android.app.Activity; import android.os.Bundle; diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidBroadcastReceiver.java b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidBroadcastReceiver.java new file mode 100644 index 00000000000..e49d0b7b518 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidBroadcastReceiver.java @@ -0,0 +1,34 @@ +package com.example.app; + +import android.app.Activity; + +import android.content.Context; +import android.content.Intent; +import android.content.BroadcastReceiver; +import android.os.Bundle; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +public class UnsafeAndroidBroadcastReceiver extends BroadcastReceiver { + //Test onCreate with JavaScript enabled but cross-origin resource access disabled while taking remote user inputs + @Override + public void onReceive(Context context, Intent intent) { + String thisUrl = intent.getStringExtra("url"); + WebView wv = null; + WebSettings webSettings = wv.getSettings(); + + webSettings.setJavaScriptEnabled(true); + webSettings.setAllowFileAccessFromFileURLs(true); + + wv.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + view.loadUrl(url); + return true; + } + }); + + wv.loadUrl(thisUrl); + } +} \ No newline at end of file From 2ee9a45e69131d8966654c7e447f7b3128348472 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Wed, 28 Oct 2020 22:05:30 +0000 Subject: [PATCH 11/15] Use proper class inheritance --- .../semmle/code/java/dataflow/FlowSources.qll | 28 ++++++------------- .../code/java/frameworks/android/Android.qll | 2 +- .../security/CWE-749/UnsafeActivity4.java | 5 +++- 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll index e51c455a124..e61998520b4 100644 --- a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll +++ b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll @@ -307,36 +307,26 @@ class ReverseDNSMethod extends Method { /** Android `Intent` that may have come from a hostile application. */ class AndroidIntentInput extends DataFlow::Node { + Type receiverType; + AndroidIntentInput() { exists(MethodAccess ma, AndroidGetIntentMethod m | ma.getMethod().overrides*(m) and - this.asExpr() = ma + this.asExpr() = ma and + receiverType = ma.getEnclosingCallable().getDeclaringType() ) or exists(Method m, AndroidReceiveIntentMethod rI | m.overrides*(rI) and - this.asParameter() = m.getParameter(1) + this.asParameter() = m.getParameter(1) and + receiverType = m.getDeclaringType() ) } } /** Exported Android `Intent` that may have come from a hostile application. */ -class ExportedAndroidIntentInput extends RemoteFlowSource { - ExportedAndroidIntentInput() { - exists(ExportableAndroidComponent exportedType | exportedType.isExported() | - exists(MethodAccess ma, AndroidGetIntentMethod m | - ma.getMethod().overrides*(m) and - this.asExpr() = ma and - exportedType = ma.getEnclosingCallable().getDeclaringType() - ) - or - exists(Method m, AndroidReceiveIntentMethod rI | - m.overrides*(rI) and - this.asParameter() = m.getParameter(1) and - exportedType = m.getDeclaringType() - ) - ) - } +class ExportedAndroidIntentInput extends RemoteFlowSource, AndroidIntentInput { + ExportedAndroidIntentInput() { receiverType.(ExportableAndroidComponent).isExported() } - override string getSourceType() { result = "Android intent source" } + override string getSourceType() { result = "Exported Android intent source" } } diff --git a/java/ql/src/semmle/code/java/frameworks/android/Android.qll b/java/ql/src/semmle/code/java/frameworks/android/Android.qll index af0ecf81b4b..058f4a30513 100644 --- a/java/ql/src/semmle/code/java/frameworks/android/Android.qll +++ b/java/ql/src/semmle/code/java/frameworks/android/Android.qll @@ -31,7 +31,7 @@ class AndroidComponent extends Class { } /** - * An Android component that is explicitly or implicitly exported. + * An Android component that can be explicitly or implicitly exported. */ class ExportableAndroidComponent extends AndroidComponent { /** Holds if this Android component is configured as `exported` or has intent filters configured without `exported` explicitly disabled in an `AndroidManifest.xml` file. */ diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity4.java b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity4.java index dc3df2957b8..4a4cb09c8f7 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity4.java +++ b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity4.java @@ -9,7 +9,10 @@ import android.webkit.WebView; import android.webkit.WebViewClient; public class UnsafeActivity4 extends Activity { - //Test onCreate with both JavaScript and cross-origin resource access enabled while taking remote user inputs from bundle extras + /** + * Test onCreate with both JavaScript and cross-origin resource access enabled while taking remote user inputs from bundle extras + * Note this case of invoking utility method that takes an Activity a then calls `a.getIntent().getStringExtra(...)` is not yet detected thus is beyond what the query is capable of. + */ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(-1); From b1d6bc5ba987216eb4809e8ab72600c76303da97 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Thu, 29 Oct 2020 12:55:03 +0000 Subject: [PATCH 12/15] Use getDeclaringType() for getIntent() method call --- java/ql/src/semmle/code/java/dataflow/FlowSources.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll index e61998520b4..ebf06eba1f8 100644 --- a/java/ql/src/semmle/code/java/dataflow/FlowSources.qll +++ b/java/ql/src/semmle/code/java/dataflow/FlowSources.qll @@ -313,7 +313,7 @@ class AndroidIntentInput extends DataFlow::Node { exists(MethodAccess ma, AndroidGetIntentMethod m | ma.getMethod().overrides*(m) and this.asExpr() = ma and - receiverType = ma.getEnclosingCallable().getDeclaringType() + receiverType = ma.getReceiverType() ) or exists(Method m, AndroidReceiveIntentMethod rI | From 7ac3fb41d51297c0a616dbb3794e09089dea1d9b Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Sat, 31 Oct 2020 13:37:36 +0000 Subject: [PATCH 13/15] Clean up query and test files --- .../semmle/code/java/dataflow/FlowSteps.qll | 1 - .../code/java/frameworks/android/Android.qll | 9 +++++++-- .../code/java/frameworks/android/Intent.qll | 2 +- .../security/CWE-927/ExecTainted.qlref | 1 - .../query-tests/security/CWE-927/options | 1 - .../dataflow/taintsources}/AndroidManifest.xml | 0 .../dataflow/taintsources}/IntentSources.java | 0 .../dataflow/taintsources/options | 2 +- .../dataflow/taintsources/remote.expected | 18 ++++++++++++++++++ 9 files changed, 27 insertions(+), 7 deletions(-) delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-927/ExecTainted.qlref delete mode 100644 java/ql/test/experimental/query-tests/security/CWE-927/options rename java/ql/test/{experimental/query-tests/security/CWE-927 => library-tests/dataflow/taintsources}/AndroidManifest.xml (100%) rename java/ql/test/{experimental/query-tests/security/CWE-927 => library-tests/dataflow/taintsources}/IntentSources.java (100%) diff --git a/java/ql/src/semmle/code/java/dataflow/FlowSteps.qll b/java/ql/src/semmle/code/java/dataflow/FlowSteps.qll index d2abbefe9ac..9ed602d86d7 100644 --- a/java/ql/src/semmle/code/java/dataflow/FlowSteps.qll +++ b/java/ql/src/semmle/code/java/dataflow/FlowSteps.qll @@ -4,7 +4,6 @@ private import java private import semmle.code.java.dataflow.DataFlow -private import semmle.code.java.dataflow.FlowSources /** * A module importing the frameworks that implement additional flow steps, diff --git a/java/ql/src/semmle/code/java/frameworks/android/Android.qll b/java/ql/src/semmle/code/java/frameworks/android/Android.qll index 058f4a30513..df543e4f11f 100644 --- a/java/ql/src/semmle/code/java/frameworks/android/Android.qll +++ b/java/ql/src/semmle/code/java/frameworks/android/Android.qll @@ -34,11 +34,16 @@ class AndroidComponent extends Class { * An Android component that can be explicitly or implicitly exported. */ class ExportableAndroidComponent extends AndroidComponent { - /** Holds if this Android component is configured as `exported` or has intent filters configured without `exported` explicitly disabled in an `AndroidManifest.xml` file. */ + /** + * Holds if this Android component is configured as `exported` or has intent + * filters configured without `exported` explicitly disabled in an + * `AndroidManifest.xml` file. + */ override predicate isExported() { getAndroidComponentXmlElement().isExported() or - not getAndroidComponentXmlElement().isNotExported() and hasIntentFilter() + hasIntentFilter() and + not getAndroidComponentXmlElement().isNotExported() } } diff --git a/java/ql/src/semmle/code/java/frameworks/android/Intent.qll b/java/ql/src/semmle/code/java/frameworks/android/Intent.qll index f9a04b870fe..f9ede6f30e6 100644 --- a/java/ql/src/semmle/code/java/frameworks/android/Intent.qll +++ b/java/ql/src/semmle/code/java/frameworks/android/Intent.qll @@ -50,4 +50,4 @@ class BundleGetterMethods extends Method, TaintPreservingCallable { } override predicate returnsTaintFrom(int arg) { arg = -1 } -} \ No newline at end of file +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-927/ExecTainted.qlref b/java/ql/test/experimental/query-tests/security/CWE-927/ExecTainted.qlref deleted file mode 100644 index 1de765a2fdf..00000000000 --- a/java/ql/test/experimental/query-tests/security/CWE-927/ExecTainted.qlref +++ /dev/null @@ -1 +0,0 @@ -Security/CWE/CWE-078/ExecTainted.ql diff --git a/java/ql/test/experimental/query-tests/security/CWE-927/options b/java/ql/test/experimental/query-tests/security/CWE-927/options deleted file mode 100644 index 43e25f608b6..00000000000 --- a/java/ql/test/experimental/query-tests/security/CWE-927/options +++ /dev/null @@ -1 +0,0 @@ -// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/google-android-9.0.0 diff --git a/java/ql/test/experimental/query-tests/security/CWE-927/AndroidManifest.xml b/java/ql/test/library-tests/dataflow/taintsources/AndroidManifest.xml similarity index 100% rename from java/ql/test/experimental/query-tests/security/CWE-927/AndroidManifest.xml rename to java/ql/test/library-tests/dataflow/taintsources/AndroidManifest.xml diff --git a/java/ql/test/experimental/query-tests/security/CWE-927/IntentSources.java b/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java similarity index 100% rename from java/ql/test/experimental/query-tests/security/CWE-927/IntentSources.java rename to java/ql/test/library-tests/dataflow/taintsources/IntentSources.java diff --git a/java/ql/test/library-tests/dataflow/taintsources/options b/java/ql/test/library-tests/dataflow/taintsources/options index 4c9e6d3e443..70b292d693a 100644 --- a/java/ql/test/library-tests/dataflow/taintsources/options +++ b/java/ql/test/library-tests/dataflow/taintsources/options @@ -1 +1 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/springframework-5.2.3 +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/springframework-5.2.3:${testdir}/../../../stubs/google-android-9.0.0 diff --git a/java/ql/test/library-tests/dataflow/taintsources/remote.expected b/java/ql/test/library-tests/dataflow/taintsources/remote.expected index 21403e9f01b..57acdb6d26c 100644 --- a/java/ql/test/library-tests/dataflow/taintsources/remote.expected +++ b/java/ql/test/library-tests/dataflow/taintsources/remote.expected @@ -5,6 +5,24 @@ | A.java:41:5:41:53 | getInputStream(...) | A.java:41:5:41:53 | getInputStream(...) | | A.java:42:5:42:45 | getInputStream(...) | A.java:42:5:42:45 | getInputStream(...) | | A.java:43:5:43:47 | getHostName(...) | A.java:43:5:43:47 | getHostName(...) | +| IntentSources.java:9:20:9:35 | getIntent(...) | ../../../stubs/google-android-9.0.0/android/content/Intent.java:1057:19:1057:32 | parameter this | +| IntentSources.java:9:20:9:35 | getIntent(...) | IntentSources.java:9:20:9:35 | getIntent(...) | +| IntentSources.java:9:20:9:35 | getIntent(...) | IntentSources.java:9:20:9:57 | getStringExtra(...) | +| IntentSources.java:9:20:9:35 | getIntent(...) | IntentSources.java:10:29:10:35 | trouble | +| IntentSources.java:16:20:16:30 | getIntent(...) | ../../../stubs/google-android-9.0.0/android/content/Intent.java:1057:19:1057:32 | parameter this | +| IntentSources.java:16:20:16:30 | getIntent(...) | IntentSources.java:16:20:16:30 | getIntent(...) | +| IntentSources.java:16:20:16:30 | getIntent(...) | IntentSources.java:16:20:16:52 | getStringExtra(...) | +| IntentSources.java:16:20:16:30 | getIntent(...) | IntentSources.java:17:29:17:35 | trouble | +| IntentSources.java:23:20:23:30 | getIntent(...) | ../../../stubs/google-android-9.0.0/android/content/Intent.java:1356:19:1356:27 | parameter this | +| IntentSources.java:23:20:23:30 | getIntent(...) | ../../../stubs/google-android-9.0.0/android/os/BaseBundle.java:599:19:599:27 | parameter this | +| IntentSources.java:23:20:23:30 | getIntent(...) | IntentSources.java:23:20:23:30 | getIntent(...) | +| IntentSources.java:23:20:23:30 | getIntent(...) | IntentSources.java:23:20:23:42 | getExtras(...) | +| IntentSources.java:23:20:23:30 | getIntent(...) | IntentSources.java:23:20:23:59 | getString(...) | +| IntentSources.java:23:20:23:30 | getIntent(...) | IntentSources.java:24:29:24:35 | trouble | +| IntentSources.java:33:20:33:33 | getIntent(...) | ../../../stubs/google-android-9.0.0/android/content/Intent.java:1057:19:1057:32 | parameter this | +| IntentSources.java:33:20:33:33 | getIntent(...) | IntentSources.java:33:20:33:33 | getIntent(...) | +| IntentSources.java:33:20:33:33 | getIntent(...) | IntentSources.java:33:20:33:55 | getStringExtra(...) | +| IntentSources.java:33:20:33:33 | getIntent(...) | IntentSources.java:34:29:34:35 | trouble | | RmiFlowImpl.java:4:30:4:40 | path | RmiFlowImpl.java:4:30:4:40 | path | | RmiFlowImpl.java:4:30:4:40 | path | RmiFlowImpl.java:5:20:5:31 | ... + ... | | RmiFlowImpl.java:4:30:4:40 | path | RmiFlowImpl.java:5:28:5:31 | path | From 8da9b9d3eaaefc0ebd641ae9dab8e054b0ce0ccc Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Mon, 2 Nov 2020 10:53:46 +0000 Subject: [PATCH 14/15] Add documentation to new library method and use the singular form --- java/ql/src/semmle/code/java/frameworks/android/Intent.qll | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/java/ql/src/semmle/code/java/frameworks/android/Intent.qll b/java/ql/src/semmle/code/java/frameworks/android/Intent.qll index f9ede6f30e6..92c49f3101a 100644 --- a/java/ql/src/semmle/code/java/frameworks/android/Intent.qll +++ b/java/ql/src/semmle/code/java/frameworks/android/Intent.qll @@ -43,8 +43,9 @@ class IntentGetExtraMethod extends Method, TaintPreservingCallable { override predicate returnsTaintFrom(int arg) { arg = -1 } } -class BundleGetterMethods extends Method, TaintPreservingCallable { - BundleGetterMethods() { +/** A getter on `android.os.BaseBundle` or `android.os.Bundle`. */ +class BundleGetterMethod extends Method, TaintPreservingCallable { + BundleGetterMethod() { getDeclaringType().hasQualifiedName("android.os", ["BaseBundle", "Bundle"]) and getName().matches("get%") } From 864411b4b97341d6e2c365178dc587177e7fda68 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Mon, 2 Nov 2020 14:06:44 +0000 Subject: [PATCH 15/15] Updates to Android stub classes --- .../android/content/BroadcastReceiver.java | 255 ++++++++++++++++++ .../android/content/Context.java | 47 ++++ .../android/content/Intent.java | 73 +++++ .../android/os/BaseBundle.java | 154 +++++++++++ 4 files changed, 529 insertions(+) create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/content/BroadcastReceiver.java diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/BroadcastReceiver.java b/java/ql/test/stubs/google-android-9.0.0/android/content/BroadcastReceiver.java new file mode 100644 index 00000000000..1d73018c96d --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/BroadcastReceiver.java @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.content; + +import android.os.Bundle; +/** + * Base class for code that will receive intents sent by sendBroadcast(). + * + *

If you don't need to send broadcasts across applications, consider using + * this class with {@link android.support.v4.content.LocalBroadcastManager} instead + * of the more general facilities described below. This will give you a much + * more efficient implementation (no cross-process communication needed) and allow + * you to avoid thinking about any security issues related to other applications + * being able to receive or send your broadcasts. + * + *

You can either dynamically register an instance of this class with + * {@link Context#registerReceiver Context.registerReceiver()} + * or statically publish an implementation through the + * {@link android.R.styleable#AndroidManifestReceiver <receiver>} + * tag in your AndroidManifest.xml. + * + *

Note: + *    If registering a receiver in your + * {@link android.app.Activity#onResume() Activity.onResume()} + * implementation, you should unregister it in + * {@link android.app.Activity#onPause() Activity.onPause()}. + * (You won't receive intents when paused, + * and this will cut down on unnecessary system overhead). Do not unregister in + * {@link android.app.Activity#onSaveInstanceState(android.os.Bundle) Activity.onSaveInstanceState()}, + * because this won't be called if the user moves back in the history + * stack. + * + *

There are two major classes of broadcasts that can be received:

+ *
    + *
  • Normal broadcasts (sent with {@link Context#sendBroadcast(Intent) + * Context.sendBroadcast}) are completely asynchronous. All receivers of the + * broadcast are run in an undefined order, often at the same time. This is + * more efficient, but means that receivers cannot use the result or abort + * APIs included here. + *
  • Ordered broadcasts (sent with {@link Context#sendOrderedBroadcast(Intent, String) + * Context.sendOrderedBroadcast}) are delivered to one receiver at a time. + * As each receiver executes in turn, it can propagate a result to the next + * receiver, or it can completely abort the broadcast so that it won't be passed + * to other receivers. The order receivers run in can be controlled with the + * {@link android.R.styleable#AndroidManifestIntentFilter_priority + * android:priority} attribute of the matching intent-filter; receivers with + * the same priority will be run in an arbitrary order. + *
+ * + *

Even in the case of normal broadcasts, the system may in some + * situations revert to delivering the broadcast one receiver at a time. In + * particular, for receivers that may require the creation of a process, only + * one will be run at a time to avoid overloading the system with new processes. + * In this situation, however, the non-ordered semantics hold: these receivers still + * cannot return results or abort their broadcast.

+ * + *

Note that, although the Intent class is used for sending and receiving + * these broadcasts, the Intent broadcast mechanism here is completely separate + * from Intents that are used to start Activities with + * {@link Context#startActivity Context.startActivity()}. + * There is no way for a BroadcastReceiver + * to see or capture Intents used with startActivity(); likewise, when + * you broadcast an Intent, you will never find or start an Activity. + * These two operations are semantically very different: starting an + * Activity with an Intent is a foreground operation that modifies what the + * user is currently interacting with; broadcasting an Intent is a background + * operation that the user is not normally aware of. + * + *

The BroadcastReceiver class (when launched as a component through + * a manifest's {@link android.R.styleable#AndroidManifestReceiver <receiver>} + * tag) is an important part of an + * application's overall lifecycle.

+ * + *

Topics covered here: + *

    + *
  1. Security + *
  2. Receiver Lifecycle + *
  3. Process Lifecycle + *
+ * + *
+ *

Developer Guides

+ *

For information about how to use this class to receive and resolve intents, read the + * Intents and Intent Filters + * developer guide.

+ *
+ * + * + *

Security

+ * + *

Receivers used with the {@link Context} APIs are by their nature a + * cross-application facility, so you must consider how other applications + * may be able to abuse your use of them. Some things to consider are: + * + *

    + *
  • The Intent namespace is global. Make sure that Intent action names and + * other strings are written in a namespace you own, or else you may inadvertantly + * conflict with other applications. + *

  • When you use {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)}, + * any application may send broadcasts to that registered receiver. You can + * control who can send broadcasts to it through permissions described below. + *

  • When you publish a receiver in your application's manifest and specify + * intent-filters for it, any other application can send broadcasts to it regardless + * of the filters you specify. To prevent others from sending to it, make it + * unavailable to them with android:exported="false". + *

  • When you use {@link Context#sendBroadcast(Intent)} or related methods, + * normally any other application can receive these broadcasts. You can control who + * can receive such broadcasts through permissions described below. Alternatively, + * starting with {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, you + * can also safely restrict the broadcast to a single application with + * {@link Intent#setPackage(String) Intent.setPackage} + *

+ * + *

None of these issues exist when using + * {@link android.support.v4.content.LocalBroadcastManager}, since intents + * broadcast it never go outside of the current process. + * + *

Access permissions can be enforced by either the sender or receiver + * of a broadcast. + * + *

To enforce a permission when sending, you supply a non-null + * permission argument to + * {@link Context#sendBroadcast(Intent, String)} or + * {@link Context#sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler, int, String, Bundle)}. + * Only receivers who have been granted this permission + * (by requesting it with the + * {@link android.R.styleable#AndroidManifestUsesPermission <uses-permission>} + * tag in their AndroidManifest.xml) will be able to receive + * the broadcast. + * + *

To enforce a permission when receiving, you supply a non-null + * permission when registering your receiver -- either when calling + * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter, String, android.os.Handler)} + * or in the static + * {@link android.R.styleable#AndroidManifestReceiver <receiver>} + * tag in your AndroidManifest.xml. Only broadcasters who have + * been granted this permission (by requesting it with the + * {@link android.R.styleable#AndroidManifestUsesPermission <uses-permission>} + * tag in their AndroidManifest.xml) will be able to send an + * Intent to the receiver. + * + *

See the Security and Permissions + * document for more information on permissions and security in general. + * + * + *

Receiver Lifecycle

+ * + *

A BroadcastReceiver object is only valid for the duration of the call + * to {@link #onReceive}. Once your code returns from this function, + * the system considers the object to be finished and no longer active. + * + *

This has important repercussions to what you can do in an + * {@link #onReceive} implementation: anything that requires asynchronous + * operation is not available, because you will need to return from the + * function to handle the asynchronous operation, but at that point the + * BroadcastReceiver is no longer active and thus the system is free to kill + * its process before the asynchronous operation completes. + * + *

In particular, you may not show a dialog or bind to a service from + * within a BroadcastReceiver. For the former, you should instead use the + * {@link android.app.NotificationManager} API. For the latter, you can + * use {@link android.content.Context#startService Context.startService()} to + * send a command to the service. + * + * + *

Process Lifecycle

+ * + *

A process that is currently executing a BroadcastReceiver (that is, + * currently running the code in its {@link #onReceive} method) is + * considered to be a foreground process and will be kept running by the + * system except under cases of extreme memory pressure. + * + *

Once you return from onReceive(), the BroadcastReceiver is no longer + * active, and its hosting process is only as important as any other application + * components that are running in it. This is especially important because if + * that process was only hosting the BroadcastReceiver (a common case for + * applications that the user has never or not recently interacted with), then + * upon returning from onReceive() the system will consider its process + * to be empty and aggressively kill it so that resources are available for other + * more important processes. + * + *

This means that for longer-running operations you will often use + * a {@link android.app.Service} in conjunction with a BroadcastReceiver to keep + * the containing process active for the entire time of your operation. + */ +public abstract class BroadcastReceiver { + + /** + * State for a result that is pending for a broadcast receiver. Returned + * by {@link BroadcastReceiver#goAsync() goAsync()} + * while in {@link BroadcastReceiver#onReceive BroadcastReceiver.onReceive()}. + * This allows you to return from onReceive() without having the broadcast + * terminate; you must call {@link #finish()} once you are done with the + * broadcast. This allows you to process the broadcast off of the main + * thread of your app. + * + *

Note on threading: the state inside of this class is not itself + * thread-safe, however you can use it from any thread if you properly + * sure that you do not have races. Typically this means you will hand + * the entire object to another thread, which will be solely responsible + * for setting any results and finally calling {@link #finish()}. + */ + + public BroadcastReceiver() { + } + /** + * This method is called when the BroadcastReceiver is receiving an Intent + * broadcast. During this time you can use the other methods on + * BroadcastReceiver to view/modify the current result values. This method + * is always called within the main thread of its process, unless you + * explicitly asked for it to be scheduled on a different thread using + * {@link android.content.Context#registerReceiver(BroadcastReceiver, + * IntentFilter, String, android.os.Handler)}. When it runs on the main + * thread you should + * never perform long-running operations in it (there is a timeout of + * 10 seconds that the system allows before considering the receiver to + * be blocked and a candidate to be killed). You cannot launch a popup dialog + * in your implementation of onReceive(). + * + *

If this BroadcastReceiver was launched through a <receiver> tag, + * then the object is no longer alive after returning from this + * function. This means you should not perform any operations that + * return a result to you asynchronously -- in particular, for interacting + * with services, you should use + * {@link Context#startService(Intent)} instead of + * {@link Context#bindService(Intent, ServiceConnection, int)}. If you wish + * to interact with a service that is already running, you can use + * {@link #peekService}. + * + *

The Intent filters used in {@link android.content.Context#registerReceiver} + * and in application manifests are not guaranteed to be exclusive. They + * are hints to the operating system about how to find suitable recipients. It is + * possible for senders to force delivery to specific recipients, bypassing filter + * resolution. For this reason, {@link #onReceive(Context, Intent) onReceive()} + * implementations should respond only to known actions, ignoring any unexpected + * Intents that they may receive. + * + * @param context The Context in which the receiver is running. + * @param intent The Intent being received. + */ + public abstract void onReceive(Context context, Intent intent); +} \ No newline at end of file diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java b/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java index 8c528a58e41..e1ce2140a06 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java @@ -813,4 +813,51 @@ public abstract class Context { * @hide */ public abstract void sendBroadcast(Intent intent, String receiverPermission, int appOp); + + /** + * Broadcast the given intent to all interested BroadcastReceivers, allowing + * an array of required permissions to be enforced. This call is asynchronous; it returns + * immediately, and you will continue executing while the receivers are run. No results are + * propagated from receivers and receivers can not abort the broadcast. If you want to allow + * receivers to propagate results or abort the broadcast, you must send an ordered broadcast + * using {@link #sendOrderedBroadcast(Intent, String)}. + * + *

See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param receiverPermissions Array of names of permissions that a receiver must hold + * in order to receive your broadcast. + * If empty, no permissions are required. + * + * @see android.content.BroadcastReceiver + * @see #registerReceiver + * @see #sendBroadcast(Intent) + * @see #sendOrderedBroadcast(Intent, String) + * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) + * @hide + */ + public abstract void sendBroadcastWithMultiplePermissions (Intent intent, String[] receiverPermissions); + + /** + * Broadcast the given intent to all interested BroadcastReceivers, delivering + * them one at a time to allow more preferred receivers to consume the + * broadcast before it is delivered to less preferred receivers. This + * call is asynchronous; it returns immediately, and you will continue + * executing while the receivers are run. + * + *

See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param receiverPermission (optional) String naming a permissions that + * a receiver must hold in order to receive your broadcast. + * If null, no permission is required. + * + * @see android.content.BroadcastReceiver + * @see #registerReceiver + * @see #sendBroadcast(Intent) + * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) + */ + public abstract void sendOrderedBroadcast(Intent intent, String receiverPermission); } \ No newline at end of file diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java b/java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java index e3ef6277dba..292069c0f71 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java @@ -1996,4 +1996,77 @@ public class Intent implements Parcelable, Cloneable { public void readFromParcel(Parcel in) { } + + /** + * Retrieve the application package name this Intent is limited to. When + * resolving an Intent, if non-null this limits the resolution to only + * components in the given application package. + * + * @return The name of the application package for the Intent. + * + * @see #resolveActivity + * @see #setPackage + */ + public String getPackage() { + return null; + } + + /** + * (Usually optional) Set an explicit application package name that limits + * the components this Intent will resolve to. If left to the default + * value of null, all components in all applications will considered. + * If non-null, the Intent can only match the components in the given + * application package. + * + * @param packageName The name of the application package to handle the + * intent, or null to allow any application package. + * + * @return Returns the same Intent object, for chaining multiple calls + * into a single statement. + * + * @see #getPackage + * @see #resolveActivity + */ + public Intent setPackage(String packageName) { + return null; + } + + /** + * Convenience for calling {@link #setComponent} with an + * explicit class name. + * + * @param packageContext A Context of the application package implementing + * this class. + * @param className The name of a class inside of the application package + * that will be used as the component for this Intent. + * + * @return Returns the same Intent object, for chaining multiple calls + * into a single statement. + * + * @see #setComponent + * @see #setClass + */ + public Intent setClassName(Context packageContext, String className) { + return null; + } + + /** + * Convenience for calling {@link #setComponent} with an + * explicit application package name and class name. + * + * @param packageName The name of the package implementing the desired + * component. + * @param className The name of a class inside of the application package + * that will be used as the component for this Intent. + * + * @return Returns the same Intent object, for chaining multiple calls + * into a single statement. + * + * @see #setComponent + * @see #setClass + */ + public Intent setClassName(String packageName, String className) { + return null; + } + } \ No newline at end of file diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/BaseBundle.java b/java/ql/test/stubs/google-android-9.0.0/android/os/BaseBundle.java index 617ec97ae8c..4b85ae8e67a 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/os/BaseBundle.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/BaseBundle.java @@ -129,6 +129,160 @@ public class BaseBundle { return false; } + /** + * Returns true if the given key is contained in the mapping + * of this Bundle. + * + * @param key a String key + * @return true if the key is part of the mapping, false otherwise + */ + public boolean containsKey(String key) { + return false; + } + + /** + * Returns the entry with the given key as an object. + * + * @param key a String key + * @return an Object, or null + */ + public Object get(String key) { + return null; + } + + /** + * Removes any entry with the given key from the mapping of this Bundle. + * + * @param key a String key + */ + public void remove(String key) { + } + + /** {@hide} */ + public void putObject(String key, Object value) { + } + + /** + * Inserts a Boolean value into the mapping of this Bundle, replacing + * any existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a boolean + */ + public void putBoolean(String key, boolean value) { + } + + /** + * Inserts a byte value into the mapping of this Bundle, replacing + * any existing value for the given key. + * + * @param key a String, or null + * @param value a byte + */ + void putByte(String key, byte value) { + } + + /** + * Inserts a char value into the mapping of this Bundle, replacing + * any existing value for the given key. + * + * @param key a String, or null + * @param value a char + */ + void putChar(String key, char value) { + } + + /** + * Inserts a short value into the mapping of this Bundle, replacing + * any existing value for the given key. + * + * @param key a String, or null + * @param value a short + */ + void putShort(String key, short value) { + } + + /** + * Inserts an int value into the mapping of this Bundle, replacing + * any existing value for the given key. + * + * @param key a String, or null + * @param value an int + */ + public void putInt(String key, int value) { + } + + /** + * Inserts a long value into the mapping of this Bundle, replacing + * any existing value for the given key. + * + * @param key a String, or null + * @param value a long + */ + public void putLong(String key, long value) { + } + + /** + * Inserts a float value into the mapping of this Bundle, replacing + * any existing value for the given key. + * + * @param key a String, or null + * @param value a float + */ + void putFloat(String key, float value) { + } + + /** + * Inserts a double value into the mapping of this Bundle, replacing + * any existing value for the given key. + * + * @param key a String, or null + * @param value a double + */ + public void putDouble(String key, double value) { + } + + /** + * Inserts a String value into the mapping of this Bundle, replacing + * any existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a String, or null + */ + public void putString(String key, String value) { + } + + /** + * Inserts a CharSequence value into the mapping of this Bundle, replacing + * any existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a CharSequence, or null + */ + void putCharSequence(String key, CharSequence value) { + } + + /** + * Inserts an ArrayList value into the mapping of this Bundle, replacing + * any existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value an ArrayList object, or null + */ + void putIntegerArrayList(String key, ArrayList value) { + } + + /** + * Inserts an ArrayList value into the mapping of this Bundle, replacing + * any existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value an ArrayList object, or null + */ + void putStringArrayList(String key, ArrayList value) { + } + + /** * Inserts an ArrayList value into the mapping of this Bundle, * replacing any existing value for the given key. Either key or value may be