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 f92aedbc038..d167b62f790 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 @@ -323,15 +324,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.getReceiverType() ) 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, AndroidIntentInput { + ExportedAndroidIntentInput() { receiverType.(ExportableAndroidComponent).isExported() } + + 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 da500afbe6e..df543e4f11f 100644 --- a/java/ql/src/semmle/code/java/frameworks/android/Android.qll +++ b/java/ql/src/semmle/code/java/frameworks/android/Android.qll @@ -30,25 +30,42 @@ class AndroidComponent extends Class { predicate hasIntentFilter() { exists(getAndroidComponentXmlElement().getAnIntentFilterElement()) } } +/** + * 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. + */ + override predicate isExported() { + getAndroidComponentXmlElement().isExported() + or + hasIntentFilter() and + not getAndroidComponentXmlElement().isNotExported() + } +} + /** An Android activity. */ -class AndroidActivity extends AndroidComponent { +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") } } /** An Android broadcast receiver. */ -class AndroidBroadcastReceiver extends AndroidComponent { +class AndroidBroadcastReceiver extends ExportableAndroidComponent { AndroidBroadcastReceiver() { this.getASupertype*().hasQualifiedName("android.content", "BroadcastReceiver") } } /** An Android content provider. */ -class AndroidContentProvider extends AndroidComponent { +class AndroidContentProvider extends ExportableAndroidComponent { AndroidContentProvider() { this.getASupertype*().hasQualifiedName("android.content", "ContentProvider") } 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..92c49f3101a 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,13 @@ class IntentGetExtraMethod extends Method, TaintPreservingCallable { override predicate returnsTaintFrom(int arg) { arg = -1 } } + +/** 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%") + } + + override predicate returnsTaintFrom(int arg) { arg = -1 } +} 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" } } /** 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..4a4cb09c8f7 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity4.java @@ -0,0 +1,38 @@ +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 + * 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); + + 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 diff --git a/java/ql/test/library-tests/dataflow/taintsources/AndroidManifest.xml b/java/ql/test/library-tests/dataflow/taintsources/AndroidManifest.xml new file mode 100644 index 00000000000..f9fe7eff10a --- /dev/null +++ b/java/ql/test/library-tests/dataflow/taintsources/AndroidManifest.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java b/java/ql/test/library-tests/dataflow/taintsources/IntentSources.java new file mode 100644 index 00000000000..960bbff0bce --- /dev/null +++ b/java/ql/test/library-tests/dataflow/taintsources/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/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 | 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:

+ * + * + *

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: + * + *

+ * + *

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