diff --git a/java/ql/src/experimental/Security/CWE/CWE-532/SensitiveInfoLog.ql b/java/ql/src/experimental/Security/CWE/CWE-532/SensitiveInfoLog.ql index b2ec0564e97..15690b7c32b 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-532/SensitiveInfoLog.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-532/SensitiveInfoLog.ql @@ -9,21 +9,21 @@ import java import semmle.code.java.dataflow.TaintTracking +import semmle.code.java.security.SensitiveActions import DataFlow import PathGraph /** - * Gets a regular expression for matching names of variables that indicate the value being held is a credential + * Gets a regular expression for matching names of variables that indicate the value being held may contain sensitive information */ -private string getACredentialRegex() { - result = "(?i).*challenge|pass(wd|word|code|phrase)(?!.*question).*" or - result = "(?i)(.*username|.*secret|url).*" -} +private string getACredentialRegex() { result = "(?i)(.*username|url).*" } /** Variable keeps sensitive information judging by its name * */ class CredentialExpr extends Expr { CredentialExpr() { - exists(Variable v | this = v.getAnAccess() | v.getName().regexpMatch(getACredentialRegex())) + exists(Variable v | this = v.getAnAccess() | + v.getName().regexpMatch([getCommonSensitiveInfoRegex(), getACredentialRegex()]) + ) } } diff --git a/java/ql/src/experimental/Security/CWE/CWE-927/SensitiveBroadcast.java b/java/ql/src/experimental/Security/CWE/CWE-927/SensitiveBroadcast.java new file mode 100644 index 00000000000..40e5f8e1bbc --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-927/SensitiveBroadcast.java @@ -0,0 +1,30 @@ +public void sendBroadcast1(Context context, String token, String refreshToken) +{ + { + // BAD: broadcast sensitive information to all listeners + Intent intent = new Intent(); + intent.setAction("com.example.custom_action"); + intent.putExtra("token", token); + intent.putExtra("refreshToken", refreshToken); + context.sendBroadcast(intent); + } + + { + // GOOD: broadcast sensitive information only to those with permission + Intent intent = new Intent(); + intent.setAction("com.example.custom_action"); + intent.putExtra("token", token); + intent.putExtra("refreshToken", refreshToken); + context.sendBroadcast(intent, "com.example.user_permission"); + } + + { + // GOOD: broadcast sensitive information to a specific application + Intent intent = new Intent(); + intent.setAction("com.example.custom_action"); + intent.setClassName("com.example2", "com.example2.UserInfoHandler"); + intent.putExtra("token", token); + intent.putExtra("refreshToken", refreshToken); + context.sendBroadcast(intent); + } +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-927/SensitiveBroadcast.qhelp b/java/ql/src/experimental/Security/CWE/CWE-927/SensitiveBroadcast.qhelp new file mode 100644 index 00000000000..0879d01e295 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-927/SensitiveBroadcast.qhelp @@ -0,0 +1,50 @@ + + + + +

Broadcast intents in an Android application are visible to all applications installed on the same mobile device, exposing all sensitive information they contain.

+

Broadcasts are vulnerable to passive eavesdropping or active denial of service attacks when an intent is broadcast without specifying any receiver permission or receiver application.

+
+ + +

+ Specify a receiver permission or application when broadcasting intents, or switch to + LocalBroadcastManager + or the latest + LiveData + library. +

+
+ + +

The following example shows two ways of broadcasting intents. In the 'BAD' case, no "receiver permission" is specified. In the 'GOOD' case, "receiver permission" or "receiver application" is specified.

+ +
+ + +
  • + CWE: + CWE-927: Use of Implicit Intent for Sensitive Communication +
  • +
  • + Android Developers: + Security considerations and best practices for sending and receiving broadcasts +
  • +
  • + SonarSource: + Broadcasting intents is security-sensitive +
  • +
  • + Android Developer Fundamentals: + Restricting broadcasts +
  • +
  • + Carnegie Mellon University: + DRD03-J. Do not broadcast sensitive information using an implicit intent +
  • +
  • + Android Developers: + Android LiveData Overview +
  • +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-927/SensitiveBroadcast.ql b/java/ql/src/experimental/Security/CWE/CWE-927/SensitiveBroadcast.ql new file mode 100644 index 00000000000..785a0f5c91c --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-927/SensitiveBroadcast.ql @@ -0,0 +1,171 @@ +/** + * @name Broadcasting sensitive data to all Android applications + * @id java/sensitive-broadcast + * @description An Android application uses implicit intents to broadcast sensitive data to all applications without specifying any receiver permission. + * @kind path-problem + * @tags security + * external/cwe-927 + */ + +import java +import semmle.code.java.dataflow.DataFlow3 +import semmle.code.java.dataflow.TaintTracking +import semmle.code.java.frameworks.android.Intent +import semmle.code.java.security.SensitiveActions +import DataFlow::PathGraph + +/** + * Gets regular expression for matching names of Android variables that indicate the value being held contains sensitive information. + */ +private string getAndroidSensitiveInfoRegex() { result = "(?i).*(email|phone|ticket).*" } + +/** + * Method call to pass information to the `Intent` object. + */ +class PutIntentExtraMethodAccess extends MethodAccess { + PutIntentExtraMethodAccess() { + ( + getMethod().getName().matches("put%Extra") or + getMethod().hasName("putExtras") + ) and + getMethod().getDeclaringType() instanceof TypeIntent + } +} + +/** + * Method call to pass information to the intent extra bundle object. + */ +class PutBundleExtraMethodAccess extends MethodAccess { + PutBundleExtraMethodAccess() { + getMethod().getName().regexpMatch("put\\w+") and + getMethod().getDeclaringType().getASupertype*().hasQualifiedName("android.os", "BaseBundle") + } +} + +/** Finds variables that hold sensitive information judging by their names. */ +class SensitiveInfoExpr extends Expr { + SensitiveInfoExpr() { + exists(Variable v | this = v.getAnAccess() | + v.getName().regexpMatch([getCommonSensitiveInfoRegex(), getAndroidSensitiveInfoRegex()]) + ) + } +} + +/** + * A method access of the `Context.sendBroadcast` family. + */ +class SendBroadcastMethodAccess extends MethodAccess { + SendBroadcastMethodAccess() { + this.getMethod().getDeclaringType() instanceof TypeContext and + this.getMethod().getName().matches("send%Broadcast%") + } +} + +private class NullArgFlowConfig extends DataFlow2::Configuration { + NullArgFlowConfig() { this = "Flow configuration with a null argument" } + + override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof NullLiteral } + + override predicate isSink(DataFlow::Node sink) { + exists(SendBroadcastMethodAccess ma | sink.asExpr() = ma.getAnArgument()) + } +} + +private class EmptyArrayArgFlowConfig extends DataFlow3::Configuration { + EmptyArrayArgFlowConfig() { this = "Flow configuration with an empty array argument" } + + override predicate isSource(DataFlow::Node src) { + src.asExpr().(ArrayCreationExpr).getFirstDimensionSize() = 0 + } + + override predicate isSink(DataFlow::Node sink) { + exists(SendBroadcastMethodAccess ma | sink.asExpr() = ma.getAnArgument()) + } +} + +/** + * Holds if a `sendBroadcast` call doesn't specify receiver permission. + */ +predicate isSensitiveBroadcastSink(DataFlow::Node sink) { + exists(SendBroadcastMethodAccess ma | + sink.asExpr() = ma.getAnArgument() and + ( + ma.getMethod().hasName("sendBroadcast") and + ( + ma.getNumArgument() = 1 // sendBroadcast(Intent intent) + or + // sendBroadcast(Intent intent, String receiverPermission) + exists(NullArgFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(ma.getArgument(1)))) + ) + or + ma.getMethod().hasName("sendBroadcastAsUser") and + ( + ma.getNumArgument() = 2 or // sendBroadcastAsUser(Intent intent, UserHandle user) + exists(NullArgFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(ma.getArgument(2)))) // sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission) + ) + or + ma.getMethod().hasName("sendBroadcastWithMultiplePermissions") and + exists(EmptyArrayArgFlowConfig config | + config.hasFlow(_, DataFlow::exprNode(ma.getArgument(1))) // sendBroadcastWithMultiplePermissions(Intent intent, String[] receiverPermissions) + ) + or + // Method calls of `sendOrderedBroadcast` whose second argument is always `receiverPermission` + ma.getMethod().hasName("sendOrderedBroadcast") and + ( + // sendOrderedBroadcast(Intent intent, String receiverPermission) or sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) + exists(NullArgFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(ma.getArgument(1)))) and + ma.getNumArgument() <= 7 + or + // sendOrderedBroadcast(Intent intent, String receiverPermission, String receiverAppOp, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) + exists(NullArgFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(ma.getArgument(1)))) and + exists(NullArgFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(ma.getArgument(2)))) and + ma.getNumArgument() = 8 + ) + or + // Method call of `sendOrderedBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)` + ma.getMethod().hasName("sendOrderedBroadcastAsUser") and + exists(NullArgFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(ma.getArgument(2)))) + ) + ) +} + +/** + * Taint configuration tracking flow from variables containing sensitive information to broadcast intents. + */ +class SensitiveBroadcastConfig extends TaintTracking::Configuration { + SensitiveBroadcastConfig() { this = "Sensitive Broadcast Configuration" } + + override predicate isSource(DataFlow::Node source) { + source.asExpr() instanceof SensitiveInfoExpr + } + + override predicate isSink(DataFlow::Node sink) { isSensitiveBroadcastSink(sink) } + + /** + * Holds if there is an additional flow step from `PutIntentExtraMethodAccess` or `PutBundleExtraMethodAccess` that taints the `Intent` or its extras `Bundle`. + */ + override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(PutIntentExtraMethodAccess pia | + node1.asExpr() = pia.getAnArgument() and node2.asExpr() = pia.getQualifier() + ) + or + exists(PutBundleExtraMethodAccess pba | + node1.asExpr() = pba.getAnArgument() and node2.asExpr() = pba.getQualifier() + ) + } + + /** + * Holds if broadcast doesn't specify receiving package name of the 3rd party app + */ + override predicate isSanitizer(DataFlow::Node node) { + exists(MethodAccess setReceiverMa | + setReceiverMa.getMethod().hasName(["setPackage", "setClass", "setClassName", "setComponent"]) and + setReceiverMa.getQualifier().(VarAccess).getVariable().getAnAccess() = node.asExpr() + ) + } +} + +from SensitiveBroadcastConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink +where cfg.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Sending $@ to broadcast.", source.getNode(), + "sensitive information" diff --git a/java/ql/src/semmle/code/java/security/SensitiveActions.qll b/java/ql/src/semmle/code/java/security/SensitiveActions.qll index 90380439500..88ae44b4556 100644 --- a/java/ql/src/semmle/code/java/security/SensitiveActions.qll +++ b/java/ql/src/semmle/code/java/security/SensitiveActions.qll @@ -27,6 +27,14 @@ private string nonSuspicious() { result = "%crypt%" } +/** + * Gets a regular expression for matching common names of variables that indicate the value being held contains sensitive information. + */ +string getCommonSensitiveInfoRegex() { + result = "(?i).*challenge|pass(wd|word|code|phrase)(?!.*question).*" or + result = "(?i).*(token|secret).*" +} + /** An expression that might contain sensitive data. */ abstract class SensitiveExpr extends Expr { } diff --git a/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.expected b/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.expected new file mode 100644 index 00000000000..2b1384b845b --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.expected @@ -0,0 +1,34 @@ +edges +| SensitiveBroadcast.java:12:34:12:38 | token : String | SensitiveBroadcast.java:14:31:14:36 | intent | +| SensitiveBroadcast.java:13:41:13:52 | refreshToken : String | SensitiveBroadcast.java:14:31:14:36 | intent | +| SensitiveBroadcast.java:25:32:25:39 | password : String | SensitiveBroadcast.java:26:31:26:36 | intent | +| SensitiveBroadcast.java:36:35:36:39 | email : String | SensitiveBroadcast.java:38:31:38:36 | intent | +| SensitiveBroadcast.java:50:22:50:29 | password : String | SensitiveBroadcast.java:52:31:52:36 | intent | +| SensitiveBroadcast.java:97:35:97:40 | ticket : String | SensitiveBroadcast.java:98:54:98:59 | intent | +| SensitiveBroadcast.java:109:32:109:39 | passcode : String | SensitiveBroadcast.java:111:54:111:59 | intent | +| SensitiveBroadcast.java:136:33:136:38 | passwd : String | SensitiveBroadcast.java:140:54:140:59 | intent | +nodes +| SensitiveBroadcast.java:12:34:12:38 | token : String | semmle.label | token : String | +| SensitiveBroadcast.java:13:41:13:52 | refreshToken : String | semmle.label | refreshToken : String | +| SensitiveBroadcast.java:14:31:14:36 | intent | semmle.label | intent | +| SensitiveBroadcast.java:25:32:25:39 | password : String | semmle.label | password : String | +| SensitiveBroadcast.java:26:31:26:36 | intent | semmle.label | intent | +| SensitiveBroadcast.java:36:35:36:39 | email : String | semmle.label | email : String | +| SensitiveBroadcast.java:38:31:38:36 | intent | semmle.label | intent | +| SensitiveBroadcast.java:50:22:50:29 | password : String | semmle.label | password : String | +| SensitiveBroadcast.java:52:31:52:36 | intent | semmle.label | intent | +| SensitiveBroadcast.java:97:35:97:40 | ticket : String | semmle.label | ticket : String | +| SensitiveBroadcast.java:98:54:98:59 | intent | semmle.label | intent | +| SensitiveBroadcast.java:109:32:109:39 | passcode : String | semmle.label | passcode : String | +| SensitiveBroadcast.java:111:54:111:59 | intent | semmle.label | intent | +| SensitiveBroadcast.java:136:33:136:38 | passwd : String | semmle.label | passwd : String | +| SensitiveBroadcast.java:140:54:140:59 | intent | semmle.label | intent | +#select +| SensitiveBroadcast.java:14:31:14:36 | intent | SensitiveBroadcast.java:12:34:12:38 | token : String | SensitiveBroadcast.java:14:31:14:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:12:34:12:38 | token | sensitive information | +| SensitiveBroadcast.java:14:31:14:36 | intent | SensitiveBroadcast.java:13:41:13:52 | refreshToken : String | SensitiveBroadcast.java:14:31:14:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:13:41:13:52 | refreshToken | sensitive information | +| SensitiveBroadcast.java:26:31:26:36 | intent | SensitiveBroadcast.java:25:32:25:39 | password : String | SensitiveBroadcast.java:26:31:26:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:25:32:25:39 | password | sensitive information | +| SensitiveBroadcast.java:38:31:38:36 | intent | SensitiveBroadcast.java:36:35:36:39 | email : String | SensitiveBroadcast.java:38:31:38:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:36:35:36:39 | email | sensitive information | +| SensitiveBroadcast.java:52:31:52:36 | intent | SensitiveBroadcast.java:50:22:50:29 | password : String | SensitiveBroadcast.java:52:31:52:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:50:22:50:29 | password | sensitive information | +| SensitiveBroadcast.java:98:54:98:59 | intent | SensitiveBroadcast.java:97:35:97:40 | ticket : String | SensitiveBroadcast.java:98:54:98:59 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:97:35:97:40 | ticket | sensitive information | +| SensitiveBroadcast.java:111:54:111:59 | intent | SensitiveBroadcast.java:109:32:109:39 | passcode : String | SensitiveBroadcast.java:111:54:111:59 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:109:32:109:39 | passcode | sensitive information | +| SensitiveBroadcast.java:140:54:140:59 | intent | SensitiveBroadcast.java:136:33:136:38 | passwd : String | SensitiveBroadcast.java:140:54:140:59 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:136:33:136:38 | passwd | sensitive information | diff --git a/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.java b/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.java new file mode 100644 index 00000000000..0529cf3c421 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.java @@ -0,0 +1,173 @@ +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import java.util.ArrayList; + +class SensitiveBroadcast { + + // BAD - Tests broadcast of access token with intent extra. + public void sendBroadcast1(Context context, String token, String refreshToken) { + Intent intent = new Intent(); + intent.setAction("com.example.custom_action"); + intent.putExtra("token", token); + intent.putExtra("refreshToken", refreshToken); + context.sendBroadcast(intent); + } + + // BAD - Tests broadcast of sensitive user information with intent extra. + public void sendBroadcast2(Context context) { + String userName = "test123"; + String password = "abc12345"; + + Intent intent = new Intent(); + intent.setAction("com.example.custom_action"); + intent.putExtra("name", userName); + intent.putExtra("pwd", password); + context.sendBroadcast(intent); + } + + // BAD - Tests broadcast of email information with extra bundle. + public void sendBroadcast3(Context context) { + String email = "user123@example.com"; + + Intent intent = new Intent(); + intent.setAction("com.example.custom_action"); + Bundle bundle = new Bundle(); + bundle.putString("email", email); + intent.putExtras(bundle); + context.sendBroadcast(intent); + } + + // BAD - Tests broadcast of sensitive user information with null permission. + public void sendBroadcast4(Context context) { + String username = "test123"; + String password = "abc12345"; + + Intent intent = new Intent(); + intent.setAction("com.example.custom_action"); + ArrayList userinfo = new ArrayList(); + userinfo.add(username); + userinfo.add(password); + intent.putStringArrayListExtra("userinfo", userinfo); + context.sendBroadcast(intent, null); + } + + // GOOD - Tests broadcast of sensitive user information with permission using string literal. + public void sendBroadcast5(Context context) { + String username = "test123"; + String password = "abc12345"; + + Intent intent = new Intent(); + intent.setAction("com.example.custom_action"); + intent.putExtra("name", username); + intent.putExtra("pwd", password); + context.sendBroadcast(intent, "com.example.user_permission"); + } + + // GOOD - Tests broadcast of access ticket with permission using string object. + public void sendBroadcast6(Context context) { + String ticket = "Tk9UIFNlY3VyZSBUaWNrZXQ="; + + Intent intent = new Intent(); + intent.setAction("com.example.custom_action"); + intent.putExtra("ticket", ticket); + String perm = "com.example.user_permission"; + context.sendBroadcast(intent, perm); + } + + // GOOD - Tests broadcast of sensitive user information to a specific application. + public void sendBroadcast7(Context context) { + String username = "test123"; + String password = "abc12345"; + + Intent intent = new Intent(); + intent.setAction("com.example.custom_action"); + intent.setClassName("com.example2", "com.example2.UserInfoHandler"); + intent.putExtra("name", username); + intent.putExtra("pwd", password); + context.sendBroadcast(intent); + } + + // BAD - Tests broadcast of access ticket with multiple permissions using direct empty array initialization. + public void sendBroadcast8(Context context) { + String ticket = "Tk9UIFNlY3VyZSBUaWNrZXQ="; + + Intent intent = new Intent(); + intent.setAction("com.example.custom_action"); + intent.putExtra("ticket", ticket); + context.sendBroadcastWithMultiplePermissions(intent, new String[]{}); + } + + // BAD - Tests broadcast of sensitive user information with multiple permissions using empty array initialization through a variable. + public void sendBroadcast9(Context context) { + String username = "test123"; + String passcode = "abc12345"; + + Intent intent = new Intent(); + intent.setAction("com.example.custom_action"); + intent.putExtra("name", username); + intent.putExtra("pwd", passcode); + String[] perms = new String[0]; + context.sendBroadcastWithMultiplePermissions(intent, perms); + } + + // GOOD - Tests broadcast of sensitive user information with multiple permissions. + public void sendBroadcast10(Context context) { + String username = "test123"; + String password = "abc12345"; + + Intent intent = new Intent(); + intent.setAction("com.example.custom_action"); + intent.putExtra("name", username); + intent.putExtra("pwd", password); + String[] perms = new String[]{"com.example.custom_action", "com.example.custom_action2"}; + context.sendBroadcastWithMultiplePermissions(intent, perms); + } + + // BAD - Tests broadcast of sensitive user information with multiple permissions using empty array initialization through two variables and `intent.putExtras(bundle)`. + public void sendBroadcast11(Context context) { + String username = "test123"; + String passwd = "abc12345"; + + Intent intent = new Intent(); + intent.setAction("com.example.custom_action"); + Bundle bundle = new Bundle(); + bundle.putString("name", username); + bundle.putString("pwd", passwd); + intent.putExtras(bundle); + String[] perms = new String[0]; + String[] perms2 = perms; + context.sendBroadcastWithMultiplePermissions(intent, perms2); + } + + /** + * BAD - Tests broadcast of sensitive user information with multiple permissions using empty array initialization through two variables and `intent.getExtras().putString()`. + * Note this case of `getExtras().putString(...)` is not yet detected thus is beyond what the query is capable of. + */ + public void sendBroadcast12(Context context) { + String username = "test123"; + String password = "abc12345"; + + Intent intent = new Intent(); + intent.setAction("com.example.custom_action"); + Bundle bundle = new Bundle(); + intent.putExtras(bundle); + intent.getExtras().putString("name", username); + intent.getExtras().putString("pwd", password); + String[] perms = new String[0]; + String[] perms2 = perms; + context.sendBroadcastWithMultiplePermissions(intent, perms2); + } + + // GOOD - Tests broadcast of sensitive user information with ordered broadcast. + public void sendBroadcast13(Context context) { + String username = "test123"; + String password = "abc12345"; + + Intent intent = new Intent(); + intent.setAction("com.example.custom_action"); + intent.putExtra("name", username); + intent.putExtra("pwd", password); + context.sendOrderedBroadcast(intent, "com.example.USER_PERM"); + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.qlref b/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.qlref new file mode 100644 index 00000000000..20c5250d80d --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-927/SensitiveBroadcast.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-927/SensitiveBroadcast.ql diff --git a/java/ql/test/experimental/query-tests/security/CWE-927/options b/java/ql/test/experimental/query-tests/security/CWE-927/options new file mode 100644 index 00000000000..43e25f608b6 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-927/options @@ -0,0 +1 @@ +// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/google-android-9.0.0