diff --git a/java/change-notes/2021-05-06-unsafe-android-access-query.md b/java/change-notes/2021-05-06-unsafe-android-access-query.md
new file mode 100644
index 00000000000..4425d32816b
--- /dev/null
+++ b/java/change-notes/2021-05-06-unsafe-android-access-query.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* The query "Unsafe resource fetching in Android WebView" (`java/android/unsafe-android-webview-fetch`) has been promoted from experimental to the main query pack. Its results will now appear by default. This query was originally [submitted as an experimental query by @luchua-bc](https://github.com/github/codeql/pull/3706).
diff --git a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.java b/java/ql/src/Security/CWE/CWE-749/UnsafeAndroidAccess.java
similarity index 100%
rename from java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.java
rename to java/ql/src/Security/CWE/CWE-749/UnsafeAndroidAccess.java
diff --git a/java/ql/src/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp b/java/ql/src/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp
new file mode 100644
index 00000000000..88756909e4b
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp
@@ -0,0 +1,35 @@
+
+
+
+
+ Android WebViews that allow externally controlled URLs to be loaded, and whose JavaScript interface is enabled, are potentially vulnerable to cross-site scripting and sensitive resource disclosure attacks.
+ A WebView whose WebSettings object has called setAllowFileAccessFromFileURLs(true) or setAllowUniversalAccessFromFileURLs(true) must not load any untrusted web content.
+ Enabling these settings allows malicious scripts loaded in a file:// context to launch cross-site scripting attacks, accessing arbitrary local files including WebView cookies, session tokens, private app data or even credentials used on arbitrary web sites.
+ This query detects the following two scenarios:
+
+ - A vulnerability introduced by WebViews when JavaScript is enabled and remote inputs are allowed.
+ - A more severe vulnerability when "allow cross-origin resource access" is also enabled. This setting was deprecated in API level 30 (Android 11), but most devices are still affected, especially since some Android phones are updated slowly or no longer updated at all.
+
+
+
+
+ Only allow trusted web content to be displayed in WebViews when JavaScript is enabled. Disallow cross-origin resource access in WebSettings to reduce the attack surface.
+
+
+
+ The following example shows both 'BAD' and 'GOOD' configurations. In the 'BAD' configuration, JavaScript and the allow access setting are enabled and URLs are loaded from externally controlled inputs. In the 'GOOD' configuration, JavaScript is disabled or only trusted web content is allowed to be loaded.
+
+
+
+
+
+ Google Help: Fixing a File-based XSS Vulnerability
+
+
+ OWASP: Testing JavaScript Execution in WebViews (MSTG-PLATFORM-5)
+
+
+ OWASP: Testing WebView Protocol Handlers (MSTG-PLATFORM-6)
+
+
+
diff --git a/java/ql/src/Security/CWE/CWE-749/UnsafeAndroidAccess.ql b/java/ql/src/Security/CWE/CWE-749/UnsafeAndroidAccess.ql
new file mode 100644
index 00000000000..9a58678367c
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-749/UnsafeAndroidAccess.ql
@@ -0,0 +1,21 @@
+/**
+ * @name Unsafe resource fetching in Android WebView
+ * @description JavaScript rendered inside WebViews can access protected
+ * application files and web resources from any origin exposing them to attack.
+ * @kind path-problem
+ * @problem.severity warning
+ * @precision medium
+ * @id java/android/unsafe-android-webview-fetch
+ * @tags security
+ * external/cwe/cwe-749
+ * external/cwe/cwe-079
+ */
+
+import java
+import semmle.code.java.security.UnsafeAndroidAccessQuery
+import DataFlow::PathGraph
+
+from DataFlow::PathNode source, DataFlow::PathNode sink, FetchUntrustedResourceConfiguration conf
+where conf.hasFlowPath(source, sink)
+select sink.getNode(), source, sink, "Unsafe resource fetching in Android WebView due to $@.",
+ source.getNode(), sink.getNode().(UrlResourceSink).getSinkType()
diff --git a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp
deleted file mode 100644
index 3ac8ec67e59..00000000000
--- a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
- Android WebViews that allow loading URLs controlled by external inputs and whose JavaScript interface is enabled are potentially vulnerable to cross-site scripting and sensitive resource disclosure attacks.
- A WebView whose WebSettings object has setAllowFileAccessFromFileURLs(true) or setAllowUniversalAccessFromFileURLs(true) called must not load any untrusted web content.
- Enabling these settings allows malicious scripts loaded in a file:// context to launch cross-site scripting attacks, either accessing arbitrary local files including WebView cookies, session tokens, private app data or even credentials used on arbitrary web sites.
- This query detects the following two scenarios:
-
- - Vulnerability introduced by WebViews with JavaScript enabled and remote inputs allowed.
- - A more severe vulnerability when allowing cross-origin resource access is also enabled. The setting was deprecated in API level 30 (Android 11), but most devices are still affected, especially given that some Android phones are updated slowly or no longer updated at all.
-
-
-
-
- Only allow trusted web content to be displayed in WebViews when JavaScript is enabled. Disallow cross-origin resource access in WebSetting to reduce the attack surface.
-
-
-
- The following example shows both 'BAD' and 'GOOD' configurations. In the 'BAD' configuration, setting is enabled and JavaScript is enabled while URLs are loaded from externally controlled inputs. In the 'GOOD' configuration, JavaScript is disabled or only trusted web content is allowed to be loaded.
-
-
-
-
-
- CWE-749
- Fixing a File-based XSS Vulnerability
- OWASP - Testing WebView Protocol Handlers (MSTG-PLATFORM-5 and MSTG-PLATFORM-6)
-
-
-
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql
deleted file mode 100644
index 24755e64f13..00000000000
--- a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql
+++ /dev/null
@@ -1,130 +0,0 @@
-/**
- * @name Unsafe resource fetching in Android webview
- * @description JavaScript rendered inside WebViews can access any protected
- * application file and web resource from any origin
- * @kind path-problem
- * @problem.severity warning
- * @precision medium
- * @id java/android/unsafe-android-webview-fetch
- * @tags security
- * external/cwe/cwe-749
- * external/cwe/cwe-079
- */
-
-import java
-import semmle.code.java.frameworks.android.Intent
-import semmle.code.java.frameworks.android.WebView
-import semmle.code.java.dataflow.FlowSources
-import DataFlow::PathGraph
-
-/**
- * Methods allowing any-local-file and cross-origin access in the WebSettings class
- */
-class CrossOriginAccessMethod extends Method {
- CrossOriginAccessMethod() {
- this.getDeclaringType() instanceof TypeWebSettings and
- (
- this.hasName("setAllowUniversalAccessFromFileURLs") or
- this.hasName("setAllowFileAccessFromFileURLs")
- )
- }
-}
-
-/**
- * `setJavaScriptEnabled` method for the webview
- */
-class AllowJavaScriptMethod extends Method {
- AllowJavaScriptMethod() {
- this.getDeclaringType() instanceof TypeWebSettings and
- this.hasName("setJavaScriptEnabled")
- }
-}
-
-/**
- * Holds if a call to `v.setJavaScriptEnabled(true)` exists
- */
-predicate isJSEnabled(Variable v) {
- exists(MethodAccess jsa |
- v.getAnAccess() = jsa.getQualifier() and
- jsa.getMethod() instanceof AllowJavaScriptMethod and
- jsa.getArgument(0).(BooleanLiteral).getBooleanValue() = true
- )
-}
-
-/**
- * Fetch URL method call on the `android.webkit.WebView` object
- */
-class FetchResourceMethodAccess extends MethodAccess {
- FetchResourceMethodAccess() {
- this.getMethod().getDeclaringType() instanceof TypeWebView and
- this.getMethod().hasName(["loadUrl", "postUrl"])
- }
-}
-
-/**
- * Holds if `ma` loads URL `sink`
- */
-predicate fetchResource(FetchResourceMethodAccess ma, Expr sink) { sink = ma.getArgument(0) }
-
-/**
- * A URL argument to a `loadUrl` or `postUrl` call, considered as a sink.
- */
-class UrlResourceSink extends DataFlow::ExprNode {
- UrlResourceSink() { fetchResource(_, this.getExpr()) }
-
- /** Gets the fetch method that fetches this sink URL. */
- FetchResourceMethodAccess getMethodAccess() { fetchResource(result, this.getExpr()) }
-
- /**
- * Holds if cross-origin access is enabled for this resource fetch.
- *
- * Specifically this looks for code like
- * `webView.getSettings().setAllow[File|Universal]AccessFromFileURLs(true);`
- */
- predicate crossOriginAccessEnabled() {
- exists(MethodAccess ma, MethodAccess getSettingsMa |
- ma.getMethod() instanceof CrossOriginAccessMethod and
- ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true and
- ma.getQualifier().(VarAccess).getVariable().getAnAssignedValue() = getSettingsMa and
- getSettingsMa.getMethod() instanceof WebViewGetSettingsMethod and
- getSettingsMa.getQualifier().(VarAccess).getVariable().getAnAccess() =
- getMethodAccess().getQualifier()
- )
- }
-
- /**
- * Returns a description of this vulnerability, assuming Javascript is enabled and
- * the fetched URL is attacker-controlled.
- */
- string getSinkType() {
- if crossOriginAccessEnabled()
- then result = "user input vulnerable to cross-origin and sensitive resource disclosure attacks"
- else result = "user input vulnerable to XSS attacks"
- }
-}
-
-/**
- * Taint configuration tracking flow from untrusted inputs to `loadUrl` or `postUrl` calls.
- */
-class FetchUntrustedResourceConfiguration extends TaintTracking::Configuration {
- FetchUntrustedResourceConfiguration() { this = "FetchUntrustedResourceConfiguration" }
-
- override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
-
- override predicate isSink(DataFlow::Node sink) {
- sink instanceof UrlResourceSink and
- exists(VarAccess webviewVa, MethodAccess getSettingsMa, Variable v |
- sink.(UrlResourceSink).getMethodAccess().getQualifier() = webviewVa and
- getSettingsMa.getMethod() instanceof WebViewGetSettingsMethod and
- webviewVa.getVariable().getAnAccess() = getSettingsMa.getQualifier() and
- v.getAnAssignedValue() = getSettingsMa and
- isJSEnabled(v)
- )
- }
-}
-
-from DataFlow::PathNode source, DataFlow::PathNode sink, FetchUntrustedResourceConfiguration conf
-where conf.hasFlowPath(source, sink)
-select sink.getNode().(UrlResourceSink).getMethodAccess(), source, sink,
- "Unsafe resource fetching in Android webview due to $@.", source.getNode(),
- sink.getNode().(UrlResourceSink).getSinkType()
diff --git a/java/ql/src/semmle/code/java/frameworks/android/XssSinks.qll b/java/ql/src/semmle/code/java/frameworks/android/XssSinks.qll
index 720e936b844..b6d590e95b3 100644
--- a/java/ql/src/semmle/code/java/frameworks/android/XssSinks.qll
+++ b/java/ql/src/semmle/code/java/frameworks/android/XssSinks.qll
@@ -9,8 +9,8 @@ private class DefaultXssSinkModel extends SinkModelCsv {
row =
[
"android.webkit;WebView;false;loadData;;;Argument[0];xss",
- "android.webkit;WebView;false;loadUrl;;;Argument[0];xss",
- "android.webkit;WebView;false;loadDataWithBaseURL;;;Argument[1];xss"
+ "android.webkit;WebView;false;loadDataWithBaseURL;;;Argument[1];xss",
+ "android.webkit;WebView;false;evaluateJavascript;;;Argument[0];xss"
]
}
}
diff --git a/java/ql/src/semmle/code/java/security/UnsafeAndroidAccess.qll b/java/ql/src/semmle/code/java/security/UnsafeAndroidAccess.qll
new file mode 100644
index 00000000000..d25f5c4a05c
--- /dev/null
+++ b/java/ql/src/semmle/code/java/security/UnsafeAndroidAccess.qll
@@ -0,0 +1,100 @@
+/**
+ * Provides classes to reason about Unsafe Resource Fetching vulnerabilities in Android.
+ */
+
+import java
+private import semmle.code.java.frameworks.android.WebView
+private import semmle.code.java.dataflow.DataFlow
+private import semmle.code.java.dataflow.ExternalFlow
+
+/**
+ * A sink that represents a method that fetches a web resource in Android.
+ *
+ * Extend this class to add your own Unsafe Resource Fetching sinks.
+ */
+abstract class UrlResourceSink extends DataFlow::Node {
+ /**
+ * Gets a description of this vulnerability.
+ */
+ abstract string getSinkType();
+}
+
+/**
+ * A cross-origin access enabled resource fetch.
+ *
+ * Only considered a valid sink when JavaScript is also enabled.
+ */
+private class CrossOriginUrlResourceSink extends JavaScriptEnabledUrlResourceSink {
+ CrossOriginUrlResourceSink() {
+ exists(Variable settings, MethodAccess ma |
+ webViewLoadUrl(this.asExpr(), settings) and
+ ma.getMethod() instanceof CrossOriginAccessMethod and
+ ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true and
+ ma.getQualifier() = settings.getAnAccess()
+ )
+ }
+
+ override string getSinkType() {
+ result = "user input vulnerable to cross-origin and sensitive resource disclosure attacks"
+ }
+}
+
+/**
+ * JavaScript enabled resource fetch.
+ */
+private class JavaScriptEnabledUrlResourceSink extends UrlResourceSink {
+ JavaScriptEnabledUrlResourceSink() {
+ exists(Variable settings |
+ webViewLoadUrl(this.asExpr(), settings) and
+ isJSEnabled(settings)
+ )
+ }
+
+ override string getSinkType() { result = "user input vulnerable to XSS attacks" }
+}
+
+/**
+ * Holds if a `WebViewLoadUrlMethod` method is called with the given `urlArg` on a
+ * WebView with settings stored in `settings`.
+ */
+private predicate webViewLoadUrl(Expr urlArg, Variable settings) {
+ exists(MethodAccess loadUrl, Variable webview, MethodAccess getSettings |
+ loadUrl.getArgument(0) = urlArg and
+ loadUrl.getMethod() instanceof WebViewLoadUrlMethod and
+ loadUrl.getQualifier() = webview.getAnAccess() and
+ getSettings.getMethod() instanceof WebViewGetSettingsMethod and
+ webview.getAnAccess() = getSettings.getQualifier() and
+ settings.getAnAssignedValue() = getSettings
+ )
+}
+
+/**
+ * A method allowing any-local-file and cross-origin access in the WebSettings class.
+ */
+private class CrossOriginAccessMethod extends Method {
+ CrossOriginAccessMethod() {
+ this.getDeclaringType() instanceof TypeWebSettings and
+ this.hasName(["setAllowUniversalAccessFromFileURLs", "setAllowFileAccessFromFileURLs"])
+ }
+}
+
+/**
+ * The `setJavaScriptEnabled` method for the webview.
+ */
+private class AllowJavaScriptMethod extends Method {
+ AllowJavaScriptMethod() {
+ this.getDeclaringType() instanceof TypeWebSettings and
+ this.hasName("setJavaScriptEnabled")
+ }
+}
+
+/**
+ * Holds if a call to `v.setJavaScriptEnabled(true)` exists.
+ */
+private predicate isJSEnabled(Variable v) {
+ exists(MethodAccess jsa |
+ v.getAnAccess() = jsa.getQualifier() and
+ jsa.getMethod() instanceof AllowJavaScriptMethod and
+ jsa.getArgument(0).(BooleanLiteral).getBooleanValue() = true
+ )
+}
diff --git a/java/ql/src/semmle/code/java/security/UnsafeAndroidAccessQuery.qll b/java/ql/src/semmle/code/java/security/UnsafeAndroidAccessQuery.qll
new file mode 100644
index 00000000000..6bd9833047d
--- /dev/null
+++ b/java/ql/src/semmle/code/java/security/UnsafeAndroidAccessQuery.qll
@@ -0,0 +1,22 @@
+/** Provides taint tracking configurations to be used in Unsafe Resource Fetching queries. */
+
+import java
+import semmle.code.java.dataflow.FlowSources
+import semmle.code.java.dataflow.TaintTracking
+import semmle.code.java.security.RequestForgery
+import semmle.code.java.security.UnsafeAndroidAccess
+
+/**
+ * A taint configuration tracking flow from untrusted inputs to a resource fetching call.
+ */
+class FetchUntrustedResourceConfiguration extends TaintTracking::Configuration {
+ FetchUntrustedResourceConfiguration() { this = "FetchUntrustedResourceConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof UrlResourceSink }
+
+ override predicate isSanitizer(DataFlow::Node sanitizer) {
+ sanitizer instanceof RequestForgerySanitizer
+ }
+}
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
deleted file mode 100644
index 9e2b153dd64..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected
+++ /dev/null
@@ -1,31 +0,0 @@
-edges
-| 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
-| 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
-| 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.qlref b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.qlref
deleted file mode 100644
index abaed120e99..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.qlref
+++ /dev/null
@@ -1 +0,0 @@
-experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/options b/java/ql/test/experimental/query-tests/security/CWE-749/options
deleted file mode 100644
index 43e25f608b6..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-749/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-749/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-749/AndroidManifest.xml
similarity index 100%
rename from java/ql/test/experimental/query-tests/security/CWE-749/AndroidManifest.xml
rename to java/ql/test/query-tests/security/CWE-749/AndroidManifest.xml
diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/IntentUtils.java b/java/ql/test/query-tests/security/CWE-749/IntentUtils.java
similarity index 100%
rename from java/ql/test/experimental/query-tests/security/CWE-749/IntentUtils.java
rename to java/ql/test/query-tests/security/CWE-749/IntentUtils.java
diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity1.java b/java/ql/test/query-tests/security/CWE-749/SafeActivity1.java
similarity index 75%
rename from java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity1.java
rename to java/ql/test/query-tests/security/CWE-749/SafeActivity1.java
index 9af651031cb..18759fa8cd1 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity1.java
+++ b/java/ql/test/query-tests/security/CWE-749/SafeActivity1.java
@@ -9,7 +9,9 @@ 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
+ // Test onCreate with both JavaScript and cross-origin resource access enabled while taking
+ // remote user inputs from bundle extras.
+ // The Activity is explicitly not exported, even though it has an intent-filter.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
@@ -29,6 +31,6 @@ public class SafeActivity1 extends Activity {
});
String thisUrl = getIntent().getExtras().getString("url");
- wv.loadUrl(thisUrl);
+ wv.loadUrl(thisUrl); // Safe
}
-}
\ No newline at end of file
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity2.java b/java/ql/test/query-tests/security/CWE-749/SafeActivity2.java
similarity index 78%
rename from java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity2.java
rename to java/ql/test/query-tests/security/CWE-749/SafeActivity2.java
index b8959c657ca..be4d262e527 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity2.java
+++ b/java/ql/test/query-tests/security/CWE-749/SafeActivity2.java
@@ -9,7 +9,9 @@ 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
+ // Test onCreate with both JavaScript and cross-origin resource access enabled while taking
+ // remote user inputs from bundle extras.
+ // The Activity is explicitly not exported.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
@@ -29,6 +31,6 @@ public class SafeActivity2 extends Activity {
});
String thisUrl = getIntent().getExtras().getString("url");
- wv.loadUrl(thisUrl);
+ wv.loadUrl(thisUrl); // Safe
}
-}
\ No newline at end of file
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity3.java b/java/ql/test/query-tests/security/CWE-749/SafeActivity3.java
similarity index 78%
rename from java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity3.java
rename to java/ql/test/query-tests/security/CWE-749/SafeActivity3.java
index 29ba013affb..8f4ed73cd4b 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-749/SafeActivity3.java
+++ b/java/ql/test/query-tests/security/CWE-749/SafeActivity3.java
@@ -9,7 +9,9 @@ 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
+ // Test onCreate with both JavaScript and cross-origin resource access enabled while taking
+ // remote user inputs from bundle extras.
+ // The Activity is implicitly not exported.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
@@ -29,6 +31,6 @@ public class SafeActivity3 extends Activity {
});
String thisUrl = getIntent().getExtras().getString("url");
- wv.loadUrl(thisUrl);
+ wv.loadUrl(thisUrl); // Safe
}
-}
\ No newline at end of file
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity1.java b/java/ql/test/query-tests/security/CWE-749/UnsafeActivity1.java
similarity index 76%
rename from java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity1.java
rename to java/ql/test/query-tests/security/CWE-749/UnsafeActivity1.java
index 3f97d7501b2..a7092839219 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity1.java
+++ b/java/ql/test/query-tests/security/CWE-749/UnsafeActivity1.java
@@ -9,7 +9,9 @@ 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
+ // Test onCreate with both JavaScript and cross-origin resource access enabled while taking
+ // remote user inputs from bundle extras.
+ // The Activity is exported and has an intent-filter.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
@@ -29,6 +31,6 @@ public class UnsafeActivity1 extends Activity {
});
String thisUrl = getIntent().getExtras().getString("url");
- wv.loadUrl(thisUrl);
+ wv.loadUrl(thisUrl); // $hasUnsafeAndroidAccess
}
-}
\ No newline at end of file
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity2.java b/java/ql/test/query-tests/security/CWE-749/UnsafeActivity2.java
similarity index 74%
rename from java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity2.java
rename to java/ql/test/query-tests/security/CWE-749/UnsafeActivity2.java
index c5b15c2ebba..5a70ba3d50b 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity2.java
+++ b/java/ql/test/query-tests/security/CWE-749/UnsafeActivity2.java
@@ -9,7 +9,9 @@ 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
+ // Test onCreate with both JavaScript and cross-origin resource access enabled while taking
+ // remote user inputs from bundle extras.
+ // The Activity is implicitly exported because it has an intent-filter.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
@@ -29,6 +31,6 @@ public class UnsafeActivity2 extends Activity {
});
String thisUrl = getIntent().getExtras().getString("url");
- wv.loadUrl(thisUrl);
+ wv.loadUrl(thisUrl); // $hasUnsafeAndroidAccess
}
-}
\ No newline at end of file
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity3.java b/java/ql/test/query-tests/security/CWE-749/UnsafeActivity3.java
similarity index 77%
rename from java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity3.java
rename to java/ql/test/query-tests/security/CWE-749/UnsafeActivity3.java
index 0fe6c342b5a..f7f88087d8f 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity3.java
+++ b/java/ql/test/query-tests/security/CWE-749/UnsafeActivity3.java
@@ -9,7 +9,9 @@ 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
+ // Test onCreate with both JavaScript and cross-origin resource access enabled while taking
+ // remote user inputs from bundle extras.
+ // The Activity is explicitly exported.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
@@ -29,6 +31,6 @@ public class UnsafeActivity3 extends Activity {
});
String thisUrl = getIntent().getExtras().getString("url");
- wv.loadUrl(thisUrl);
+ wv.loadUrl(thisUrl); // $hasUnsafeAndroidAccess
}
-}
\ No newline at end of file
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity4.java b/java/ql/test/query-tests/security/CWE-749/UnsafeActivity4.java
similarity index 69%
rename from java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity4.java
rename to java/ql/test/query-tests/security/CWE-749/UnsafeActivity4.java
index 4a4cb09c8f7..992e753f38d 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeActivity4.java
+++ b/java/ql/test/query-tests/security/CWE-749/UnsafeActivity4.java
@@ -9,9 +9,15 @@ 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.
+ /*
+ * Test onCreate with both JavaScript and cross-origin resource access enabled while taking
+ * remote user inputs from bundle extras.
+ *
+ * The Activity is explicitly exported.
+ *
+ * Note this case of invoking a utility method that takes an Activity and 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);
@@ -33,6 +39,6 @@ public class UnsafeActivity4 extends Activity {
String thisUrl = IntentUtils.getIntentUrl(this);
thisUrl = IntentUtils.getBundleUrl(this);
- wv.loadUrl(thisUrl);
+ wv.loadUrl(thisUrl); // $ MISSING: hasUnsafeAndroidAccess
}
-}
\ No newline at end of file
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.java b/java/ql/test/query-tests/security/CWE-749/UnsafeAndroidAccess.java
similarity index 61%
rename from java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.java
rename to java/ql/test/query-tests/security/CWE-749/UnsafeAndroidAccess.java
index ddddafd95d1..95dbc326d3d 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.java
+++ b/java/ql/test/query-tests/security/CWE-749/UnsafeAndroidAccess.java
@@ -8,8 +8,10 @@ import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
+// The Activity is implicitly exported because it has an intent-filter.
public class UnsafeAndroidAccess 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
public void testOnCreate1(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
@@ -29,10 +31,11 @@ public class UnsafeAndroidAccess extends Activity {
});
String thisUrl = getIntent().getExtras().getString("url");
- wv.loadUrl(thisUrl);
+ wv.loadUrl(thisUrl); // $hasUnsafeAndroidAccess
}
- //Test onCreate with both JavaScript and cross-origin resource access enabled while taking remote user inputs from string extra
+ // Test onCreate with both JavaScript and cross-origin resource access enabled while taking
+ // remote user inputs from string extra
public void testOnCreate2(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
@@ -52,10 +55,11 @@ public class UnsafeAndroidAccess extends Activity {
});
String thisUrl = getIntent().getStringExtra("url");
- wv.loadUrl(thisUrl);
+ wv.loadUrl(thisUrl); // $hasUnsafeAndroidAccess
}
- //Test onCreate with both JavaScript and cross-origin resource access disabled by default while taking remote user inputs
+ // Test onCreate with both JavaScript and cross-origin resource access disabled by default while
+ // taking remote user inputs
public void testOnCreate3(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
@@ -72,10 +76,11 @@ public class UnsafeAndroidAccess extends Activity {
});
String thisUrl = getIntent().getStringExtra("url");
- wv.loadUrl(thisUrl);
+ wv.loadUrl(thisUrl); // Safe
}
- //Test onCreate with JavaScript enabled but cross-origin resource access disabled while taking remote user inputs
+ // Test onCreate with JavaScript enabled but cross-origin resource access disabled while taking
+ // remote user inputs
public void testOnCreate4(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
@@ -94,10 +99,11 @@ public class UnsafeAndroidAccess extends Activity {
});
String thisUrl = getIntent().getStringExtra("url");
- wv.loadUrl(thisUrl);
+ wv.loadUrl(thisUrl); // $hasUnsafeAndroidAccess
}
- //Test onCreate with both JavaScript and cross-origin resource access enabled while not taking remote user inputs
+ // Test onCreate with both JavaScript and cross-origin resource access enabled while not taking
+ // remote user inputs
public void testOnCreate5(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
@@ -116,6 +122,30 @@ public class UnsafeAndroidAccess extends Activity {
}
});
- wv.loadUrl("https://www.mycorp.com");
+ wv.loadUrl("https://www.mycorp.com"); // Safe
}
-}
\ No newline at end of file
+
+ // Test onCreate with both JavaScript and cross-origin resource access enabled while taking
+ // remote user inputs and concatenating them to a safe URL.
+ public void testOnCreate6(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().getStringExtra("url");
+ wv.loadUrl("https://www.mycorp.com/" + thisUrl); // Safe
+ }
+}
diff --git a/java/ql/test/query-tests/security/CWE-749/UnsafeAndroidAccessTest.expected b/java/ql/test/query-tests/security/CWE-749/UnsafeAndroidAccessTest.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/java/ql/test/query-tests/security/CWE-749/UnsafeAndroidAccessTest.ql b/java/ql/test/query-tests/security/CWE-749/UnsafeAndroidAccessTest.ql
new file mode 100644
index 00000000000..14f72c5e88c
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-749/UnsafeAndroidAccessTest.ql
@@ -0,0 +1,20 @@
+import java
+import semmle.code.java.security.UnsafeAndroidAccessQuery
+import TestUtilities.InlineExpectationsTest
+
+class UnsafeAndroidAccessTest extends InlineExpectationsTest {
+ UnsafeAndroidAccessTest() { this = "HasUnsafeAndroidAccess" }
+
+ override string getARelevantTag() { result = "hasUnsafeAndroidAccess" }
+
+ override predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "hasUnsafeAndroidAccess" and
+ exists(DataFlow::Node src, DataFlow::Node sink, FetchUntrustedResourceConfiguration conf |
+ conf.hasFlow(src, sink)
+ |
+ sink.getLocation() = location and
+ element = sink.toString() and
+ value = ""
+ )
+ }
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidBroadcastReceiver.java b/java/ql/test/query-tests/security/CWE-749/UnsafeAndroidBroadcastReceiver.java
similarity index 82%
rename from java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidBroadcastReceiver.java
rename to java/ql/test/query-tests/security/CWE-749/UnsafeAndroidBroadcastReceiver.java
index e49d0b7b518..c25a042ea89 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidBroadcastReceiver.java
+++ b/java/ql/test/query-tests/security/CWE-749/UnsafeAndroidBroadcastReceiver.java
@@ -11,7 +11,8 @@ 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
+ // 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");
@@ -29,6 +30,6 @@ public class UnsafeAndroidBroadcastReceiver extends BroadcastReceiver {
}
});
- wv.loadUrl(thisUrl);
+ wv.loadUrl(thisUrl); // $hasUnsafeAndroidAccess
}
-}
\ No newline at end of file
+}
diff --git a/java/ql/test/query-tests/security/CWE-749/options b/java/ql/test/query-tests/security/CWE-749/options
new file mode 100644
index 00000000000..d6a9adcece3
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-749/options
@@ -0,0 +1 @@
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/android
diff --git a/java/ql/test/stubs/android/android/app/Activity.java b/java/ql/test/stubs/android/android/app/Activity.java
new file mode 100644
index 00000000000..0ad9be7975f
--- /dev/null
+++ b/java/ql/test/stubs/android/android/app/Activity.java
@@ -0,0 +1,38 @@
+/*
+ * 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.app;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+public class Activity {
+
+ public void onCreate(Bundle bundle) {
+ }
+
+ public Intent getIntent() {
+ return null;
+ }
+
+ public Object findViewById(int id) {
+ return null;
+ }
+
+ public void setContentView(int view) {
+ }
+
+}
diff --git a/java/ql/test/stubs/android/android/content/BroadcastReceiver.java b/java/ql/test/stubs/android/android/content/BroadcastReceiver.java
new file mode 100644
index 00000000000..1d73018c96d
--- /dev/null
+++ b/java/ql/test/stubs/android/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:
+ *
+ * - Security
+ *
- Receiver Lifecycle
+ *
- 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/android/android/content/Intent.java b/java/ql/test/stubs/android/android/content/Intent.java
new file mode 100644
index 00000000000..32b167509d3
--- /dev/null
+++ b/java/ql/test/stubs/android/android/content/Intent.java
@@ -0,0 +1,196 @@
+/*
+ * 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.net.Uri;
+import android.os.Bundle;
+
+import java.io.PrintWriter;
+import java.net.URISyntaxException;
+import java.util.Set;
+
+public class Intent implements Cloneable {
+
+ public static Intent createChooser(Intent target, CharSequence title) {
+ return null;
+ }
+
+ public Intent() {
+ }
+
+ public Intent(Intent o) {
+ }
+
+ @Override
+ public Object clone() {
+ return null;
+ }
+
+ public Intent(String action) {
+ }
+
+ public Intent(String action, Uri uri) {
+ }
+
+ public Intent(Context packageContext, Class> cls) {
+ }
+
+ public Intent(String action, Uri uri, Context packageContext, Class> cls) {
+ }
+
+ public static Intent makeMainSelectorActivity(String selectorAction, String selectorCategory) {
+ return null;
+ }
+
+ public static Intent getIntent(String uri) throws URISyntaxException {
+ return null;
+ }
+
+ public static Intent getIntentOld(String uri) throws URISyntaxException {
+ return null;
+ }
+
+ public static void printIntentArgsHelp(PrintWriter pw, String prefix) {
+ }
+
+ public boolean hasCategory(String category) {
+ return false;
+ }
+
+ public Set getCategories() {
+ return null;
+ }
+
+ public int getContentUserHint() {
+ return 0;
+ }
+
+ public String getLaunchToken() {
+ return null;
+ }
+
+ public void setLaunchToken(String launchToken) {
+ }
+
+ public boolean hasExtra(String name) {
+ return false;
+ }
+
+ public boolean hasFileDescriptors() {
+ return false;
+ }
+
+ public void setAllowFds(boolean allowFds) {
+ }
+
+ public void setDefusable(boolean defusable) {
+ }
+
+ public Object getExtra(String name) {
+ return null;
+ }
+
+ public boolean getBooleanExtra(String name, boolean defaultValue) {
+ return false;
+ }
+
+ public byte getByteExtra(String name, byte defaultValue) {
+ return 0;
+ }
+
+ public short getShortExtra(String name, short defaultValue) {
+ return 0;
+ }
+
+ public char getCharExtra(String name, char defaultValue) {
+ return '0';
+ }
+
+ public int getIntExtra(String name, int defaultValue) {
+ return 0;
+ }
+
+ public long getLongExtra(String name, long defaultValue) {
+ return 0;
+ }
+
+ public float getFloatExtra(String name, float defaultValue) {
+ return 0;
+ }
+
+ public double getDoubleExtra(String name, double defaultValue) {
+ return 0;
+ }
+
+ public String getStringExtra(String string) {
+ return null;
+ }
+
+ public void removeUnsafeExtras() {
+ }
+
+ public boolean canStripForHistory() {
+ return false;
+ }
+
+ public Intent maybeStripForHistory() {
+ return null;
+ }
+
+ public boolean isExcludingStopped() {
+ return false;
+ }
+
+ public void removeCategory(String category) {
+ }
+
+ public void prepareToLeaveUser(int userId) {
+ }
+
+ public boolean filterEquals(Intent other) {
+ return false;
+ }
+
+ public int filterHashCode() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return null;
+ }
+
+ public String toInsecureString() {
+ return null;
+ }
+
+ public String toInsecureStringWithClip() {
+ return null;
+ }
+
+ public String toShortString(boolean secure, boolean comp, boolean extras, boolean clip) {
+ return null;
+ }
+
+ public void toShortString(StringBuilder b, boolean secure, boolean comp, boolean extras, boolean clip) {
+ }
+
+ public Bundle getExtras() {
+ return null;
+ }
+
+}
diff --git a/java/ql/test/stubs/android/android/os/Bundle.java b/java/ql/test/stubs/android/android/os/Bundle.java
new file mode 100644
index 00000000000..fa49a640f30
--- /dev/null
+++ b/java/ql/test/stubs/android/android/os/Bundle.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2007 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.os;
+
+public final class Bundle {
+ public Bundle() {
+ }
+
+ public Bundle(ClassLoader loader) {
+ }
+
+ public Bundle(int capacity) {
+ }
+
+ public Bundle(Bundle b) {
+ }
+
+ public static Bundle forPair(String key, String value) {
+ return null;
+ }
+
+ public boolean setAllowFds(boolean allowFds) {
+ return false;
+ }
+
+ public void setDefusable(boolean defusable) {
+ }
+
+ public static Bundle setDefusable(Bundle bundle, boolean defusable) {
+ return null;
+ }
+
+ @Override
+ public Object clone() {
+ return null;
+ }
+
+ public Bundle deepCopy() {
+ return null;
+ }
+
+ public void remove(String key) {
+ }
+
+ public void putAll(Bundle bundle) {
+ }
+
+ public int getSize() {
+ return 0;
+ }
+
+ public boolean hasFileDescriptors() {
+ return false;
+ }
+
+ public Bundle filterValues() {
+ return null;
+ }
+
+ @Override
+ public synchronized String toString() {
+ return null;
+ }
+
+ public synchronized String toShortString() {
+ return null;
+ }
+
+ public String getString(String string) {
+ return null;
+ }
+
+}
diff --git a/java/ql/test/stubs/android/android/util/AttributeSet.java b/java/ql/test/stubs/android/android/util/AttributeSet.java
new file mode 100644
index 00000000000..27159b91485
--- /dev/null
+++ b/java/ql/test/stubs/android/android/util/AttributeSet.java
@@ -0,0 +1,74 @@
+/*
+ * 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.util;
+
+public interface AttributeSet {
+ public int getAttributeCount();
+
+ default String getAttributeNamespace (int index) {
+ return null;
+ }
+
+ public String getAttributeName(int index);
+
+ public String getAttributeValue(int index);
+
+ public String getAttributeValue(String namespace, String name);
+
+ public String getPositionDescription();
+
+ public int getAttributeNameResource(int index);
+
+ public int getAttributeListValue(String namespace, String attribute,
+ String[] options, int defaultValue);
+
+ public boolean getAttributeBooleanValue(String namespace, String attribute,
+ boolean defaultValue);
+
+ public int getAttributeResourceValue(String namespace, String attribute,
+ int defaultValue);
+
+ public int getAttributeIntValue(String namespace, String attribute,
+ int defaultValue);
+
+ public int getAttributeUnsignedIntValue(String namespace, String attribute,
+ int defaultValue);
+
+ public float getAttributeFloatValue(String namespace, String attribute,
+ float defaultValue);
+
+ public int getAttributeListValue(int index, String[] options, int defaultValue);
+
+ public boolean getAttributeBooleanValue(int index, boolean defaultValue);
+
+ public int getAttributeResourceValue(int index, int defaultValue);
+
+ public int getAttributeIntValue(int index, int defaultValue);
+
+ public int getAttributeUnsignedIntValue(int index, int defaultValue);
+
+ public float getAttributeFloatValue(int index, float defaultValue);
+
+ public String getIdAttribute();
+
+ public String getClassAttribute();
+
+ public int getIdAttributeResourceValue(int defaultValue);
+
+ public int getStyleAttribute();
+
+}
diff --git a/java/ql/test/stubs/android/android/webkit/WebSettings.java b/java/ql/test/stubs/android/android/webkit/WebSettings.java
new file mode 100644
index 00000000000..ff1daf01404
--- /dev/null
+++ b/java/ql/test/stubs/android/android/webkit/WebSettings.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2007 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.webkit;
+
+// create a class derived from this, and return an instance of it in the
+// WebViewProvider.getWebSettingsProvider() method implementation.
+public abstract class WebSettings {
+
+ public @interface CacheMode {
+ }
+
+ public void setJavaScriptEnabled(boolean b) {
+ }
+
+ public void setAllowUniversalAccessFromFileURLs(boolean b) {
+ }
+
+ public void setAllowFileAccessFromFileURLs(boolean b) {
+ }
+}
\ No newline at end of file
diff --git a/java/ql/test/stubs/android/android/webkit/WebView.java b/java/ql/test/stubs/android/android/webkit/WebView.java
new file mode 100644
index 00000000000..6f09c7448d4
--- /dev/null
+++ b/java/ql/test/stubs/android/android/webkit/WebView.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2008 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.webkit;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+public class WebView {
+ public WebView(Context context) {
+ }
+
+ public WebView(Context context, AttributeSet attrs) {
+ }
+
+ public WebView(Context context, AttributeSet attrs, int defStyle) {
+ }
+
+ public void setHorizontalScrollbarOverlay(boolean overlay) {
+ }
+
+ public void setVerticalScrollbarOverlay(boolean overlay) {
+ }
+
+ public boolean overlayHorizontalScrollbar() {
+ return false;
+ }
+
+ public boolean overlayVerticalScrollbar() {
+ return false;
+ }
+
+ public void savePassword(String host, String username, String password) {
+ }
+
+ public void setHttpAuthUsernamePassword(String host, String realm, String username, String password) {
+ }
+
+ public String[] getHttpAuthUsernamePassword(String host, String realm) {
+ return null;
+ }
+
+ public void destroy() {
+ }
+
+ public static void enablePlatformNotifications() {
+ }
+
+ public static void disablePlatformNotifications() {
+ }
+
+ public void loadUrl(String url) {
+ }
+
+ public void loadData(String data, String mimeType, String encoding) {
+ }
+
+ public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String failUrl) {
+ }
+
+ public void stopLoading() {
+ }
+
+ public void reload() {
+ }
+
+ public boolean canGoBack() {
+ return false;
+ }
+
+ public void goBack() {
+ }
+
+ public boolean canGoForward() {
+ return false;
+ }
+
+ public void goForward() {
+ }
+
+ public boolean canGoBackOrForward(int steps) {
+ return false;
+ }
+
+ public void goBackOrForward(int steps) {
+ }
+
+ public boolean pageUp(boolean top) {
+ return false;
+ }
+
+ public boolean pageDown(boolean bottom) {
+ return false;
+ }
+
+ public void clearView() {
+ }
+
+ public float getScale() {
+ return 0;
+ }
+
+ public void setInitialScale(int scaleInPercent) {
+ }
+
+ public void invokeZoomPicker() {
+ }
+
+ public String getUrl() {
+ return null;
+ }
+
+ public String getTitle() {
+ return null;
+ }
+
+ public int getProgress() {
+ return 0;
+ }
+
+ public int getContentHeight() {
+ return 0;
+ }
+
+ public void pauseTimers() {
+ }
+
+ public void resumeTimers() {
+ }
+
+ public void clearCache() {
+ }
+
+ public void clearFormData() {
+ }
+
+ public void clearHistory() {
+ }
+
+ public void clearSslPreferences() {
+ }
+
+ public static String findAddress(String addr) {
+ return null;
+ }
+
+ public void addJavascriptInterface(Object obj, String interfaceName) {
+ }
+
+ public boolean zoomIn() {
+ return false;
+ }
+
+ public boolean zoomOut() {
+ return false;
+ }
+
+ public WebSettings getSettings() {
+ return null;
+ }
+
+ public void setWebViewClient(WebViewClient webViewClient) {
+ }
+
+}
diff --git a/java/ql/test/stubs/android/android/webkit/WebViewClient.java b/java/ql/test/stubs/android/android/webkit/WebViewClient.java
new file mode 100644
index 00000000000..ba874c389c6
--- /dev/null
+++ b/java/ql/test/stubs/android/android/webkit/WebViewClient.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2008 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.webkit;
+
+public class WebViewClient {
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ return false;
+ }
+
+ public void onPageFinished(WebView view, String url) {
+ }
+
+ public void onLoadResource(WebView view, String url) {
+ }
+
+ public void onPageCommitVisible(WebView view, String url) {
+ }
+
+ public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
+ }
+
+ public void onScaleChanged(WebView view, float oldScale, float newScale) {
+ }
+
+}
diff --git a/java/ql/test/stubs/android/com/android/internal/R.java b/java/ql/test/stubs/android/com/android/internal/R.java
new file mode 100644
index 00000000000..2fca344c2aa
--- /dev/null
+++ b/java/ql/test/stubs/android/com/android/internal/R.java
@@ -0,0 +1,13 @@
+package com.android.internal;
+
+public class R {
+
+ public static class id {
+ public static int my_webview;
+ }
+
+ public static class layout {
+ public static int webview;
+ }
+
+}