diff --git a/java/ql/lib/semmle/code/java/dataflow/ApiSinks.qll b/java/ql/lib/semmle/code/java/dataflow/ApiSinks.qll new file mode 100644 index 000000000000..c600bb1672d8 --- /dev/null +++ b/java/ql/lib/semmle/code/java/dataflow/ApiSinks.qll @@ -0,0 +1,39 @@ +/** Provides classes representing various flow sinks for data flow / taint tracking. */ + +private import semmle.code.java.dataflow.FlowSinks as FlowSinks + +final class SinkNode = FlowSinks::ApiSinkNode; + +/** + * Module that adds all API like sinks to `SinkNode`, excluding sinks for cryptography based + * queries, and queries where sinks are not succifiently defined (eg. using broad method name matching). + */ +private module AllApiSinks { + private import semmle.code.java.security.AndroidSensitiveCommunicationQuery + private import semmle.code.java.security.ArbitraryApkInstallation + private import semmle.code.java.security.CleartextStorageAndroidDatabaseQuery + private import semmle.code.java.security.CleartextStorageAndroidFilesystemQuery + private import semmle.code.java.security.CleartextStorageCookieQuery + private import semmle.code.java.security.CleartextStorageSharedPrefsQuery + private import semmle.code.java.security.ExternallyControlledFormatStringQuery + private import semmle.code.java.security.InsecureBasicAuth + private import semmle.code.java.security.IntentUriPermissionManipulation + private import semmle.code.java.security.InsecureLdapAuth + private import semmle.code.java.security.InsecureTrustManager + private import semmle.code.java.security.JndiInjection + private import semmle.code.java.security.JWT + private import semmle.code.java.security.OgnlInjection + private import semmle.code.java.security.SensitiveResultReceiverQuery + private import semmle.code.java.security.SensitiveUiQuery + private import semmle.code.java.security.SpelInjection + private import semmle.code.java.security.SpelInjectionQuery + private import semmle.code.java.security.QueryInjection + private import semmle.code.java.security.TempDirLocalInformationDisclosureQuery + private import semmle.code.java.security.UnsafeAndroidAccess + private import semmle.code.java.security.UnsafeContentUriResolution + private import semmle.code.java.security.UnsafeDeserializationQuery + private import semmle.code.java.security.UrlRedirect + private import semmle.code.java.security.WebviewDebuggingEnabledQuery + private import semmle.code.java.security.XPath + private import semmle.code.java.security.XSS +} diff --git a/java/ql/lib/semmle/code/java/dataflow/ApiSources.qll b/java/ql/lib/semmle/code/java/dataflow/ApiSources.qll new file mode 100644 index 000000000000..5f825ad5445f --- /dev/null +++ b/java/ql/lib/semmle/code/java/dataflow/ApiSources.qll @@ -0,0 +1,23 @@ +/** Provides classes representing various flow sources for data flow / taint tracking. */ + +private import semmle.code.java.dataflow.FlowSources as FlowSources + +final class SourceNode = FlowSources::ApiSourceNode; + +/** + * Module that adds all API like sources to `SourceNode`, excluding some sources for cryptography based + * queries, and queries where sources are not succifiently defined (eg. using broad method name matching). + */ +private module AllApiSources { + private import semmle.code.java.security.ArbitraryApkInstallation + private import semmle.code.java.security.CleartextStorageAndroidDatabaseQuery + private import semmle.code.java.security.CleartextStorageAndroidFilesystemQuery + private import semmle.code.java.security.CleartextStorageCookieQuery + private import semmle.code.java.security.CleartextStorageSharedPrefsQuery + private import semmle.code.java.security.ImplicitPendingIntentsQuery + private import semmle.code.java.security.ImproperIntentVerificationQuery + private import semmle.code.java.security.InsecureTrustManager + private import semmle.code.java.security.JWT + private import semmle.code.java.security.StackTraceExposureQuery + private import semmle.code.java.security.ZipSlipQuery +} diff --git a/java/ql/lib/semmle/code/java/dataflow/FlowSinks.qll b/java/ql/lib/semmle/code/java/dataflow/FlowSinks.qll new file mode 100644 index 000000000000..72cd96f6745c --- /dev/null +++ b/java/ql/lib/semmle/code/java/dataflow/FlowSinks.qll @@ -0,0 +1,18 @@ +/** Provides classes representing various flow sinks for data flow / taint tracking. */ + +private import java +private import semmle.code.java.dataflow.ExternalFlow +private import semmle.code.java.dataflow.DataFlow + +/** + * A data flow sink node for an API, which should be considered + * supported for a modeling perspective. + */ +abstract class ApiSinkNode extends DataFlow::Node { } + +/** + * Add all sink models as data sinks. + */ +private class ApiSinkNodeExternal extends ApiSinkNode { + ApiSinkNodeExternal() { sinkNode(this, _) } +} diff --git a/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll b/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll index 425eb3ccaa60..6befe289a17c 100644 --- a/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll +++ b/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll @@ -194,15 +194,17 @@ private class AndroidExternalStorageSource extends RemoteFlowSource { } /** Class for `tainted` user input. */ -abstract class UserInput extends DataFlow::Node { } +abstract class UserInput extends SourceNode { } /** * Input that may be controlled by a remote user. */ -private class RemoteUserInput extends UserInput instanceof RemoteFlowSource { } +private class RemoteUserInput extends UserInput instanceof RemoteFlowSource { + override string getThreatModel() { result = RemoteFlowSource.super.getThreatModel() } +} /** A node with input that may be controlled by a local user. */ -abstract class LocalUserInput extends UserInput, SourceNode { +abstract class LocalUserInput extends UserInput { override string getThreatModel() { result = "local" } } @@ -385,3 +387,18 @@ class AndroidJavascriptInterfaceMethodParameter extends RemoteFlowSource { result = "Parameter of method with JavascriptInterface annotation" } } + +/** + * A data flow source node for an API, which should be considered + * supported for a modeling perspective. + */ +abstract class ApiSourceNode extends DataFlow::Node { } + +private class AddSourceNodes extends ApiSourceNode instanceof SourceNode { } + +/** + * Add all source models as data sources. + */ +private class ApiSourceNodeExternal extends ApiSourceNode { + ApiSourceNodeExternal() { sourceNode(this, _) } +} diff --git a/java/ql/lib/semmle/code/java/security/AndroidSensitiveCommunicationQuery.qll b/java/ql/lib/semmle/code/java/security/AndroidSensitiveCommunicationQuery.qll index 53adc7132c5e..a4f9713ac308 100644 --- a/java/ql/lib/semmle/code/java/security/AndroidSensitiveCommunicationQuery.qll +++ b/java/ql/lib/semmle/code/java/security/AndroidSensitiveCommunicationQuery.qll @@ -4,6 +4,7 @@ import java import semmle.code.java.dataflow.TaintTracking import semmle.code.java.frameworks.android.Intent import semmle.code.java.security.SensitiveActions +private import semmle.code.java.dataflow.FlowSinks /** * Gets regular expression for matching names of Android variables that indicate the value being held contains sensitive information. @@ -151,17 +152,24 @@ deprecated class SensitiveCommunicationConfig extends TaintTracking::Configurati } } +/** + * A sensitive communication sink node. + */ +private class SensitiveCommunicationSink extends ApiSinkNode { + SensitiveCommunicationSink() { + isSensitiveBroadcastSink(this) + or + isStartActivityOrServiceSink(this) + } +} + /** * Taint configuration tracking flow from variables containing sensitive information to broadcast Intents. */ module SensitiveCommunicationConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { source.asExpr() instanceof SensitiveInfoExpr } - predicate isSink(DataFlow::Node sink) { - isSensitiveBroadcastSink(sink) - or - isStartActivityOrServiceSink(sink) - } + predicate isSink(DataFlow::Node sink) { sink instanceof SensitiveCommunicationSink } /** * Holds if broadcast doesn't specify receiving package name of the 3rd party app diff --git a/java/ql/lib/semmle/code/java/security/ArbitraryApkInstallation.qll b/java/ql/lib/semmle/code/java/security/ArbitraryApkInstallation.qll index 3aa59286fcd0..d7c5fe94f28a 100644 --- a/java/ql/lib/semmle/code/java/security/ArbitraryApkInstallation.qll +++ b/java/ql/lib/semmle/code/java/security/ArbitraryApkInstallation.qll @@ -4,6 +4,7 @@ import java import semmle.code.java.frameworks.android.Intent import semmle.code.java.dataflow.DataFlow private import semmle.code.java.dataflow.ExternalFlow +private import semmle.code.java.dataflow.FlowSinks private import semmle.code.java.dataflow.FlowSources /** A string literal that represents the MIME type for Android APKs. */ @@ -48,7 +49,7 @@ class SetDataMethod extends Method { } /** A dataflow sink for the URI of an intent. */ -class SetDataSink extends DataFlow::ExprNode { +class SetDataSink extends ApiSinkNode, DataFlow::ExprNode { SetDataSink() { exists(MethodCall ma | this.getExpr() = ma.getQualifier() and @@ -69,7 +70,7 @@ class UriConstructorMethod extends Method { * A dataflow source representing the URIs which an APK not controlled by the * application may come from. Including external storage and web URLs. */ -class ExternalApkSource extends DataFlow::Node { +class ExternalApkSource extends ApiSourceNode { ExternalApkSource() { sourceNode(this, "android-external-storage-dir") or this.asExpr().(MethodCall).getMethod() instanceof UriConstructorMethod or diff --git a/java/ql/lib/semmle/code/java/security/CleartextStorageAndroidDatabaseQuery.qll b/java/ql/lib/semmle/code/java/security/CleartextStorageAndroidDatabaseQuery.qll index b42389a1df6e..5ee9248d9eb5 100644 --- a/java/ql/lib/semmle/code/java/security/CleartextStorageAndroidDatabaseQuery.qll +++ b/java/ql/lib/semmle/code/java/security/CleartextStorageAndroidDatabaseQuery.qll @@ -6,6 +6,8 @@ import semmle.code.java.frameworks.android.ContentProviders import semmle.code.java.frameworks.android.Intent import semmle.code.java.frameworks.android.SQLite import semmle.code.java.security.CleartextStorageQuery +private import semmle.code.java.dataflow.FlowSinks +private import semmle.code.java.dataflow.FlowSources private class LocalDatabaseCleartextStorageSink extends CleartextStorageSink { LocalDatabaseCleartextStorageSink() { localDatabaseInput(_, this.asExpr()) } @@ -96,15 +98,24 @@ private predicate localDatabaseStore(DataFlow::Node database, MethodCall store) ) } +/** + * A local database open method call source node. + */ +private class LocalDatabaseOpenMethodCallSource extends ApiSourceNode { + LocalDatabaseOpenMethodCallSource() { this.asExpr() instanceof LocalDatabaseOpenMethodCall } +} + +/** + * A local database sink node. + */ +private class LocalDatabaseSink extends ApiSinkNode { + LocalDatabaseSink() { localDatabaseInput(this, _) or localDatabaseStore(this, _) } +} + private module LocalDatabaseFlowConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { - source.asExpr() instanceof LocalDatabaseOpenMethodCall - } + predicate isSource(DataFlow::Node source) { source instanceof LocalDatabaseOpenMethodCallSource } - predicate isSink(DataFlow::Node sink) { - localDatabaseInput(sink, _) or - localDatabaseStore(sink, _) - } + predicate isSink(DataFlow::Node sink) { sink instanceof LocalDatabaseSink } predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) { // Adds a step for tracking databases through field flow, that is, a database is opened and diff --git a/java/ql/lib/semmle/code/java/security/CleartextStorageAndroidFilesystemQuery.qll b/java/ql/lib/semmle/code/java/security/CleartextStorageAndroidFilesystemQuery.qll index d7097f1ecf23..06fa83813124 100644 --- a/java/ql/lib/semmle/code/java/security/CleartextStorageAndroidFilesystemQuery.qll +++ b/java/ql/lib/semmle/code/java/security/CleartextStorageAndroidFilesystemQuery.qll @@ -5,9 +5,11 @@ import java import semmle.code.java.dataflow.DataFlow -private import semmle.code.java.dataflow.ExternalFlow import semmle.code.java.security.CleartextStorageQuery import semmle.code.xml.AndroidManifest +private import semmle.code.java.dataflow.ExternalFlow +private import semmle.code.java.dataflow.FlowSinks +private import semmle.code.java.dataflow.FlowSources private class AndroidFilesystemCleartextStorageSink extends CleartextStorageSink { AndroidFilesystemCleartextStorageSink() { @@ -79,13 +81,27 @@ private class CloseFileMethod extends Method { } } -private module FilesystemFlowConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node src) { src.asExpr() instanceof LocalFileOpenCall } +/** + * A local file open call source node. + */ +private class LocalFileOpenCallSource extends ApiSourceNode { + LocalFileOpenCallSource() { this.asExpr() instanceof LocalFileOpenCall } +} - predicate isSink(DataFlow::Node sink) { - filesystemInput(sink, _) or - closesFile(sink, _) +/** + * A local file sink node. + */ +private class LocalFileSink extends ApiSinkNode { + LocalFileSink() { + filesystemInput(this, _) or + closesFile(this, _) } +} + +private module FilesystemFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src instanceof LocalFileOpenCallSource } + + predicate isSink(DataFlow::Node sink) { sink instanceof LocalFileSink } predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { // Add nested Writer constructors as extra data flow steps diff --git a/java/ql/lib/semmle/code/java/security/CleartextStorageCookieQuery.qll b/java/ql/lib/semmle/code/java/security/CleartextStorageCookieQuery.qll index 2b6534e8cbda..a36a4754584a 100644 --- a/java/ql/lib/semmle/code/java/security/CleartextStorageCookieQuery.qll +++ b/java/ql/lib/semmle/code/java/security/CleartextStorageCookieQuery.qll @@ -4,6 +4,8 @@ import java import semmle.code.java.dataflow.DataFlow deprecated import semmle.code.java.dataflow.DataFlow3 import semmle.code.java.security.CleartextStorageQuery +private import semmle.code.java.dataflow.FlowSinks +private import semmle.code.java.dataflow.FlowSources private class CookieCleartextStorageSink extends CleartextStorageSink { CookieCleartextStorageSink() { this.asExpr() = cookieInput(_) } @@ -37,10 +39,24 @@ private predicate cookieStore(DataFlow::Node cookie, Expr store) { ) } +/** + * A cookie source node. + */ +private class CookieSource extends ApiSourceNode { + CookieSource() { this.asExpr() instanceof Cookie } +} + +/** + * A cookie store sink node. + */ +private class CookieStoreSink extends ApiSinkNode { + CookieStoreSink() { cookieStore(this, _) } +} + private module CookieToStoreFlowConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node src) { src.asExpr() instanceof Cookie } + predicate isSource(DataFlow::Node src) { src instanceof CookieSource } - predicate isSink(DataFlow::Node sink) { cookieStore(sink, _) } + predicate isSink(DataFlow::Node sink) { sink instanceof CookieStoreSink } } private module CookieToStoreFlow = DataFlow::Global; diff --git a/java/ql/lib/semmle/code/java/security/CleartextStorageSharedPrefsQuery.qll b/java/ql/lib/semmle/code/java/security/CleartextStorageSharedPrefsQuery.qll index 39e1ffa3c75d..f72d40106e35 100644 --- a/java/ql/lib/semmle/code/java/security/CleartextStorageSharedPrefsQuery.qll +++ b/java/ql/lib/semmle/code/java/security/CleartextStorageSharedPrefsQuery.qll @@ -4,6 +4,8 @@ import java import semmle.code.java.dataflow.DataFlow import semmle.code.java.frameworks.android.SharedPreferences import semmle.code.java.security.CleartextStorageQuery +private import semmle.code.java.dataflow.FlowSinks +private import semmle.code.java.dataflow.FlowSources private class SharedPrefsCleartextStorageSink extends CleartextStorageSink { SharedPrefsCleartextStorageSink() { @@ -67,16 +69,30 @@ private predicate sharedPreferencesStore(DataFlow::Node editor, MethodCall m) { editor.asExpr() = m.getQualifier().getUnderlyingExpr() } -/** Flow from `SharedPreferences.Editor` to either a setter or a store method. */ -private module SharedPreferencesFlowConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node src) { - src.asExpr() instanceof SharedPreferencesEditorMethodCall +/** + * A shared preferences editor method call source node. + */ +private class SharedPreferencesEditorMethodCallSource extends ApiSourceNode { + SharedPreferencesEditorMethodCallSource() { + this.asExpr() instanceof SharedPreferencesEditorMethodCall } +} - predicate isSink(DataFlow::Node sink) { - sharedPreferencesInput(sink, _) or - sharedPreferencesStore(sink, _) +/** + * A shared preferences sink node. + */ +private class SharedPreferencesSink extends ApiSinkNode { + SharedPreferencesSink() { + sharedPreferencesInput(this, _) or + sharedPreferencesStore(this, _) } } +/** Flow from `SharedPreferences.Editor` to either a setter or a store method. */ +private module SharedPreferencesFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src instanceof SharedPreferencesEditorMethodCallSource } + + predicate isSink(DataFlow::Node sink) { sink instanceof SharedPreferencesSink } +} + private module SharedPreferencesFlow = DataFlow::Global; diff --git a/java/ql/lib/semmle/code/java/security/ExternallyControlledFormatStringQuery.qll b/java/ql/lib/semmle/code/java/security/ExternallyControlledFormatStringQuery.qll index a71ebc964f64..606e31a07cb7 100644 --- a/java/ql/lib/semmle/code/java/security/ExternallyControlledFormatStringQuery.qll +++ b/java/ql/lib/semmle/code/java/security/ExternallyControlledFormatStringQuery.qll @@ -1,18 +1,24 @@ /** Provides a taint-tracking configuration to reason about externally controlled format string vulnerabilities. */ import java +private import semmle.code.java.dataflow.FlowSinks private import semmle.code.java.dataflow.FlowSources private import semmle.code.java.StringFormat +/** + * A string format sink node. + */ +private class StringFormatSink extends ApiSinkNode { + StringFormatSink() { this.asExpr() = any(StringFormat formatCall).getFormatArgument() } +} + /** * A taint-tracking configuration for externally controlled format string vulnerabilities. */ module ExternallyControlledFormatStringConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { source instanceof ThreatModelFlowSource } - predicate isSink(DataFlow::Node sink) { - sink.asExpr() = any(StringFormat formatCall).getFormatArgument() - } + predicate isSink(DataFlow::Node sink) { sink instanceof StringFormatSink } predicate isBarrier(DataFlow::Node node) { node.getType() instanceof NumericType or node.getType() instanceof BooleanType diff --git a/java/ql/lib/semmle/code/java/security/ImplicitPendingIntents.qll b/java/ql/lib/semmle/code/java/security/ImplicitPendingIntents.qll index 4cac7715b98a..a5d8f256b036 100644 --- a/java/ql/lib/semmle/code/java/security/ImplicitPendingIntents.qll +++ b/java/ql/lib/semmle/code/java/security/ImplicitPendingIntents.qll @@ -2,8 +2,7 @@ import java private import semmle.code.java.dataflow.ExternalFlow -private import semmle.code.java.dataflow.TaintTracking -private import semmle.code.java.frameworks.android.Intent +private import semmle.code.java.dataflow.FlowSources private import semmle.code.java.frameworks.android.PendingIntent private newtype TPendingIntentState = @@ -27,7 +26,7 @@ class NoState extends PendingIntentState, TNoState { } /** A source for an implicit `PendingIntent` flow. */ -abstract class ImplicitPendingIntentSource extends DataFlow::Node { +abstract class ImplicitPendingIntentSource extends ApiSourceNode { /** * DEPRECATED: Open-ended flow state is not intended to be part of the extension points. * diff --git a/java/ql/lib/semmle/code/java/security/InsecureBasicAuth.qll b/java/ql/lib/semmle/code/java/security/InsecureBasicAuth.qll index df9b6bdf4a11..b21492406adf 100644 --- a/java/ql/lib/semmle/code/java/security/InsecureBasicAuth.qll +++ b/java/ql/lib/semmle/code/java/security/InsecureBasicAuth.qll @@ -4,6 +4,7 @@ import java import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.TaintTracking import semmle.code.java.security.HttpsUrls +private import semmle.code.java.dataflow.FlowSinks /** * A source that represents HTTP URLs. @@ -20,7 +21,7 @@ private class DefaultInsecureBasicAuthSource extends InsecureBasicAuthSource { * A sink that represents a method that sets Basic Authentication. * Extend this class to add your own Insecure Basic Authentication sinks. */ -abstract class InsecureBasicAuthSink extends DataFlow::Node { } +abstract class InsecureBasicAuthSink extends ApiSinkNode { } /** A default sink representing methods that set an Authorization header. */ private class DefaultInsecureBasicAuthSink extends InsecureBasicAuthSink { diff --git a/java/ql/lib/semmle/code/java/security/InsecureLdapAuth.qll b/java/ql/lib/semmle/code/java/security/InsecureLdapAuth.qll index 9a8cd91b1fcf..52d58afc9e76 100644 --- a/java/ql/lib/semmle/code/java/security/InsecureLdapAuth.qll +++ b/java/ql/lib/semmle/code/java/security/InsecureLdapAuth.qll @@ -2,6 +2,7 @@ import java private import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.dataflow.FlowSinks private import semmle.code.java.frameworks.Networking private import semmle.code.java.frameworks.Jndi @@ -32,7 +33,7 @@ class InsecureLdapUrl extends Expr { /** * A sink representing the construction of a `DirContextEnvironment`. */ -class InsecureLdapUrlSink extends DataFlow::Node { +class InsecureLdapUrlSink extends ApiSinkNode { InsecureLdapUrlSink() { exists(ConstructorCall cc | cc.getConstructedType().getAnAncestor() instanceof TypeDirContext and diff --git a/java/ql/lib/semmle/code/java/security/InsecureRandomnessQuery.qll b/java/ql/lib/semmle/code/java/security/InsecureRandomnessQuery.qll index f983876d7b38..423046b6746e 100644 --- a/java/ql/lib/semmle/code/java/security/InsecureRandomnessQuery.qll +++ b/java/ql/lib/semmle/code/java/security/InsecureRandomnessQuery.qll @@ -4,6 +4,7 @@ import java private import semmle.code.java.frameworks.OpenSaml private import semmle.code.java.frameworks.Servlets private import semmle.code.java.dataflow.ExternalFlow +private import semmle.code.java.dataflow.FlowSinks private import semmle.code.java.dataflow.TaintTracking private import semmle.code.java.security.Cookies private import semmle.code.java.security.RandomQuery @@ -49,7 +50,7 @@ abstract class InsecureRandomnessSink extends DataFlow::Node { } /** * A node which sets the value of a cookie. */ -private class CookieSink extends InsecureRandomnessSink { +private class CookieSink extends InsecureRandomnessSink, ApiSinkNode { CookieSink() { this.asExpr() instanceof SetCookieValue } } diff --git a/java/ql/lib/semmle/code/java/security/InsecureTrustManager.qll b/java/ql/lib/semmle/code/java/security/InsecureTrustManager.qll index d82f088cf155..41d8f28573ca 100644 --- a/java/ql/lib/semmle/code/java/security/InsecureTrustManager.qll +++ b/java/ql/lib/semmle/code/java/security/InsecureTrustManager.qll @@ -2,11 +2,12 @@ import java private import semmle.code.java.controlflow.Guards +private import semmle.code.java.dataflow.FlowSinks private import semmle.code.java.security.Encryption private import semmle.code.java.security.SecurityFlag /** The creation of an insecure `TrustManager`. */ -abstract class InsecureTrustManagerSource extends DataFlow::Node { } +abstract class InsecureTrustManagerSource extends ApiSourceNode { } private class DefaultInsecureTrustManagerSource extends InsecureTrustManagerSource { DefaultInsecureTrustManagerSource() { @@ -18,7 +19,7 @@ private class DefaultInsecureTrustManagerSource extends InsecureTrustManagerSour * The use of a `TrustManager` in an SSL context. * Intentionally insecure connections are not considered sinks. */ -abstract class InsecureTrustManagerSink extends DataFlow::Node { +abstract class InsecureTrustManagerSink extends ApiSinkNode { InsecureTrustManagerSink() { not isGuardedByInsecureFlag(this) } } diff --git a/java/ql/lib/semmle/code/java/security/IntentUriPermissionManipulation.qll b/java/ql/lib/semmle/code/java/security/IntentUriPermissionManipulation.qll index 4309af8b3c80..2f9470f2bb9a 100644 --- a/java/ql/lib/semmle/code/java/security/IntentUriPermissionManipulation.qll +++ b/java/ql/lib/semmle/code/java/security/IntentUriPermissionManipulation.qll @@ -6,6 +6,7 @@ import java private import semmle.code.java.controlflow.Guards private import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.dataflow.FlowSinks private import semmle.code.java.dataflow.TaintTracking private import semmle.code.java.frameworks.android.Android private import semmle.code.java.frameworks.android.Intent @@ -14,7 +15,7 @@ private import semmle.code.java.frameworks.android.Intent * A sink for Intent URI permission manipulation vulnerabilities in Android, * that is, method calls that return an Intent as the result of an Activity. */ -abstract class IntentUriPermissionManipulationSink extends DataFlow::Node { } +abstract class IntentUriPermissionManipulationSink extends ApiSinkNode { } /** * A sanitizer that makes sure that an Intent is safe to be returned to another Activity. diff --git a/java/ql/lib/semmle/code/java/security/JWT.qll b/java/ql/lib/semmle/code/java/security/JWT.qll index 183495d85652..5ba47072dc68 100644 --- a/java/ql/lib/semmle/code/java/security/JWT.qll +++ b/java/ql/lib/semmle/code/java/security/JWT.qll @@ -1,10 +1,11 @@ /** Provides classes for working with JSON Web Token (JWT) libraries. */ import java -private import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.dataflow.FlowSinks +private import semmle.code.java.dataflow.FlowSources /** A method access that assigns signing keys to a JWT parser. */ -class JwtParserWithInsecureParseSource extends DataFlow::Node { +class JwtParserWithInsecureParseSource extends ApiSourceNode { JwtParserWithInsecureParseSource() { exists(MethodCall ma, Method m | m.getDeclaringType().getAnAncestor() instanceof TypeJwtParser or @@ -24,7 +25,7 @@ class JwtParserWithInsecureParseSource extends DataFlow::Node { * the qualifier of a call to a `parse(token, handler)` method * where the `handler` is considered insecure. */ -class JwtParserWithInsecureParseSink extends DataFlow::Node { +class JwtParserWithInsecureParseSink extends ApiSinkNode { MethodCall insecureParseMa; JwtParserWithInsecureParseSink() { diff --git a/java/ql/lib/semmle/code/java/security/JndiInjection.qll b/java/ql/lib/semmle/code/java/security/JndiInjection.qll index d7282996057d..3df8d6df378e 100644 --- a/java/ql/lib/semmle/code/java/security/JndiInjection.qll +++ b/java/ql/lib/semmle/code/java/security/JndiInjection.qll @@ -3,11 +3,12 @@ import java private import semmle.code.java.dataflow.DataFlow private import semmle.code.java.dataflow.ExternalFlow +private import semmle.code.java.dataflow.FlowSinks private import semmle.code.java.frameworks.Jndi private import semmle.code.java.frameworks.SpringLdap /** A data flow sink for unvalidated user input that is used in JNDI lookup. */ -abstract class JndiInjectionSink extends DataFlow::Node { } +abstract class JndiInjectionSink extends ApiSinkNode { } /** A sanitizer for JNDI injection vulnerabilities. */ abstract class JndiInjectionSanitizer extends DataFlow::Node { } diff --git a/java/ql/lib/semmle/code/java/security/OgnlInjection.qll b/java/ql/lib/semmle/code/java/security/OgnlInjection.qll index d5297702bef4..37f31618fc32 100644 --- a/java/ql/lib/semmle/code/java/security/OgnlInjection.qll +++ b/java/ql/lib/semmle/code/java/security/OgnlInjection.qll @@ -2,6 +2,7 @@ import java private import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.dataflow.FlowSinks private import semmle.code.java.dataflow.ExternalFlow private import semmle.code.java.frameworks.MyBatis @@ -10,7 +11,7 @@ private import semmle.code.java.frameworks.MyBatis * * Extend this class to add your own OGNL injection sinks. */ -abstract class OgnlInjectionSink extends DataFlow::Node { } +abstract class OgnlInjectionSink extends ApiSinkNode { } /** * A unit class for adding additional taint steps. diff --git a/java/ql/lib/semmle/code/java/security/QueryInjection.qll b/java/ql/lib/semmle/code/java/security/QueryInjection.qll index aa92aa16a14c..df316155ba1a 100644 --- a/java/ql/lib/semmle/code/java/security/QueryInjection.qll +++ b/java/ql/lib/semmle/code/java/security/QueryInjection.qll @@ -5,9 +5,10 @@ import semmle.code.java.dataflow.DataFlow import semmle.code.java.frameworks.javaee.Persistence private import semmle.code.java.frameworks.MyBatis private import semmle.code.java.dataflow.ExternalFlow +private import semmle.code.java.dataflow.FlowSinks /** A sink for database query language injection vulnerabilities. */ -abstract class QueryInjectionSink extends DataFlow::Node { } +abstract class QueryInjectionSink extends ApiSinkNode { } /** * A unit class for adding additional taint steps. diff --git a/java/ql/lib/semmle/code/java/security/SensitiveResultReceiverQuery.qll b/java/ql/lib/semmle/code/java/security/SensitiveResultReceiverQuery.qll index 63d6d88d83cb..8269a42c5c25 100644 --- a/java/ql/lib/semmle/code/java/security/SensitiveResultReceiverQuery.qll +++ b/java/ql/lib/semmle/code/java/security/SensitiveResultReceiverQuery.qll @@ -4,6 +4,7 @@ import java import semmle.code.java.dataflow.TaintTracking import semmle.code.java.dataflow.FlowSources import semmle.code.java.security.SensitiveActions +private import semmle.code.java.dataflow.FlowSinks private class ResultReceiverSendCall extends MethodCall { ResultReceiverSendCall() { @@ -50,15 +51,22 @@ deprecated private class SensitiveResultReceiverConf extends TaintTracking::Conf } } -private module SensitiveResultReceiverConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node node) { node.asExpr() instanceof SensitiveExpr } - - predicate isSink(DataFlow::Node node) { +/** + * A sensitive result receiver sink node. + */ +private class SensitiveResultReceiverSink extends ApiSinkNode { + SensitiveResultReceiverSink() { exists(ResultReceiverSendCall call | untrustedResultReceiverSend(_, call) and - node.asExpr() = call.getSentData() + this.asExpr() = call.getSentData() ) } +} + +private module SensitiveResultReceiverConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { node.asExpr() instanceof SensitiveExpr } + + predicate isSink(DataFlow::Node node) { node instanceof SensitiveResultReceiverSink } predicate allowImplicitRead(DataFlow::Node n, DataFlow::ContentSet c) { isSink(n) and exists(c) } } diff --git a/java/ql/lib/semmle/code/java/security/SensitiveUiQuery.qll b/java/ql/lib/semmle/code/java/security/SensitiveUiQuery.qll index 38a5aeb93cf9..a7e76d0e2e31 100644 --- a/java/ql/lib/semmle/code/java/security/SensitiveUiQuery.qll +++ b/java/ql/lib/semmle/code/java/security/SensitiveUiQuery.qll @@ -2,6 +2,7 @@ import java private import semmle.code.java.dataflow.ExternalFlow +private import semmle.code.java.dataflow.FlowSinks private import semmle.code.java.dataflow.TaintTracking private import semmle.code.java.security.SensitiveActions private import semmle.code.java.frameworks.android.Layout @@ -53,16 +54,23 @@ private class MaskCall extends MethodCall { } } -/** A configuration for tracking sensitive information to text fields. */ -private module TextFieldTrackingConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SensitiveExpr } - - predicate isSink(DataFlow::Node sink) { +/** + * A text field sink node. + */ +private class TextFieldSink extends ApiSinkNode { + TextFieldSink() { exists(SetTextCall call | - sink.asExpr() = call.getStringArgument() and + this.asExpr() = call.getStringArgument() and not setTextCallIsMasked(call) ) } +} + +/** A configuration for tracking sensitive information to text fields. */ +private module TextFieldTrackingConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SensitiveExpr } + + predicate isSink(DataFlow::Node sink) { sink instanceof TextFieldSink } predicate isBarrier(DataFlow::Node node) { node instanceof SimpleTypeSanitizer } diff --git a/java/ql/lib/semmle/code/java/security/SpelInjection.qll b/java/ql/lib/semmle/code/java/security/SpelInjection.qll index 1aed2049afe4..13eb195eae46 100644 --- a/java/ql/lib/semmle/code/java/security/SpelInjection.qll +++ b/java/ql/lib/semmle/code/java/security/SpelInjection.qll @@ -2,10 +2,11 @@ import java private import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.dataflow.FlowSinks private import semmle.code.java.frameworks.spring.SpringExpression /** A data flow sink for unvalidated user input that is used to construct SpEL expressions. */ -abstract class SpelExpressionEvaluationSink extends DataFlow::ExprNode { } +abstract class SpelExpressionEvaluationSink extends ApiSinkNode, DataFlow::ExprNode { } /** * A unit class for adding additional taint steps. diff --git a/java/ql/lib/semmle/code/java/security/StackTraceExposureQuery.qll b/java/ql/lib/semmle/code/java/security/StackTraceExposureQuery.qll index dba9492d137b..1a2fe31e8794 100644 --- a/java/ql/lib/semmle/code/java/security/StackTraceExposureQuery.qll +++ b/java/ql/lib/semmle/code/java/security/StackTraceExposureQuery.qll @@ -1,8 +1,7 @@ /** Provides predicates to reason about exposure of stack-traces. */ import java -private import semmle.code.java.dataflow.DataFlow -private import semmle.code.java.dataflow.TaintTracking +private import semmle.code.java.dataflow.FlowSources private import semmle.code.java.security.InformationLeak /** @@ -19,7 +18,7 @@ private class PrintStackTraceMethod extends Method { } private module ServletWriterSourceToPrintStackTraceMethodFlowConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node src) { src.asExpr() instanceof XssVulnerableWriterSource } + predicate isSource(DataFlow::Node src) { src instanceof XssVulnerableWriterSourceNode } predicate isSink(DataFlow::Node sink) { exists(MethodCall ma | @@ -95,7 +94,10 @@ predicate stringifiedStackFlowsExternally(DataFlow::Node externalExpr, Expr stac ) } -private class GetMessageFlowSource extends DataFlow::Node { +/** + * A get message source node. + */ +private class GetMessageFlowSource extends ApiSourceNode { GetMessageFlowSource() { exists(Method method | this.asExpr().(MethodCall).getMethod() = method | method.hasName("getMessage") and diff --git a/java/ql/lib/semmle/code/java/security/TempDirLocalInformationDisclosureQuery.qll b/java/ql/lib/semmle/code/java/security/TempDirLocalInformationDisclosureQuery.qll index 843db3b5934b..f1ffcaecc515 100644 --- a/java/ql/lib/semmle/code/java/security/TempDirLocalInformationDisclosureQuery.qll +++ b/java/ql/lib/semmle/code/java/security/TempDirLocalInformationDisclosureQuery.qll @@ -1,6 +1,7 @@ /** Provides classes to reason about local information disclosure in a temporary directory. */ import java +private import semmle.code.java.dataflow.FlowSinks private import semmle.code.java.dataflow.TaintTracking private import semmle.code.java.os.OSCheck private import semmle.code.java.security.TempDirUtils @@ -29,7 +30,7 @@ private class MethodFileFileCreation extends MethodFileSystemFileCreation { /** * A dataflow node that creates a file or directory in the file system. */ -abstract private class FileCreationSink extends DataFlow::Node { } +abstract private class FileCreationSink extends ApiSinkNode { } /** * The qualifier of a call to one of `File`'s file-creating or directory-creating methods, diff --git a/java/ql/lib/semmle/code/java/security/UnsafeAndroidAccess.qll b/java/ql/lib/semmle/code/java/security/UnsafeAndroidAccess.qll index 499475cff3ec..afd3af221bed 100644 --- a/java/ql/lib/semmle/code/java/security/UnsafeAndroidAccess.qll +++ b/java/ql/lib/semmle/code/java/security/UnsafeAndroidAccess.qll @@ -4,6 +4,7 @@ import java private import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.dataflow.FlowSinks private import semmle.code.java.frameworks.android.WebView private import semmle.code.java.frameworks.kotlin.Kotlin @@ -12,7 +13,7 @@ private import semmle.code.java.frameworks.kotlin.Kotlin * * Extend this class to add your own Unsafe Resource Fetching sinks. */ -abstract class UrlResourceSink extends DataFlow::Node { +abstract class UrlResourceSink extends ApiSinkNode { /** * Gets a description of this vulnerability. */ diff --git a/java/ql/lib/semmle/code/java/security/UnsafeContentUriResolution.qll b/java/ql/lib/semmle/code/java/security/UnsafeContentUriResolution.qll index 5537add5a2ca..b19d06bbf88c 100644 --- a/java/ql/lib/semmle/code/java/security/UnsafeContentUriResolution.qll +++ b/java/ql/lib/semmle/code/java/security/UnsafeContentUriResolution.qll @@ -1,13 +1,14 @@ /** Provides classes to reason about vulnerabilites related to content URIs. */ import java +private import semmle.code.java.dataflow.FlowSinks private import semmle.code.java.dataflow.TaintTracking private import semmle.code.java.frameworks.android.Android private import semmle.code.java.security.PathSanitizer private import semmle.code.java.security.Sanitizers /** A URI that gets resolved by a `ContentResolver`. */ -abstract class ContentUriResolutionSink extends DataFlow::Node { } +abstract class ContentUriResolutionSink extends ApiSinkNode { } /** A sanitizer for content URIs. */ abstract class ContentUriResolutionSanitizer extends DataFlow::Node { } diff --git a/java/ql/lib/semmle/code/java/security/UnsafeDeserializationQuery.qll b/java/ql/lib/semmle/code/java/security/UnsafeDeserializationQuery.qll index 272c483f7a21..734ad4c89fe6 100644 --- a/java/ql/lib/semmle/code/java/security/UnsafeDeserializationQuery.qll +++ b/java/ql/lib/semmle/code/java/security/UnsafeDeserializationQuery.qll @@ -3,6 +3,7 @@ */ import semmle.code.java.dataflow.FlowSources +private import semmle.code.java.dataflow.FlowSinks private import semmle.code.java.dataflow.TaintTracking2 private import semmle.code.java.dispatch.VirtualDispatch private import semmle.code.java.frameworks.Kryo @@ -235,7 +236,7 @@ predicate unsafeDeserialization(MethodCall ma, Expr sink) { } /** A sink for unsafe deserialization. */ -class UnsafeDeserializationSink extends DataFlow::ExprNode { +class UnsafeDeserializationSink extends ApiSinkNode, DataFlow::ExprNode { UnsafeDeserializationSink() { unsafeDeserialization(_, this.getExpr()) } /** Gets a call that triggers unsafe deserialization. */ diff --git a/java/ql/lib/semmle/code/java/security/UrlRedirect.qll b/java/ql/lib/semmle/code/java/security/UrlRedirect.qll index e806905c1674..02f66e3f0e95 100644 --- a/java/ql/lib/semmle/code/java/security/UrlRedirect.qll +++ b/java/ql/lib/semmle/code/java/security/UrlRedirect.qll @@ -2,14 +2,15 @@ import java import semmle.code.java.dataflow.DataFlow -private import semmle.code.java.dataflow.ExternalFlow import semmle.code.java.frameworks.Servlets import semmle.code.java.frameworks.ApacheHttp +private import semmle.code.java.dataflow.ExternalFlow +private import semmle.code.java.dataflow.FlowSinks private import semmle.code.java.frameworks.JaxWS private import semmle.code.java.security.RequestForgery /** A URL redirection sink. */ -abstract class UrlRedirectSink extends DataFlow::Node { } +abstract class UrlRedirectSink extends ApiSinkNode { } /** A URL redirection sanitizer. */ abstract class UrlRedirectSanitizer extends DataFlow::Node { } diff --git a/java/ql/lib/semmle/code/java/security/WebviewDebuggingEnabledQuery.qll b/java/ql/lib/semmle/code/java/security/WebviewDebuggingEnabledQuery.qll index 00b9c715f752..f10b0132b5ac 100644 --- a/java/ql/lib/semmle/code/java/security/WebviewDebuggingEnabledQuery.qll +++ b/java/ql/lib/semmle/code/java/security/WebviewDebuggingEnabledQuery.qll @@ -4,6 +4,7 @@ import java import semmle.code.java.dataflow.DataFlow import semmle.code.java.controlflow.Guards import semmle.code.java.security.SecurityTests +private import semmle.code.java.dataflow.FlowSinks /** Holds if `ex` looks like a check that this is a debug build. */ private predicate isDebugCheck(Expr ex) { @@ -44,18 +45,25 @@ deprecated class WebviewDebugEnabledConfig extends DataFlow::Configuration { } } +/** + * A webview debug sink node. + */ +private class WebviewDebugSink extends ApiSinkNode { + WebviewDebugSink() { + exists(MethodCall ma | + ma.getMethod().hasQualifiedName("android.webkit", "WebView", "setWebContentsDebuggingEnabled") and + this.asExpr() = ma.getArgument(0) + ) + } +} + /** A configuration to find instances of `setWebContentDebuggingEnabled` called with `true` values. */ module WebviewDebugEnabledConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node node) { node.asExpr().(BooleanLiteral).getBooleanValue() = true } - predicate isSink(DataFlow::Node node) { - exists(MethodCall ma | - ma.getMethod().hasQualifiedName("android.webkit", "WebView", "setWebContentsDebuggingEnabled") and - node.asExpr() = ma.getArgument(0) - ) - } + predicate isSink(DataFlow::Node node) { node instanceof WebviewDebugSink } predicate isBarrier(DataFlow::Node node) { exists(Guard debug | isDebugCheck(debug) and debug.controls(node.asExpr().getBasicBlock(), _)) diff --git a/java/ql/lib/semmle/code/java/security/XPath.qll b/java/ql/lib/semmle/code/java/security/XPath.qll index 573d6530b330..c2992fdc272a 100644 --- a/java/ql/lib/semmle/code/java/security/XPath.qll +++ b/java/ql/lib/semmle/code/java/security/XPath.qll @@ -3,12 +3,13 @@ import java import semmle.code.java.dataflow.DataFlow private import semmle.code.java.dataflow.ExternalFlow +private import semmle.code.java.dataflow.FlowSinks /** * A sink that represents a method that interprets XPath expressions. * Extend this class to add your own XPath Injection sinks. */ -abstract class XPathInjectionSink extends DataFlow::Node { } +abstract class XPathInjectionSink extends ApiSinkNode { } /** A default sink representing methods susceptible to XPath Injection attacks. */ private class DefaultXPathInjectionSink extends XPathInjectionSink { diff --git a/java/ql/lib/semmle/code/java/security/XSS.qll b/java/ql/lib/semmle/code/java/security/XSS.qll index 9edee5823bfc..777e5fae0627 100644 --- a/java/ql/lib/semmle/code/java/security/XSS.qll +++ b/java/ql/lib/semmle/code/java/security/XSS.qll @@ -10,9 +10,11 @@ private import semmle.code.java.frameworks.hudson.Hudson import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.TaintTracking private import semmle.code.java.dataflow.ExternalFlow +private import semmle.code.java.dataflow.FlowSources +private import semmle.code.java.dataflow.FlowSinks /** A sink that represent a method that outputs data without applying contextual output encoding. */ -abstract class XssSink extends DataFlow::Node { } +abstract class XssSink extends ApiSinkNode { } /** A sanitizer that neutralizes dangerous characters that can be used to perform a XSS attack. */ abstract class XssSanitizer extends DataFlow::Node { } @@ -62,7 +64,7 @@ private class DefaultXssSanitizer extends XssSanitizer { /** A configuration that tracks data from a servlet writer to an output method. */ private module XssVulnerableWriterSourceToWritingMethodFlowConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node src) { src.asExpr() instanceof XssVulnerableWriterSource } + predicate isSource(DataFlow::Node src) { src instanceof XssVulnerableWriterSourceNode } predicate isSink(DataFlow::Node sink) { exists(MethodCall ma | @@ -105,6 +107,13 @@ class XssVulnerableWriterSource extends MethodCall { } } +/** + * A xss vulnerable writer source node. + */ +class XssVulnerableWriterSourceNode extends ApiSourceNode { + XssVulnerableWriterSourceNode() { this.asExpr() instanceof XssVulnerableWriterSource } +} + /** * Holds if `s` is an HTTP Content-Type vulnerable to XSS. */ diff --git a/java/ql/lib/semmle/code/java/security/ZipSlipQuery.qll b/java/ql/lib/semmle/code/java/security/ZipSlipQuery.qll index 7ba99a31e268..75e2f7000c55 100644 --- a/java/ql/lib/semmle/code/java/security/ZipSlipQuery.qll +++ b/java/ql/lib/semmle/code/java/security/ZipSlipQuery.qll @@ -4,6 +4,7 @@ import java import semmle.code.java.dataflow.TaintTracking import semmle.code.java.security.PathSanitizer private import semmle.code.java.dataflow.ExternalFlow +private import semmle.code.java.dataflow.FlowSources private import semmle.code.java.security.PathCreation /** @@ -21,13 +22,20 @@ private class ArchiveEntryNameMethod extends Method { } } +/** + * An entry name method source node. + */ +private class ArchiveEntryNameMethodSource extends ApiSourceNode { + ArchiveEntryNameMethodSource() { + this.asExpr().(MethodCall).getMethod() instanceof ArchiveEntryNameMethod + } +} + /** * A taint-tracking configuration for reasoning about unsafe zip file extraction. */ module ZipSlipConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { - source.asExpr().(MethodCall).getMethod() instanceof ArchiveEntryNameMethod - } + predicate isSource(DataFlow::Node source) { source instanceof ArchiveEntryNameMethodSource } predicate isSink(DataFlow::Node sink) { sink instanceof FileCreationSink } diff --git a/java/ql/src/Telemetry/ExternalApi.qll b/java/ql/src/Telemetry/ExternalApi.qll index 47527dfbb46a..a548593c36a3 100644 --- a/java/ql/src/Telemetry/ExternalApi.qll +++ b/java/ql/src/Telemetry/ExternalApi.qll @@ -1,6 +1,8 @@ /** Provides classes and predicates related to handling APIs from external libraries. */ private import java +private import semmle.code.java.dataflow.ApiSources as ApiSources +private import semmle.code.java.dataflow.ApiSinks as ApiSinks private import semmle.code.java.dataflow.DataFlow private import semmle.code.java.dataflow.ExternalFlow private import semmle.code.java.dataflow.FlowSources @@ -69,13 +71,11 @@ class ExternalApi extends Callable { } pragma[nomagic] - predicate isSource() { - this.getAnOutput() instanceof RemoteFlowSource or sourceNode(this.getAnOutput(), _) - } + predicate isSource() { this.getAnOutput() instanceof ApiSources::SourceNode } /** Holds if this API is a known sink. */ pragma[nomagic] - predicate isSink() { sinkNode(this.getAnInput(), _) } + predicate isSink() { this.getAnInput() instanceof ApiSinks::SinkNode } /** Holds if this API is a known neutral. */ pragma[nomagic] diff --git a/java/ql/test/query-tests/Telemetry/SupportedExternalApis/SupportedExternalApis.expected b/java/ql/test/query-tests/Telemetry/SupportedExternalApis/SupportedExternalApis.expected index 68dc7169533f..d08da075f3fd 100644 --- a/java/ql/test/query-tests/Telemetry/SupportedExternalApis/SupportedExternalApis.expected +++ b/java/ql/test/query-tests/Telemetry/SupportedExternalApis/SupportedExternalApis.expected @@ -13,4 +13,5 @@ | java.util.Map#put(Object,Object) | 1 | | java.util.Map$Entry#getKey() | 1 | | java.util.Set#iterator() | 1 | +| javax.servlet.http.HttpServletResponse#sendRedirect(String) | 1 | | org.apache.commons.io.FileUtils#deleteDirectory(File) | 1 | diff --git a/java/ql/test/query-tests/Telemetry/SupportedExternalApis/SupportedExternalApis.java b/java/ql/test/query-tests/Telemetry/SupportedExternalApis/SupportedExternalApis.java index 880d183aecdb..8fd5f679824e 100644 --- a/java/ql/test/query-tests/Telemetry/SupportedExternalApis/SupportedExternalApis.java +++ b/java/ql/test/query-tests/Telemetry/SupportedExternalApis/SupportedExternalApis.java @@ -1,10 +1,13 @@ +import java.io.File; +import java.io.FileWriter; +import java.io.InputStream; +import java.io.IOException; +import java.net.URL; import java.time.Duration; import java.util.HashMap; import java.util.Map; -import java.io.InputStream; -import java.net.URL; -import java.io.File; -import java.io.FileWriter; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.ServletException; import org.apache.commons.io.FileUtils; class SupportedExternalApis { @@ -30,4 +33,8 @@ public static void main(String[] args) throws Exception { file.compareTo(file); // supported neutral sink (compareTo) } + + public static void doSendRedirect(HttpServletResponse req) throws ServletException, IOException { + req.sendRedirect("myredirect"); // supported sink (sendRedirect) + } }